Make WordPress Core


Ignore:
Timestamp:
09/13/2017 06:07:48 AM (8 years ago)
Author:
westonruter
Message:

Editor: Add CodeMirror-powered code editor with syntax highlighting, linting, and auto-completion.

  • Code editor is integrated into the Theme/Plugin Editor, Additional CSS in Customizer, and Custom HTML widget. Code editor is not yet integrated into the post editor, and it may not be until accessibility concerns are addressed.
  • The CodeMirror component in the Custom HTML widget is integrated in a similar way to TinyMCE being integrated into the Text widget, adopting the same approach for integrating dynamic JavaScript-initialized fields.
  • Linting is performed for JS, CSS, HTML, and JSON via JSHint, CSSLint, HTMLHint, and JSONLint respectively. Linting is not yet supported for PHP.
  • When user lacks unfiltered_html the capability, the Custom HTML widget will report any Kses-invalid elements and attributes as errors via a custom Kses rule for HTMLHint.
  • When linting errors are detected, the user will be prevented from saving the code until the errors are fixed, reducing instances of broken websites.
  • The placeholder value is removed from Custom CSS in favor of a fleshed-out section description which now auto-expands when the CSS field is empty. See #39892.
  • The CodeMirror library is included as wp.CodeMirror to prevent conflicts with any existing CodeMirror global.
  • An wp.codeEditor.initialize() API in JS is provided to convert a textarea into CodeMirror, with a wp_enqueue_code_editor() function in PHP to manage enqueueing the assets and settings needed to edit a given type of code.
  • A user preference is added to manage whether or not "syntax highlighting" is enabled. The feature is opt-out, being enabled by default.
  • Allowed file extensions in the theme and plugin editors have been updated to include formats which CodeMirror has modes for: conf, css, diff, patch, html, htm, http, js, json, jsx, less, md, php, phtml, php3, php4, php5, php7, phps, scss, sass, sh, bash, sql, svg, xml, yml, yaml, txt.

Props westonruter, georgestephanis, obenland, melchoyce, pixolin, mizejewski, michelleweber, afercia, grahamarmfield, samikeijonen, rianrietveld, iseulde.
See #38707.
Fixes #12423, #39892.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/customize/class-wp-customize-custom-css-setting.php

    r41162 r41376  
    149149     * Notifications are rendered when the customizer state is saved.
    150150     *
    151      * @todo There are cases where valid CSS can be incorrectly marked as invalid when strings or comments include balancing characters. To fix, CSS tokenization needs to be used.
    152      *
    153151     * @since 4.7.0
     152     * @since 4.9.0 Checking for balanced characters has been moved client-side via linting in code editor.
    154153     *
    155154     * @param string $css The input string.
     
    161160        if ( preg_match( '#</?\w+#', $css ) ) {
    162161            $validity->add( 'illegal_markup', __( 'Markup is not allowed in CSS.' ) );
    163         }
    164 
    165         $imbalanced = false;
    166 
    167         // Make sure that there is a closing brace for each opening brace.
    168         if ( ! $this->validate_balanced_characters( '{', '}', $css ) ) {
    169             $validity->add( 'imbalanced_curly_brackets', sprintf(
    170                 /* translators: 1: {}, 2: }, 3: { */
    171                 __( 'Your curly brackets %1$s are imbalanced. Make sure there is a closing %2$s for every opening %3$s.' ),
    172                 '<code>{}</code>',
    173                 '<code>}</code>',
    174                 '<code>{</code>'
    175             ) );
    176             $imbalanced = true;
    177         }
    178 
    179         // Ensure brackets are balanced.
    180         if ( ! $this->validate_balanced_characters( '[', ']', $css ) ) {
    181             $validity->add( 'imbalanced_braces', sprintf(
    182                 /* translators: 1: [], 2: ], 3: [ */
    183                 __( 'Your brackets %1$s are imbalanced. Make sure there is a closing %2$s for every opening %3$s.' ),
    184                 '<code>[]</code>',
    185                 '<code>]</code>',
    186                 '<code>[</code>'
    187             ) );
    188             $imbalanced = true;
    189         }
    190 
    191         // Ensure parentheses are balanced.
    192         if ( ! $this->validate_balanced_characters( '(', ')', $css ) ) {
    193             $validity->add( 'imbalanced_parentheses', sprintf(
    194                 /* translators: 1: (), 2: ), 3: ( */
    195                 __( 'Your parentheses %1$s are imbalanced. Make sure there is a closing %2$s for every opening %3$s.' ),
    196                 '<code>()</code>',
    197                 '<code>)</code>',
    198                 '<code>(</code>'
    199             ) );
    200             $imbalanced = true;
    201         }
    202 
    203         // Ensure double quotes are equal.
    204         if ( ! $this->validate_equal_characters( '"', $css ) ) {
    205             $validity->add( 'unequal_double_quotes', sprintf(
    206                 /* translators: 1: " (double quote) */
    207                 __( 'Your double quotes %1$s are uneven. Make sure there is a closing %1$s for every opening %1$s.' ),
    208                 '<code>"</code>'
    209             ) );
    210             $imbalanced = true;
    211         }
    212 
    213         /*
    214          * Make sure any code comments are closed properly.
    215          *
    216          * The first check could miss stray an unpaired comment closing figure, so if
    217          * The number appears to be balanced, then check for equal numbers
    218          * of opening/closing comment figures.
    219          *
    220          * Although it may initially appear redundant, we use the first method
    221          * to give more specific feedback to the user.
    222          */
    223         $unclosed_comment_count = $this->validate_count_unclosed_comments( $css );
    224         if ( 0 < $unclosed_comment_count ) {
    225             $validity->add( 'unclosed_comment', sprintf(
    226                 /* translators: 1: number of unclosed comments, 2: *​/ */
    227                 _n(
    228                     'There is %1$s unclosed code comment. Close each comment with %2$s.',
    229                     'There are %1$s unclosed code comments. Close each comment with %2$s.',
    230                     $unclosed_comment_count
    231                 ),
    232                 $unclosed_comment_count,
    233                 '<code>*/</code>'
    234             ) );
    235             $imbalanced = true;
    236         } elseif ( ! $this->validate_balanced_characters( '/*', '*/', $css ) ) {
    237             $validity->add( 'imbalanced_comments', sprintf(
    238                 /* translators: 1: *​/, 2: /​* */
    239                 __( 'There is an extra %1$s, indicating an end to a comment. Be sure that there is an opening %2$s for every closing %1$s.' ),
    240                 '<code>*/</code>',
    241                 '<code>/*</code>'
    242             ) );
    243             $imbalanced = true;
    244         }
    245         if ( $imbalanced && $this->is_possible_content_error( $css ) ) {
    246             $validity->add( 'possible_false_positive', sprintf(
    247                 /* translators: %s: content: ""; */
    248                 __( 'Imbalanced/unclosed character errors can be caused by %s declarations. You may need to remove this or add it to a custom CSS file.' ),
    249                 '<code>content: "";</code>'
    250             ) );
    251162        }
    252163
     
    286197        return $post_id;
    287198    }
    288 
    289     /**
    290      * Ensure there are a balanced number of paired characters.
    291      *
    292      * This is used to check that the number of opening and closing
    293      * characters is equal.
    294      *
    295      * For instance, there should be an equal number of braces ("{", "}")
    296      * in the CSS.
    297      *
    298      * @since 4.7.0
    299      *
    300      * @param string $opening_char The opening character.
    301      * @param string $closing_char The closing character.
    302      * @param string $css The CSS input string.
    303      *
    304      * @return bool
    305      */
    306     private function validate_balanced_characters( $opening_char, $closing_char, $css ) {
    307         return substr_count( $css, $opening_char ) === substr_count( $css, $closing_char );
    308     }
    309 
    310     /**
    311      * Ensure there are an even number of paired characters.
    312      *
    313      * This is used to check that the number of a specific
    314      * character is even.
    315      *
    316      * For instance, there should be an even number of double quotes
    317      * in the CSS.
    318      *
    319      * @since 4.7.0
    320      *
    321      * @param string $char A character.
    322      * @param string $css The CSS input string.
    323      * @return bool Equality.
    324      */
    325     private function validate_equal_characters( $char, $css ) {
    326         $char_count = substr_count( $css, $char );
    327         return ( 0 === $char_count % 2 );
    328     }
    329 
    330     /**
    331      * Count unclosed CSS Comments.
    332      *
    333      * Used during validation.
    334      *
    335      * @see self::validate()
    336      *
    337      * @since 4.7.0
    338      *
    339      * @param string $css The CSS input string.
    340      * @return int Count.
    341      */
    342     private function validate_count_unclosed_comments( $css ) {
    343         $count = 0;
    344         $comments = explode( '/*', $css );
    345 
    346         if ( ! is_array( $comments ) || ( 1 >= count( $comments ) ) ) {
    347             return $count;
    348         }
    349 
    350         unset( $comments[0] ); // The first item is before the first comment.
    351         foreach ( $comments as $comment ) {
    352             if ( false === strpos( $comment, '*/' ) ) {
    353                 $count++;
    354             }
    355         }
    356         return $count;
    357     }
    358 
    359     /**
    360      * Find "content:" within a string.
    361      *
    362      * Imbalanced/Unclosed validation errors may be caused
    363      * when a character is used in a "content:" declaration.
    364      *
    365      * This function is used to detect if this is a possible
    366      * cause of the validation error, so that if it is,
    367      * a notification may be added to the Validation Errors.
    368      *
    369      * Example:
    370      * .element::before {
    371      *   content: "(\"";
    372      * }
    373      * .element::after {
    374      *   content: "\")";
    375      * }
    376      *
    377      * Using ! empty() because strpos() may return non-boolean values
    378      * that evaluate to false. This would be problematic when
    379      * using a strict "false === strpos()" comparison.
    380      *
    381      * @since 4.7.0
    382      *
    383      * @param string $css The CSS input string.
    384      * @return bool
    385      */
    386     private function is_possible_content_error( $css ) {
    387         $found = preg_match( '/\bcontent\s*:/', $css );
    388         if ( ! empty( $found ) ) {
    389             return true;
    390         }
    391         return false;
    392     }
    393199}
Note: See TracChangeset for help on using the changeset viewer.