github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/app/public/codemirror/mode/smarty/smarty.js (about)

     1  // CodeMirror, copyright (c) by Marijn Haverbeke and others
     2  // Distributed under an MIT license: http://codemirror.net/LICENSE
     3  
     4  /**
     5   * Smarty 2 and 3 mode.
     6   */
     7  
     8  (function(mod) {
     9    if (typeof exports == "object" && typeof module == "object") // CommonJS
    10      mod(require("../../lib/codemirror"));
    11    else if (typeof define == "function" && define.amd) // AMD
    12      define(["../../lib/codemirror"], mod);
    13    else // Plain browser env
    14      mod(CodeMirror);
    15  })(function(CodeMirror) {
    16    "use strict";
    17  
    18    CodeMirror.defineMode("smarty", function(config, parserConf) {
    19      var rightDelimiter = parserConf.rightDelimiter || "}";
    20      var leftDelimiter = parserConf.leftDelimiter || "{";
    21      var version = parserConf.version || 2;
    22      var baseMode = CodeMirror.getMode(config, parserConf.baseMode || "null");
    23  
    24      var keyFunctions = ["debug", "extends", "function", "include", "literal"];
    25      var regs = {
    26        operatorChars: /[+\-*&%=<>!?]/,
    27        validIdentifier: /[a-zA-Z0-9_]/,
    28        stringChar: /['"]/
    29      };
    30  
    31      var last;
    32      function cont(style, lastType) {
    33        last = lastType;
    34        return style;
    35      }
    36  
    37      function chain(stream, state, parser) {
    38        state.tokenize = parser;
    39        return parser(stream, state);
    40      }
    41  
    42      // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode
    43      function doesNotCount(stream, pos) {
    44        if (pos == null) pos = stream.pos;
    45        return version === 3 && leftDelimiter == "{" &&
    46          (pos == stream.string.length || /\s/.test(stream.string.charAt(pos)));
    47      }
    48  
    49      function tokenTop(stream, state) {
    50        var string = stream.string;
    51        for (var scan = stream.pos;;) {
    52          var nextMatch = string.indexOf(leftDelimiter, scan);
    53          scan = nextMatch + leftDelimiter.length;
    54          if (nextMatch == -1 || !doesNotCount(stream, nextMatch + leftDelimiter.length)) break;
    55        }
    56        if (nextMatch == stream.pos) {
    57          stream.match(leftDelimiter);
    58          if (stream.eat("*")) {
    59            return chain(stream, state, tokenBlock("comment", "*" + rightDelimiter));
    60          } else {
    61            state.depth++;
    62            state.tokenize = tokenSmarty;
    63            last = "startTag";
    64            return "tag";
    65          }
    66        }
    67  
    68        if (nextMatch > -1) stream.string = string.slice(0, nextMatch);
    69        var token = baseMode.token(stream, state.base);
    70        if (nextMatch > -1) stream.string = string;
    71        return token;
    72      }
    73  
    74      // parsing Smarty content
    75      function tokenSmarty(stream, state) {
    76        if (stream.match(rightDelimiter, true)) {
    77          if (version === 3) {
    78            state.depth--;
    79            if (state.depth <= 0) {
    80              state.tokenize = tokenTop;
    81            }
    82          } else {
    83            state.tokenize = tokenTop;
    84          }
    85          return cont("tag", null);
    86        }
    87  
    88        if (stream.match(leftDelimiter, true)) {
    89          state.depth++;
    90          return cont("tag", "startTag");
    91        }
    92  
    93        var ch = stream.next();
    94        if (ch == "$") {
    95          stream.eatWhile(regs.validIdentifier);
    96          return cont("variable-2", "variable");
    97        } else if (ch == "|") {
    98          return cont("operator", "pipe");
    99        } else if (ch == ".") {
   100          return cont("operator", "property");
   101        } else if (regs.stringChar.test(ch)) {
   102          state.tokenize = tokenAttribute(ch);
   103          return cont("string", "string");
   104        } else if (regs.operatorChars.test(ch)) {
   105          stream.eatWhile(regs.operatorChars);
   106          return cont("operator", "operator");
   107        } else if (ch == "[" || ch == "]") {
   108          return cont("bracket", "bracket");
   109        } else if (ch == "(" || ch == ")") {
   110          return cont("bracket", "operator");
   111        } else if (/\d/.test(ch)) {
   112          stream.eatWhile(/\d/);
   113          return cont("number", "number");
   114        } else {
   115  
   116          if (state.last == "variable") {
   117            if (ch == "@") {
   118              stream.eatWhile(regs.validIdentifier);
   119              return cont("property", "property");
   120            } else if (ch == "|") {
   121              stream.eatWhile(regs.validIdentifier);
   122              return cont("qualifier", "modifier");
   123            }
   124          } else if (state.last == "pipe") {
   125            stream.eatWhile(regs.validIdentifier);
   126            return cont("qualifier", "modifier");
   127          } else if (state.last == "whitespace") {
   128            stream.eatWhile(regs.validIdentifier);
   129            return cont("attribute", "modifier");
   130          } if (state.last == "property") {
   131            stream.eatWhile(regs.validIdentifier);
   132            return cont("property", null);
   133          } else if (/\s/.test(ch)) {
   134            last = "whitespace";
   135            return null;
   136          }
   137  
   138          var str = "";
   139          if (ch != "/") {
   140            str += ch;
   141          }
   142          var c = null;
   143          while (c = stream.eat(regs.validIdentifier)) {
   144            str += c;
   145          }
   146          for (var i=0, j=keyFunctions.length; i<j; i++) {
   147            if (keyFunctions[i] == str) {
   148              return cont("keyword", "keyword");
   149            }
   150          }
   151          if (/\s/.test(ch)) {
   152            return null;
   153          }
   154          return cont("tag", "tag");
   155        }
   156      }
   157  
   158      function tokenAttribute(quote) {
   159        return function(stream, state) {
   160          var prevChar = null;
   161          var currChar = null;
   162          while (!stream.eol()) {
   163            currChar = stream.peek();
   164            if (stream.next() == quote && prevChar !== '\\') {
   165              state.tokenize = tokenSmarty;
   166              break;
   167            }
   168            prevChar = currChar;
   169          }
   170          return "string";
   171        };
   172      }
   173  
   174      function tokenBlock(style, terminator) {
   175        return function(stream, state) {
   176          while (!stream.eol()) {
   177            if (stream.match(terminator)) {
   178              state.tokenize = tokenTop;
   179              break;
   180            }
   181            stream.next();
   182          }
   183          return style;
   184        };
   185      }
   186  
   187      return {
   188        startState: function() {
   189          return {
   190            base: CodeMirror.startState(baseMode),
   191            tokenize: tokenTop,
   192            last: null,
   193            depth: 0
   194          };
   195        },
   196        copyState: function(state) {
   197          return {
   198            base: CodeMirror.copyState(baseMode, state.base),
   199            tokenize: state.tokenize,
   200            last: state.last,
   201            depth: state.depth
   202          };
   203        },
   204        innerMode: function(state) {
   205          if (state.tokenize == tokenTop)
   206            return {mode: baseMode, state: state.base};
   207        },
   208        token: function(stream, state) {
   209          var style = state.tokenize(stream, state);
   210          state.last = last;
   211          return style;
   212        },
   213        indent: function(state, text) {
   214          if (state.tokenize == tokenTop && baseMode.indent)
   215            return baseMode.indent(state.base, text);
   216          else
   217            return CodeMirror.Pass;
   218        },
   219        blockCommentStart: leftDelimiter + "*",
   220        blockCommentEnd: "*" + rightDelimiter
   221      };
   222    });
   223  
   224    CodeMirror.defineMIME("text/x-smarty", "smarty");
   225  });