all repos — NoPaste @ 3e3c433f026c49e04d0efa0f0f39fc688ed32bb1

Resurrected - The PussTheCat.org fork of NoPaste

scripts/CodeMirror/mode/velocity/velocity.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"));
  7  else if (typeof define == "function" && define.amd) // AMD
  8    define(["../../lib/codemirror"], mod);
  9  else // Plain browser env
 10    mod(CodeMirror);
 11})(function(CodeMirror) {
 12"use strict";
 13
 14CodeMirror.defineMode("velocity", function() {
 15    function parseWords(str) {
 16        var obj = {}, words = str.split(" ");
 17        for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
 18        return obj;
 19    }
 20
 21    var keywords = parseWords("#end #else #break #stop #[[ #]] " +
 22                              "#{end} #{else} #{break} #{stop}");
 23    var functions = parseWords("#if #elseif #foreach #set #include #parse #macro #define #evaluate " +
 24                               "#{if} #{elseif} #{foreach} #{set} #{include} #{parse} #{macro} #{define} #{evaluate}");
 25    var specials = parseWords("$foreach.count $foreach.hasNext $foreach.first $foreach.last $foreach.topmost $foreach.parent.count $foreach.parent.hasNext $foreach.parent.first $foreach.parent.last $foreach.parent $velocityCount $!bodyContent $bodyContent");
 26    var isOperatorChar = /[+\-*&%=<>!?:\/|]/;
 27
 28    function chain(stream, state, f) {
 29        state.tokenize = f;
 30        return f(stream, state);
 31    }
 32    function tokenBase(stream, state) {
 33        var beforeParams = state.beforeParams;
 34        state.beforeParams = false;
 35        var ch = stream.next();
 36        // start of unparsed string?
 37        if ((ch == "'") && !state.inString && state.inParams) {
 38            state.lastTokenWasBuiltin = false;
 39            return chain(stream, state, tokenString(ch));
 40        }
 41        // start of parsed string?
 42        else if ((ch == '"')) {
 43            state.lastTokenWasBuiltin = false;
 44            if (state.inString) {
 45                state.inString = false;
 46                return "string";
 47            }
 48            else if (state.inParams)
 49                return chain(stream, state, tokenString(ch));
 50        }
 51        // is it one of the special signs []{}().,;? Seperator?
 52        else if (/[\[\]{}\(\),;\.]/.test(ch)) {
 53            if (ch == "(" && beforeParams)
 54                state.inParams = true;
 55            else if (ch == ")") {
 56                state.inParams = false;
 57                state.lastTokenWasBuiltin = true;
 58            }
 59            return null;
 60        }
 61        // start of a number value?
 62        else if (/\d/.test(ch)) {
 63            state.lastTokenWasBuiltin = false;
 64            stream.eatWhile(/[\w\.]/);
 65            return "number";
 66        }
 67        // multi line comment?
 68        else if (ch == "#" && stream.eat("*")) {
 69            state.lastTokenWasBuiltin = false;
 70            return chain(stream, state, tokenComment);
 71        }
 72        // unparsed content?
 73        else if (ch == "#" && stream.match(/ *\[ *\[/)) {
 74            state.lastTokenWasBuiltin = false;
 75            return chain(stream, state, tokenUnparsed);
 76        }
 77        // single line comment?
 78        else if (ch == "#" && stream.eat("#")) {
 79            state.lastTokenWasBuiltin = false;
 80            stream.skipToEnd();
 81            return "comment";
 82        }
 83        // variable?
 84        else if (ch == "$") {
 85            stream.eatWhile(/[\w\d\$_\.{}-]/);
 86            // is it one of the specials?
 87            if (specials && specials.propertyIsEnumerable(stream.current())) {
 88                return "keyword";
 89            }
 90            else {
 91                state.lastTokenWasBuiltin = true;
 92                state.beforeParams = true;
 93                return "builtin";
 94            }
 95        }
 96        // is it a operator?
 97        else if (isOperatorChar.test(ch)) {
 98            state.lastTokenWasBuiltin = false;
 99            stream.eatWhile(isOperatorChar);
100            return "operator";
101        }
102        else {
103            // get the whole word
104            stream.eatWhile(/[\w\$_{}@]/);
105            var word = stream.current();
106            // is it one of the listed keywords?
107            if (keywords && keywords.propertyIsEnumerable(word))
108                return "keyword";
109            // is it one of the listed functions?
110            if (functions && functions.propertyIsEnumerable(word) ||
111                    (stream.current().match(/^#@?[a-z0-9_]+ *$/i) && stream.peek()=="(") &&
112                     !(functions && functions.propertyIsEnumerable(word.toLowerCase()))) {
113                state.beforeParams = true;
114                state.lastTokenWasBuiltin = false;
115                return "keyword";
116            }
117            if (state.inString) {
118                state.lastTokenWasBuiltin = false;
119                return "string";
120            }
121            if (stream.pos > word.length && stream.string.charAt(stream.pos-word.length-1)=="." && state.lastTokenWasBuiltin)
122                return "builtin";
123            // default: just a "word"
124            state.lastTokenWasBuiltin = false;
125            return null;
126        }
127    }
128
129    function tokenString(quote) {
130        return function(stream, state) {
131            var escaped = false, next, end = false;
132            while ((next = stream.next()) != null) {
133                if ((next == quote) && !escaped) {
134                    end = true;
135                    break;
136                }
137                if (quote=='"' && stream.peek() == '$' && !escaped) {
138                    state.inString = true;
139                    end = true;
140                    break;
141                }
142                escaped = !escaped && next == "\\";
143            }
144            if (end) state.tokenize = tokenBase;
145            return "string";
146        };
147    }
148
149    function tokenComment(stream, state) {
150        var maybeEnd = false, ch;
151        while (ch = stream.next()) {
152            if (ch == "#" && maybeEnd) {
153                state.tokenize = tokenBase;
154                break;
155            }
156            maybeEnd = (ch == "*");
157        }
158        return "comment";
159    }
160
161    function tokenUnparsed(stream, state) {
162        var maybeEnd = 0, ch;
163        while (ch = stream.next()) {
164            if (ch == "#" && maybeEnd == 2) {
165                state.tokenize = tokenBase;
166                break;
167            }
168            if (ch == "]")
169                maybeEnd++;
170            else if (ch != " ")
171                maybeEnd = 0;
172        }
173        return "meta";
174    }
175    // Interface
176
177    return {
178        startState: function() {
179            return {
180                tokenize: tokenBase,
181                beforeParams: false,
182                inParams: false,
183                inString: false,
184                lastTokenWasBuiltin: false
185            };
186        },
187
188        token: function(stream, state) {
189            if (stream.eatSpace()) return null;
190            return state.tokenize(stream, state);
191        },
192        blockCommentStart: "#*",
193        blockCommentEnd: "*#",
194        lineComment: "##",
195        fold: "velocity"
196    };
197});
198
199CodeMirror.defineMIME("text/velocity", "velocity");
200
201});