| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @constructor | 6 * @constructor |
| 7 * @implements {WebInspector.FormatterWorkerContentParser} | 7 * @implements {WebInspector.FormatterWorkerContentParser} |
| 8 */ | 8 */ |
| 9 WebInspector.SCSSParser = function() | 9 WebInspector.SCSSParser = function() |
| 10 { | 10 { |
| 11 } | 11 } |
| 12 | 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 = { | 13 WebInspector.SCSSParser.prototype = { |
| 24 /** | 14 /** |
| 25 * @override | 15 * @override |
| 26 * @param {string} content | 16 * @param {string} content |
| 27 * @return {!WebInspector.SCSSParser.Result} | 17 * @return {!Array<!WebInspector.SCSSParser.Rule>} |
| 28 */ | 18 */ |
| 29 parse: function(content) | 19 parse: function(content) |
| 30 { | 20 { |
| 31 var result = new WebInspector.SCSSParser.Result(); | |
| 32 var ast = null; | 21 var ast = null; |
| 33 try { | 22 try { |
| 34 ast = gonzales.parse(content, {syntax: "scss"}); | 23 ast = gonzales.parse(content, {syntax: "scss"}); |
| 35 } catch (e) { | 24 } catch (e) { |
| 36 return result; | 25 return []; |
| 37 } | 26 } |
| 38 | 27 |
| 39 var extractedNodes = []; | 28 /** @type {!{properties: !Array<!Gonzales.Node>, node: !Gonzales.Node}}
*/ |
| 40 WebInspector.SCSSParser.extractNodes(ast, extractedNodes); | 29 var rootBlock = { |
| 30 properties: [], |
| 31 node: ast |
| 32 }; |
| 33 /** @type {!Array<!{properties: !Array<!Gonzales.Node>, node: !Gonzales.
Node}>} */ |
| 34 var blocks = [rootBlock]; |
| 35 ast.selectors = []; |
| 36 WebInspector.SCSSParser.extractNodes(ast, blocks, rootBlock); |
| 41 | 37 |
| 42 for (var node of extractedNodes) { | 38 var rules = []; |
| 39 for (var block of blocks) |
| 40 this._handleBlock(block, rules); |
| 41 return rules; |
| 42 }, |
| 43 |
| 44 /** |
| 45 * @param {!{node: !Gonzales.Node, properties: !Array<!Gonzales.Node>}} bloc
k |
| 46 * @param {!Array<!WebInspector.SCSSParser.Rule>} output |
| 47 */ |
| 48 _handleBlock: function(block, output) |
| 49 { |
| 50 var selectors = block.node.selectors.map(WebInspector.SCSSParser.rangeFr
omNode); |
| 51 var properties = []; |
| 52 var styleRange = WebInspector.SCSSParser.rangeFromNode(block.node); |
| 53 styleRange.startColumn += 1; |
| 54 styleRange.endColumn -= 1; |
| 55 for (var node of block.properties) { |
| 43 if (node.type === "declaration") | 56 if (node.type === "declaration") |
| 44 this._handleDeclaration(node, result); | 57 this._handleDeclaration(node, properties); |
| 45 else if (node.type === "include") | 58 else if (node.type === "include") |
| 46 this._handleInclude(node, result); | 59 this._handleInclude(node, properties); |
| 47 else if (node.type === "multilineComment" && node.start.line === nod
e.end.line) | 60 else if (node.type === "multilineComment" && node.start.line === nod
e.end.line) |
| 48 this._handleComment(node, result); | 61 this._handleComment(node, properties); |
| 49 } | 62 } |
| 50 return result; | 63 if (!selectors.length && !properties.length) |
| 64 return; |
| 65 var rule = new WebInspector.SCSSParser.Rule(selectors, properties, style
Range); |
| 66 output.push(rule); |
| 51 }, | 67 }, |
| 52 | 68 |
| 53 /** | 69 /** |
| 54 * @param {!Gonzales.Node} node | 70 * @param {!Gonzales.Node} node |
| 55 * @param {!WebInspector.SCSSParser.Result} result | 71 * @param {!Array<!WebInspector.SCSSParser.Property>} output |
| 56 */ | 72 */ |
| 57 _handleDeclaration: function(node, result) | 73 _handleDeclaration: function(node, output) |
| 58 { | 74 { |
| 59 var propertyNode = node.content.find(node => node.type === "property"); | 75 var propertyNode = node.content.find(node => node.type === "property"); |
| 60 var delimeterNode = node.content.find(node => node.type === "propertyDel
imiter"); | 76 var delimeterNode = node.content.find(node => node.type === "propertyDel
imiter"); |
| 61 var valueNode = node.content.find(node => node.type === "value"); | 77 var valueNode = node.content.find(node => node.type === "value"); |
| 62 if (!propertyNode || !delimeterNode || !valueNode) | 78 if (!propertyNode || !delimeterNode || !valueNode) |
| 63 return; | 79 return; |
| 64 | 80 |
| 65 var nameRange = new WebInspector.TextRange(propertyNode.start.line - 1,
propertyNode.start.column - 1, delimeterNode.start.line - 1, delimeterNode.start
.column - 1); | 81 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); | 82 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
); | 83 var range = /** @type {!WebInspector.TextRange} */(node.declarationRange
); |
| 68 | 84 |
| 69 var property = new WebInspector.SCSSParser.Property(range, nameRange, va
lueRange, false); | 85 var property = new WebInspector.SCSSParser.Property(range, nameRange, va
lueRange, false); |
| 70 var isVariable = !!propertyNode.content.find(node => node.type === "vari
able"); | 86 output.push(property); |
| 71 if (isVariable) | |
| 72 result.variables.push(property); | |
| 73 else | |
| 74 result.properties.push(property); | |
| 75 }, | 87 }, |
| 76 | 88 |
| 77 /** | 89 /** |
| 78 * @param {!Gonzales.Node} node | 90 * @param {!Gonzales.Node} node |
| 79 * @param {!WebInspector.SCSSParser.Result} result | 91 * @param {!Array<!WebInspector.SCSSParser.Property>} output |
| 80 */ | 92 */ |
| 81 _handleInclude: function(node, result) | 93 _handleInclude: function(node, output) |
| 82 { | 94 { |
| 83 var mixinName = node.content.find(node => node.type === "ident"); | 95 var mixinName = node.content.find(node => node.type === "ident"); |
| 84 if (!mixinName) | 96 if (!mixinName) |
| 85 return; | 97 return; |
| 86 var nameRange = WebInspector.SCSSParser.rangeFromNode(mixinName); | 98 var nameRange = WebInspector.SCSSParser.rangeFromNode(mixinName); |
| 87 var mixinArguments = node.content.find(node => node.type === "arguments"
); | 99 var mixinArguments = node.content.find(node => node.type === "arguments"
); |
| 88 if (!mixinArguments) | 100 if (!mixinArguments) |
| 89 return; | 101 return; |
| 90 var parameters = mixinArguments.content.filter(node => node.type !== "de
limiter" && node.type !== "space"); | 102 var parameters = mixinArguments.content.filter(node => node.type !== "de
limiter" && node.type !== "space"); |
| 91 for (var parameter of parameters) { | 103 for (var parameter of parameters) { |
| 92 var range = WebInspector.SCSSParser.rangeFromNode(node); | 104 var range = WebInspector.SCSSParser.rangeFromNode(node); |
| 93 var valueRange = WebInspector.SCSSParser.rangeFromNode(parameter); | 105 var valueRange = WebInspector.SCSSParser.rangeFromNode(parameter); |
| 94 var property = new WebInspector.SCSSParser.Property(range, nameRange
, valueRange, false); | 106 var property = new WebInspector.SCSSParser.Property(range, nameRange
, valueRange, false); |
| 95 result.mixins.push(property); | 107 output.push(property); |
| 96 } | 108 } |
| 97 }, | 109 }, |
| 98 | 110 |
| 99 /** | 111 /** |
| 100 * @param {!Gonzales.Node} node | 112 * @param {!Gonzales.Node} node |
| 101 * @param {!WebInspector.SCSSParser.Result} result | 113 * @param {!Array<!WebInspector.SCSSParser.Property>} output |
| 102 */ | 114 */ |
| 103 _handleComment: function(node, result) | 115 _handleComment: function(node, output) |
| 104 { | 116 { |
| 105 if (node.start.line !== node.end.line) | 117 if (node.start.line !== node.end.line) |
| 106 return; | 118 return; |
| 107 var innerText = /** @type {string} */(node.content); | 119 var innerText = /** @type {string} */(node.content); |
| 108 var innerResult = this.parse(innerText); | 120 var innerResult = this.parse(innerText); |
| 109 if (innerResult.properties.length !== 1 || innerResult.variables.length
!== 0 || innerResult.mixins.length !== 0) | 121 if (innerResult.length !== 1 || innerResult[0].properties.length !== 1) |
| 110 return; | 122 return; |
| 111 var property = innerResult.properties[0]; | 123 var property = innerResult[0].properties[0]; |
| 112 var disabledProperty = property.rebaseInsideOneLineComment(node); | 124 var disabledProperty = property.rebaseInsideOneLineComment(node); |
| 113 result.properties.push(disabledProperty); | 125 output.push(disabledProperty); |
| 114 }, | 126 }, |
| 115 } | 127 } |
| 116 | 128 |
| 117 /** | 129 /** |
| 118 * @param {!Gonzales.Node} node | 130 * @param {!Gonzales.Node} node |
| 119 * @return {!WebInspector.TextRange} | 131 * @return {!WebInspector.TextRange} |
| 120 */ | 132 */ |
| 121 WebInspector.SCSSParser.rangeFromNode = function(node) | 133 WebInspector.SCSSParser.rangeFromNode = function(node) |
| 122 { | 134 { |
| 123 return new WebInspector.TextRange(node.start.line - 1, node.start.column - 1
, node.end.line - 1, node.end.column); | 135 return new WebInspector.TextRange(node.start.line - 1, node.start.column - 1
, node.end.line - 1, node.end.column); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 160 * @return {!WebInspector.TextRange} | 172 * @return {!WebInspector.TextRange} |
| 161 */ | 173 */ |
| 162 function rebaseRange(range, lineOffset, columnOffset) | 174 function rebaseRange(range, lineOffset, columnOffset) |
| 163 { | 175 { |
| 164 return new WebInspector.TextRange(range.startLine + lineOffset, rang
e.startColumn + columnOffset, range.endLine + lineOffset, range.endColumn + colu
mnOffset); | 176 return new WebInspector.TextRange(range.startLine + lineOffset, rang
e.startColumn + columnOffset, range.endLine + lineOffset, range.endColumn + colu
mnOffset); |
| 165 } | 177 } |
| 166 } | 178 } |
| 167 } | 179 } |
| 168 | 180 |
| 169 /** | 181 /** |
| 182 * @constructor |
| 183 * @param {!Array<!WebInspector.TextRange>} selectors |
| 184 * @param {!Array<!WebInspector.SCSSParser.Property>} properties |
| 185 * @param {!WebInspector.TextRange} styleRange |
| 186 */ |
| 187 WebInspector.SCSSParser.Rule = function(selectors, properties, styleRange) |
| 188 { |
| 189 this.selectors = selectors; |
| 190 this.properties = properties; |
| 191 this.styleRange = styleRange; |
| 192 } |
| 193 |
| 194 /** |
| 170 * @param {!Gonzales.Node} node | 195 * @param {!Gonzales.Node} node |
| 171 * @param {!Array<!Gonzales.Node>} output | 196 * @param {!Array<{node: !Gonzales.Node, properties: !Array<!Gonzales.Node>}>} b
locks |
| 197 * @param {!{node: !Gonzales.Node, properties: !Array<!Gonzales.Node>}} lastBloc
k |
| 172 */ | 198 */ |
| 173 WebInspector.SCSSParser.extractNodes = function(node, output) | 199 WebInspector.SCSSParser.extractNodes = function(node, blocks, lastBlock) |
| 174 { | 200 { |
| 175 if (!Array.isArray(node.content)) | 201 if (!Array.isArray(node.content)) |
| 176 return; | 202 return; |
| 203 if (node.type === "block") { |
| 204 lastBlock = { |
| 205 node: node, |
| 206 properties: [] |
| 207 }; |
| 208 blocks.push(lastBlock); |
| 209 } |
| 177 var lastDeclaration = null; | 210 var lastDeclaration = null; |
| 211 var selectors = []; |
| 178 for (var i = 0; i < node.content.length; ++i) { | 212 for (var i = 0; i < node.content.length; ++i) { |
| 179 var child = node.content[i]; | 213 var child = node.content[i]; |
| 180 if (child.type === "declarationDelimiter" && lastDeclaration) { | 214 if (child.type === "declarationDelimiter" && lastDeclaration) { |
| 181 lastDeclaration.declarationRange.endLine = child.end.line - 1; | 215 lastDeclaration.declarationRange.endLine = child.end.line - 1; |
| 182 lastDeclaration.declarationRange.endColumn = child.end.column; | 216 lastDeclaration.declarationRange.endColumn = child.end.column; |
| 183 lastDeclaration = null; | 217 lastDeclaration = null; |
| 218 } else if (child.type === "selector") { |
| 219 selectors.push(child); |
| 220 } else if (child.type === "block") { |
| 221 child.selectors = selectors; |
| 222 selectors = []; |
| 184 } | 223 } |
| 185 if (child.type === "include" || child.type === "declaration" || child.ty
pe === "multilineComment") | 224 if (child.type === "include" || child.type === "declaration" || child.ty
pe === "multilineComment") |
| 186 output.push(child); | 225 lastBlock.properties.push(child); |
| 187 if (child.type === "declaration") { | 226 if (child.type === "declaration") { |
| 188 lastDeclaration = child; | 227 lastDeclaration = child; |
| 189 lastDeclaration.declarationRange = WebInspector.TextRange.createFrom
Location(child.start.line - 1, child.start.column - 1); | 228 lastDeclaration.declarationRange = WebInspector.TextRange.createFrom
Location(child.start.line - 1, child.start.column - 1); |
| 190 } | 229 } |
| 191 WebInspector.SCSSParser.extractNodes(child, output); | 230 WebInspector.SCSSParser.extractNodes(child, blocks, lastBlock); |
| 192 } | 231 } |
| 193 if (lastDeclaration) { | 232 if (lastDeclaration) { |
| 194 lastDeclaration.declarationRange.endLine = node.end.line - 1; | 233 lastDeclaration.declarationRange.endLine = node.end.line - 1; |
| 195 lastDeclaration.declarationRange.endColumn = node.end.column - 1; | 234 lastDeclaration.declarationRange.endColumn = node.end.column - 1; |
| 196 } | 235 } |
| 197 } | 236 } |
| OLD | NEW |