зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1154809 - Rewrite prettifyCSS to use CSSLexer. r=pbrosset
--HG-- extra : rebase_source : afa69805be07b9f3918333ea8edcb8a7b2115c4a extra : histedit_source : cdb808df2d2d01a2817c3675ace9a3a4f85a0c1f
This commit is contained in:
Родитель
412abec581
Коммит
4a110448f0
|
@ -12,12 +12,10 @@ const PRETTIFIED_SOURCE = "" +
|
|||
"body \{\r?\n" + // body{
|
||||
"\tbackground\:white;\r?\n" + // background:white;
|
||||
"\}\r?\n" + // }
|
||||
"\r?\n" + //
|
||||
"div \{\r?\n" + // div{
|
||||
"\tfont\-size\:4em;\r?\n" + // font-size:4em;
|
||||
"\tcolor\:red\r?\n" + // color:red
|
||||
"\}\r?\n" + // }
|
||||
"\r?\n" + //
|
||||
"span \{\r?\n" + // span{
|
||||
"\tcolor\:green;\r?\n" // color:green;
|
||||
"\}\r?\n"; // }
|
||||
|
|
|
@ -995,50 +995,172 @@ CssLogic.prettifyCSS = function(text, ruleCount) {
|
|||
return text;
|
||||
}
|
||||
|
||||
let parts = []; // indented parts
|
||||
let partStart = 0; // start offset of currently parsed part
|
||||
// We reformat the text using a simple state machine. The
|
||||
// reformatting preserves most of the input text, changing only
|
||||
// whitespace. The rules are:
|
||||
//
|
||||
// * After a "{" or ";" symbol, ensure there is a newline and
|
||||
// indentation before the next non-comment, non-whitespace token.
|
||||
// * Additionally after a "{" symbol, increase the indentation.
|
||||
// * A "}" symbol ensures there is a preceding newline, and
|
||||
// decreases the indentation level.
|
||||
// * Ensure there is whitespace before a "{".
|
||||
//
|
||||
// This approach can be confused sometimes, but should do ok on a
|
||||
// minified file.
|
||||
let indent = "";
|
||||
let indentLevel = 0;
|
||||
let tokens = domUtils.getCSSLexer(text);
|
||||
let result = "";
|
||||
let pushbackToken = undefined;
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let c = text[i];
|
||||
let shouldIndent = false;
|
||||
|
||||
switch (c) {
|
||||
case "}":
|
||||
if (i - partStart > 1) {
|
||||
// there's more than just } on the line, add line
|
||||
parts.push(indent + text.substring(partStart, i));
|
||||
partStart = i;
|
||||
// A helper function that reads tokens, looking for the next
|
||||
// non-comment, non-whitespace token. Comment and whitespace tokens
|
||||
// are appended to |result|. If this encounters EOF, it returns
|
||||
// null. Otherwise it returns the last whitespace token that was
|
||||
// seen. This function also updates |pushbackToken|.
|
||||
let readUntilSignificantToken = () => {
|
||||
while (true) {
|
||||
let token = tokens.nextToken();
|
||||
if (!token || token.tokenType !== "whitespace") {
|
||||
pushbackToken = token;
|
||||
return token;
|
||||
}
|
||||
indent = TAB_CHARS.repeat(--indentLevel);
|
||||
/* fallthrough */
|
||||
case ";":
|
||||
case "{":
|
||||
shouldIndent = true;
|
||||
// Saw whitespace. Before committing to it, check the next
|
||||
// token.
|
||||
let nextToken = tokens.nextToken();
|
||||
if (!nextToken || nextToken.tokenType !== "comment") {
|
||||
pushbackToken = nextToken;
|
||||
return token;
|
||||
}
|
||||
// Saw whitespace + comment. Update the result and continue.
|
||||
result = result + text.substring(token.startOffset, nextToken.endOffset);
|
||||
}
|
||||
};
|
||||
|
||||
// State variables for readUntilNewlineNeeded.
|
||||
//
|
||||
// Starting index of the accumulated tokens.
|
||||
let startIndex;
|
||||
// Ending index of the accumulated tokens.
|
||||
let endIndex;
|
||||
// True if any non-whitespace token was seen.
|
||||
let anyNonWS;
|
||||
// True if the terminating token is "}".
|
||||
let isCloseBrace;
|
||||
// True if the token just before the terminating token was
|
||||
// whitespace.
|
||||
let lastWasWS;
|
||||
|
||||
// A helper function that reads tokens until there is a reason to
|
||||
// insert a newline. This updates the state variables as needed.
|
||||
// If this encounters EOF, it returns null. Otherwise it returns
|
||||
// the final token read. Note that if the returned token is "{",
|
||||
// then it will not be included in the computed start/end token
|
||||
// range. This is used to handle whitespace insertion before a "{".
|
||||
let readUntilNewlineNeeded = () => {
|
||||
let token;
|
||||
while (true) {
|
||||
if (pushbackToken) {
|
||||
token = pushbackToken;
|
||||
pushbackToken = undefined;
|
||||
} else {
|
||||
token = tokens.nextToken();
|
||||
}
|
||||
if (!token) {
|
||||
endIndex = text.length;
|
||||
break;
|
||||
}
|
||||
|
||||
if (shouldIndent) {
|
||||
let la = text[i+1]; // one-character lookahead
|
||||
if (!/\n/.test(la) || /^\s+$/.test(text.substring(i+1, text.length))) {
|
||||
// following character should be a new line, but isn't,
|
||||
// or it's whitespace at the end of the file
|
||||
parts.push(indent + text.substring(partStart, i + 1));
|
||||
if (c == "}") {
|
||||
parts.push(""); // for extra line separator
|
||||
// A "}" symbol must be inserted later, to deal with indentation
|
||||
// and newline.
|
||||
if (token.tokenType === "symbol" && token.text === "}") {
|
||||
isCloseBrace = true;
|
||||
break;
|
||||
} else if (token.tokenType === "symbol" && token.text === "{") {
|
||||
break;
|
||||
}
|
||||
partStart = i + 1;
|
||||
|
||||
if (token.tokenType !== "whitespace") {
|
||||
anyNonWS = true;
|
||||
}
|
||||
|
||||
if (startIndex === undefined) {
|
||||
startIndex = token.startOffset;
|
||||
}
|
||||
endIndex = token.endOffset;
|
||||
|
||||
if (token.tokenType === "symbol" && token.text === ';') {
|
||||
break;
|
||||
}
|
||||
|
||||
lastWasWS = token.tokenType === "whitespace";
|
||||
}
|
||||
return token;
|
||||
};
|
||||
|
||||
while (true) {
|
||||
// Set the initial state.
|
||||
startIndex = undefined;
|
||||
endIndex = undefined;
|
||||
anyNonWS = false;
|
||||
isCloseBrace = false;
|
||||
lastWasWS = false;
|
||||
|
||||
// Read tokens until we see a reason to insert a newline.
|
||||
let token = readUntilNewlineNeeded();
|
||||
|
||||
// Append any saved up text to the result, applying indentation.
|
||||
if (startIndex !== undefined) {
|
||||
if (isCloseBrace && !anyNonWS) {
|
||||
// If we saw only whitespace followed by a "}", then we don't
|
||||
// need anything here.
|
||||
} else {
|
||||
return text; // assume it is not minified, early exit
|
||||
result = result + indent + text.substring(startIndex, endIndex);
|
||||
if (isCloseBrace)
|
||||
result += CssLogic.LINE_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == "{") {
|
||||
if (isCloseBrace) {
|
||||
indent = TAB_CHARS.repeat(--indentLevel);
|
||||
result = result + indent + '}';
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (token.tokenType === "symbol" && token.text === '{') {
|
||||
if (!lastWasWS) {
|
||||
result += ' ';
|
||||
}
|
||||
result += '{';
|
||||
indent = TAB_CHARS.repeat(++indentLevel);
|
||||
}
|
||||
|
||||
// Now it is time to insert a newline. However first we want to
|
||||
// deal with any trailing comments.
|
||||
token = readUntilSignificantToken();
|
||||
|
||||
// "Early" bail-out if the text does not appear to be minified.
|
||||
// Here we ignore the case where whitespace appears at the end of
|
||||
// the text.
|
||||
if (pushbackToken && token && token.tokenType === "whitespace" &&
|
||||
/\n/g.test(text.substring(token.startOffset, token.endOffset))) {
|
||||
return text;
|
||||
}
|
||||
return parts.join(CssLogic.LINE_SEPARATOR);
|
||||
|
||||
// Finally time for that newline.
|
||||
result = result + CssLogic.LINE_SEPARATOR;
|
||||
|
||||
// Maybe we hit EOF.
|
||||
if (!pushbackToken) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test prettifyCSS.
|
||||
|
||||
"use strict";
|
||||
|
||||
const loader = new DevToolsLoader();
|
||||
const require = loader.require;
|
||||
const {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||
|
||||
|
||||
const TESTS = [
|
||||
{ name: "simple test",
|
||||
input: "div { font-family:'Arial Black', Arial, sans-serif; }",
|
||||
expected: [
|
||||
"div {",
|
||||
"\tfont-family:'Arial Black', Arial, sans-serif;",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
|
||||
{ name: "whitespace before open brace",
|
||||
input: "div{}",
|
||||
expected: [
|
||||
"div {",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
|
||||
{ name: "minified with trailing newline",
|
||||
input: "\nbody{background:white;}div{font-size:4em;color:red}span{color:green;}\n",
|
||||
expected: [
|
||||
"",
|
||||
"body {",
|
||||
"\tbackground:white;",
|
||||
"}",
|
||||
"div {",
|
||||
"\tfont-size:4em;",
|
||||
"\tcolor:red",
|
||||
"}",
|
||||
"span {",
|
||||
"\tcolor:green;",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
function run_test() {
|
||||
for (let test of TESTS) {
|
||||
do_print(test.name);
|
||||
|
||||
// Note that CssLogic.LINE_SEPARATOR is computed lazily, so we
|
||||
// have to do things in the right order here.
|
||||
let output = CssLogic.prettifyCSS(test.input);
|
||||
let expected = test.expected.join(CssLogic.LINE_SEPARATOR) +
|
||||
CssLogic.LINE_SEPARATOR;
|
||||
equal(output, expected, test.name);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ support-files =
|
|||
[test_defineLazyPrototypeGetter.js]
|
||||
[test_async-utils.js]
|
||||
[test_consoleID.js]
|
||||
[test_prettifyCSS.js]
|
||||
[test_require_lazy.js]
|
||||
[test_require.js]
|
||||
[test_stack.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче