Index: third_party/WebKit/Source/devtools/front_end/gonzales/SCSSParser.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/gonzales/SCSSParser.js b/third_party/WebKit/Source/devtools/front_end/gonzales/SCSSParser.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..04cd9eb82e0ba9056ae9eede1a504dfdf83023ae |
--- /dev/null |
+++ b/third_party/WebKit/Source/devtools/front_end/gonzales/SCSSParser.js |
@@ -0,0 +1,197 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+/** |
+ * @constructor |
+ * @implements {WebInspector.FormatterWorkerContentParser} |
+ */ |
+WebInspector.SCSSParser = function() |
+{ |
+} |
+ |
+/** |
+ * @constructor |
+ */ |
+WebInspector.SCSSParser.Result = function() |
+{ |
+ this.properties = []; |
+ this.variables = []; |
+ this.mixins = []; |
+} |
+ |
+WebInspector.SCSSParser.prototype = { |
+ /** |
+ * @override |
+ * @param {string} content |
+ * @return {!WebInspector.SCSSParser.Result} |
+ */ |
+ parse: function(content) |
+ { |
+ var result = new WebInspector.SCSSParser.Result(); |
+ var ast = null; |
+ try { |
+ ast = gonzales.parse(content, {syntax: "scss"}); |
+ } catch (e) { |
+ return result; |
+ } |
+ |
+ var extractedNodes = []; |
+ WebInspector.SCSSParser.extractNodes(ast, extractedNodes); |
+ |
+ for (var node of extractedNodes) { |
+ if (node.type === "declaration") |
+ this._handleDeclaration(node, result); |
+ else if (node.type === "include") |
+ this._handleInclude(node, result); |
+ else if (node.type === "multilineComment" && node.start.line === node.end.line) |
+ this._handleComment(node, result); |
+ } |
+ return result; |
+ }, |
+ |
+ /** |
+ * @param {!Gonzales.Node} node |
+ * @param {!WebInspector.SCSSParser.Result} result |
+ */ |
+ _handleDeclaration: function(node, result) |
+ { |
+ var propertyNode = node.content.find(node => node.type === "property"); |
+ var delimeterNode = node.content.find(node => node.type === "propertyDelimiter"); |
+ var valueNode = node.content.find(node => node.type === "value"); |
+ if (!propertyNode || !delimeterNode || !valueNode) |
+ return; |
+ |
+ var nameRange = new WebInspector.TextRange(propertyNode.start.line - 1, propertyNode.start.column - 1, delimeterNode.start.line - 1, delimeterNode.start.column - 1); |
+ var valueRange = new WebInspector.TextRange(delimeterNode.end.line - 1, delimeterNode.end.column, valueNode.end.line - 1, valueNode.end.column); |
+ var range = /** @type {!WebInspector.TextRange} */(node.declarationRange); |
+ |
+ var property = new WebInspector.SCSSParser.Property(range, nameRange, valueRange, false); |
+ var isVariable = !!propertyNode.content.find(node => node.type === "variable"); |
+ if (isVariable) |
+ result.variables.push(property); |
+ else |
+ result.properties.push(property); |
+ }, |
+ |
+ /** |
+ * @param {!Gonzales.Node} node |
+ * @param {!WebInspector.SCSSParser.Result} result |
+ */ |
+ _handleInclude: function(node, result) |
+ { |
+ var mixinName = node.content.find(node => node.type === "ident"); |
+ if (!mixinName) |
+ return; |
+ var nameRange = WebInspector.SCSSParser.rangeFromNode(mixinName); |
+ var mixinArguments = node.content.find(node => node.type === "arguments"); |
+ if (!mixinArguments) |
+ return; |
+ var parameters = mixinArguments.content.filter(node => node.type !== "delimiter" && node.type !== "space"); |
+ for (var parameter of parameters) { |
+ var range = WebInspector.SCSSParser.rangeFromNode(node); |
+ var valueRange = WebInspector.SCSSParser.rangeFromNode(parameter); |
+ var property = new WebInspector.SCSSParser.Property(range, nameRange, valueRange, false); |
+ result.mixins.push(property); |
+ } |
+ }, |
+ |
+ /** |
+ * @param {!Gonzales.Node} node |
+ * @param {!WebInspector.SCSSParser.Result} result |
+ */ |
+ _handleComment: function(node, result) |
+ { |
+ if (node.start.line !== node.end.line) |
+ return; |
+ var innerText = /** @type {string} */(node.content); |
+ var innerResult = this.parse(innerText); |
+ if (innerResult.properties.length !== 1 || innerResult.variables.length !== 0 || innerResult.mixins.length !== 0) |
+ return; |
+ var property = innerResult.properties[0]; |
+ var disabledProperty = property.rebaseInsideOneLineComment(node); |
+ result.properties.push(disabledProperty); |
+ }, |
+} |
+ |
+/** |
+ * @param {!Gonzales.Node} node |
+ * @return {!WebInspector.TextRange} |
+ */ |
+WebInspector.SCSSParser.rangeFromNode = function(node) |
+{ |
+ return new WebInspector.TextRange(node.start.line - 1, node.start.column - 1, node.end.line - 1, node.end.column); |
+} |
+ |
+/** |
+ * @constructor |
+ * @param {!WebInspector.TextRange} range |
+ * @param {!WebInspector.TextRange} nameRange |
+ * @param {!WebInspector.TextRange} valueRange |
+ * @param {boolean} disabled |
+ */ |
+WebInspector.SCSSParser.Property = function(range, nameRange, valueRange, disabled) |
+{ |
+ this.range = range; |
+ this.name = nameRange; |
+ this.value = valueRange; |
+ this.disabled = disabled; |
+} |
+ |
+WebInspector.SCSSParser.Property.prototype = { |
+ /** |
+ * @param {!Gonzales.Node} commentNode |
+ * @return {!WebInspector.SCSSParser.Property} |
+ */ |
+ rebaseInsideOneLineComment: function(commentNode) |
+ { |
+ var lineOffset = commentNode.start.line - 1; |
+ // Account for the "/*". |
+ var columnOffset = commentNode.start.column - 1 + 2; |
+ var range = WebInspector.SCSSParser.rangeFromNode(commentNode); |
+ var name = rebaseRange(this.name, lineOffset, columnOffset); |
+ var value = rebaseRange(this.value, lineOffset, columnOffset); |
+ return new WebInspector.SCSSParser.Property(range, name, value, true); |
+ |
+ /** |
+ * @param {!WebInspector.TextRange} range |
+ * @param {number} lineOffset |
+ * @param {number} columnOffset |
+ * @return {!WebInspector.TextRange} |
+ */ |
+ function rebaseRange(range, lineOffset, columnOffset) |
+ { |
+ return new WebInspector.TextRange(range.startLine + lineOffset, range.startColumn + columnOffset, range.endLine + lineOffset, range.endColumn + columnOffset); |
+ } |
+ } |
+} |
+ |
+/** |
+ * @param {!Gonzales.Node} node |
+ * @param {!Array<!Gonzales.Node>} output |
+ */ |
+WebInspector.SCSSParser.extractNodes = function(node, output) |
+{ |
+ if (!Array.isArray(node.content)) |
+ return; |
+ var lastDeclaration = null; |
+ for (var i = 0; i < node.content.length; ++i) { |
+ var child = node.content[i]; |
+ if (child.type === "declarationDelimiter" && lastDeclaration) { |
+ lastDeclaration.declarationRange.endLine = child.end.line - 1; |
+ lastDeclaration.declarationRange.endColumn = child.end.column; |
+ lastDeclaration = null; |
+ } |
+ if (child.type === "include" || child.type === "declaration" || child.type === "multilineComment") |
+ output.push(child); |
+ if (child.type === "declaration") { |
+ lastDeclaration = child; |
+ lastDeclaration.declarationRange = WebInspector.TextRange.createFromLocation(child.start.line - 1, child.start.column - 1); |
+ } |
+ WebInspector.SCSSParser.extractNodes(child, output); |
+ } |
+ if (lastDeclaration) { |
+ lastDeclaration.declarationRange.endLine = node.end.line - 1; |
+ lastDeclaration.declarationRange.endColumn = node.end.column - 1; |
+ } |
+} |