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