1 |
|
2 |
|
3 |
|
4 | (function(mod) {
|
5 | if (typeof exports == "object" && typeof module == "object")
|
6 | mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed"));
|
7 | else if (typeof define == "function" && define.amd)
|
8 | define(["../../lib/codemirror", "../javascript/javascript", "../css/css", "../htmlmixed/htmlmixed"], mod);
|
9 | else
|
10 | mod(CodeMirror);
|
11 | })(function(CodeMirror) {
|
12 | "use strict";
|
13 |
|
14 | CodeMirror.defineMode("pug", function (config) {
|
15 |
|
16 | var KEYWORD = 'keyword';
|
17 | var DOCTYPE = 'meta';
|
18 | var ID = 'builtin';
|
19 | var CLASS = 'qualifier';
|
20 |
|
21 | var ATTRS_NEST = {
|
22 | '{': '}',
|
23 | '(': ')',
|
24 | '[': ']'
|
25 | };
|
26 |
|
27 | var jsMode = CodeMirror.getMode(config, 'javascript');
|
28 |
|
29 | function State() {
|
30 | this.javaScriptLine = false;
|
31 | this.javaScriptLineExcludesColon = false;
|
32 |
|
33 | this.javaScriptArguments = false;
|
34 | this.javaScriptArgumentsDepth = 0;
|
35 |
|
36 | this.isInterpolating = false;
|
37 | this.interpolationNesting = 0;
|
38 |
|
39 | this.jsState = CodeMirror.startState(jsMode);
|
40 |
|
41 | this.restOfLine = '';
|
42 |
|
43 | this.isIncludeFiltered = false;
|
44 | this.isEach = false;
|
45 |
|
46 | this.lastTag = '';
|
47 | this.scriptType = '';
|
48 |
|
49 |
|
50 | this.isAttrs = false;
|
51 | this.attrsNest = [];
|
52 | this.inAttributeName = true;
|
53 | this.attributeIsType = false;
|
54 | this.attrValue = '';
|
55 |
|
56 |
|
57 | this.indentOf = Infinity;
|
58 | this.indentToken = '';
|
59 |
|
60 | this.innerMode = null;
|
61 | this.innerState = null;
|
62 |
|
63 | this.innerModeForLine = false;
|
64 | }
|
65 | |
66 |
|
67 |
|
68 |
|
69 |
|
70 | State.prototype.copy = function () {
|
71 | var res = new State();
|
72 | res.javaScriptLine = this.javaScriptLine;
|
73 | res.javaScriptLineExcludesColon = this.javaScriptLineExcludesColon;
|
74 | res.javaScriptArguments = this.javaScriptArguments;
|
75 | res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;
|
76 | res.isInterpolating = this.isInterpolating;
|
77 | res.interpolationNesting = this.interpolationNesting;
|
78 |
|
79 | res.jsState = CodeMirror.copyState(jsMode, this.jsState);
|
80 |
|
81 | res.innerMode = this.innerMode;
|
82 | if (this.innerMode && this.innerState) {
|
83 | res.innerState = CodeMirror.copyState(this.innerMode, this.innerState);
|
84 | }
|
85 |
|
86 | res.restOfLine = this.restOfLine;
|
87 |
|
88 | res.isIncludeFiltered = this.isIncludeFiltered;
|
89 | res.isEach = this.isEach;
|
90 | res.lastTag = this.lastTag;
|
91 | res.scriptType = this.scriptType;
|
92 | res.isAttrs = this.isAttrs;
|
93 | res.attrsNest = this.attrsNest.slice();
|
94 | res.inAttributeName = this.inAttributeName;
|
95 | res.attributeIsType = this.attributeIsType;
|
96 | res.attrValue = this.attrValue;
|
97 | res.indentOf = this.indentOf;
|
98 | res.indentToken = this.indentToken;
|
99 |
|
100 | res.innerModeForLine = this.innerModeForLine;
|
101 |
|
102 | return res;
|
103 | };
|
104 |
|
105 | function javaScript(stream, state) {
|
106 | if (stream.sol()) {
|
107 |
|
108 | state.javaScriptLine = false;
|
109 | state.javaScriptLineExcludesColon = false;
|
110 | }
|
111 | if (state.javaScriptLine) {
|
112 | if (state.javaScriptLineExcludesColon && stream.peek() === ':') {
|
113 | state.javaScriptLine = false;
|
114 | state.javaScriptLineExcludesColon = false;
|
115 | return;
|
116 | }
|
117 | var tok = jsMode.token(stream, state.jsState);
|
118 | if (stream.eol()) state.javaScriptLine = false;
|
119 | return tok || true;
|
120 | }
|
121 | }
|
122 | function javaScriptArguments(stream, state) {
|
123 | if (state.javaScriptArguments) {
|
124 | if (state.javaScriptArgumentsDepth === 0 && stream.peek() !== '(') {
|
125 | state.javaScriptArguments = false;
|
126 | return;
|
127 | }
|
128 | if (stream.peek() === '(') {
|
129 | state.javaScriptArgumentsDepth++;
|
130 | } else if (stream.peek() === ')') {
|
131 | state.javaScriptArgumentsDepth--;
|
132 | }
|
133 | if (state.javaScriptArgumentsDepth === 0) {
|
134 | state.javaScriptArguments = false;
|
135 | return;
|
136 | }
|
137 |
|
138 | var tok = jsMode.token(stream, state.jsState);
|
139 | return tok || true;
|
140 | }
|
141 | }
|
142 |
|
143 | function yieldStatement(stream) {
|
144 | if (stream.match(/^yield\b/)) {
|
145 | return 'keyword';
|
146 | }
|
147 | }
|
148 |
|
149 | function doctype(stream) {
|
150 | if (stream.match(/^(?:doctype) *([^\n]+)?/)) {
|
151 | return DOCTYPE;
|
152 | }
|
153 | }
|
154 |
|
155 | function interpolation(stream, state) {
|
156 | if (stream.match('#{')) {
|
157 | state.isInterpolating = true;
|
158 | state.interpolationNesting = 0;
|
159 | return 'punctuation';
|
160 | }
|
161 | }
|
162 |
|
163 | function interpolationContinued(stream, state) {
|
164 | if (state.isInterpolating) {
|
165 | if (stream.peek() === '}') {
|
166 | state.interpolationNesting--;
|
167 | if (state.interpolationNesting < 0) {
|
168 | stream.next();
|
169 | state.isInterpolating = false;
|
170 | return 'punctuation';
|
171 | }
|
172 | } else if (stream.peek() === '{') {
|
173 | state.interpolationNesting++;
|
174 | }
|
175 | return jsMode.token(stream, state.jsState) || true;
|
176 | }
|
177 | }
|
178 |
|
179 | function caseStatement(stream, state) {
|
180 | if (stream.match(/^case\b/)) {
|
181 | state.javaScriptLine = true;
|
182 | return KEYWORD;
|
183 | }
|
184 | }
|
185 |
|
186 | function when(stream, state) {
|
187 | if (stream.match(/^when\b/)) {
|
188 | state.javaScriptLine = true;
|
189 | state.javaScriptLineExcludesColon = true;
|
190 | return KEYWORD;
|
191 | }
|
192 | }
|
193 |
|
194 | function defaultStatement(stream) {
|
195 | if (stream.match(/^default\b/)) {
|
196 | return KEYWORD;
|
197 | }
|
198 | }
|
199 |
|
200 | function extendsStatement(stream, state) {
|
201 | if (stream.match(/^extends?\b/)) {
|
202 | state.restOfLine = 'string';
|
203 | return KEYWORD;
|
204 | }
|
205 | }
|
206 |
|
207 | function append(stream, state) {
|
208 | if (stream.match(/^append\b/)) {
|
209 | state.restOfLine = 'variable';
|
210 | return KEYWORD;
|
211 | }
|
212 | }
|
213 | function prepend(stream, state) {
|
214 | if (stream.match(/^prepend\b/)) {
|
215 | state.restOfLine = 'variable';
|
216 | return KEYWORD;
|
217 | }
|
218 | }
|
219 | function block(stream, state) {
|
220 | if (stream.match(/^block\b *(?:(prepend|append)\b)?/)) {
|
221 | state.restOfLine = 'variable';
|
222 | return KEYWORD;
|
223 | }
|
224 | }
|
225 |
|
226 | function include(stream, state) {
|
227 | if (stream.match(/^include\b/)) {
|
228 | state.restOfLine = 'string';
|
229 | return KEYWORD;
|
230 | }
|
231 | }
|
232 |
|
233 | function includeFiltered(stream, state) {
|
234 | if (stream.match(/^include:([a-zA-Z0-9\-]+)/, false) && stream.match('include')) {
|
235 | state.isIncludeFiltered = true;
|
236 | return KEYWORD;
|
237 | }
|
238 | }
|
239 |
|
240 | function includeFilteredContinued(stream, state) {
|
241 | if (state.isIncludeFiltered) {
|
242 | var tok = filter(stream, state);
|
243 | state.isIncludeFiltered = false;
|
244 | state.restOfLine = 'string';
|
245 | return tok;
|
246 | }
|
247 | }
|
248 |
|
249 | function mixin(stream, state) {
|
250 | if (stream.match(/^mixin\b/)) {
|
251 | state.javaScriptLine = true;
|
252 | return KEYWORD;
|
253 | }
|
254 | }
|
255 |
|
256 | function call(stream, state) {
|
257 | if (stream.match(/^\+([-\w]+)/)) {
|
258 | if (!stream.match(/^\( *[-\w]+ *=/, false)) {
|
259 | state.javaScriptArguments = true;
|
260 | state.javaScriptArgumentsDepth = 0;
|
261 | }
|
262 | return 'variable';
|
263 | }
|
264 | if (stream.match(/^\+#{/, false)) {
|
265 | stream.next();
|
266 | state.mixinCallAfter = true;
|
267 | return interpolation(stream, state);
|
268 | }
|
269 | }
|
270 | function callArguments(stream, state) {
|
271 | if (state.mixinCallAfter) {
|
272 | state.mixinCallAfter = false;
|
273 | if (!stream.match(/^\( *[-\w]+ *=/, false)) {
|
274 | state.javaScriptArguments = true;
|
275 | state.javaScriptArgumentsDepth = 0;
|
276 | }
|
277 | return true;
|
278 | }
|
279 | }
|
280 |
|
281 | function conditional(stream, state) {
|
282 | if (stream.match(/^(if|unless|else if|else)\b/)) {
|
283 | state.javaScriptLine = true;
|
284 | return KEYWORD;
|
285 | }
|
286 | }
|
287 |
|
288 | function each(stream, state) {
|
289 | if (stream.match(/^(- *)?(each|for)\b/)) {
|
290 | state.isEach = true;
|
291 | return KEYWORD;
|
292 | }
|
293 | }
|
294 | function eachContinued(stream, state) {
|
295 | if (state.isEach) {
|
296 | if (stream.match(/^ in\b/)) {
|
297 | state.javaScriptLine = true;
|
298 | state.isEach = false;
|
299 | return KEYWORD;
|
300 | } else if (stream.sol() || stream.eol()) {
|
301 | state.isEach = false;
|
302 | } else if (stream.next()) {
|
303 | while (!stream.match(/^ in\b/, false) && stream.next());
|
304 | return 'variable';
|
305 | }
|
306 | }
|
307 | }
|
308 |
|
309 | function whileStatement(stream, state) {
|
310 | if (stream.match(/^while\b/)) {
|
311 | state.javaScriptLine = true;
|
312 | return KEYWORD;
|
313 | }
|
314 | }
|
315 |
|
316 | function tag(stream, state) {
|
317 | var captures;
|
318 | if (captures = stream.match(/^(\w(?:[-:\w]*\w)?)\/?/)) {
|
319 | state.lastTag = captures[1].toLowerCase();
|
320 | if (state.lastTag === 'script') {
|
321 | state.scriptType = 'application/javascript';
|
322 | }
|
323 | return 'tag';
|
324 | }
|
325 | }
|
326 |
|
327 | function filter(stream, state) {
|
328 | if (stream.match(/^:([\w\-]+)/)) {
|
329 | var innerMode;
|
330 | if (config && config.innerModes) {
|
331 | innerMode = config.innerModes(stream.current().substring(1));
|
332 | }
|
333 | if (!innerMode) {
|
334 | innerMode = stream.current().substring(1);
|
335 | }
|
336 | if (typeof innerMode === 'string') {
|
337 | innerMode = CodeMirror.getMode(config, innerMode);
|
338 | }
|
339 | setInnerMode(stream, state, innerMode);
|
340 | return 'atom';
|
341 | }
|
342 | }
|
343 |
|
344 | function code(stream, state) {
|
345 | if (stream.match(/^(!?=|-)/)) {
|
346 | state.javaScriptLine = true;
|
347 | return 'punctuation';
|
348 | }
|
349 | }
|
350 |
|
351 | function id(stream) {
|
352 | if (stream.match(/^#([\w-]+)/)) {
|
353 | return ID;
|
354 | }
|
355 | }
|
356 |
|
357 | function className(stream) {
|
358 | if (stream.match(/^\.([\w-]+)/)) {
|
359 | return CLASS;
|
360 | }
|
361 | }
|
362 |
|
363 | function attrs(stream, state) {
|
364 | if (stream.peek() == '(') {
|
365 | stream.next();
|
366 | state.isAttrs = true;
|
367 | state.attrsNest = [];
|
368 | state.inAttributeName = true;
|
369 | state.attrValue = '';
|
370 | state.attributeIsType = false;
|
371 | return 'punctuation';
|
372 | }
|
373 | }
|
374 |
|
375 | function attrsContinued(stream, state) {
|
376 | if (state.isAttrs) {
|
377 | if (ATTRS_NEST[stream.peek()]) {
|
378 | state.attrsNest.push(ATTRS_NEST[stream.peek()]);
|
379 | }
|
380 | if (state.attrsNest[state.attrsNest.length - 1] === stream.peek()) {
|
381 | state.attrsNest.pop();
|
382 | } else if (stream.eat(')')) {
|
383 | state.isAttrs = false;
|
384 | return 'punctuation';
|
385 | }
|
386 | if (state.inAttributeName && stream.match(/^[^=,\)!]+/)) {
|
387 | if (stream.peek() === '=' || stream.peek() === '!') {
|
388 | state.inAttributeName = false;
|
389 | state.jsState = CodeMirror.startState(jsMode);
|
390 | if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') {
|
391 | state.attributeIsType = true;
|
392 | } else {
|
393 | state.attributeIsType = false;
|
394 | }
|
395 | }
|
396 | return 'attribute';
|
397 | }
|
398 |
|
399 | var tok = jsMode.token(stream, state.jsState);
|
400 | if (state.attributeIsType && tok === 'string') {
|
401 | state.scriptType = stream.current().toString();
|
402 | }
|
403 | if (state.attrsNest.length === 0 && (tok === 'string' || tok === 'variable' || tok === 'keyword')) {
|
404 | try {
|
405 | Function('', 'var x ' + state.attrValue.replace(/,\s*$/, '').replace(/^!/, ''));
|
406 | state.inAttributeName = true;
|
407 | state.attrValue = '';
|
408 | stream.backUp(stream.current().length);
|
409 | return attrsContinued(stream, state);
|
410 | } catch (ex) {
|
411 |
|
412 | }
|
413 | }
|
414 | state.attrValue += stream.current();
|
415 | return tok || true;
|
416 | }
|
417 | }
|
418 |
|
419 | function attributesBlock(stream, state) {
|
420 | if (stream.match(/^&attributes\b/)) {
|
421 | state.javaScriptArguments = true;
|
422 | state.javaScriptArgumentsDepth = 0;
|
423 | return 'keyword';
|
424 | }
|
425 | }
|
426 |
|
427 | function indent(stream) {
|
428 | if (stream.sol() && stream.eatSpace()) {
|
429 | return 'indent';
|
430 | }
|
431 | }
|
432 |
|
433 | function comment(stream, state) {
|
434 | if (stream.match(/^ *\/\/(-)?([^\n]*)/)) {
|
435 | state.indentOf = stream.indentation();
|
436 | state.indentToken = 'comment';
|
437 | return 'comment';
|
438 | }
|
439 | }
|
440 |
|
441 | function colon(stream) {
|
442 | if (stream.match(/^: */)) {
|
443 | return 'colon';
|
444 | }
|
445 | }
|
446 |
|
447 | function text(stream, state) {
|
448 | if (stream.match(/^(?:\| ?| )([^\n]+)/)) {
|
449 | return 'string';
|
450 | }
|
451 | if (stream.match(/^(<[^\n]*)/, false)) {
|
452 |
|
453 | setInnerMode(stream, state, 'htmlmixed');
|
454 | state.innerModeForLine = true;
|
455 | return innerMode(stream, state, true);
|
456 | }
|
457 | }
|
458 |
|
459 | function dot(stream, state) {
|
460 | if (stream.eat('.')) {
|
461 | var innerMode = null;
|
462 | if (state.lastTag === 'script' && state.scriptType.toLowerCase().indexOf('javascript') != -1) {
|
463 | innerMode = state.scriptType.toLowerCase().replace(/"|'/g, '');
|
464 | } else if (state.lastTag === 'style') {
|
465 | innerMode = 'css';
|
466 | }
|
467 | setInnerMode(stream, state, innerMode);
|
468 | return 'dot';
|
469 | }
|
470 | }
|
471 |
|
472 | function fail(stream) {
|
473 | stream.next();
|
474 | return null;
|
475 | }
|
476 |
|
477 |
|
478 | function setInnerMode(stream, state, mode) {
|
479 | mode = CodeMirror.mimeModes[mode] || mode;
|
480 | mode = config.innerModes ? config.innerModes(mode) || mode : mode;
|
481 | mode = CodeMirror.mimeModes[mode] || mode;
|
482 | mode = CodeMirror.getMode(config, mode);
|
483 | state.indentOf = stream.indentation();
|
484 |
|
485 | if (mode && mode.name !== 'null') {
|
486 | state.innerMode = mode;
|
487 | } else {
|
488 | state.indentToken = 'string';
|
489 | }
|
490 | }
|
491 | function innerMode(stream, state, force) {
|
492 | if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) {
|
493 | if (state.innerMode) {
|
494 | if (!state.innerState) {
|
495 | state.innerState = state.innerMode.startState ? CodeMirror.startState(state.innerMode, stream.indentation()) : {};
|
496 | }
|
497 | return stream.hideFirstChars(state.indentOf + 2, function () {
|
498 | return state.innerMode.token(stream, state.innerState) || true;
|
499 | });
|
500 | } else {
|
501 | stream.skipToEnd();
|
502 | return state.indentToken;
|
503 | }
|
504 | } else if (stream.sol()) {
|
505 | state.indentOf = Infinity;
|
506 | state.indentToken = null;
|
507 | state.innerMode = null;
|
508 | state.innerState = null;
|
509 | }
|
510 | }
|
511 | function restOfLine(stream, state) {
|
512 | if (stream.sol()) {
|
513 |
|
514 | state.restOfLine = '';
|
515 | }
|
516 | if (state.restOfLine) {
|
517 | stream.skipToEnd();
|
518 | var tok = state.restOfLine;
|
519 | state.restOfLine = '';
|
520 | return tok;
|
521 | }
|
522 | }
|
523 |
|
524 |
|
525 | function startState() {
|
526 | return new State();
|
527 | }
|
528 | function copyState(state) {
|
529 | return state.copy();
|
530 | }
|
531 | |
532 |
|
533 |
|
534 |
|
535 |
|
536 |
|
537 | function nextToken(stream, state) {
|
538 | var tok = innerMode(stream, state)
|
539 | || restOfLine(stream, state)
|
540 | || interpolationContinued(stream, state)
|
541 | || includeFilteredContinued(stream, state)
|
542 | || eachContinued(stream, state)
|
543 | || attrsContinued(stream, state)
|
544 | || javaScript(stream, state)
|
545 | || javaScriptArguments(stream, state)
|
546 | || callArguments(stream, state)
|
547 |
|
548 | || yieldStatement(stream)
|
549 | || doctype(stream)
|
550 | || interpolation(stream, state)
|
551 | || caseStatement(stream, state)
|
552 | || when(stream, state)
|
553 | || defaultStatement(stream)
|
554 | || extendsStatement(stream, state)
|
555 | || append(stream, state)
|
556 | || prepend(stream, state)
|
557 | || block(stream, state)
|
558 | || include(stream, state)
|
559 | || includeFiltered(stream, state)
|
560 | || mixin(stream, state)
|
561 | || call(stream, state)
|
562 | || conditional(stream, state)
|
563 | || each(stream, state)
|
564 | || whileStatement(stream, state)
|
565 | || tag(stream, state)
|
566 | || filter(stream, state)
|
567 | || code(stream, state)
|
568 | || id(stream)
|
569 | || className(stream)
|
570 | || attrs(stream, state)
|
571 | || attributesBlock(stream, state)
|
572 | || indent(stream)
|
573 | || text(stream, state)
|
574 | || comment(stream, state)
|
575 | || colon(stream)
|
576 | || dot(stream, state)
|
577 | || fail(stream);
|
578 |
|
579 | return tok === true ? null : tok;
|
580 | }
|
581 | return {
|
582 | startState: startState,
|
583 | copyState: copyState,
|
584 | token: nextToken
|
585 | };
|
586 | }, 'javascript', 'css', 'htmlmixed');
|
587 |
|
588 | CodeMirror.defineMIME('text/x-pug', 'pug');
|
589 | CodeMirror.defineMIME('text/x-jade', 'pug');
|
590 |
|
591 | });
|