File modules/editor/codemirror/mode/django/django.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("../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 });
Download modules/editor/codemirror/mode/django/django.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.