Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (183999 => 184000)
--- trunk/Source/WebInspectorUI/ChangeLog 2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/ChangeLog 2015-05-08 18:37:49 UTC (rev 184000)
@@ -1,3 +1,36 @@
+2015-05-08 Tobias Reiss <[email protected]>
+
+ Web Inspector: Styles sidebar editing with incomplete property looks poor in UI
+ https://bugs.webkit.org/show_bug.cgi?id=141692
+
+ Reviewed by Timothy Hatcher.
+
+ Add "css-rule" Formatter that breaks CSS declarations into multiple lines,
+ keeps comments and invalid styles and adds whitespace.
+
+ * Tools/PrettyPrinting/css-rule-tests/*.css: Added.
+ Add test cases.
+
+ * Tools/PrettyPrinting/index.html:
+ Enable Test setup to be able to run "css-rule" Formatter tests.
+
+ * UserInterface/Controllers/Formatter.js:
+ (Formatter.prototype._handleToken):
+ * UserInterface/Controllers/FormatterContentBuilder.js:
+ (FormatterContentBuilder.prototype.removeLastNewline):
+ (FormatterContentBuilder.prototype.removeLastWhitespace):
+ (FormatterContentBuilder.prototype._popFormattedContent):
+ (FormatterContentBuilder.prototype._popNewLine): Deleted.
+ * UserInterface/Views/CSSStyleDeclarationTextEditor.js:
+ (WebInspector.CSSStyleDeclarationTextEditor.prototype._formattedContentFromEditor):
+ (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.set this):
+ (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.get this):
+ (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update):
+ (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent):
+ (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.countNewLineCharacters): Deleted.
+ (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.else): Deleted.
+ * UserInterface/Views/CodeMirrorFormatters.js:
+
2015-05-07 Joseph Pecoraro <[email protected]>
Web Inspector: Expanding Object with only __proto__ looks poor should have a label
Modified: trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html (183999 => 184000)
--- trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html 2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html 2015-05-08 18:37:49 UTC (rev 184000)
@@ -8,6 +8,8 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
@@ -21,6 +23,7 @@
<select id="mode">
<option selected value="text/_javascript_">_javascript_</option>
<option value="text/css">CSS</option>
+ <option value="css-rule">CSS-Rule</option>
</select>
<button id="populate">Populate</button>
<button id="run-tests">Run Tests</button>
@@ -230,6 +233,8 @@
if (modePicker.value === "text/_javascript_")
runJavaScriptTests(completedCallback);
+ else if (modePicker.value === "css-rule")
+ runCssRuleTests(completedCallback);
else
runCSSTests(completedCallback);
}
@@ -240,6 +245,34 @@
"js-tests/switch-case-default.js",
]);
}
+ function runCssRuleTests(callback) {
+ _runTests(callback, [
+ "css-rule-tests/invalid-property-is-not-removed.css",
+ "css-rule-tests/remove-whitespace-before-colon.css",
+ "css-rule-tests/remove-whitespace-before-semicolon.css",
+ "css-rule-tests/remove-whitespace-before-property.css",
+ "css-rule-tests/remove-whitespace-before-prefixed-property.css",
+ "css-rule-tests/remove-whitespace-before-invalid-property.css",
+ "css-rule-tests/remove-whitespace-before-comment.css",
+ "css-rule-tests/split-comment-followed-by-property.css",
+ "css-rule-tests/split-comment-followed-by-prefixed-property.css",
+ "css-rule-tests/split-comment-followed-by-invalid-property.css",
+ "css-rule-tests/split-comment-followed-by-comment.css",
+ "css-rule-tests/split-property-followed-by-property.css",
+ "css-rule-tests/split-property-followed-by-prefixed-property.css",
+ "css-rule-tests/split-property-followed-by-invalid-property.css",
+ "css-rule-tests/split-property-followed-by-comment.css",
+ "css-rule-tests/split-invalid-property-followed-by-property.css",
+ "css-rule-tests/split-invalid-property-followed-by-prefixed-property.css",
+ "css-rule-tests/split-invalid-property-followed-by-invalid-property.css",
+ "css-rule-tests/split-invalid-property-followed-by-comment.css",
+ "css-rule-tests/split-property-without-semicolon-followed-by-comment-and-property.css",
+ "css-rule-tests/add-whitespace-after-colon.css",
+ "css-rule-tests/add-whitespace-after-comma.css",
+ "css-rule-tests/do-not-append-semicolon.css",
+ "css-rule-tests/keep-prefixed-value.css",
+ ]);
+ }
function runCSSTests(callback) {
_runTests(callback, [
"css-tests/basic.css",
Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js (183999 => 184000)
--- trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js 2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js 2015-05-08 18:37:49 UTC (rev 184000)
@@ -89,7 +89,7 @@
if (startOfNewLine)
this._builder.appendNewline();
- // Whitespace. Collapse to a single space.
+ // Whitespace. Remove all spaces or collapse to a single space.
if (isWhiteSpace) {
this._builder.appendSpace();
return;
@@ -101,6 +101,10 @@
if (mode.modifyStateForTokenPre)
mode.modifyStateForTokenPre(this._lastToken, this._lastContent, token, state, content, isComment);
+ // Should we remove the last whitespace?
+ if (this._builder.lastTokenWasWhitespace && mode.removeLastWhitespace(this._lastToken, this._lastContent, token, state, content, isComment))
+ this._builder.removeLastWhitespace();
+
// Should we remove the last newline?
if (this._builder.lastTokenWasNewline && mode.removeLastNewline(this._lastToken, this._lastContent, token, state, content, isComment, firstTokenOnLine))
this._builder.removeLastNewline();
Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js (183999 => 184000)
--- trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js 2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js 2015-05-08 18:37:49 UTC (rev 184000)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Tobias Reiss <[email protected]>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -137,13 +138,27 @@
console.assert(this.lastTokenWasNewline);
console.assert(this._formattedContent.lastValue === "\n");
if (this.lastTokenWasNewline) {
- this._popNewLine();
+ this._popFormattedContent();
+ this._formattedLineEndings.pop();
this._startOfLine = false;
this.lastTokenWasNewline = false;
this.lastTokenWasWhitespace = false;
}
}
+ removeLastWhitespace()
+ {
+ console.assert(this.lastTokenWasWhitespace);
+ console.assert(this._formattedContent.lastValue === " ");
+ if (this.lastTokenWasWhitespace) {
+ this._popFormattedContent();
+ // No need to worry about `_startOfLine` and `lastTokenWasNewline`
+ // because `appendSpace` takes care of not adding whitespace
+ // to the beginning of a line.
+ this.lastTokenWasWhitespace = false;
+ }
+ }
+
indent()
{
++this._indent;
@@ -170,11 +185,10 @@
// Private
- _popNewLine()
+ _popFormattedContent()
{
var removed = this._formattedContent.pop();
this._formattedContentLength -= removed.length;
- this._formattedLineEndings.pop();
}
_append(str)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js (183999 => 184000)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js 2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js 2015-05-08 18:37:49 UTC (rev 184000)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Tobias Reiss <[email protected]>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -776,6 +777,20 @@
}
}
+ _formattedContentFromEditor()
+ {
+ var mapping = {original: [0], formatted: [0]};
+ // FIXME: <rdar://problem/10593948> Provide a way to change the tab width in the Web Inspector
+ var indentString = " ";
+ var builder = new FormatterContentBuilder(mapping, [], [], 0, 0, indentString);
+ var formatter = new Formatter(this._codeMirror, builder);
+ var start = {line: 0, ch: 0};
+ var end = {line: this._codeMirror.lineCount() - 1};
+ formatter.format(start, end);
+
+ return builder.formattedContent.trim();
+ }
+
_resetContent()
{
if (this._commitChangesTimeout) {
@@ -816,115 +831,63 @@
// Remember the cursor position/selection.
var selectionAnchor = this._codeMirror.getCursor("anchor");
var selectionHead = this._codeMirror.getCursor("head");
+ var isEditorReadOnly = this._codeMirror.getOption("readOnly");
+ var styleText = this._style.text.trim();
+ var findWhitespace = /\s+/g;
- function countNewLineCharacters(text)
- {
- var matches = text.match(/\n/g);
- return matches ? matches.length : 0;
+ // Only format non-empty styles. Keep in mind that styleText is always empty
+ // for "readOnly" Editors. But prepare Checkbox placeholders in any case.
+ // Because that will indent the cursor when the User starts typing.
+ if (!styleText && !isEditorReadOnly) {
+ this._markLinesWithCheckboxPlaceholder();
+ return;
}
- var styleText = this._style.text;
+ // Set non-optimized, valid and invalid styles in preparation for the Formatter.
+ // Set empty string in case of readonly styles.
+ this._codeMirror.setValue(styleText);
- // Pretty print the content if there are more properties than there are lines.
- // This could be an option exposed to the user; however, it is almost always
- // desired in this case.
-
- if (styleText && this._style.visibleProperties.length <= countNewLineCharacters(styleText.trim()) + 1) {
- // This style has formatted text content, so use it for a high-fidelity experience.
-
- var prefixWhitespaceMatch = styleText.match(/^[ \t]*\n/);
- this._prefixWhitespace = prefixWhitespaceMatch ? prefixWhitespaceMatch[0] : "";
-
- var suffixWhitespaceMatch = styleText.match(/\n[ \t]*$/);
- this._suffixWhitespace = suffixWhitespaceMatch ? suffixWhitespaceMatch[0] : "";
-
- this._codeMirror.setValue(styleText);
-
- if (this._prefixWhitespace)
- this._codeMirror.replaceRange("", {line: 0, ch: 0}, {line: 1, ch: 0});
-
- if (this._suffixWhitespace) {
- var lineCount = this._codeMirror.lineCount();
- this._codeMirror.replaceRange("", {line: lineCount - 2}, {line: lineCount - 1});
- }
-
- this._linePrefixWhitespace = "";
-
- var linesToStrip = [];
-
- // Remember the whitespace so it can be restored on commit.
- var lineCount = this._codeMirror.lineCount();
- for (var i = 0; i < lineCount; ++i) {
- var lineContent = this._codeMirror.getLine(i);
- var prefixWhitespaceMatch = lineContent.match(/^\s+/);
-
- // If there is no prefix whitespace (except for empty lines) then the prefix
- // whitespace of all other lines will be retained as is. Update markers and return.
- if (!prefixWhitespaceMatch) {
- if (!lineContent)
- continue;
- this._linePrefixWhitespace = "";
- this._updateTextMarkers(true);
- return;
- }
-
- linesToStrip.push(i);
-
- // Only remember the shortest whitespace so we don't loose any of the
- // original author's whitespace if their indentation lengths differed.
- // Using the shortest also makes the adjustment work in _updateTextMarkers.
-
- // FIXME: This messes up if there is a mix of spaces and tabs. A tab
- // is treated the same as a space when prefix whitespace is omitted,
- // so if the shortest prefixed whitespace is, say, two tab characters,
- // lines that begin with four spaces will only have a two space indent.
- if (!this._linePrefixWhitespace || prefixWhitespaceMatch[0].length < this._linePrefixWhitespace.length)
- this._linePrefixWhitespace = prefixWhitespaceMatch[0];
- }
-
- // Strip the whitespace from the beginning of each line.
- for (var i = 0; i < linesToStrip.length; ++i) {
- var lineNumber = linesToStrip[i];
- var from = {line: lineNumber, ch: 0};
- var to = {line: lineNumber, ch: this._linePrefixWhitespace.length};
- this._codeMirror.replaceRange("", from, to);
- }
-
- // Update all the text markers.
- this._updateTextMarkers(true);
- } else {
- // This style does not have text content or it is minified, so we want to synthesize the text content.
-
- this._prefixWhitespace = "";
- this._suffixWhitespace = "";
- this._linePrefixWhitespace = "";
-
- this._codeMirror.setValue("");
-
+ if (isEditorReadOnly) {
var lineNumber = 0;
-
- // Iterate only visible properties if we have original style text. That way we known we only synthesize
- // what was originaly in the style text.
- this._iterateOverProperties(styleText ? true : false, function(property) {
- // Some property text can have line breaks, so consider that in the ranges below.
- var propertyText = property.synthesizedText;
- var propertyLineCount = countNewLineCharacters(propertyText);
-
+ this._iterateOverProperties(false, function(property) {
var from = {line: lineNumber, ch: 0};
- var to = {line: lineNumber + propertyLineCount};
-
- this._codeMirror.replaceRange((lineNumber ? "\n" : "") + propertyText, from);
+ var to = {line: lineNumber};
+ // Readonly properties are pretty printed by `synthesizedText` and not the Formatter.
+ this._codeMirror.replaceRange((lineNumber ? "\n" : "") + property.synthesizedText, from);
this._createTextMarkerForPropertyIfNeeded(from, to, property);
-
- lineNumber += propertyLineCount + 1;
+ lineNumber++;
});
- // Look for colors and make swatches.
- this._createColorSwatches(true);
+ return;
}
- this._markLinesWithCheckboxPlaceholder();
+ // Now the Formatter pretty prints the styles.
+ this._codeMirror.setValue(this._formattedContentFromEditor());
+ // We need to workaround the fact that...
+ // 1) `this._style.properties` only holds valid CSSProperty instances but not
+ // comments and invalid properties like `color;`.
+ // 2) `_createTextMarkerForPropertyIfNeeded` relies on CSSProperty instances.
+ var cssPropertiesMap = new Map();
+ this._iterateOverProperties(false, function(cssProperty) {
+ cssPropertiesMap.set(cssProperty.text.replace(findWhitespace, ""), cssProperty);
+ });
+
+ // Go through the Editor line by line and create TextMarker when a
+ // CSSProperty instance for that property exists. If not, then don't create a TextMarker.
+ this._codeMirror.eachLine(function(lineHandler) {
+ var lineNumber = lineHandler.lineNo();
+ var lineContentSansWhitespace = lineHandler.text.replace(findWhitespace, "");
+ if (cssPropertiesMap.has(lineContentSansWhitespace)) {
+ var from = {line: lineNumber, ch: 0};
+ var to = {line: lineNumber};
+ this._createTextMarkerForPropertyIfNeeded(from, to, cssPropertiesMap.get(lineContentSansWhitespace));
+ }
+ }.bind(this));
+
+ // Look for colors and make swatches.
+ this._createColorSwatches(true);
+
// Restore the cursor position/selection.
this._codeMirror.setSelection(selectionAnchor, selectionHead);
@@ -934,6 +897,8 @@
// Mark the editor as clean (unedited state).
this._codeMirror.markClean();
+
+ this._markLinesWithCheckboxPlaceholder();
}
// This needs to be done first and as a separate operation to avoid an exception in CodeMirror.
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js (183999 => 184000)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js 2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js 2015-05-08 18:37:49 UTC (rev 184000)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Tobias Reiss <[email protected]>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -106,6 +107,11 @@
return 0;
},
+ removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return false;
+ },
+
removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine)
{
if (!token) {
@@ -374,6 +380,11 @@
return 0;
},
+ removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return false;
+ },
+
removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine)
{
if (isComment) { // Comment after semicolon.
@@ -419,3 +430,83 @@
state._cssPrettyPrint.lineLength = 0;
}
});
+
+CodeMirror.extendMode("css-rule", {
+ shouldHaveSpaceBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return lastContent === ":" && !lastToken;
+ },
+
+ shouldHaveSpaceAfterLastToken: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return lastContent === "," && !lastToken;
+ },
+
+ newlinesAfterToken: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return 0;
+ },
+
+ removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ // Remove whitespace before a comment which moves the comment to the beginning of the line.
+ if (isComment)
+ return true;
+
+ // A semicolon indicates the end of line. So remove whitespace before next line.
+ if (!lastToken)
+ return lastContent === ";";
+
+ // Remove whitespace before semicolon. Like `prop: value ;`.
+ // Remove whitespace before colon. Like `prop : value;`.
+ if (!token)
+ return content === ";" || content === ":";
+
+ // A comment is supposed to be in its own line. So remove whitespace before next line.
+ if (/\bcomment\b/.test(lastToken))
+ return true;
+
+ return false;
+ },
+
+ removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine)
+ {
+ return false;
+ },
+
+ indentAfterToken: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return false;
+ },
+
+ newlineBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ // Add new line before comments.
+ if (isComment)
+ return true;
+
+ // Add new line before a prefixed property like `-webkit-animation`.
+ if (state.state === "block")
+ return /\bmeta\b/.test(token);
+
+ // Add new line after comment
+ if (/\bcomment\b/.test(lastToken))
+ return true;
+
+ // Add new line before a regular property like `display`.
+ if (/\bproperty\b/.test(token))
+ return !(/\bmeta\b/.test(lastToken));
+
+ return false;
+ },
+
+ indentBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return false;
+ },
+
+ dedentsBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+ {
+ return 0;
+ }
+});