codemirror-rails 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,146 @@
1
+ CodeMirror.defineMode("velocity", function(config) {
2
+ function parseWords(str) {
3
+ var obj = {}, words = str.split(" ");
4
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
5
+ return obj;
6
+ }
7
+
8
+ var indentUnit = config.indentUnit
9
+ var keywords = parseWords("#end #else #break #stop #[[ #]] " +
10
+ "#{end} #{else} #{break} #{stop}");
11
+ var functions = parseWords("#if #elseif #foreach #set #include #parse #macro #define #evaluate " +
12
+ "#{if} #{elseif} #{foreach} #{set} #{include} #{parse} #{macro} #{define} #{evaluate}");
13
+ var specials = parseWords("$foreach.count $foreach.hasNext $foreach.first $foreach.last $foreach.topmost $foreach.parent $velocityCount");
14
+ var isOperatorChar = /[+\-*&%=<>!?:\/|]/;
15
+ var multiLineStrings =true;
16
+
17
+ function chain(stream, state, f) {
18
+ state.tokenize = f;
19
+ return f(stream, state);
20
+ }
21
+ function tokenBase(stream, state) {
22
+ var beforeParams = state.beforeParams;
23
+ state.beforeParams = false;
24
+ var ch = stream.next();
25
+ // start of string?
26
+ if ((ch == '"' || ch == "'") && state.inParams)
27
+ return chain(stream, state, tokenString(ch));
28
+ // is it one of the special signs []{}().,;? Seperator?
29
+ else if (/[\[\]{}\(\),;\.]/.test(ch)) {
30
+ if (ch == "(" && beforeParams) state.inParams = true;
31
+ else if (ch == ")") state.inParams = false;
32
+ return null;
33
+ }
34
+ // start of a number value?
35
+ else if (/\d/.test(ch)) {
36
+ stream.eatWhile(/[\w\.]/);
37
+ return "number";
38
+ }
39
+ // multi line comment?
40
+ else if (ch == "#" && stream.eat("*")) {
41
+ return chain(stream, state, tokenComment);
42
+ }
43
+ // unparsed content?
44
+ else if (ch == "#" && stream.match(/ *\[ *\[/)) {
45
+ return chain(stream, state, tokenUnparsed);
46
+ }
47
+ // single line comment?
48
+ else if (ch == "#" && stream.eat("#")) {
49
+ stream.skipToEnd();
50
+ return "comment";
51
+ }
52
+ // variable?
53
+ else if (ch == "$") {
54
+ stream.eatWhile(/[\w\d\$_\.{}]/);
55
+ // is it one of the specials?
56
+ if (specials && specials.propertyIsEnumerable(stream.current().toLowerCase())) {
57
+ return "keyword";
58
+ }
59
+ else {
60
+ state.beforeParams = true;
61
+ return "builtin";
62
+ }
63
+ }
64
+ // is it a operator?
65
+ else if (isOperatorChar.test(ch)) {
66
+ stream.eatWhile(isOperatorChar);
67
+ return "operator";
68
+ }
69
+ else {
70
+ // get the whole word
71
+ stream.eatWhile(/[\w\$_{}]/);
72
+ var word = stream.current().toLowerCase();
73
+ // is it one of the listed keywords?
74
+ if (keywords && keywords.propertyIsEnumerable(word))
75
+ return "keyword";
76
+ // is it one of the listed functions?
77
+ if (functions && functions.propertyIsEnumerable(word) ||
78
+ stream.current().match(/^#[a-z0-9_]+ *$/i) && stream.peek()=="(") {
79
+ state.beforeParams = true;
80
+ return "keyword";
81
+ }
82
+ // default: just a "word"
83
+ return null;
84
+ }
85
+ }
86
+
87
+ function tokenString(quote) {
88
+ return function(stream, state) {
89
+ var escaped = false, next, end = false;
90
+ while ((next = stream.next()) != null) {
91
+ if (next == quote && !escaped) {
92
+ end = true;
93
+ break;
94
+ }
95
+ escaped = !escaped && next == "\\";
96
+ }
97
+ if (end) state.tokenize = tokenBase;
98
+ return "string";
99
+ };
100
+ }
101
+
102
+ function tokenComment(stream, state) {
103
+ var maybeEnd = false, ch;
104
+ while (ch = stream.next()) {
105
+ if (ch == "#" && maybeEnd) {
106
+ state.tokenize = tokenBase;
107
+ break;
108
+ }
109
+ maybeEnd = (ch == "*");
110
+ }
111
+ return "comment";
112
+ }
113
+
114
+ function tokenUnparsed(stream, state) {
115
+ var maybeEnd = 0, ch;
116
+ while (ch = stream.next()) {
117
+ if (ch == "#" && maybeEnd == 2) {
118
+ state.tokenize = tokenBase;
119
+ break;
120
+ }
121
+ if (ch == "]")
122
+ maybeEnd++;
123
+ else if (ch != " ")
124
+ maybeEnd = 0;
125
+ }
126
+ return "meta";
127
+ }
128
+ // Interface
129
+
130
+ return {
131
+ startState: function(basecolumn) {
132
+ return {
133
+ tokenize: tokenBase,
134
+ beforeParams: false,
135
+ inParams: false
136
+ };
137
+ },
138
+
139
+ token: function(stream, state) {
140
+ if (stream.eatSpace()) return null;
141
+ return state.tokenize(stream, state);
142
+ }
143
+ };
144
+ });
145
+
146
+ CodeMirror.defineMIME("text/velocity", "velocity");
@@ -0,0 +1,481 @@
1
+ /**
2
+ * xmlpure.js
3
+ *
4
+ * Building upon and improving the CodeMirror 2 XML parser
5
+ * @author: Dror BG (deebug.dev@gmail.com)
6
+ * @date: August, 2011
7
+ */
8
+
9
+ CodeMirror.defineMode("xmlpure", function(config, parserConfig) {
10
+ // constants
11
+ var STYLE_ERROR = "error";
12
+ var STYLE_INSTRUCTION = "comment";
13
+ var STYLE_COMMENT = "comment";
14
+ var STYLE_ELEMENT_NAME = "tag";
15
+ var STYLE_ATTRIBUTE = "attribute";
16
+ var STYLE_WORD = "string";
17
+ var STYLE_TEXT = "atom";
18
+
19
+ var TAG_INSTRUCTION = "!instruction";
20
+ var TAG_CDATA = "!cdata";
21
+ var TAG_COMMENT = "!comment";
22
+ var TAG_TEXT = "!text";
23
+
24
+ var doNotIndent = {
25
+ "!cdata": true,
26
+ "!comment": true,
27
+ "!text": true,
28
+ "!instruction": true
29
+ };
30
+
31
+ // options
32
+ var indentUnit = config.indentUnit;
33
+
34
+ ///////////////////////////////////////////////////////////////////////////
35
+ // helper functions
36
+
37
+ // chain a parser to another parser
38
+ function chain(stream, state, parser) {
39
+ state.tokenize = parser;
40
+ return parser(stream, state);
41
+ }
42
+
43
+ // parse a block (comment, CDATA or text)
44
+ function inBlock(style, terminator, nextTokenize) {
45
+ return function(stream, state) {
46
+ while (!stream.eol()) {
47
+ if (stream.match(terminator)) {
48
+ popContext(state);
49
+ state.tokenize = nextTokenize;
50
+ break;
51
+ }
52
+ stream.next();
53
+ }
54
+ return style;
55
+ };
56
+ }
57
+
58
+ // go down a level in the document
59
+ // (hint: look at who calls this function to know what the contexts are)
60
+ function pushContext(state, tagName) {
61
+ var noIndent = doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.doIndent);
62
+ var newContext = {
63
+ tagName: tagName,
64
+ prev: state.context,
65
+ indent: state.context ? state.context.indent + indentUnit : 0,
66
+ lineNumber: state.lineNumber,
67
+ indented: state.indented,
68
+ noIndent: noIndent
69
+ };
70
+ state.context = newContext;
71
+ }
72
+
73
+ // go up a level in the document
74
+ function popContext(state) {
75
+ if (state.context) {
76
+ var oldContext = state.context;
77
+ state.context = oldContext.prev;
78
+ return oldContext;
79
+ }
80
+
81
+ // we shouldn't be here - it means we didn't have a context to pop
82
+ return null;
83
+ }
84
+
85
+ // return true if the current token is seperated from the tokens before it
86
+ // which means either this is the start of the line, or there is at least
87
+ // one space or tab character behind the token
88
+ // otherwise returns false
89
+ function isTokenSeparated(stream) {
90
+ return stream.sol() ||
91
+ stream.string.charAt(stream.start - 1) == " " ||
92
+ stream.string.charAt(stream.start - 1) == "\t";
93
+ }
94
+
95
+ ///////////////////////////////////////////////////////////////////////////
96
+ // context: document
97
+ //
98
+ // an XML document can contain:
99
+ // - a single declaration (if defined, it must be the very first line)
100
+ // - exactly one root element
101
+ // @todo try to actually limit the number of root elements to 1
102
+ // - zero or more comments
103
+ function parseDocument(stream, state) {
104
+ if(stream.eat("<")) {
105
+ if(stream.eat("?")) {
106
+ // processing instruction
107
+ pushContext(state, TAG_INSTRUCTION);
108
+ state.tokenize = parseProcessingInstructionStartTag;
109
+ return STYLE_INSTRUCTION;
110
+ } else if(stream.match("!--")) {
111
+ // new context: comment
112
+ pushContext(state, TAG_COMMENT);
113
+ return chain(stream, state, inBlock(STYLE_COMMENT, "-->", parseDocument));
114
+ } else if(stream.eatSpace() || stream.eol() ) {
115
+ stream.skipToEnd();
116
+ return STYLE_ERROR;
117
+ } else {
118
+ // element
119
+ state.tokenize = parseElementTagName;
120
+ return STYLE_ELEMENT_NAME;
121
+ }
122
+ }
123
+
124
+ // error on line
125
+ stream.skipToEnd();
126
+ return STYLE_ERROR;
127
+ }
128
+
129
+ ///////////////////////////////////////////////////////////////////////////
130
+ // context: XML element start-tag or end-tag
131
+ //
132
+ // - element start-tag can contain attributes
133
+ // - element start-tag may self-close (or start an element block if it doesn't)
134
+ // - element end-tag can contain only the tag name
135
+ function parseElementTagName(stream, state) {
136
+ // get the name of the tag
137
+ var startPos = stream.pos;
138
+ if(stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*/)) {
139
+ // element start-tag
140
+ var tagName = stream.string.substring(startPos, stream.pos);
141
+ pushContext(state, tagName);
142
+ state.tokenize = parseElement;
143
+ return STYLE_ELEMENT_NAME;
144
+ } else if(stream.match(/^\/[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*>/)) {
145
+ // element end-tag
146
+ var endTagName = stream.string.substring(startPos + 1, stream.pos - 1).trim();
147
+ var oldContext = popContext(state);
148
+ state.tokenize = state.context == null ? parseDocument : parseElementBlock;
149
+ if(oldContext == null || endTagName != oldContext.tagName) {
150
+ // the start and end tag names should match - error
151
+ return STYLE_ERROR;
152
+ }
153
+ return STYLE_ELEMENT_NAME;
154
+ } else {
155
+ // no tag name - error
156
+ state.tokenize = state.context == null ? parseDocument : parseElementBlock;
157
+ stream.eatWhile(/[^>]/);
158
+ stream.eat(">");
159
+ return STYLE_ERROR;
160
+ }
161
+
162
+ stream.skipToEnd();
163
+ return null;
164
+ }
165
+
166
+ function parseElement(stream, state) {
167
+ if(stream.match(/^\/>/)) {
168
+ // self-closing tag
169
+ popContext(state);
170
+ state.tokenize = state.context == null ? parseDocument : parseElementBlock;
171
+ return STYLE_ELEMENT_NAME;
172
+ } else if(stream.eat(/^>/)) {
173
+ state.tokenize = parseElementBlock;
174
+ return STYLE_ELEMENT_NAME;
175
+ } else if(isTokenSeparated(stream) && stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*=/)) {
176
+ // attribute
177
+ state.tokenize = parseAttribute;
178
+ return STYLE_ATTRIBUTE;
179
+ }
180
+
181
+ // no other options - this is an error
182
+ state.tokenize = state.context == null ? parseDocument : parseDocument;
183
+ stream.eatWhile(/[^>]/);
184
+ stream.eat(">");
185
+ return STYLE_ERROR;
186
+ }
187
+
188
+ ///////////////////////////////////////////////////////////////////////////
189
+ // context: attribute
190
+ //
191
+ // attribute values may contain everything, except:
192
+ // - the ending quote (with ' or ") - this marks the end of the value
193
+ // - the character "<" - should never appear
194
+ // - ampersand ("&") - unless it starts a reference: a string that ends with a semi-colon (";")
195
+ // ---> note: this parser is lax in what may be put into a reference string,
196
+ // ---> consult http://www.w3.org/TR/REC-xml/#NT-Reference if you want to make it tighter
197
+ function parseAttribute(stream, state) {
198
+ var quote = stream.next();
199
+ if(quote != "\"" && quote != "'") {
200
+ // attribute must be quoted
201
+ stream.skipToEnd();
202
+ state.tokenize = parseElement;
203
+ return STYLE_ERROR;
204
+ }
205
+
206
+ state.tokParams.quote = quote;
207
+ state.tokenize = parseAttributeValue;
208
+ return STYLE_WORD;
209
+ }
210
+
211
+ // @todo: find out whether this attribute value spans multiple lines,
212
+ // and if so, push a context for it in order not to indent it
213
+ // (or something of the sort..)
214
+ function parseAttributeValue(stream, state) {
215
+ var ch = "";
216
+ while(!stream.eol()) {
217
+ ch = stream.next();
218
+ if(ch == state.tokParams.quote) {
219
+ // end quote found
220
+ state.tokenize = parseElement;
221
+ return STYLE_WORD;
222
+ } else if(ch == "<") {
223
+ // can't have less-than signs in an attribute value, ever
224
+ stream.skipToEnd()
225
+ state.tokenize = parseElement;
226
+ return STYLE_ERROR;
227
+ } else if(ch == "&") {
228
+ // reference - look for a semi-colon, or return error if none found
229
+ ch = stream.next();
230
+
231
+ // make sure that semi-colon isn't right after the ampersand
232
+ if(ch == ';') {
233
+ stream.skipToEnd()
234
+ state.tokenize = parseElement;
235
+ return STYLE_ERROR;
236
+ }
237
+
238
+ // make sure no less-than characters slipped in
239
+ while(!stream.eol() && ch != ";") {
240
+ if(ch == "<") {
241
+ // can't have less-than signs in an attribute value, ever
242
+ stream.skipToEnd()
243
+ state.tokenize = parseElement;
244
+ return STYLE_ERROR;
245
+ }
246
+ ch = stream.next();
247
+ }
248
+ if(stream.eol() && ch != ";") {
249
+ // no ampersand found - error
250
+ stream.skipToEnd();
251
+ state.tokenize = parseElement;
252
+ return STYLE_ERROR;
253
+ }
254
+ }
255
+ }
256
+
257
+ // attribute value continues to next line
258
+ return STYLE_WORD;
259
+ }
260
+
261
+ ///////////////////////////////////////////////////////////////////////////
262
+ // context: element block
263
+ //
264
+ // a block can contain:
265
+ // - elements
266
+ // - text
267
+ // - CDATA sections
268
+ // - comments
269
+ function parseElementBlock(stream, state) {
270
+ if(stream.eat("<")) {
271
+ if(stream.match("?")) {
272
+ pushContext(state, TAG_INSTRUCTION);
273
+ state.tokenize = parseProcessingInstructionStartTag;
274
+ return STYLE_INSTRUCTION;
275
+ } else if(stream.match("!--")) {
276
+ // new context: comment
277
+ pushContext(state, TAG_COMMENT);
278
+ return chain(stream, state, inBlock(STYLE_COMMENT, "-->",
279
+ state.context == null ? parseDocument : parseElementBlock));
280
+ } else if(stream.match("![CDATA[")) {
281
+ // new context: CDATA section
282
+ pushContext(state, TAG_CDATA);
283
+ return chain(stream, state, inBlock(STYLE_TEXT, "]]>",
284
+ state.context == null ? parseDocument : parseElementBlock));
285
+ } else if(stream.eatSpace() || stream.eol() ) {
286
+ stream.skipToEnd();
287
+ return STYLE_ERROR;
288
+ } else {
289
+ // element
290
+ state.tokenize = parseElementTagName;
291
+ return STYLE_ELEMENT_NAME;
292
+ }
293
+ } else {
294
+ // new context: text
295
+ pushContext(state, TAG_TEXT);
296
+ state.tokenize = parseText;
297
+ return null;
298
+ }
299
+
300
+ state.tokenize = state.context == null ? parseDocument : parseElementBlock;
301
+ stream.skipToEnd();
302
+ return null;
303
+ }
304
+
305
+ function parseText(stream, state) {
306
+ stream.eatWhile(/[^<]/);
307
+ if(!stream.eol()) {
308
+ // we cannot possibly be in the document context,
309
+ // just inside an element block
310
+ popContext(state);
311
+ state.tokenize = parseElementBlock;
312
+ }
313
+ return STYLE_TEXT;
314
+ }
315
+
316
+ ///////////////////////////////////////////////////////////////////////////
317
+ // context: XML processing instructions
318
+ //
319
+ // XML processing instructions (PIs) allow documents to contain instructions for applications.
320
+ // PI format: <?name data?>
321
+ // - 'name' can be anything other than 'xml' (case-insensitive)
322
+ // - 'data' can be anything which doesn't contain '?>'
323
+ // XML declaration is a special PI (see XML declaration context below)
324
+ function parseProcessingInstructionStartTag(stream, state) {
325
+ if(stream.match("xml", true, true)) {
326
+ // xml declaration
327
+ if(state.lineNumber > 1 || stream.pos > 5) {
328
+ state.tokenize = parseDocument;
329
+ stream.skipToEnd();
330
+ return STYLE_ERROR;
331
+ } else {
332
+ state.tokenize = parseDeclarationVersion;
333
+ return STYLE_INSTRUCTION;
334
+ }
335
+ }
336
+
337
+ // regular processing instruction
338
+ if(isTokenSeparated(stream) || stream.match("?>")) {
339
+ // we have a space after the start-tag, or nothing but the end-tag
340
+ // either way - error!
341
+ state.tokenize = parseDocument;
342
+ stream.skipToEnd();
343
+ return STYLE_ERROR;
344
+ }
345
+
346
+ state.tokenize = parseProcessingInstructionBody;
347
+ return STYLE_INSTRUCTION;
348
+ }
349
+
350
+ function parseProcessingInstructionBody(stream, state) {
351
+ stream.eatWhile(/[^?]/);
352
+ if(stream.eat("?")) {
353
+ if(stream.eat(">")) {
354
+ popContext(state);
355
+ state.tokenize = state.context == null ? parseDocument : parseElementBlock;
356
+ }
357
+ }
358
+ return STYLE_INSTRUCTION;
359
+ }
360
+
361
+
362
+ ///////////////////////////////////////////////////////////////////////////
363
+ // context: XML declaration
364
+ //
365
+ // XML declaration is of the following format:
366
+ // <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
367
+ // - must start at the first character of the first line
368
+ // - may span multiple lines
369
+ // - must include 'version'
370
+ // - may include 'encoding' and 'standalone' (in that order after 'version')
371
+ // - attribute names must be lowercase
372
+ // - cannot contain anything else on the line
373
+ function parseDeclarationVersion(stream, state) {
374
+ state.tokenize = parseDeclarationEncoding;
375
+
376
+ if(isTokenSeparated(stream) && stream.match(/^version( )*=( )*"([a-zA-Z0-9_.:]|\-)+"/)) {
377
+ return STYLE_INSTRUCTION;
378
+ }
379
+ stream.skipToEnd();
380
+ return STYLE_ERROR;
381
+ }
382
+
383
+ function parseDeclarationEncoding(stream, state) {
384
+ state.tokenize = parseDeclarationStandalone;
385
+
386
+ if(isTokenSeparated(stream) && stream.match(/^encoding( )*=( )*"[A-Za-z]([A-Za-z0-9._]|\-)*"/)) {
387
+ return STYLE_INSTRUCTION;
388
+ }
389
+ return null;
390
+ }
391
+
392
+ function parseDeclarationStandalone(stream, state) {
393
+ state.tokenize = parseDeclarationEndTag;
394
+
395
+ if(isTokenSeparated(stream) && stream.match(/^standalone( )*=( )*"(yes|no)"/)) {
396
+ return STYLE_INSTRUCTION;
397
+ }
398
+ return null;
399
+ }
400
+
401
+ function parseDeclarationEndTag(stream, state) {
402
+ state.tokenize = parseDocument;
403
+
404
+ if(stream.match("?>") && stream.eol()) {
405
+ popContext(state);
406
+ return STYLE_INSTRUCTION;
407
+ }
408
+ stream.skipToEnd();
409
+ return STYLE_ERROR;
410
+ }
411
+
412
+ ///////////////////////////////////////////////////////////////////////////
413
+ // returned object
414
+ return {
415
+ electricChars: "/",
416
+
417
+ startState: function() {
418
+ return {
419
+ tokenize: parseDocument,
420
+ tokParams: {},
421
+ lineNumber: 0,
422
+ lineError: false,
423
+ context: null,
424
+ indented: 0
425
+ };
426
+ },
427
+
428
+ token: function(stream, state) {
429
+ if(stream.sol()) {
430
+ // initialize a new line
431
+ state.lineNumber++;
432
+ state.lineError = false;
433
+ state.indented = stream.indentation();
434
+ }
435
+
436
+ // eat all (the spaces) you can
437
+ if(stream.eatSpace()) return null;
438
+
439
+ // run the current tokenize function, according to the state
440
+ var style = state.tokenize(stream, state);
441
+
442
+ // is there an error somewhere in the line?
443
+ state.lineError = (state.lineError || style == "error");
444
+
445
+ return style;
446
+ },
447
+
448
+ blankLine: function(state) {
449
+ // blank lines are lines too!
450
+ state.lineNumber++;
451
+ state.lineError = false;
452
+ },
453
+
454
+ indent: function(state, textAfter) {
455
+ if(state.context) {
456
+ if(state.context.noIndent == true) {
457
+ // do not indent - no return value at all
458
+ return;
459
+ }
460
+ if(textAfter.match(/^<\/.*/)) {
461
+ // eng-tag - indent back to last context
462
+ return state.context.indent;
463
+ }
464
+ // indent to last context + regular indent unit
465
+ return state.context.indent + indentUnit;
466
+ }
467
+ return 0;
468
+ },
469
+
470
+ compareStates: function(a, b) {
471
+ if (a.indented != b.indented) return false;
472
+ for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
473
+ if (!ca || !cb) return ca == cb;
474
+ if (ca.tagName != cb.tagName) return false;
475
+ }
476
+ }
477
+ };
478
+ });
479
+
480
+ CodeMirror.defineMIME("application/xml", "purexml");
481
+ CodeMirror.defineMIME("text/xml", "purexml");