all repos — NoPaste @ 3e3c433f026c49e04d0efa0f0f39fc688ed32bb1

Resurrected - The PussTheCat.org fork of NoPaste

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

  1// CodeMirror, copyright (c) by Marijn Haverbeke and others
  2// Distributed under an MIT license: https://codemirror.net/LICENSE
  3
  4(function(mod) {
  5  if (typeof exports == "object" && typeof module == "object") // CommonJS
  6    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"),
  7        require("../../addon/mode/overlay"));
  8  else if (typeof define == "function" && define.amd) // AMD
  9    define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
 10            "../../addon/mode/overlay"], mod);
 11  else // Plain browser env
 12    mod(CodeMirror);
 13})(function(CodeMirror) {
 14  "use strict";
 15
 16  CodeMirror.defineMode("django:inner", function() {
 17    var keywords = ["block", "endblock", "for", "endfor", "true", "false", "filter", "endfilter",
 18                    "loop", "none", "self", "super", "if", "elif", "endif", "as", "else", "import",
 19                    "with", "endwith", "without", "context", "ifequal", "endifequal", "ifnotequal",
 20                    "endifnotequal", "extends", "include", "load", "comment", "endcomment",
 21                    "empty", "url", "static", "trans", "blocktrans", "endblocktrans", "now",
 22                    "regroup", "lorem", "ifchanged", "endifchanged", "firstof", "debug", "cycle",
 23                    "csrf_token", "autoescape", "endautoescape", "spaceless", "endspaceless",
 24                    "ssi", "templatetag", "verbatim", "endverbatim", "widthratio"],
 25        filters = ["add", "addslashes", "capfirst", "center", "cut", "date",
 26                   "default", "default_if_none", "dictsort",
 27                   "dictsortreversed", "divisibleby", "escape", "escapejs",
 28                   "filesizeformat", "first", "floatformat", "force_escape",
 29                   "get_digit", "iriencode", "join", "last", "length",
 30                   "length_is", "linebreaks", "linebreaksbr", "linenumbers",
 31                   "ljust", "lower", "make_list", "phone2numeric", "pluralize",
 32                   "pprint", "random", "removetags", "rjust", "safe",
 33                   "safeseq", "slice", "slugify", "stringformat", "striptags",
 34                   "time", "timesince", "timeuntil", "title", "truncatechars",
 35                   "truncatechars_html", "truncatewords", "truncatewords_html",
 36                   "unordered_list", "upper", "urlencode", "urlize",
 37                   "urlizetrunc", "wordcount", "wordwrap", "yesno"],
 38        operators = ["==", "!=", "<", ">", "<=", ">="],
 39        wordOperators = ["in", "not", "or", "and"];
 40
 41    keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b");
 42    filters = new RegExp("^\\b(" + filters.join("|") + ")\\b");
 43    operators = new RegExp("^\\b(" + operators.join("|") + ")\\b");
 44    wordOperators = new RegExp("^\\b(" + wordOperators.join("|") + ")\\b");
 45
 46    // We have to return "null" instead of null, in order to avoid string
 47    // styling as the default, when using Django templates inside HTML
 48    // element attributes
 49    function tokenBase (stream, state) {
 50      // Attempt to identify a variable, template or comment tag respectively
 51      if (stream.match("{{")) {
 52        state.tokenize = inVariable;
 53        return "tag";
 54      } else if (stream.match("{%")) {
 55        state.tokenize = inTag;
 56        return "tag";
 57      } else if (stream.match("{#")) {
 58        state.tokenize = inComment;
 59        return "comment";
 60      }
 61
 62      // Ignore completely any stream series that do not match the
 63      // Django template opening tags.
 64      while (stream.next() != null && !stream.match(/\{[{%#]/, false)) {}
 65      return null;
 66    }
 67
 68    // A string can be included in either single or double quotes (this is
 69    // the delimiter). Mark everything as a string until the start delimiter
 70    // occurs again.
 71    function inString (delimiter, previousTokenizer) {
 72      return function (stream, state) {
 73        if (!state.escapeNext && stream.eat(delimiter)) {
 74          state.tokenize = previousTokenizer;
 75        } else {
 76          if (state.escapeNext) {
 77            state.escapeNext = false;
 78          }
 79
 80          var ch = stream.next();
 81
 82          // Take into account the backslash for escaping characters, such as
 83          // the string delimiter.
 84          if (ch == "\\") {
 85            state.escapeNext = true;
 86          }
 87        }
 88
 89        return "string";
 90      };
 91    }
 92
 93    // Apply Django template variable syntax highlighting
 94    function inVariable (stream, state) {
 95      // Attempt to match a dot that precedes a property
 96      if (state.waitDot) {
 97        state.waitDot = false;
 98
 99        if (stream.peek() != ".") {
100          return "null";
101        }
102
103        // Dot followed by a non-word character should be considered an error.
104        if (stream.match(/\.\W+/)) {
105          return "error";
106        } else if (stream.eat(".")) {
107          state.waitProperty = true;
108          return "null";
109        } else {
110          throw Error ("Unexpected error while waiting for property.");
111        }
112      }
113
114      // Attempt to match a pipe that precedes a filter
115      if (state.waitPipe) {
116        state.waitPipe = false;
117
118        if (stream.peek() != "|") {
119          return "null";
120        }
121
122        // Pipe followed by a non-word character should be considered an error.
123        if (stream.match(/\.\W+/)) {
124          return "error";
125        } else if (stream.eat("|")) {
126          state.waitFilter = true;
127          return "null";
128        } else {
129          throw Error ("Unexpected error while waiting for filter.");
130        }
131      }
132
133      // Highlight properties
134      if (state.waitProperty) {
135        state.waitProperty = false;
136        if (stream.match(/\b(\w+)\b/)) {
137          state.waitDot = true;  // A property can be followed by another property
138          state.waitPipe = true;  // A property can be followed by a filter
139          return "property";
140        }
141      }
142
143      // Highlight filters
144      if (state.waitFilter) {
145          state.waitFilter = false;
146        if (stream.match(filters)) {
147          return "variable-2";
148        }
149      }
150
151      // Ignore all white spaces
152      if (stream.eatSpace()) {
153        state.waitProperty = false;
154        return "null";
155      }
156
157      // Identify numbers
158      if (stream.match(/\b\d+(\.\d+)?\b/)) {
159        return "number";
160      }
161
162      // Identify strings
163      if (stream.match("'")) {
164        state.tokenize = inString("'", state.tokenize);
165        return "string";
166      } else if (stream.match('"')) {
167        state.tokenize = inString('"', state.tokenize);
168        return "string";
169      }
170
171      // Attempt to find the variable
172      if (stream.match(/\b(\w+)\b/) && !state.foundVariable) {
173        state.waitDot = true;
174        state.waitPipe = true;  // A property can be followed by a filter
175        return "variable";
176      }
177
178      // If found closing tag reset
179      if (stream.match("}}")) {
180        state.waitProperty = null;
181        state.waitFilter = null;
182        state.waitDot = null;
183        state.waitPipe = null;
184        state.tokenize = tokenBase;
185        return "tag";
186      }
187
188      // If nothing was found, advance to the next character
189      stream.next();
190      return "null";
191    }
192
193    function inTag (stream, state) {
194      // Attempt to match a dot that precedes a property
195      if (state.waitDot) {
196        state.waitDot = false;
197
198        if (stream.peek() != ".") {
199          return "null";
200        }
201
202        // Dot followed by a non-word character should be considered an error.
203        if (stream.match(/\.\W+/)) {
204          return "error";
205        } else if (stream.eat(".")) {
206          state.waitProperty = true;
207          return "null";
208        } else {
209          throw Error ("Unexpected error while waiting for property.");
210        }
211      }
212
213      // Attempt to match a pipe that precedes a filter
214      if (state.waitPipe) {
215        state.waitPipe = false;
216
217        if (stream.peek() != "|") {
218          return "null";
219        }
220
221        // Pipe followed by a non-word character should be considered an error.
222        if (stream.match(/\.\W+/)) {
223          return "error";
224        } else if (stream.eat("|")) {
225          state.waitFilter = true;
226          return "null";
227        } else {
228          throw Error ("Unexpected error while waiting for filter.");
229        }
230      }
231
232      // Highlight properties
233      if (state.waitProperty) {
234        state.waitProperty = false;
235        if (stream.match(/\b(\w+)\b/)) {
236          state.waitDot = true;  // A property can be followed by another property
237          state.waitPipe = true;  // A property can be followed by a filter
238          return "property";
239        }
240      }
241
242      // Highlight filters
243      if (state.waitFilter) {
244          state.waitFilter = false;
245        if (stream.match(filters)) {
246          return "variable-2";
247        }
248      }
249
250      // Ignore all white spaces
251      if (stream.eatSpace()) {
252        state.waitProperty = false;
253        return "null";
254      }
255
256      // Identify numbers
257      if (stream.match(/\b\d+(\.\d+)?\b/)) {
258        return "number";
259      }
260
261      // Identify strings
262      if (stream.match("'")) {
263        state.tokenize = inString("'", state.tokenize);
264        return "string";
265      } else if (stream.match('"')) {
266        state.tokenize = inString('"', state.tokenize);
267        return "string";
268      }
269
270      // Attempt to match an operator
271      if (stream.match(operators)) {
272        return "operator";
273      }
274
275      // Attempt to match a word operator
276      if (stream.match(wordOperators)) {
277        return "keyword";
278      }
279
280      // Attempt to match a keyword
281      var keywordMatch = stream.match(keywords);
282      if (keywordMatch) {
283        if (keywordMatch[0] == "comment") {
284          state.blockCommentTag = true;
285        }
286        return "keyword";
287      }
288
289      // Attempt to match a variable
290      if (stream.match(/\b(\w+)\b/)) {
291        state.waitDot = true;
292        state.waitPipe = true;  // A property can be followed by a filter
293        return "variable";
294      }
295
296      // If found closing tag reset
297      if (stream.match("%}")) {
298        state.waitProperty = null;
299        state.waitFilter = null;
300        state.waitDot = null;
301        state.waitPipe = null;
302        // If the tag that closes is a block comment tag, we want to mark the
303        // following code as comment, until the tag closes.
304        if (state.blockCommentTag) {
305          state.blockCommentTag = false;  // Release the "lock"
306          state.tokenize = inBlockComment;
307        } else {
308          state.tokenize = tokenBase;
309        }
310        return "tag";
311      }
312
313      // If nothing was found, advance to the next character
314      stream.next();
315      return "null";
316    }
317
318    // Mark everything as comment inside the tag and the tag itself.
319    function inComment (stream, state) {
320      if (stream.match(/^.*?#\}/)) state.tokenize = tokenBase
321      else stream.skipToEnd()
322      return "comment";
323    }
324
325    // Mark everything as a comment until the `blockcomment` tag closes.
326    function inBlockComment (stream, state) {
327      if (stream.match(/\{%\s*endcomment\s*%\}/, false)) {
328        state.tokenize = inTag;
329        stream.match("{%");
330        return "tag";
331      } else {
332        stream.next();
333        return "comment";
334      }
335    }
336
337    return {
338      startState: function () {
339        return {tokenize: tokenBase};
340      },
341      token: function (stream, state) {
342        return state.tokenize(stream, state);
343      },
344      blockCommentStart: "{% comment %}",
345      blockCommentEnd: "{% endcomment %}"
346    };
347  });
348
349  CodeMirror.defineMode("django", function(config) {
350    var htmlBase = CodeMirror.getMode(config, "text/html");
351    var djangoInner = CodeMirror.getMode(config, "django:inner");
352    return CodeMirror.overlayMode(htmlBase, djangoInner);
353  });
354
355  CodeMirror.defineMIME("text/x-django", "django");
356});