all repos — NoPaste @ 5c12c8a8f515faa64a6cd1d3f04d9aa60561322d

Resurrected - The PussTheCat.org fork of NoPaste

scripts/CodeMirror/mode/smarty/smarty.js (view raw)

  1// CodeMirror, copyright (c) by Marijn Haverbeke and others
  2// Distributed under an MIT license: https://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, line) {
214        if (state.tokenize == tokenTop && baseMode.indent)
215          return baseMode.indent(state.base, text, line);
216        else
217          return CodeMirror.Pass;
218      },
219      blockCommentStart: leftDelimiter + "*",
220      blockCommentEnd: "*" + rightDelimiter
221    };
222  });
223
224  CodeMirror.defineMIME("text/x-smarty", "smarty");
225});