OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 WebInspector.SASSSupport = {} | |
6 | |
7 /** | |
8 * @param {string} url | |
9 * @param {string} text | |
10 * @param {!WebInspector.TokenizerFactory} tokenizerFactory | |
11 * @return {!WebInspector.SASSSupport.AST} | |
12 */ | |
13 WebInspector.SASSSupport.parseSCSS = function(url, text, tokenizerFactory) | |
14 { | |
15 var result = WebInspector.SASSSupport._innerParseSCSS(text, tokenizerFactory ); | |
16 | |
17 var rules = [ | |
18 new WebInspector.SASSSupport.Rule("variables", result.variables), | |
19 new WebInspector.SASSSupport.Rule("properties", result.properties), | |
20 new WebInspector.SASSSupport.Rule("mixins", result.mixins) | |
21 ]; | |
22 | |
23 return new WebInspector.SASSSupport.AST(url, text, rules); | |
24 } | |
25 | |
26 /** @enum {string} */ | |
27 WebInspector.SASSSupport.SCSSParserStates = { | |
28 Initial: "Initial", | |
29 PropertyName: "PropertyName", | |
30 PropertyValue: "PropertyValue", | |
31 VariableName: "VariableName", | |
32 VariableValue: "VariableValue", | |
33 MixinName: "MixinName", | |
34 MixinValue: "MixinValue", | |
35 Media: "Media", | |
36 } | |
37 | |
38 /** | |
39 * @param {string} text | |
40 * @param {!WebInspector.TokenizerFactory} tokenizerFactory | |
41 * @return {!{variables: !Array<!WebInspector.SASSSupport.Property>, properties: !Array<!WebInspector.SASSSupport.Property>, mixins: !Array<!WebInspector.SASSSu pport.Property>}} | |
42 */ | |
43 WebInspector.SASSSupport._innerParseSCSS = function(text, tokenizerFactory) | |
44 { | |
45 var lines = text.split("\n"); | |
46 var properties = []; | |
47 var variables = []; | |
48 var mixins = []; | |
49 | |
50 var States = WebInspector.SASSSupport.SCSSParserStates; | |
51 var state = States.Initial; | |
52 var propertyName, propertyValue; | |
53 var variableName, variableValue; | |
54 var mixinName, mixinValue; | |
55 var UndefTokenType = {}; | |
56 | |
57 /** | |
58 * @param {string} tokenValue | |
59 * @param {?string} tokenTypes | |
60 * @param {number} column | |
61 * @param {number} newColumn | |
62 */ | |
63 function processToken(tokenValue, tokenTypes, column, newColumn) | |
64 { | |
65 var tokenType = tokenTypes ? tokenTypes.split(" ").keySet() : UndefToken Type; | |
66 switch (state) { | |
67 case States.Initial: | |
68 if (tokenType["css-variable-2"]) { | |
69 variableName = new WebInspector.SASSSupport.TextNode(tokenValue, new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)); | |
70 state = States.VariableName; | |
71 } else if (tokenType["css-property"] || tokenType["css-meta"]) { | |
72 propertyName = new WebInspector.SASSSupport.TextNode(tokenValue, new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)); | |
73 state = States.PropertyName; | |
74 } else if (tokenType["css-def"] && tokenValue === "@include") { | |
75 mixinName = new WebInspector.SASSSupport.TextNode(tokenValue, ne w WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)); | |
76 state = States.MixinName; | |
77 } else if (tokenType["css-comment"]) { | |
78 // The |processToken| is called per-line, so no token spans more then one line. | |
pfeldman
2015/12/09 03:50:59
huh?
| |
79 // Support only a one-line comments. | |
80 if (tokenValue.substring(0, 2) !== "/*" || tokenValue.substring( tokenValue.length - 2) !== "*/") | |
81 break; | |
82 var uncommentedText = tokenValue.substring(2, tokenValue.length - 2); | |
83 var fakeRule = "a{\n" + uncommentedText + "}"; | |
84 var result = WebInspector.SASSSupport._innerParseSCSS(fakeRule, tokenizerFactory); | |
85 if (result.properties.length === 1 && result.variables.length == = 0 && result.mixins.length === 0) { | |
86 var disabledProperty = result.properties[0]; | |
87 // We should offset property to current coordinates. | |
88 var offset = column + 2; | |
89 disabledProperty.disabled = true; | |
90 disabledProperty.range = WebInspector.TextRange.createFromLo cation(lineNumber, column); | |
91 disabledProperty.range.endColumn = newColumn; | |
92 disabledProperty.name.range.startLine = lineNumber; | |
93 disabledProperty.name.range.startColumn += offset; | |
94 disabledProperty.name.range.endLine = lineNumber; | |
95 disabledProperty.name.range.endColumn += offset; | |
96 disabledProperty.value.range.startLine = lineNumber; | |
97 disabledProperty.value.range.startColumn += offset; | |
98 disabledProperty.value.range.endLine = lineNumber; | |
99 disabledProperty.value.range.endColumn += offset; | |
100 properties.push(disabledProperty); | |
101 } | |
102 } else if (tokenType["css-def"] && tokenValue === "@media") { | |
103 state = States.Media; | |
104 } | |
105 break; | |
106 case States.VariableName: | |
107 if (tokenValue === ")" && tokenType === UndefTokenType) { | |
108 state = States.Initial; | |
109 } else if (tokenValue === ":" && tokenType === UndefTokenType) { | |
110 state = States.VariableValue; | |
111 variableValue = new WebInspector.SASSSupport.TextNode("", WebIns pector.TextRange.createFromLocation(lineNumber, newColumn)); | |
112 } else if (tokenType !== UndefTokenType) { | |
113 state = States.Initial; | |
114 } | |
115 break; | |
116 case States.VariableValue: | |
117 if (tokenValue === ";" && tokenType === UndefTokenType) { | |
118 variableValue.range.endLine = lineNumber; | |
119 variableValue.range.endColumn = column; | |
120 var variable = new WebInspector.SASSSupport.Property(variableNam e, variableValue, variableName.range.clone(), false); | |
121 variable.range.endLine = lineNumber; | |
122 variable.range.endColumn = newColumn; | |
123 variables.push(variable); | |
124 state = States.Initial; | |
125 } else { | |
126 variableValue.text += tokenValue; | |
127 } | |
128 break; | |
129 case States.PropertyName: | |
130 if (tokenValue === ":" && tokenType === UndefTokenType) { | |
131 state = States.PropertyValue; | |
132 propertyName.range.endLine = lineNumber; | |
133 propertyName.range.endColumn = column; | |
134 propertyValue = new WebInspector.SASSSupport.TextNode("", WebIns pector.TextRange.createFromLocation(lineNumber, newColumn)); | |
135 } else if (tokenType["css-property"]) { | |
136 propertyName.text += tokenValue; | |
137 } | |
138 break; | |
139 case States.PropertyValue: | |
140 if ((tokenValue === "}" || tokenValue === ";") && tokenType === Unde fTokenType) { | |
141 propertyValue.range.endLine = lineNumber; | |
142 propertyValue.range.endColumn = column; | |
143 var property = new WebInspector.SASSSupport.Property(propertyNam e, propertyValue, propertyName.range.clone(), false); | |
144 property.range.endLine = lineNumber; | |
145 property.range.endColumn = newColumn; | |
146 properties.push(property); | |
147 state = States.Initial; | |
148 } else { | |
149 propertyValue.text += tokenValue; | |
150 } | |
151 break; | |
152 case States.MixinName: | |
153 if (tokenValue === "(" && tokenType === UndefTokenType) { | |
154 state = States.MixinValue; | |
155 mixinName.range.endLine = lineNumber; | |
156 mixinName.range.endColumn = column; | |
157 mixinValue = new WebInspector.SASSSupport.TextNode("", WebInspec tor.TextRange.createFromLocation(lineNumber, newColumn)); | |
158 } else if (tokenValue === ";" && tokenType === UndefTokenType) { | |
159 state = States.Initial; | |
160 mixinValue = null; | |
161 } else { | |
162 mixinName.text += tokenValue; | |
163 } | |
164 break; | |
165 case States.MixinValue: | |
166 if (tokenValue === ")" && tokenType === UndefTokenType) { | |
167 mixinValue.range.endLine = lineNumber; | |
168 mixinValue.range.endColumn = column; | |
169 var mixin = new WebInspector.SASSSupport.Property(mixinName, /** @type {!WebInspector.SASSSupport.TextNode} */(mixinValue), mixinName.range.clon e(), false); | |
170 mixin.range.endLine = lineNumber; | |
171 mixin.range.endColumn = newColumn; | |
172 mixins.push(mixin); | |
173 state = States.Initial; | |
174 } else { | |
175 mixinValue.text += tokenValue; | |
176 } | |
177 break; | |
178 case States.Media: | |
179 if (tokenValue === "{" && tokenType === UndefTokenType) | |
180 state = States.Initial; | |
181 break; | |
182 default: | |
183 console.assert(false, "Unknown SASS parser state."); | |
184 } | |
185 } | |
186 var tokenizer = tokenizerFactory.createTokenizer("text/x-scss"); | |
187 var lineNumber; | |
188 for (lineNumber = 0; lineNumber < lines.length; ++lineNumber) { | |
189 var line = lines[lineNumber]; | |
190 tokenizer(line, processToken); | |
191 processToken("\n", null, line.length, line.length + 1); | |
192 } | |
193 return { | |
194 variables: variables, | |
195 properties: properties, | |
196 mixins: mixins | |
197 }; | |
198 } | |
199 | |
200 /** | |
201 * @constructor | |
202 */ | |
203 WebInspector.SASSSupport.Node = function() | |
204 { | |
205 this.parent = null; | |
pfeldman
2015/12/09 03:50:59
Why is the AST tree not reflective? I.e. why can't
lushnikov
2015/12/09 07:04:57
Because it's structure is simple enough to avoid r
| |
206 } | |
207 | |
208 WebInspector.SASSSupport.Node.prototype = { | |
209 /** | |
210 * @return {!WebInspector.SASSSupport.AST} | |
211 */ | |
212 root: function() | |
213 { | |
214 if (this._root) | |
215 return this._root; | |
216 this._root = this; | |
217 while (this._root.parent) | |
218 this._root = this._root.parent; | |
219 return /** @type {!WebInspector.SASSSupport.AST} */(this._root); | |
220 } | |
221 } | |
222 | |
223 /** | |
224 * @constructor | |
225 * @extends {WebInspector.SASSSupport.Node} | |
226 * @param {string} text | |
227 * @param {!WebInspector.TextRange} range | |
228 */ | |
229 WebInspector.SASSSupport.TextNode = function(text, range) | |
230 { | |
231 WebInspector.SASSSupport.Node.call(this); | |
232 this.text = text; | |
233 this.range = range; | |
234 } | |
235 | |
236 WebInspector.SASSSupport.TextNode.prototype = { | |
237 /** | |
238 * @return {!WebInspector.SASSSupport.TextNode} | |
239 */ | |
240 clone: function() | |
241 { | |
242 return new WebInspector.SASSSupport.TextNode(this.text, this.range.clone ()); | |
243 }, | |
244 | |
245 __proto__: WebInspector.SASSSupport.Node.prototype | |
246 } | |
247 | |
248 /** | |
249 * @constructor | |
250 * @extends {WebInspector.SASSSupport.Node} | |
251 * @param {!WebInspector.SASSSupport.TextNode} name | |
252 * @param {!WebInspector.SASSSupport.TextNode} value | |
253 * @param {!WebInspector.TextRange} range | |
254 * @param {boolean} disabled | |
255 */ | |
256 WebInspector.SASSSupport.Property = function(name, value, range, disabled) | |
257 { | |
258 WebInspector.SASSSupport.Node.call(this); | |
259 this.name = name; | |
260 this.value = value; | |
261 this.range = range; | |
262 this.name.parent = this; | |
263 this.value.parent = this; | |
264 this.disabled = disabled; | |
265 } | |
266 | |
267 WebInspector.SASSSupport.Property.prototype = { | |
268 /** | |
269 * @return {!WebInspector.SASSSupport.Property} | |
270 */ | |
271 clone: function() | |
272 { | |
273 return new WebInspector.SASSSupport.Property(this.name.clone(), this.val ue.clone(), this.range.clone(), this.disabled); | |
274 }, | |
275 | |
276 __proto__: WebInspector.SASSSupport.Node.prototype | |
277 } | |
278 | |
279 /** | |
280 * @constructor | |
281 * @extends {WebInspector.SASSSupport.Node} | |
282 * @param {string} selector | |
283 * @param {!Array<!WebInspector.SASSSupport.Property>} properties | |
284 */ | |
285 WebInspector.SASSSupport.Rule = function(selector, properties) | |
286 { | |
287 WebInspector.SASSSupport.Node.call(this); | |
288 this.selector = selector; | |
289 this.properties = properties; | |
290 for (var i = 0; i < this.properties.length; ++i) | |
291 this.properties[i].parent = this; | |
292 } | |
293 | |
294 WebInspector.SASSSupport.Rule.prototype = { | |
295 /** | |
296 * @return {!WebInspector.SASSSupport.Rule} | |
297 */ | |
298 clone: function() | |
299 { | |
300 var properties = []; | |
301 for (var i = 0; i < this.properties.length; ++i) | |
302 properties.push(this.properties[i].clone()); | |
303 return new WebInspector.SASSSupport.Rule(this.selector, properties); | |
304 }, | |
305 | |
306 __proto__: WebInspector.SASSSupport.Node.prototype | |
307 } | |
308 | |
309 /** | |
310 * @constructor | |
311 * @extends {WebInspector.SASSSupport.Node} | |
312 * @param {string} url | |
313 * @param {string} text | |
314 * @param {!Array<!WebInspector.SASSSupport.Rule>} rules | |
315 */ | |
316 WebInspector.SASSSupport.AST = function(url, text, rules) | |
317 { | |
318 WebInspector.SASSSupport.Node.call(this); | |
319 this.url = url; | |
320 this.rules = rules; | |
321 for (var i = 0; i < rules.length; ++i) | |
322 rules[i].parent = this; | |
323 this.text = text; | |
324 } | |
325 | |
326 WebInspector.SASSSupport.AST.prototype = { | |
327 /** | |
328 * @return {!WebInspector.SASSSupport.AST} | |
329 */ | |
330 clone: function() | |
331 { | |
332 var rules = []; | |
333 for (var i = 0; i < this.rules.length; ++i) | |
334 rules.push(this.rules[i].clone()); | |
335 return new WebInspector.SASSSupport.AST(this.url, this.text, rules); | |
336 }, | |
337 | |
338 __proto__: WebInspector.SASSSupport.Node.prototype | |
339 } | |
340 | |
OLD | NEW |