openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

pug.js (16046B)


      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 });