PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / trunk
SiteOrigin CSS vtrunk
1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.10 1.5.11 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0
so-css / lib / codemirror / mode / sass / sass.js
so-css / lib / codemirror / mode / sass Last commit date
index.html 9 years ago sass.js 9 years ago sass.min.js 1 year ago test.js 9 years ago test.min.js 6 years ago
sass.js
455 lines
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../css/css"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../css/css"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 CodeMirror.defineMode("sass", function(config) {
15 var cssMode = CodeMirror.mimeModes["text/css"];
16 var propertyKeywords = cssMode.propertyKeywords || {},
17 colorKeywords = cssMode.colorKeywords || {},
18 valueKeywords = cssMode.valueKeywords || {},
19 fontProperties = cssMode.fontProperties || {};
20
21 function tokenRegexp(words) {
22 return new RegExp("^" + words.join("|"));
23 }
24
25 var keywords = ["true", "false", "null", "auto"];
26 var keywordsRegexp = new RegExp("^" + keywords.join("|"));
27
28 var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-",
29 "\\!=", "/", "\\*", "%", "and", "or", "not", ";","\\{","\\}",":"];
30 var opRegexp = tokenRegexp(operators);
31
32 var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/;
33
34 var word;
35
36 function isEndLine(stream) {
37 return !stream.peek() || stream.match(/\s+$/, false);
38 }
39
40 function urlTokens(stream, state) {
41 var ch = stream.peek();
42
43 if (ch === ")") {
44 stream.next();
45 state.tokenizer = tokenBase;
46 return "operator";
47 } else if (ch === "(") {
48 stream.next();
49 stream.eatSpace();
50
51 return "operator";
52 } else if (ch === "'" || ch === '"') {
53 state.tokenizer = buildStringTokenizer(stream.next());
54 return "string";
55 } else {
56 state.tokenizer = buildStringTokenizer(")", false);
57 return "string";
58 }
59 }
60 function comment(indentation, multiLine) {
61 return function(stream, state) {
62 if (stream.sol() && stream.indentation() <= indentation) {
63 state.tokenizer = tokenBase;
64 return tokenBase(stream, state);
65 }
66
67 if (multiLine && stream.skipTo("*/")) {
68 stream.next();
69 stream.next();
70 state.tokenizer = tokenBase;
71 } else {
72 stream.skipToEnd();
73 }
74
75 return "comment";
76 };
77 }
78
79 function buildStringTokenizer(quote, greedy) {
80 if (greedy == null) { greedy = true; }
81
82 function stringTokenizer(stream, state) {
83 var nextChar = stream.next();
84 var peekChar = stream.peek();
85 var previousChar = stream.string.charAt(stream.pos-2);
86
87 var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\"));
88
89 if (endingString) {
90 if (nextChar !== quote && greedy) { stream.next(); }
91 if (isEndLine(stream)) {
92 state.cursorHalf = 0;
93 }
94 state.tokenizer = tokenBase;
95 return "string";
96 } else if (nextChar === "#" && peekChar === "{") {
97 state.tokenizer = buildInterpolationTokenizer(stringTokenizer);
98 stream.next();
99 return "operator";
100 } else {
101 return "string";
102 }
103 }
104
105 return stringTokenizer;
106 }
107
108 function buildInterpolationTokenizer(currentTokenizer) {
109 return function(stream, state) {
110 if (stream.peek() === "}") {
111 stream.next();
112 state.tokenizer = currentTokenizer;
113 return "operator";
114 } else {
115 return tokenBase(stream, state);
116 }
117 };
118 }
119
120 function indent(state) {
121 if (state.indentCount == 0) {
122 state.indentCount++;
123 var lastScopeOffset = state.scopes[0].offset;
124 var currentOffset = lastScopeOffset + config.indentUnit;
125 state.scopes.unshift({ offset:currentOffset });
126 }
127 }
128
129 function dedent(state) {
130 if (state.scopes.length == 1) return;
131
132 state.scopes.shift();
133 }
134
135 function tokenBase(stream, state) {
136 var ch = stream.peek();
137
138 // Comment
139 if (stream.match("/*")) {
140 state.tokenizer = comment(stream.indentation(), true);
141 return state.tokenizer(stream, state);
142 }
143 if (stream.match("//")) {
144 state.tokenizer = comment(stream.indentation(), false);
145 return state.tokenizer(stream, state);
146 }
147
148 // Interpolation
149 if (stream.match("#{")) {
150 state.tokenizer = buildInterpolationTokenizer(tokenBase);
151 return "operator";
152 }
153
154 // Strings
155 if (ch === '"' || ch === "'") {
156 stream.next();
157 state.tokenizer = buildStringTokenizer(ch);
158 return "string";
159 }
160
161 if(!state.cursorHalf){// state.cursorHalf === 0
162 // first half i.e. before : for key-value pairs
163 // including selectors
164
165 if (ch === "-") {
166 if (stream.match(/^-\w+-/)) {
167 return "meta";
168 }
169 }
170
171 if (ch === ".") {
172 stream.next();
173 if (stream.match(/^[\w-]+/)) {
174 indent(state);
175 return "qualifier";
176 } else if (stream.peek() === "#") {
177 indent(state);
178 return "tag";
179 }
180 }
181
182 if (ch === "#") {
183 stream.next();
184 // ID selectors
185 if (stream.match(/^[\w-]+/)) {
186 indent(state);
187 return "builtin";
188 }
189 if (stream.peek() === "#") {
190 indent(state);
191 return "tag";
192 }
193 }
194
195 // Variables
196 if (ch === "$") {
197 stream.next();
198 stream.eatWhile(/[\w-]/);
199 return "variable-2";
200 }
201
202 // Numbers
203 if (stream.match(/^-?[0-9\.]+/))
204 return "number";
205
206 // Units
207 if (stream.match(/^(px|em|in)\b/))
208 return "unit";
209
210 if (stream.match(keywordsRegexp))
211 return "keyword";
212
213 if (stream.match(/^url/) && stream.peek() === "(") {
214 state.tokenizer = urlTokens;
215 return "atom";
216 }
217
218 if (ch === "=") {
219 // Match shortcut mixin definition
220 if (stream.match(/^=[\w-]+/)) {
221 indent(state);
222 return "meta";
223 }
224 }
225
226 if (ch === "+") {
227 // Match shortcut mixin definition
228 if (stream.match(/^\+[\w-]+/)){
229 return "variable-3";
230 }
231 }
232
233 if(ch === "@"){
234 if(stream.match(/@extend/)){
235 if(!stream.match(/\s*[\w]/))
236 dedent(state);
237 }
238 }
239
240
241 // Indent Directives
242 if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {
243 indent(state);
244 return "def";
245 }
246
247 // Other Directives
248 if (ch === "@") {
249 stream.next();
250 stream.eatWhile(/[\w-]/);
251 return "def";
252 }
253
254 if (stream.eatWhile(/[\w-]/)){
255 if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){
256 word = stream.current().toLowerCase();
257 var prop = state.prevProp + "-" + word;
258 if (propertyKeywords.hasOwnProperty(prop)) {
259 return "property";
260 } else if (propertyKeywords.hasOwnProperty(word)) {
261 state.prevProp = word;
262 return "property";
263 } else if (fontProperties.hasOwnProperty(word)) {
264 return "property";
265 }
266 return "tag";
267 }
268 else if(stream.match(/ *:/,false)){
269 indent(state);
270 state.cursorHalf = 1;
271 state.prevProp = stream.current().toLowerCase();
272 return "property";
273 }
274 else if(stream.match(/ *,/,false)){
275 return "tag";
276 }
277 else{
278 indent(state);
279 return "tag";
280 }
281 }
282
283 if(ch === ":"){
284 if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element
285 return "variable-3";
286 }
287 stream.next();
288 state.cursorHalf=1;
289 return "operator";
290 }
291
292 } // cursorHalf===0 ends here
293 else{
294
295 if (ch === "#") {
296 stream.next();
297 // Hex numbers
298 if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
299 if (isEndLine(stream)) {
300 state.cursorHalf = 0;
301 }
302 return "number";
303 }
304 }
305
306 // Numbers
307 if (stream.match(/^-?[0-9\.]+/)){
308 if (isEndLine(stream)) {
309 state.cursorHalf = 0;
310 }
311 return "number";
312 }
313
314 // Units
315 if (stream.match(/^(px|em|in)\b/)){
316 if (isEndLine(stream)) {
317 state.cursorHalf = 0;
318 }
319 return "unit";
320 }
321
322 if (stream.match(keywordsRegexp)){
323 if (isEndLine(stream)) {
324 state.cursorHalf = 0;
325 }
326 return "keyword";
327 }
328
329 if (stream.match(/^url/) && stream.peek() === "(") {
330 state.tokenizer = urlTokens;
331 if (isEndLine(stream)) {
332 state.cursorHalf = 0;
333 }
334 return "atom";
335 }
336
337 // Variables
338 if (ch === "$") {
339 stream.next();
340 stream.eatWhile(/[\w-]/);
341 if (isEndLine(stream)) {
342 state.cursorHalf = 0;
343 }
344 return "variable-2";
345 }
346
347 // bang character for !important, !default, etc.
348 if (ch === "!") {
349 stream.next();
350 state.cursorHalf = 0;
351 return stream.match(/^[\w]+/) ? "keyword": "operator";
352 }
353
354 if (stream.match(opRegexp)){
355 if (isEndLine(stream)) {
356 state.cursorHalf = 0;
357 }
358 return "operator";
359 }
360
361 // attributes
362 if (stream.eatWhile(/[\w-]/)) {
363 if (isEndLine(stream)) {
364 state.cursorHalf = 0;
365 }
366 word = stream.current().toLowerCase();
367 if (valueKeywords.hasOwnProperty(word)) {
368 return "atom";
369 } else if (colorKeywords.hasOwnProperty(word)) {
370 return "keyword";
371 } else if (propertyKeywords.hasOwnProperty(word)) {
372 state.prevProp = stream.current().toLowerCase();
373 return "property";
374 } else {
375 return "tag";
376 }
377 }
378
379 //stream.eatSpace();
380 if (isEndLine(stream)) {
381 state.cursorHalf = 0;
382 return null;
383 }
384
385 } // else ends here
386
387 if (stream.match(opRegexp))
388 return "operator";
389
390 // If we haven't returned by now, we move 1 character
391 // and return an error
392 stream.next();
393 return null;
394 }
395
396 function tokenLexer(stream, state) {
397 if (stream.sol()) state.indentCount = 0;
398 var style = state.tokenizer(stream, state);
399 var current = stream.current();
400
401 if (current === "@return" || current === "}"){
402 dedent(state);
403 }
404
405 if (style !== null) {
406 var startOfToken = stream.pos - current.length;
407
408 var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount);
409
410 var newScopes = [];
411
412 for (var i = 0; i < state.scopes.length; i++) {
413 var scope = state.scopes[i];
414
415 if (scope.offset <= withCurrentIndent)
416 newScopes.push(scope);
417 }
418
419 state.scopes = newScopes;
420 }
421
422
423 return style;
424 }
425
426 return {
427 startState: function() {
428 return {
429 tokenizer: tokenBase,
430 scopes: [{offset: 0, type: "sass"}],
431 indentCount: 0,
432 cursorHalf: 0, // cursor half tells us if cursor lies after (1)
433 // or before (0) colon (well... more or less)
434 definedVars: [],
435 definedMixins: []
436 };
437 },
438 token: function(stream, state) {
439 var style = tokenLexer(stream, state);
440
441 state.lastToken = { style: style, content: stream.current() };
442
443 return style;
444 },
445
446 indent: function(state) {
447 return state.scopes[0].offset;
448 }
449 };
450 });
451
452 CodeMirror.defineMIME("text/x-sass", "sass");
453
454 });
455