Mercurial
comparison .cms/lib/codemirror/mode/pug/pug.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | |
2 // Distributed under an MIT license: https://codemirror.net/5/LICENSE | |
3 | |
4 (function(mod) { | |
5 if (typeof exports == "object" && typeof module == "object") // CommonJS | |
6 mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed")); | |
7 else if (typeof define == "function" && define.amd) // AMD | |
8 define(["../../lib/codemirror", "../javascript/javascript", "../css/css", "../htmlmixed/htmlmixed"], mod); | |
9 else // Plain browser env | |
10 mod(CodeMirror); | |
11 })(function(CodeMirror) { | |
12 "use strict"; | |
13 | |
14 CodeMirror.defineMode("pug", function (config) { | |
15 // token types | |
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 // Attributes Mode | |
50 this.isAttrs = false; | |
51 this.attrsNest = []; | |
52 this.inAttributeName = true; | |
53 this.attributeIsType = false; | |
54 this.attrValue = ''; | |
55 | |
56 // Indented Mode | |
57 this.indentOf = Infinity; | |
58 this.indentToken = ''; | |
59 | |
60 this.innerMode = null; | |
61 this.innerState = null; | |
62 | |
63 this.innerModeForLine = false; | |
64 } | |
65 /** | |
66 * Safely copy a state | |
67 * | |
68 * @return {State} | |
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 // if javaScriptLine was set at end of line, ignore it | |
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 //not the end of an attribute | |
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 // html string | |
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 // if restOfLine was set at end of line, ignore it | |
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 * Get the next token in the stream | |
533 * | |
534 * @param {Stream} stream | |
535 * @param {State} state | |
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 }); |