Changeset 184000 in webkit
- Timestamp:
- May 8, 2015 11:37:49 AM (9 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r183947 r184000 1 2015-05-08 Tobias Reiss <tobi+webkit@basecode.de> 2 3 Web Inspector: Styles sidebar editing with incomplete property looks poor in UI 4 https://bugs.webkit.org/show_bug.cgi?id=141692 5 6 Reviewed by Timothy Hatcher. 7 8 Add "css-rule" Formatter that breaks CSS declarations into multiple lines, 9 keeps comments and invalid styles and adds whitespace. 10 11 * Tools/PrettyPrinting/css-rule-tests/*.css: Added. 12 Add test cases. 13 14 * Tools/PrettyPrinting/index.html: 15 Enable Test setup to be able to run "css-rule" Formatter tests. 16 17 * UserInterface/Controllers/Formatter.js: 18 (Formatter.prototype._handleToken): 19 * UserInterface/Controllers/FormatterContentBuilder.js: 20 (FormatterContentBuilder.prototype.removeLastNewline): 21 (FormatterContentBuilder.prototype.removeLastWhitespace): 22 (FormatterContentBuilder.prototype._popFormattedContent): 23 (FormatterContentBuilder.prototype._popNewLine): Deleted. 24 * UserInterface/Views/CSSStyleDeclarationTextEditor.js: 25 (WebInspector.CSSStyleDeclarationTextEditor.prototype._formattedContentFromEditor): 26 (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.set this): 27 (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.get this): 28 (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update): 29 (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent): 30 (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.countNewLineCharacters): Deleted. 31 (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.else): Deleted. 32 * UserInterface/Views/CodeMirrorFormatters.js: 33 1 34 2015-05-07 Joseph Pecoraro <pecoraro@apple.com> 2 35 -
trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html
r183043 r184000 9 9 <script src="../../UserInterface/External/CodeMirror/javascript.js"></script> 10 10 <script src="../../UserInterface/External/CodeMirror/css.js"></script> 11 <script src="../../UserInterface/Base/WebInspector.js"></script> 12 <script src="../../UserInterface/Views/CodeMirrorAdditions.js"></script> 11 13 <script src="../../UserInterface/Controllers/Formatter.js"></script> 12 14 <script src="FormatterDebug.js"></script> … … 22 24 <option selected value="text/javascript">JavaScript</option> 23 25 <option value="text/css">CSS</option> 26 <option value="css-rule">CSS-Rule</option> 24 27 </select> 25 28 <button id="populate">Populate</button> … … 231 234 if (modePicker.value === "text/javascript") 232 235 runJavaScriptTests(completedCallback); 236 else if (modePicker.value === "css-rule") 237 runCssRuleTests(completedCallback); 233 238 else 234 239 runCSSTests(completedCallback); … … 239 244 "js-tests/single-statement-blocks.js", 240 245 "js-tests/switch-case-default.js", 246 ]); 247 } 248 function runCssRuleTests(callback) { 249 _runTests(callback, [ 250 "css-rule-tests/invalid-property-is-not-removed.css", 251 "css-rule-tests/remove-whitespace-before-colon.css", 252 "css-rule-tests/remove-whitespace-before-semicolon.css", 253 "css-rule-tests/remove-whitespace-before-property.css", 254 "css-rule-tests/remove-whitespace-before-prefixed-property.css", 255 "css-rule-tests/remove-whitespace-before-invalid-property.css", 256 "css-rule-tests/remove-whitespace-before-comment.css", 257 "css-rule-tests/split-comment-followed-by-property.css", 258 "css-rule-tests/split-comment-followed-by-prefixed-property.css", 259 "css-rule-tests/split-comment-followed-by-invalid-property.css", 260 "css-rule-tests/split-comment-followed-by-comment.css", 261 "css-rule-tests/split-property-followed-by-property.css", 262 "css-rule-tests/split-property-followed-by-prefixed-property.css", 263 "css-rule-tests/split-property-followed-by-invalid-property.css", 264 "css-rule-tests/split-property-followed-by-comment.css", 265 "css-rule-tests/split-invalid-property-followed-by-property.css", 266 "css-rule-tests/split-invalid-property-followed-by-prefixed-property.css", 267 "css-rule-tests/split-invalid-property-followed-by-invalid-property.css", 268 "css-rule-tests/split-invalid-property-followed-by-comment.css", 269 "css-rule-tests/split-property-without-semicolon-followed-by-comment-and-property.css", 270 "css-rule-tests/add-whitespace-after-colon.css", 271 "css-rule-tests/add-whitespace-after-comma.css", 272 "css-rule-tests/do-not-append-semicolon.css", 273 "css-rule-tests/keep-prefixed-value.css", 241 274 ]); 242 275 } -
trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js
r181844 r184000 90 90 this._builder.appendNewline(); 91 91 92 // Whitespace. Collapse to a single space.92 // Whitespace. Remove all spaces or collapse to a single space. 93 93 if (isWhiteSpace) { 94 94 this._builder.appendSpace(); … … 101 101 if (mode.modifyStateForTokenPre) 102 102 mode.modifyStateForTokenPre(this._lastToken, this._lastContent, token, state, content, isComment); 103 104 // Should we remove the last whitespace? 105 if (this._builder.lastTokenWasWhitespace && mode.removeLastWhitespace(this._lastToken, this._lastContent, token, state, content, isComment)) 106 this._builder.removeLastWhitespace(); 103 107 104 108 // Should we remove the last newline? -
trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js
r181844 r184000 1 1 /* 2 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * Copyright (C) 2015 Tobias Reiss <tobi+webkit@basecode.de> 3 4 * 4 5 * Redistribution and use in source and binary forms, with or without … … 138 139 console.assert(this._formattedContent.lastValue === "\n"); 139 140 if (this.lastTokenWasNewline) { 140 this._popNewLine(); 141 this._popFormattedContent(); 142 this._formattedLineEndings.pop(); 141 143 this._startOfLine = false; 142 144 this.lastTokenWasNewline = false; … … 145 147 } 146 148 149 removeLastWhitespace() 150 { 151 console.assert(this.lastTokenWasWhitespace); 152 console.assert(this._formattedContent.lastValue === " "); 153 if (this.lastTokenWasWhitespace) { 154 this._popFormattedContent(); 155 // No need to worry about `_startOfLine` and `lastTokenWasNewline` 156 // because `appendSpace` takes care of not adding whitespace 157 // to the beginning of a line. 158 this.lastTokenWasWhitespace = false; 159 } 160 } 161 147 162 indent() 148 163 { … … 171 186 // Private 172 187 173 _pop NewLine()188 _popFormattedContent() 174 189 { 175 190 var removed = this._formattedContent.pop(); 176 191 this._formattedContentLength -= removed.length; 177 this._formattedLineEndings.pop();178 192 } 179 193 -
trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js
r183331 r184000 1 1 /* 2 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * Copyright (C) 2015 Tobias Reiss <tobi+webkit@basecode.de> 3 4 * 4 5 * Redistribution and use in source and binary forms, with or without … … 777 778 } 778 779 780 _formattedContentFromEditor() 781 { 782 var mapping = {original: [0], formatted: [0]}; 783 // FIXME: <rdar://problem/10593948> Provide a way to change the tab width in the Web Inspector 784 var indentString = " "; 785 var builder = new FormatterContentBuilder(mapping, [], [], 0, 0, indentString); 786 var formatter = new Formatter(this._codeMirror, builder); 787 var start = {line: 0, ch: 0}; 788 var end = {line: this._codeMirror.lineCount() - 1}; 789 formatter.format(start, end); 790 791 return builder.formattedContent.trim(); 792 } 793 779 794 _resetContent() 780 795 { … … 817 832 var selectionAnchor = this._codeMirror.getCursor("anchor"); 818 833 var selectionHead = this._codeMirror.getCursor("head"); 819 820 function countNewLineCharacters(text) 821 { 822 var matches = text.match(/\n/g); 823 return matches ? matches.length : 0; 834 var isEditorReadOnly = this._codeMirror.getOption("readOnly"); 835 var styleText = this._style.text.trim(); 836 var findWhitespace = /\s+/g; 837 838 // Only format non-empty styles. Keep in mind that styleText is always empty 839 // for "readOnly" Editors. But prepare Checkbox placeholders in any case. 840 // Because that will indent the cursor when the User starts typing. 841 if (!styleText && !isEditorReadOnly) { 842 this._markLinesWithCheckboxPlaceholder(); 843 return; 824 844 } 825 845 826 var styleText = this._style.text; 827 828 // Pretty print the content if there are more properties than there are lines. 829 // This could be an option exposed to the user; however, it is almost always 830 // desired in this case. 831 832 if (styleText && this._style.visibleProperties.length <= countNewLineCharacters(styleText.trim()) + 1) { 833 // This style has formatted text content, so use it for a high-fidelity experience. 834 835 var prefixWhitespaceMatch = styleText.match(/^[ \t]*\n/); 836 this._prefixWhitespace = prefixWhitespaceMatch ? prefixWhitespaceMatch[0] : ""; 837 838 var suffixWhitespaceMatch = styleText.match(/\n[ \t]*$/); 839 this._suffixWhitespace = suffixWhitespaceMatch ? suffixWhitespaceMatch[0] : ""; 840 841 this._codeMirror.setValue(styleText); 842 843 if (this._prefixWhitespace) 844 this._codeMirror.replaceRange("", {line: 0, ch: 0}, {line: 1, ch: 0}); 845 846 if (this._suffixWhitespace) { 847 var lineCount = this._codeMirror.lineCount(); 848 this._codeMirror.replaceRange("", {line: lineCount - 2}, {line: lineCount - 1}); 846 // Set non-optimized, valid and invalid styles in preparation for the Formatter. 847 // Set empty string in case of readonly styles. 848 this._codeMirror.setValue(styleText); 849 850 if (isEditorReadOnly) { 851 var lineNumber = 0; 852 this._iterateOverProperties(false, function(property) { 853 var from = {line: lineNumber, ch: 0}; 854 var to = {line: lineNumber}; 855 // Readonly properties are pretty printed by `synthesizedText` and not the Formatter. 856 this._codeMirror.replaceRange((lineNumber ? "\n" : "") + property.synthesizedText, from); 857 this._createTextMarkerForPropertyIfNeeded(from, to, property); 858 lineNumber++; 859 }); 860 861 return; 862 } 863 864 // Now the Formatter pretty prints the styles. 865 this._codeMirror.setValue(this._formattedContentFromEditor()); 866 867 // We need to workaround the fact that... 868 // 1) `this._style.properties` only holds valid CSSProperty instances but not 869 // comments and invalid properties like `color;`. 870 // 2) `_createTextMarkerForPropertyIfNeeded` relies on CSSProperty instances. 871 var cssPropertiesMap = new Map(); 872 this._iterateOverProperties(false, function(cssProperty) { 873 cssPropertiesMap.set(cssProperty.text.replace(findWhitespace, ""), cssProperty); 874 }); 875 876 // Go through the Editor line by line and create TextMarker when a 877 // CSSProperty instance for that property exists. If not, then don't create a TextMarker. 878 this._codeMirror.eachLine(function(lineHandler) { 879 var lineNumber = lineHandler.lineNo(); 880 var lineContentSansWhitespace = lineHandler.text.replace(findWhitespace, ""); 881 if (cssPropertiesMap.has(lineContentSansWhitespace)) { 882 var from = {line: lineNumber, ch: 0}; 883 var to = {line: lineNumber}; 884 this._createTextMarkerForPropertyIfNeeded(from, to, cssPropertiesMap.get(lineContentSansWhitespace)); 849 885 } 850 851 this._linePrefixWhitespace = ""; 852 853 var linesToStrip = []; 854 855 // Remember the whitespace so it can be restored on commit. 856 var lineCount = this._codeMirror.lineCount(); 857 for (var i = 0; i < lineCount; ++i) { 858 var lineContent = this._codeMirror.getLine(i); 859 var prefixWhitespaceMatch = lineContent.match(/^\s+/); 860 861 // If there is no prefix whitespace (except for empty lines) then the prefix 862 // whitespace of all other lines will be retained as is. Update markers and return. 863 if (!prefixWhitespaceMatch) { 864 if (!lineContent) 865 continue; 866 this._linePrefixWhitespace = ""; 867 this._updateTextMarkers(true); 868 return; 869 } 870 871 linesToStrip.push(i); 872 873 // Only remember the shortest whitespace so we don't loose any of the 874 // original author's whitespace if their indentation lengths differed. 875 // Using the shortest also makes the adjustment work in _updateTextMarkers. 876 877 // FIXME: This messes up if there is a mix of spaces and tabs. A tab 878 // is treated the same as a space when prefix whitespace is omitted, 879 // so if the shortest prefixed whitespace is, say, two tab characters, 880 // lines that begin with four spaces will only have a two space indent. 881 if (!this._linePrefixWhitespace || prefixWhitespaceMatch[0].length < this._linePrefixWhitespace.length) 882 this._linePrefixWhitespace = prefixWhitespaceMatch[0]; 883 } 884 885 // Strip the whitespace from the beginning of each line. 886 for (var i = 0; i < linesToStrip.length; ++i) { 887 var lineNumber = linesToStrip[i]; 888 var from = {line: lineNumber, ch: 0}; 889 var to = {line: lineNumber, ch: this._linePrefixWhitespace.length}; 890 this._codeMirror.replaceRange("", from, to); 891 } 892 893 // Update all the text markers. 894 this._updateTextMarkers(true); 895 } else { 896 // This style does not have text content or it is minified, so we want to synthesize the text content. 897 898 this._prefixWhitespace = ""; 899 this._suffixWhitespace = ""; 900 this._linePrefixWhitespace = ""; 901 902 this._codeMirror.setValue(""); 903 904 var lineNumber = 0; 905 906 // Iterate only visible properties if we have original style text. That way we known we only synthesize 907 // what was originaly in the style text. 908 this._iterateOverProperties(styleText ? true : false, function(property) { 909 // Some property text can have line breaks, so consider that in the ranges below. 910 var propertyText = property.synthesizedText; 911 var propertyLineCount = countNewLineCharacters(propertyText); 912 913 var from = {line: lineNumber, ch: 0}; 914 var to = {line: lineNumber + propertyLineCount}; 915 916 this._codeMirror.replaceRange((lineNumber ? "\n" : "") + propertyText, from); 917 this._createTextMarkerForPropertyIfNeeded(from, to, property); 918 919 lineNumber += propertyLineCount + 1; 920 }); 921 922 // Look for colors and make swatches. 923 this._createColorSwatches(true); 924 } 925 926 this._markLinesWithCheckboxPlaceholder(); 886 }.bind(this)); 887 888 // Look for colors and make swatches. 889 this._createColorSwatches(true); 927 890 928 891 // Restore the cursor position/selection. … … 935 898 // Mark the editor as clean (unedited state). 936 899 this._codeMirror.markClean(); 900 901 this._markLinesWithCheckboxPlaceholder(); 937 902 } 938 903 -
trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js
r183043 r184000 1 1 /* 2 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * Copyright (C) 2015 Tobias Reiss <tobi+webkit@basecode.de> 3 4 * 4 5 * Redistribution and use in source and binary forms, with or without … … 105 106 106 107 return 0; 108 }, 109 110 removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment) 111 { 112 return false; 107 113 }, 108 114 … … 375 381 }, 376 382 383 removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment) 384 { 385 return false; 386 }, 387 377 388 removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine) 378 389 { … … 420 431 } 421 432 }); 433 434 CodeMirror.extendMode("css-rule", { 435 shouldHaveSpaceBeforeToken: function(lastToken, lastContent, token, state, content, isComment) 436 { 437 return lastContent === ":" && !lastToken; 438 }, 439 440 shouldHaveSpaceAfterLastToken: function(lastToken, lastContent, token, state, content, isComment) 441 { 442 return lastContent === "," && !lastToken; 443 }, 444 445 newlinesAfterToken: function(lastToken, lastContent, token, state, content, isComment) 446 { 447 return 0; 448 }, 449 450 removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment) 451 { 452 // Remove whitespace before a comment which moves the comment to the beginning of the line. 453 if (isComment) 454 return true; 455 456 // A semicolon indicates the end of line. So remove whitespace before next line. 457 if (!lastToken) 458 return lastContent === ";"; 459 460 // Remove whitespace before semicolon. Like `prop: value ;`. 461 // Remove whitespace before colon. Like `prop : value;`. 462 if (!token) 463 return content === ";" || content === ":"; 464 465 // A comment is supposed to be in its own line. So remove whitespace before next line. 466 if (/\bcomment\b/.test(lastToken)) 467 return true; 468 469 return false; 470 }, 471 472 removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine) 473 { 474 return false; 475 }, 476 477 indentAfterToken: function(lastToken, lastContent, token, state, content, isComment) 478 { 479 return false; 480 }, 481 482 newlineBeforeToken: function(lastToken, lastContent, token, state, content, isComment) 483 { 484 // Add new line before comments. 485 if (isComment) 486 return true; 487 488 // Add new line before a prefixed property like `-webkit-animation`. 489 if (state.state === "block") 490 return /\bmeta\b/.test(token); 491 492 // Add new line after comment 493 if (/\bcomment\b/.test(lastToken)) 494 return true; 495 496 // Add new line before a regular property like `display`. 497 if (/\bproperty\b/.test(token)) 498 return !(/\bmeta\b/.test(lastToken)); 499 500 return false; 501 }, 502 503 indentBeforeToken: function(lastToken, lastContent, token, state, content, isComment) 504 { 505 return false; 506 }, 507 508 dedentsBeforeToken: function(lastToken, lastContent, token, state, content, isComment) 509 { 510 return 0; 511 } 512 });
Note: See TracChangeset
for help on using the changeset viewer.