OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * @constructor |
| 7 * @implements {WebInspector.FormatterWorkerContentParser} |
| 8 */ |
| 9 WebInspector.SCSSParser = function() |
| 10 { |
| 11 } |
| 12 |
| 13 /** |
| 14 * @constructor |
| 15 */ |
| 16 WebInspector.SCSSParser.Result = function() |
| 17 { |
| 18 this.properties = []; |
| 19 this.variables = []; |
| 20 this.mixins = []; |
| 21 } |
| 22 |
| 23 WebInspector.SCSSParser.prototype = { |
| 24 /** |
| 25 * @override |
| 26 * @param {string} content |
| 27 * @return {!WebInspector.SCSSParser.Result} |
| 28 */ |
| 29 parse: function(content) |
| 30 { |
| 31 var result = new WebInspector.SCSSParser.Result(); |
| 32 var ast = null; |
| 33 try { |
| 34 ast = gonzales.parse(content, {syntax: "scss"}); |
| 35 } catch (e) { |
| 36 return result; |
| 37 } |
| 38 |
| 39 var extractedNodes = []; |
| 40 WebInspector.SCSSParser.extractNodes(ast, extractedNodes); |
| 41 |
| 42 for (var node of extractedNodes) { |
| 43 if (node.type === "declaration") |
| 44 this._handleDeclaration(node, result); |
| 45 else if (node.type === "include") |
| 46 this._handleInclude(node, result); |
| 47 else if (node.type === "multilineComment" && node.start.line === nod
e.end.line) |
| 48 this._handleComment(node, result); |
| 49 } |
| 50 return result; |
| 51 }, |
| 52 |
| 53 /** |
| 54 * @param {!Gonzales.Node} node |
| 55 * @param {!WebInspector.SCSSParser.Result} result |
| 56 */ |
| 57 _handleDeclaration: function(node, result) |
| 58 { |
| 59 var propertyNode = node.content.find(node => node.type === "property"); |
| 60 var delimeterNode = node.content.find(node => node.type === "propertyDel
imiter"); |
| 61 var valueNode = node.content.find(node => node.type === "value"); |
| 62 if (!propertyNode || !delimeterNode || !valueNode) |
| 63 return; |
| 64 |
| 65 var nameRange = new WebInspector.TextRange(propertyNode.start.line - 1,
propertyNode.start.column - 1, delimeterNode.start.line - 1, delimeterNode.start
.column - 1); |
| 66 var valueRange = new WebInspector.TextRange(delimeterNode.end.line - 1,
delimeterNode.end.column, valueNode.end.line - 1, valueNode.end.column); |
| 67 var range = /** @type {!WebInspector.TextRange} */(node.declarationRange
); |
| 68 |
| 69 var property = new WebInspector.SCSSParser.Property(range, nameRange, va
lueRange, false); |
| 70 var isVariable = !!propertyNode.content.find(node => node.type === "vari
able"); |
| 71 if (isVariable) |
| 72 result.variables.push(property); |
| 73 else |
| 74 result.properties.push(property); |
| 75 }, |
| 76 |
| 77 /** |
| 78 * @param {!Gonzales.Node} node |
| 79 * @param {!WebInspector.SCSSParser.Result} result |
| 80 */ |
| 81 _handleInclude: function(node, result) |
| 82 { |
| 83 var mixinName = node.content.find(node => node.type === "ident"); |
| 84 if (!mixinName) |
| 85 return; |
| 86 var nameRange = WebInspector.SCSSParser.rangeFromNode(mixinName); |
| 87 var mixinArguments = node.content.find(node => node.type === "arguments"
); |
| 88 if (!mixinArguments) |
| 89 return; |
| 90 var parameters = mixinArguments.content.filter(node => node.type !== "de
limiter" && node.type !== "space"); |
| 91 for (var parameter of parameters) { |
| 92 var range = WebInspector.SCSSParser.rangeFromNode(node); |
| 93 var valueRange = WebInspector.SCSSParser.rangeFromNode(parameter); |
| 94 var property = new WebInspector.SCSSParser.Property(range, nameRange
, valueRange, false); |
| 95 result.mixins.push(property); |
| 96 } |
| 97 }, |
| 98 |
| 99 /** |
| 100 * @param {!Gonzales.Node} node |
| 101 * @param {!WebInspector.SCSSParser.Result} result |
| 102 */ |
| 103 _handleComment: function(node, result) |
| 104 { |
| 105 if (node.start.line !== node.end.line) |
| 106 return; |
| 107 var innerText = /** @type {string} */(node.content); |
| 108 var innerResult = this.parse(innerText); |
| 109 if (innerResult.properties.length !== 1 || innerResult.variables.length
!== 0 || innerResult.mixins.length !== 0) |
| 110 return; |
| 111 var property = innerResult.properties[0]; |
| 112 var disabledProperty = property.rebaseInsideOneLineComment(node); |
| 113 result.properties.push(disabledProperty); |
| 114 }, |
| 115 } |
| 116 |
| 117 /** |
| 118 * @param {!Gonzales.Node} node |
| 119 * @return {!WebInspector.TextRange} |
| 120 */ |
| 121 WebInspector.SCSSParser.rangeFromNode = function(node) |
| 122 { |
| 123 return new WebInspector.TextRange(node.start.line - 1, node.start.column - 1
, node.end.line - 1, node.end.column); |
| 124 } |
| 125 |
| 126 /** |
| 127 * @constructor |
| 128 * @param {!WebInspector.TextRange} range |
| 129 * @param {!WebInspector.TextRange} nameRange |
| 130 * @param {!WebInspector.TextRange} valueRange |
| 131 * @param {boolean} disabled |
| 132 */ |
| 133 WebInspector.SCSSParser.Property = function(range, nameRange, valueRange, disabl
ed) |
| 134 { |
| 135 this.range = range; |
| 136 this.name = nameRange; |
| 137 this.value = valueRange; |
| 138 this.disabled = disabled; |
| 139 } |
| 140 |
| 141 WebInspector.SCSSParser.Property.prototype = { |
| 142 /** |
| 143 * @param {!Gonzales.Node} commentNode |
| 144 * @return {!WebInspector.SCSSParser.Property} |
| 145 */ |
| 146 rebaseInsideOneLineComment: function(commentNode) |
| 147 { |
| 148 var lineOffset = commentNode.start.line - 1; |
| 149 // Account for the "/*". |
| 150 var columnOffset = commentNode.start.column - 1 + 2; |
| 151 var range = WebInspector.SCSSParser.rangeFromNode(commentNode); |
| 152 var name = rebaseRange(this.name, lineOffset, columnOffset); |
| 153 var value = rebaseRange(this.value, lineOffset, columnOffset); |
| 154 return new WebInspector.SCSSParser.Property(range, name, value, true); |
| 155 |
| 156 /** |
| 157 * @param {!WebInspector.TextRange} range |
| 158 * @param {number} lineOffset |
| 159 * @param {number} columnOffset |
| 160 * @return {!WebInspector.TextRange} |
| 161 */ |
| 162 function rebaseRange(range, lineOffset, columnOffset) |
| 163 { |
| 164 return new WebInspector.TextRange(range.startLine + lineOffset, rang
e.startColumn + columnOffset, range.endLine + lineOffset, range.endColumn + colu
mnOffset); |
| 165 } |
| 166 } |
| 167 } |
| 168 |
| 169 /** |
| 170 * @param {!Gonzales.Node} node |
| 171 * @param {!Array<!Gonzales.Node>} output |
| 172 */ |
| 173 WebInspector.SCSSParser.extractNodes = function(node, output) |
| 174 { |
| 175 if (!Array.isArray(node.content)) |
| 176 return; |
| 177 var lastDeclaration = null; |
| 178 for (var i = 0; i < node.content.length; ++i) { |
| 179 var child = node.content[i]; |
| 180 if (child.type === "declarationDelimiter" && lastDeclaration) { |
| 181 lastDeclaration.declarationRange.endLine = child.end.line - 1; |
| 182 lastDeclaration.declarationRange.endColumn = child.end.column; |
| 183 lastDeclaration = null; |
| 184 } |
| 185 if (child.type === "include" || child.type === "declaration" || child.ty
pe === "multilineComment") |
| 186 output.push(child); |
| 187 if (child.type === "declaration") { |
| 188 lastDeclaration = child; |
| 189 lastDeclaration.declarationRange = WebInspector.TextRange.createFrom
Location(child.start.line - 1, child.start.column - 1); |
| 190 } |
| 191 WebInspector.SCSSParser.extractNodes(child, output); |
| 192 } |
| 193 if (lastDeclaration) { |
| 194 lastDeclaration.declarationRange.endLine = node.end.line - 1; |
| 195 lastDeclaration.declarationRange.endColumn = node.end.column - 1; |
| 196 } |
| 197 } |
OLD | NEW |