Chromium Code Reviews| 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 */ | |
| 8 WebInspector.SCSSParser = function() | |
| 9 { | |
| 10 } | |
| 11 | |
| 12 /** | |
| 13 * @constructor | |
| 14 */ | |
| 15 WebInspector.SCSSParser.Result = function() | |
| 16 { | |
| 17 this.properties = []; | |
| 18 this.variables = []; | |
| 19 this.mixins = []; | |
| 20 } | |
| 21 | |
| 22 WebInspector.SCSSParser.prototype = { | |
| 23 /** | |
| 24 * @param {string} content | |
| 25 * @return {!Promise<!WebInspector.SCSSParser.Result>} | |
| 26 */ | |
| 27 parse: function(content) | |
| 28 { | |
| 29 this._content = content; | |
|
dgozman
2016/04/29 01:16:17
Stateless!
lushnikov
2016/04/29 01:55:41
Done.
| |
| 30 return self.runtime.loadModulePromise("gonzales") | |
|
dgozman
2016/04/29 01:16:17
Let's put this file into gonzales module and have
lushnikov
2016/04/29 01:55:42
Done.
| |
| 31 .then(this._innerParse.bind(this, content)) | |
| 32 .catch(onError) | |
| 33 | |
| 34 /** | |
| 35 * @param {*} error | |
| 36 */ | |
| 37 function onError(error) | |
| 38 { | |
| 39 console.error(error); | |
| 40 return new WebInspector.SCSSParser.Result(); | |
| 41 } | |
| 42 }, | |
| 43 | |
| 44 /** | |
| 45 * @param {string} content | |
| 46 * @return {!WebInspector.SCSSParser.Result} | |
| 47 */ | |
| 48 _innerParse: function(content) | |
| 49 { | |
| 50 var result = new WebInspector.SCSSParser.Result(); | |
| 51 var ast = null; | |
| 52 try { | |
| 53 ast = gonzales.parse(content, {syntax: "scss"}); | |
| 54 } catch (e) { | |
| 55 return result; | |
| 56 } | |
| 57 | |
| 58 var extractedNodes = []; | |
| 59 WebInspector.SCSSParser.extractNodes(ast, extractedNodes); | |
| 60 | |
| 61 for (var node of extractedNodes) { | |
| 62 if (node.type === "declaration") | |
| 63 this._handleDeclaration(node, result); | |
| 64 else if (node.type === "include") | |
| 65 this._handleInclude(node, result); | |
| 66 else if (node.type === "multilineComment" && node.start.line === nod e.end.line) | |
| 67 this._handleComment(node, result); | |
| 68 } | |
| 69 return result; | |
| 70 }, | |
| 71 | |
| 72 /** | |
| 73 * @param {!Gonzales.Node} node | |
| 74 * @param {!WebInspector.SCSSParser.Result} result | |
| 75 */ | |
| 76 _handleDeclaration: function(node, result) | |
| 77 { | |
| 78 var propertyNode = node.content.find(node => node.type === "property"); | |
| 79 var delimeterNode = node.content.find(node => node.type === "propertyDel imiter"); | |
| 80 var valueNode = node.content.find(node => node.type === "value"); | |
| 81 if (!propertyNode || !delimeterNode || !valueNode) | |
| 82 return; | |
| 83 | |
| 84 var nameRange = new WebInspector.TextRange(propertyNode.start.line - 1, propertyNode.start.column - 1, delimeterNode.start.line - 1, delimeterNode.start .column - 1); | |
| 85 var valueRange = new WebInspector.TextRange(delimeterNode.end.line - 1, delimeterNode.end.column, valueNode.end.line - 1, valueNode.end.column); | |
| 86 var range = /** @type {!WebInspector.TextRange} */(node.declarationRange ); | |
| 87 | |
| 88 var property = new WebInspector.SCSSParser.Property(range, nameRange, va lueRange, false); | |
| 89 var isVariable = !!propertyNode.content.find(node => node.type === "vari able"); | |
| 90 if (isVariable) | |
| 91 result.variables.push(property); | |
| 92 else | |
| 93 result.properties.push(property); | |
| 94 }, | |
| 95 | |
| 96 /** | |
| 97 * @param {!Gonzales.Node} node | |
| 98 * @param {!WebInspector.SCSSParser.Result} result | |
| 99 */ | |
| 100 _handleInclude: function(node, result) | |
| 101 { | |
| 102 var mixinName = node.content.find(node => node.type === "ident"); | |
| 103 if (!mixinName) | |
| 104 return; | |
| 105 var nameRange = WebInspector.SCSSParser.rangeFromNode(mixinName); | |
| 106 var mixinArguments = node.content.find(node => node.type === "arguments" ); | |
| 107 if (!mixinArguments) | |
| 108 return; | |
| 109 var parameters = mixinArguments.content.filter(node => node.type !== "de limiter" && node.type !== "space"); | |
|
dgozman
2016/04/29 01:16:17
Can we whitelist them?
lushnikov
2016/04/29 01:55:41
There are multiple different nodes: "15px" would b
| |
| 110 for (var parameter of parameters) { | |
| 111 var range = WebInspector.SCSSParser.rangeFromNode(node); | |
| 112 var valueRange = WebInspector.SCSSParser.rangeFromNode(parameter); | |
| 113 var property = new WebInspector.SCSSParser.Property(range, nameRange , valueRange, false); | |
| 114 result.mixins.push(property); | |
| 115 } | |
| 116 }, | |
| 117 | |
| 118 /** | |
| 119 * @param {!Gonzales.Node} node | |
| 120 * @param {!WebInspector.SCSSParser.Result} result | |
| 121 */ | |
| 122 _handleComment: function(node, result) | |
| 123 { | |
| 124 if (node.start.line !== node.end.line) | |
| 125 return; | |
| 126 var innerText = /** @type {string} */(node.content); | |
| 127 var innerResult = this._innerParse(innerText); | |
| 128 if (innerResult.properties.length !== 1 || innerResult.variables.length !== 0 || innerResult.mixins.length !== 0) | |
| 129 return; | |
| 130 var property = innerResult.properties[0]; | |
| 131 var disabledProperty = property.rebaseInsideOneLineComment(node); | |
| 132 result.properties.push(disabledProperty); | |
| 133 }, | |
| 134 } | |
| 135 | |
| 136 /** | |
| 137 * @param {!Gonzales.Node} node | |
| 138 * @return {!WebInspector.TextRange} | |
| 139 */ | |
| 140 WebInspector.SCSSParser.rangeFromNode = function(node) | |
| 141 { | |
| 142 return new WebInspector.TextRange(node.start.line - 1, node.start.column - 1 , node.end.line - 1, node.end.column); | |
| 143 } | |
| 144 | |
| 145 /** | |
| 146 * @constructor | |
| 147 * @param {!WebInspector.TextRange} range | |
| 148 * @param {!WebInspector.TextRange} nameRange | |
| 149 * @param {!WebInspector.TextRange} valueRange | |
| 150 * @param {boolean} disabled | |
| 151 */ | |
| 152 WebInspector.SCSSParser.Property = function(range, nameRange, valueRange, disabl ed) | |
| 153 { | |
| 154 this.range = range; | |
| 155 this.name = nameRange; | |
| 156 this.value = valueRange; | |
| 157 this.disabled = disabled; | |
| 158 } | |
| 159 | |
| 160 WebInspector.SCSSParser.Property.prototype = { | |
| 161 /** | |
| 162 * @param {!Gonzales.Node} commentNode | |
| 163 * @return {!WebInspector.SCSSParser.Property} | |
| 164 */ | |
| 165 rebaseInsideOneLineComment: function(commentNode) | |
| 166 { | |
| 167 var lineOffset = commentNode.start.line - 1; | |
| 168 var columnOffset = commentNode.start.column - 1 + 2; | |
|
dgozman
2016/04/29 01:16:17
// account 2 for "/*"
lushnikov
2016/04/29 01:55:41
Done.
| |
| 169 var range = WebInspector.SCSSParser.rangeFromNode(commentNode); | |
| 170 var name = rebaseRange(this.name, lineOffset, columnOffset); | |
| 171 var value = rebaseRange(this.value, lineOffset, columnOffset); | |
| 172 return new WebInspector.SCSSParser.Property(range, name, value, true); | |
| 173 | |
| 174 /** | |
| 175 * @param {!WebInspector.TextRange} range | |
| 176 * @param {number} lineOffset | |
| 177 * @param {number} columnOffset | |
| 178 * @return {!WebInspector.TextRange} | |
| 179 */ | |
| 180 function rebaseRange(range, lineOffset, columnOffset) | |
| 181 { | |
| 182 return new WebInspector.TextRange(range.startLine + lineOffset, rang e.startColumn + columnOffset, range.endLine + lineOffset, range.endColumn + colu mnOffset); | |
| 183 } | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 /** | |
| 188 * @param {!Gonzales.Node} node | |
| 189 * @param {!Array<!Gonzales.Node>} output | |
| 190 */ | |
| 191 WebInspector.SCSSParser.extractNodes = function(node, output) | |
| 192 { | |
| 193 if (!Array.isArray(node.content)) | |
| 194 return; | |
| 195 var lastDeclaration = null; | |
| 196 for (var i = 0; i < node.content.length; ++i) { | |
| 197 var child = node.content[i]; | |
| 198 if (child.type === "declarationDelimiter" && lastDeclaration) { | |
| 199 lastDeclaration.declarationRange.endLine = child.end.line - 1; | |
| 200 lastDeclaration.declarationRange.endColumn = child.end.column; | |
| 201 lastDeclaration = null; | |
| 202 } | |
| 203 if (child.type === "include" || child.type === "declaration" || child.ty pe === "multilineComment") | |
| 204 output.push(child); | |
| 205 if (child.type === "declaration") { | |
| 206 lastDeclaration = child; | |
| 207 lastDeclaration.declarationRange = WebInspector.TextRange.createFrom Location(child.start.line - 1, child.start.column - 1); | |
| 208 } | |
| 209 WebInspector.SCSSParser.extractNodes(child, output); | |
| 210 } | |
| 211 if (lastDeclaration) { | |
| 212 lastDeclaration.declarationRange.endLine = node.end.line - 1; | |
| 213 lastDeclaration.declarationRange.endColumn = node.end.column - 1; | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 /** | |
| 218 * @param {string} text | |
| 219 */ | |
| 220 WebInspector.SCSSParser.parse = function(text) | |
| 221 { | |
| 222 var parser = new WebInspector.SCSSParser(); | |
| 223 parser.parse(text).then(postMessage); | |
| 224 } | |
| 225 | |
| OLD | NEW |