codemirror-rails 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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");