Mercurial
comparison .cms/lib/codemirror/mode/coffeescript/coffeescript.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 /** | |
5 * Link to the project's GitHub page: | |
6 * https://github.com/pickhardt/coffeescript-codemirror-mode | |
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("coffeescript", function(conf, parserConf) { | |
19 var ERRORCLASS = "error"; | |
20 | |
21 function wordRegexp(words) { | |
22 return new RegExp("^((" + words.join(")|(") + "))\\b"); | |
23 } | |
24 | |
25 var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/; | |
26 var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/; | |
27 var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/; | |
28 var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/; | |
29 | |
30 var wordOperators = wordRegexp(["and", "or", "not", | |
31 "is", "isnt", "in", | |
32 "instanceof", "typeof"]); | |
33 var indentKeywords = ["for", "while", "loop", "if", "unless", "else", | |
34 "switch", "try", "catch", "finally", "class"]; | |
35 var commonKeywords = ["break", "by", "continue", "debugger", "delete", | |
36 "do", "in", "of", "new", "return", "then", | |
37 "this", "@", "throw", "when", "until", "extends"]; | |
38 | |
39 var keywords = wordRegexp(indentKeywords.concat(commonKeywords)); | |
40 | |
41 indentKeywords = wordRegexp(indentKeywords); | |
42 | |
43 | |
44 var stringPrefixes = /^('{3}|\"{3}|['\"])/; | |
45 var regexPrefixes = /^(\/{3}|\/)/; | |
46 var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"]; | |
47 var constants = wordRegexp(commonConstants); | |
48 | |
49 // Tokenizers | |
50 function tokenBase(stream, state) { | |
51 // Handle scope changes | |
52 if (stream.sol()) { | |
53 if (state.scope.align === null) state.scope.align = false; | |
54 var scopeOffset = state.scope.offset; | |
55 if (stream.eatSpace()) { | |
56 var lineOffset = stream.indentation(); | |
57 if (lineOffset > scopeOffset && state.scope.type == "coffee") { | |
58 return "indent"; | |
59 } else if (lineOffset < scopeOffset) { | |
60 return "dedent"; | |
61 } | |
62 return null; | |
63 } else { | |
64 if (scopeOffset > 0) { | |
65 dedent(stream, state); | |
66 } | |
67 } | |
68 } | |
69 if (stream.eatSpace()) { | |
70 return null; | |
71 } | |
72 | |
73 var ch = stream.peek(); | |
74 | |
75 // Handle docco title comment (single line) | |
76 if (stream.match("####")) { | |
77 stream.skipToEnd(); | |
78 return "comment"; | |
79 } | |
80 | |
81 // Handle multi line comments | |
82 if (stream.match("###")) { | |
83 state.tokenize = longComment; | |
84 return state.tokenize(stream, state); | |
85 } | |
86 | |
87 // Single line comment | |
88 if (ch === "#") { | |
89 stream.skipToEnd(); | |
90 return "comment"; | |
91 } | |
92 | |
93 // Handle number literals | |
94 if (stream.match(/^-?[0-9\.]/, false)) { | |
95 var floatLiteral = false; | |
96 // Floats | |
97 if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) { | |
98 floatLiteral = true; | |
99 } | |
100 if (stream.match(/^-?\d+\.\d*/)) { | |
101 floatLiteral = true; | |
102 } | |
103 if (stream.match(/^-?\.\d+/)) { | |
104 floatLiteral = true; | |
105 } | |
106 | |
107 if (floatLiteral) { | |
108 // prevent from getting extra . on 1.. | |
109 if (stream.peek() == "."){ | |
110 stream.backUp(1); | |
111 } | |
112 return "number"; | |
113 } | |
114 // Integers | |
115 var intLiteral = false; | |
116 // Hex | |
117 if (stream.match(/^-?0x[0-9a-f]+/i)) { | |
118 intLiteral = true; | |
119 } | |
120 // Decimal | |
121 if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) { | |
122 intLiteral = true; | |
123 } | |
124 // Zero by itself with no other piece of number. | |
125 if (stream.match(/^-?0(?![\dx])/i)) { | |
126 intLiteral = true; | |
127 } | |
128 if (intLiteral) { | |
129 return "number"; | |
130 } | |
131 } | |
132 | |
133 // Handle strings | |
134 if (stream.match(stringPrefixes)) { | |
135 state.tokenize = tokenFactory(stream.current(), false, "string"); | |
136 return state.tokenize(stream, state); | |
137 } | |
138 // Handle regex literals | |
139 if (stream.match(regexPrefixes)) { | |
140 if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division | |
141 state.tokenize = tokenFactory(stream.current(), true, "string-2"); | |
142 return state.tokenize(stream, state); | |
143 } else { | |
144 stream.backUp(1); | |
145 } | |
146 } | |
147 | |
148 | |
149 | |
150 // Handle operators and delimiters | |
151 if (stream.match(operators) || stream.match(wordOperators)) { | |
152 return "operator"; | |
153 } | |
154 if (stream.match(delimiters)) { | |
155 return "punctuation"; | |
156 } | |
157 | |
158 if (stream.match(constants)) { | |
159 return "atom"; | |
160 } | |
161 | |
162 if (stream.match(atProp) || state.prop && stream.match(identifiers)) { | |
163 return "property"; | |
164 } | |
165 | |
166 if (stream.match(keywords)) { | |
167 return "keyword"; | |
168 } | |
169 | |
170 if (stream.match(identifiers)) { | |
171 return "variable"; | |
172 } | |
173 | |
174 // Handle non-detected items | |
175 stream.next(); | |
176 return ERRORCLASS; | |
177 } | |
178 | |
179 function tokenFactory(delimiter, singleline, outclass) { | |
180 return function(stream, state) { | |
181 while (!stream.eol()) { | |
182 stream.eatWhile(/[^'"\/\\]/); | |
183 if (stream.eat("\\")) { | |
184 stream.next(); | |
185 if (singleline && stream.eol()) { | |
186 return outclass; | |
187 } | |
188 } else if (stream.match(delimiter)) { | |
189 state.tokenize = tokenBase; | |
190 return outclass; | |
191 } else { | |
192 stream.eat(/['"\/]/); | |
193 } | |
194 } | |
195 if (singleline) { | |
196 if (parserConf.singleLineStringErrors) { | |
197 outclass = ERRORCLASS; | |
198 } else { | |
199 state.tokenize = tokenBase; | |
200 } | |
201 } | |
202 return outclass; | |
203 }; | |
204 } | |
205 | |
206 function longComment(stream, state) { | |
207 while (!stream.eol()) { | |
208 stream.eatWhile(/[^#]/); | |
209 if (stream.match("###")) { | |
210 state.tokenize = tokenBase; | |
211 break; | |
212 } | |
213 stream.eatWhile("#"); | |
214 } | |
215 return "comment"; | |
216 } | |
217 | |
218 function indent(stream, state, type) { | |
219 type = type || "coffee"; | |
220 var offset = 0, align = false, alignOffset = null; | |
221 for (var scope = state.scope; scope; scope = scope.prev) { | |
222 if (scope.type === "coffee" || scope.type == "}") { | |
223 offset = scope.offset + conf.indentUnit; | |
224 break; | |
225 } | |
226 } | |
227 if (type !== "coffee") { | |
228 align = null; | |
229 alignOffset = stream.column() + stream.current().length; | |
230 } else if (state.scope.align) { | |
231 state.scope.align = false; | |
232 } | |
233 state.scope = { | |
234 offset: offset, | |
235 type: type, | |
236 prev: state.scope, | |
237 align: align, | |
238 alignOffset: alignOffset | |
239 }; | |
240 } | |
241 | |
242 function dedent(stream, state) { | |
243 if (!state.scope.prev) return; | |
244 if (state.scope.type === "coffee") { | |
245 var _indent = stream.indentation(); | |
246 var matched = false; | |
247 for (var scope = state.scope; scope; scope = scope.prev) { | |
248 if (_indent === scope.offset) { | |
249 matched = true; | |
250 break; | |
251 } | |
252 } | |
253 if (!matched) { | |
254 return true; | |
255 } | |
256 while (state.scope.prev && state.scope.offset !== _indent) { | |
257 state.scope = state.scope.prev; | |
258 } | |
259 return false; | |
260 } else { | |
261 state.scope = state.scope.prev; | |
262 return false; | |
263 } | |
264 } | |
265 | |
266 function tokenLexer(stream, state) { | |
267 var style = state.tokenize(stream, state); | |
268 var current = stream.current(); | |
269 | |
270 // Handle scope changes. | |
271 if (current === "return") { | |
272 state.dedent = true; | |
273 } | |
274 if (((current === "->" || current === "=>") && stream.eol()) | |
275 || style === "indent") { | |
276 indent(stream, state); | |
277 } | |
278 var delimiter_index = "[({".indexOf(current); | |
279 if (delimiter_index !== -1) { | |
280 indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); | |
281 } | |
282 if (indentKeywords.exec(current)){ | |
283 indent(stream, state); | |
284 } | |
285 if (current == "then"){ | |
286 dedent(stream, state); | |
287 } | |
288 | |
289 | |
290 if (style === "dedent") { | |
291 if (dedent(stream, state)) { | |
292 return ERRORCLASS; | |
293 } | |
294 } | |
295 delimiter_index = "])}".indexOf(current); | |
296 if (delimiter_index !== -1) { | |
297 while (state.scope.type == "coffee" && state.scope.prev) | |
298 state.scope = state.scope.prev; | |
299 if (state.scope.type == current) | |
300 state.scope = state.scope.prev; | |
301 } | |
302 if (state.dedent && stream.eol()) { | |
303 if (state.scope.type == "coffee" && state.scope.prev) | |
304 state.scope = state.scope.prev; | |
305 state.dedent = false; | |
306 } | |
307 | |
308 return style; | |
309 } | |
310 | |
311 var external = { | |
312 startState: function(basecolumn) { | |
313 return { | |
314 tokenize: tokenBase, | |
315 scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false}, | |
316 prop: false, | |
317 dedent: 0 | |
318 }; | |
319 }, | |
320 | |
321 token: function(stream, state) { | |
322 var fillAlign = state.scope.align === null && state.scope; | |
323 if (fillAlign && stream.sol()) fillAlign.align = false; | |
324 | |
325 var style = tokenLexer(stream, state); | |
326 if (style && style != "comment") { | |
327 if (fillAlign) fillAlign.align = true; | |
328 state.prop = style == "punctuation" && stream.current() == "." | |
329 } | |
330 | |
331 return style; | |
332 }, | |
333 | |
334 indent: function(state, text) { | |
335 if (state.tokenize != tokenBase) return 0; | |
336 var scope = state.scope; | |
337 var closer = text && "])}".indexOf(text.charAt(0)) > -1; | |
338 if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev; | |
339 var closes = closer && scope.type === text.charAt(0); | |
340 if (scope.align) | |
341 return scope.alignOffset - (closes ? 1 : 0); | |
342 else | |
343 return (closes ? scope.prev : scope).offset; | |
344 }, | |
345 | |
346 lineComment: "#", | |
347 fold: "indent" | |
348 }; | |
349 return external; | |
350 }); | |
351 | |
352 // IANA registered media type | |
353 // https://www.iana.org/assignments/media-types/ | |
354 CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript"); | |
355 | |
356 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); | |
357 CodeMirror.defineMIME("text/coffeescript", "coffeescript"); | |
358 | |
359 }); |