aboutsummaryrefslogtreecommitdiff
path: root/js/codemirror/mode/javascript
diff options
context:
space:
mode:
Diffstat (limited to 'js/codemirror/mode/javascript')
-rw-r--r--js/codemirror/mode/javascript/index.html78
-rw-r--r--js/codemirror/mode/javascript/javascript.js348
2 files changed, 426 insertions, 0 deletions
diff --git a/js/codemirror/mode/javascript/index.html b/js/codemirror/mode/javascript/index.html
new file mode 100644
index 00000000..2454c818
--- /dev/null
+++ b/js/codemirror/mode/javascript/index.html
@@ -0,0 +1,78 @@
1<!doctype html>
2<html>
3 <head>
4 <title>CodeMirror 2: JavaScript mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
7 <script src="javascript.js"></script>
8 <link rel="stylesheet" href="../../theme/default.css">
9 <link rel="stylesheet" href="../../css/docs.css">
10 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11 </head>
12 <body>
13 <h1>CodeMirror 2: JavaScript mode</h1>
14
15<div><textarea id="code" name="code">
16// Demo code (the actual new parser character stream implementation)
17
18function StringStream(string) {
19 this.pos = 0;
20 this.string = string;
21}
22
23StringStream.prototype = {
24 done: function() {return this.pos >= this.string.length;},
25 peek: function() {return this.string.charAt(this.pos);},
26 next: function() {
27 if (this.pos &lt; this.string.length)
28 return this.string.charAt(this.pos++);
29 },
30 eat: function(match) {
31 var ch = this.string.charAt(this.pos);
32 if (typeof match == "string") var ok = ch == match;
33 else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);
34 if (ok) {this.pos++; return ch;}
35 },
36 eatWhile: function(match) {
37 var start = this.pos;
38 while (this.eat(match));
39 if (this.pos > start) return this.string.slice(start, this.pos);
40 },
41 backUp: function(n) {this.pos -= n;},
42 column: function() {return this.pos;},
43 eatSpace: function() {
44 var start = this.pos;
45 while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
46 return this.pos - start;
47 },
48 match: function(pattern, consume, caseInsensitive) {
49 if (typeof pattern == "string") {
50 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
51 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
52 if (consume !== false) this.pos += str.length;
53 return true;
54 }
55 }
56 else {
57 var match = this.string.slice(this.pos).match(pattern);
58 if (match &amp;&amp; consume !== false) this.pos += match[0].length;
59 return match;
60 }
61 }
62};
63</textarea></div>
64
65 <script>
66 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
67 lineNumbers: true,
68 matchBrackets: true
69 });
70 </script>
71
72 <p>JavaScript mode supports a single configuration
73 option, <code>json</code>, which will set the mode to expect JSON
74 data rather than a JavaScript program.</p>
75
76 <p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p>
77 </body>
78</html>
diff --git a/js/codemirror/mode/javascript/javascript.js b/js/codemirror/mode/javascript/javascript.js
new file mode 100644
index 00000000..bdf99525
--- /dev/null
+++ b/js/codemirror/mode/javascript/javascript.js
@@ -0,0 +1,348 @@
1CodeMirror.defineMode("javascript", function(config, parserConfig) {
2 var indentUnit = config.indentUnit;
3 var jsonMode = parserConfig.json;
4
5 // Tokenizer
6
7 var keywords = function(){
8 function kw(type) {return {type: type, style: "keyword"};}
9 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
10 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
11 return {
12 "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
13 "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
14 "var": kw("var"), "function": kw("function"), "catch": kw("catch"),
15 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
16 "in": operator, "typeof": operator, "instanceof": operator,
17 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
18 };
19 }();
20
21 var isOperatorChar = /[+\-*&%=<>!?|]/;
22
23 function chain(stream, state, f) {
24 state.tokenize = f;
25 return f(stream, state);
26 }
27
28 function nextUntilUnescaped(stream, end) {
29 var escaped = false, next;
30 while ((next = stream.next()) != null) {
31 if (next == end && !escaped)
32 return false;
33 escaped = !escaped && next == "\\";
34 }
35 return escaped;
36 }
37
38 // Used as scratch variables to communicate multiple values without
39 // consing up tons of objects.
40 var type, content;
41 function ret(tp, style, cont) {
42 type = tp; content = cont;
43 return style;
44 }
45
46 function jsTokenBase(stream, state) {
47 var ch = stream.next();
48 if (ch == '"' || ch == "'")
49 return chain(stream, state, jsTokenString(ch));
50 else if (/[\[\]{}\(\),;\:\.]/.test(ch))
51 return ret(ch);
52 else if (ch == "0" && stream.eat(/x/i)) {
53 stream.eatWhile(/[\da-f]/i);
54 return ret("number", "number");
55 }
56 else if (/\d/.test(ch)) {
57 stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/);
58 return ret("number", "number");
59 }
60 else if (ch == "/") {
61 if (stream.eat("*")) {
62 return chain(stream, state, jsTokenComment);
63 }
64 else if (stream.eat("/")) {
65 stream.skipToEnd();
66 return ret("comment", "comment");
67 }
68 else if (state.reAllowed) {
69 nextUntilUnescaped(stream, "/");
70 stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
71 return ret("regexp", "string");
72 }
73 else {
74 stream.eatWhile(isOperatorChar);
75 return ret("operator", null, stream.current());
76 }
77 }
78 else if (isOperatorChar.test(ch)) {
79 stream.eatWhile(isOperatorChar);
80 return ret("operator", null, stream.current());
81 }
82 else {
83 stream.eatWhile(/[\w\$_]/);
84 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
85 return known ? ret(known.type, known.style, word) :
86 ret("variable", "variable", word);
87 }
88 }
89
90 function jsTokenString(quote) {
91 return function(stream, state) {
92 if (!nextUntilUnescaped(stream, quote))
93 state.tokenize = jsTokenBase;
94 return ret("string", "string");
95 };
96 }
97
98 function jsTokenComment(stream, state) {
99 var maybeEnd = false, ch;
100 while (ch = stream.next()) {
101 if (ch == "/" && maybeEnd) {
102 state.tokenize = jsTokenBase;
103 break;
104 }
105 maybeEnd = (ch == "*");
106 }
107 return ret("comment", "comment");
108 }
109
110 // Parser
111
112 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
113
114 function JSLexical(indented, column, type, align, prev, info) {
115 this.indented = indented;
116 this.column = column;
117 this.type = type;
118 this.prev = prev;
119 this.info = info;
120 if (align != null) this.align = align;
121 }
122
123 function inScope(state, varname) {
124 for (var v = state.localVars; v; v = v.next)
125 if (v.name == varname) return true;
126 }
127
128 function parseJS(state, style, type, content, stream) {
129 var cc = state.cc;
130 // Communicate our context to the combinators.
131 // (Less wasteful than consing up a hundred closures on every call.)
132 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
133
134 if (!state.lexical.hasOwnProperty("align"))
135 state.lexical.align = true;
136
137 while(true) {
138 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
139 if (combinator(type, content)) {
140 while(cc.length && cc[cc.length - 1].lex)
141 cc.pop()();
142 if (cx.marked) return cx.marked;
143 if (type == "variable" && inScope(state, content)) return "variable-2";
144 return style;
145 }
146 }
147 }
148
149 // Combinator utils