File modules/editor/codemirror/mode/pug/pug.js

Last commit: Sun Dec 17 01:14:09 2017 +0100	Jan Dankert	Integration eines weiteren Code-Editors: Codemirror. Demnächst müssen wir hier mal aufräumen und andere Editoren rauswerfen.
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("../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, state) 549 || doctype(stream, state) 550 || interpolation(stream, state) 551 || caseStatement(stream, state) 552 || when(stream, state) 553 || defaultStatement(stream, state) 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, state) 569 || className(stream, state) 570 || attrs(stream, state) 571 || attributesBlock(stream, state) 572 || indent(stream, state) 573 || text(stream, state) 574 || comment(stream, state) 575 || colon(stream, state) 576 || dot(stream, state) 577 || fail(stream, state); 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 });
Download modules/editor/codemirror/mode/pug/pug.js
History Sun, 17 Dec 2017 01:14:09 +0100 Jan Dankert Integration eines weiteren Code-Editors: Codemirror. Demnächst müssen wir hier mal aufräumen und andere Editoren rauswerfen.