move a few things away in to-be-integrated/
[myslice.git] / to-be-integrated / third-party / codemirror-3.15 / mode / sass / sass.js
1 CodeMirror.defineMode("sass", function(config) {
2   var tokenRegexp = function(words){
3     return new RegExp("^" + words.join("|"));
4   };
5
6   var keywords = ["true", "false", "null", "auto"];
7   var keywordsRegexp = new RegExp("^" + keywords.join("|"));
8
9   var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"];
10   var opRegexp = tokenRegexp(operators);
11
12   var pseudoElementsRegexp = /^::?[\w\-]+/;
13
14   var urlTokens = function(stream, state){
15     var ch = stream.peek();
16
17     if (ch === ")"){
18       stream.next();
19       state.tokenizer = tokenBase;
20       return "operator";
21     }else if (ch === "("){
22       stream.next();
23       stream.eatSpace();
24
25       return "operator";
26     }else if (ch === "'" || ch === '"'){
27       state.tokenizer = buildStringTokenizer(stream.next());
28       return "string";
29     }else{
30       state.tokenizer = buildStringTokenizer(")", false);
31       return "string";
32     }
33   };
34   var multilineComment = function(stream, state) {
35     if (stream.skipTo("*/")){
36       stream.next();
37       stream.next();
38       state.tokenizer = tokenBase;
39     }else {
40       stream.next();
41     }
42
43     return "comment";
44   };
45
46   var buildStringTokenizer = function(quote, greedy){
47     if(greedy == null){ greedy = true; }
48
49     function stringTokenizer(stream, state){
50       var nextChar = stream.next();
51       var peekChar = stream.peek();
52       var previousChar = stream.string.charAt(stream.pos-2);
53
54       var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\"));
55
56       /*
57       console.log("previousChar: " + previousChar);
58       console.log("nextChar: " + nextChar);
59       console.log("peekChar: " + peekChar);
60       console.log("ending: " + endingString);
61       */
62
63       if (endingString){
64         if (nextChar !== quote && greedy) { stream.next(); }
65         state.tokenizer = tokenBase;
66         return "string";
67       }else if (nextChar === "#" && peekChar === "{"){
68         state.tokenizer = buildInterpolationTokenizer(stringTokenizer);
69         stream.next();
70         return "operator";
71       }else {
72         return "string";
73       }
74     }
75
76     return stringTokenizer;
77   };
78
79   var buildInterpolationTokenizer = function(currentTokenizer){
80     return function(stream, state){
81       if (stream.peek() === "}"){
82         stream.next();
83         state.tokenizer = currentTokenizer;
84         return "operator";
85       }else{
86         return tokenBase(stream, state);
87       }
88     };
89   };
90
91   var indent = function(state){
92     if (state.indentCount == 0){
93       state.indentCount++;
94       var lastScopeOffset = state.scopes[0].offset;
95       var currentOffset = lastScopeOffset + config.indentUnit;
96       state.scopes.unshift({ offset:currentOffset });
97     }
98   };
99
100   var dedent = function(state){
101     if (state.scopes.length == 1) { return; }
102
103     state.scopes.shift();
104   };
105
106   var tokenBase = function(stream, state) {
107     var ch = stream.peek();
108
109     // Single line Comment
110     if (stream.match('//')) {
111       stream.skipToEnd();
112       return "comment";
113     }
114
115     // Multiline Comment
116     if (stream.match('/*')){
117       state.tokenizer = multilineComment;
118       return state.tokenizer(stream, state);
119     }
120
121     // Interpolation
122     if (stream.match('#{')){
123     state.tokenizer = buildInterpolationTokenizer(tokenBase);
124       return "operator";
125     }
126
127     if (ch === "."){
128       stream.next();
129
130       // Match class selectors
131       if (stream.match(/^[\w-]+/)){
132         indent(state);
133         return "atom";
134       }else if (stream.peek() === "#"){
135         indent(state);
136         return "atom";
137       }else{
138         return "operator";
139       }
140     }
141
142     if (ch === "#"){
143       stream.next();
144
145       // Hex numbers
146       if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
147         return "number";
148       }
149
150       // ID selectors
151       if (stream.match(/^[\w-]+/)){
152         indent(state);
153         return "atom";
154       }
155
156       if (stream.peek() === "#"){
157         indent(state);
158         return "atom";
159       }
160     }
161
162     // Numbers
163     if (stream.match(/^-?[0-9\.]+/)){
164       return "number";
165     }
166
167     // Units
168     if (stream.match(/^(px|em|in)\b/)){
169       return "unit";
170     }
171
172     if (stream.match(keywordsRegexp)){
173       return "keyword";
174     }
175
176     if (stream.match(/^url/) && stream.peek() === "("){
177       state.tokenizer = urlTokens;
178       return "atom";
179     }
180
181     // Variables
182     if (ch === "$"){
183       stream.next();
184       stream.eatWhile(/[\w-]/);
185
186       if (stream.peek() === ":"){
187         stream.next();
188         return "variable-2";
189       }else{
190         return "variable-3";
191       }
192     }
193
194     if (ch === "!"){
195       stream.next();
196
197       if (stream.match(/^[\w]+/)){
198         return "keyword";
199       }
200
201       return "operator";
202     }
203
204     if (ch === "="){
205       stream.next();
206
207       // Match shortcut mixin definition
208       if (stream.match(/^[\w-]+/)){
209         indent(state);
210         return "meta";
211       }else {
212         return "operator";
213       }
214     }
215
216     if (ch === "+"){
217       stream.next();
218
219       // Match shortcut mixin definition
220       if (stream.match(/^[\w-]+/)){
221         return "variable-3";
222       }else {
223         return "operator";
224       }
225     }
226
227     // Indent Directives
228     if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)){
229       indent(state);
230       return "meta";
231     }
232
233     // Other Directives
234     if (ch === "@"){
235       stream.next();
236       stream.eatWhile(/[\w-]/);
237       return "meta";
238     }
239
240     // Strings
241     if (ch === '"' || ch === "'"){
242       stream.next();
243       state.tokenizer = buildStringTokenizer(ch);
244       return "string";
245     }
246
247     // Pseudo element selectors
248     if (ch == ':' && stream.match(pseudoElementsRegexp)){
249       return "keyword";
250     }
251
252     // atoms
253     if (stream.eatWhile(/[\w-&]/)){
254       // matches a property definition
255       if (stream.peek() === ":" && !stream.match(pseudoElementsRegexp, false))
256         return "property";
257       else
258         return "atom";
259     }
260
261     if (stream.match(opRegexp)){
262       return "operator";
263     }
264
265     // If we haven't returned by now, we move 1 character
266     // and return an error
267     stream.next();
268     return null;
269   };
270
271   var tokenLexer = function(stream, state) {
272     if (stream.sol()){
273       state.indentCount = 0;
274     }
275     var style = state.tokenizer(stream, state);
276     var current = stream.current();
277
278     if (current === "@return"){
279       dedent(state);
280     }
281
282     if (style === "atom"){
283       indent(state);
284     }
285
286     if (style !== null){
287       var startOfToken = stream.pos - current.length;
288       var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount);
289
290       var newScopes = [];
291
292       for (var i = 0; i < state.scopes.length; i++){
293         var scope = state.scopes[i];
294
295         if (scope.offset <= withCurrentIndent){
296           newScopes.push(scope);
297         }
298       }
299
300       state.scopes = newScopes;
301     }
302
303
304     return style;
305   };
306
307   return {
308     startState: function() {
309       return {
310         tokenizer: tokenBase,
311         scopes: [{offset: 0, type: 'sass'}],
312         definedVars: [],
313         definedMixins: []
314       };
315     },
316     token: function(stream, state) {
317       var style = tokenLexer(stream, state);
318
319       state.lastToken = { style: style, content: stream.current() };
320
321       return style;
322     },
323
324     indent: function(state) {
325       return state.scopes[0].offset;
326     }
327   };
328 });
329
330 CodeMirror.defineMIME("text/x-sass", "sass");