Mercurial
comparison .cms/lib/codemirror/mode/velocity/velocity.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | |
2 // Distributed under an MIT license: https://codemirror.net/5/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 | |
14 CodeMirror.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 []{}().,;? Separator? | |
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.eat("!"); | |
86 stream.eatWhile(/[\w\d\$_\.{}-]/); | |
87 // is it one of the specials? | |
88 if (specials && specials.propertyIsEnumerable(stream.current())) { | |
89 return "keyword"; | |
90 } | |
91 else { | |
92 state.lastTokenWasBuiltin = true; | |
93 state.beforeParams = true; | |
94 return "builtin"; | |
95 } | |
96 } | |
97 // is it a operator? | |
98 else if (isOperatorChar.test(ch)) { | |
99 state.lastTokenWasBuiltin = false; | |
100 stream.eatWhile(isOperatorChar); | |
101 return "operator"; | |
102 } | |
103 else { | |
104 // get the whole word | |
105 stream.eatWhile(/[\w\$_{}@]/); | |
106 var word = stream.current(); | |
107 // is it one of the listed keywords? | |
108 if (keywords && keywords.propertyIsEnumerable(word)) | |
109 return "keyword"; | |
110 // is it one of the listed functions? | |
111 if (functions && functions.propertyIsEnumerable(word) || | |
112 (stream.current().match(/^#@?[a-z0-9_]+ *$/i) && stream.peek()=="(") && | |
113 !(functions && functions.propertyIsEnumerable(word.toLowerCase()))) { | |
114 state.beforeParams = true; | |
115 state.lastTokenWasBuiltin = false; | |
116 return "keyword"; | |
117 } | |
118 if (state.inString) { | |
119 state.lastTokenWasBuiltin = false; | |
120 return "string"; | |
121 } | |
122 if (stream.pos > word.length && stream.string.charAt(stream.pos-word.length-1)=="." && state.lastTokenWasBuiltin) | |
123 return "builtin"; | |
124 // default: just a "word" | |
125 state.lastTokenWasBuiltin = false; | |
126 return null; | |
127 } | |
128 } | |
129 | |
130 function tokenString(quote) { | |
131 return function(stream, state) { | |
132 var escaped = false, next, end = false; | |
133 while ((next = stream.next()) != null) { | |
134 if ((next == quote) && !escaped) { | |
135 end = true; | |
136 break; | |
137 } | |
138 if (quote=='"' && stream.peek() == '$' && !escaped) { | |
139 state.inString = true; | |
140 end = true; | |
141 break; | |
142 } | |
143 escaped = !escaped && next == "\\"; | |
144 } | |
145 if (end) state.tokenize = tokenBase; | |
146 return "string"; | |
147 }; | |
148 } | |
149 | |
150 function tokenComment(stream, state) { | |
151 var maybeEnd = false, ch; | |
152 while (ch = stream.next()) { | |
153 if (ch == "#" && maybeEnd) { | |
154 state.tokenize = tokenBase; | |
155 break; | |
156 } | |
157 maybeEnd = (ch == "*"); | |
158 } | |
159 return "comment"; | |
160 } | |
161 | |
162 function tokenUnparsed(stream, state) { | |
163 var maybeEnd = 0, ch; | |
164 while (ch = stream.next()) { | |
165 if (ch == "#" && maybeEnd == 2) { | |
166 state.tokenize = tokenBase; | |
167 break; | |
168 } | |
169 if (ch == "]") | |
170 maybeEnd++; | |
171 else if (ch != " ") | |
172 maybeEnd = 0; | |
173 } | |
174 return "meta"; | |
175 } | |
176 // Interface | |
177 | |
178 return { | |
179 startState: function() { | |
180 return { | |
181 tokenize: tokenBase, | |
182 beforeParams: false, | |
183 inParams: false, | |
184 inString: false, | |
185 lastTokenWasBuiltin: false | |
186 }; | |
187 }, | |
188 | |
189 token: function(stream, state) { | |
190 if (stream.eatSpace()) return null; | |
191 return state.tokenize(stream, state); | |
192 }, | |
193 blockCommentStart: "#*", | |
194 blockCommentEnd: "*#", | |
195 lineComment: "##", | |
196 fold: "velocity" | |
197 }; | |
198 }); | |
199 | |
200 CodeMirror.defineMIME("text/velocity", "velocity"); | |
201 | |
202 }); |