0
|
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("groovy", function(config) {
|
|
15 function words(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 var keywords = words(
|
|
21 "abstract as assert boolean break byte case catch char class const continue def default " +
|
|
22 "do double else enum extends final finally float for goto if implements import in " +
|
|
23 "instanceof int interface long native new package private protected public return " +
|
|
24 "short static strictfp super switch synchronized threadsafe throw throws trait transient " +
|
|
25 "try void volatile while");
|
|
26 var blockKeywords = words("catch class def do else enum finally for if interface switch trait try while");
|
|
27 var standaloneKeywords = words("return break continue");
|
|
28 var atoms = words("null true false this");
|
|
29
|
|
30 var curPunc;
|
|
31 function tokenBase(stream, state) {
|
|
32 var ch = stream.next();
|
|
33 if (ch == '"' || ch == "'") {
|
|
34 return startString(ch, stream, state);
|
|
35 }
|
|
36 if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
|
37 curPunc = ch;
|
|
38 return null;
|
|
39 }
|
|
40 if (/\d/.test(ch)) {
|
|
41 stream.eatWhile(/[\w\.]/);
|
|
42 if (stream.eat(/eE/)) { stream.eat(/\+\-/); stream.eatWhile(/\d/); }
|
|
43 return "number";
|
|
44 }
|
|
45 if (ch == "/") {
|
|
46 if (stream.eat("*")) {
|
|
47 state.tokenize.push(tokenComment);
|
|
48 return tokenComment(stream, state);
|
|
49 }
|
|
50 if (stream.eat("/")) {
|
|
51 stream.skipToEnd();
|
|
52 return "comment";
|
|
53 }
|
|
54 if (expectExpression(state.lastToken, false)) {
|
|
55 return startString(ch, stream, state);
|
|
56 }
|
|
57 }
|
|
58 if (ch == "-" && stream.eat(">")) {
|
|
59 curPunc = "->";
|
|
60 return null;
|
|
61 }
|
|
62 if (/[+\-*&%=<>!?|\/~]/.test(ch)) {
|
|
63 stream.eatWhile(/[+\-*&%=<>|~]/);
|
|
64 return "operator";
|
|
65 }
|
|
66 stream.eatWhile(/[\w\$_]/);
|
|
67 if (ch == "@") { stream.eatWhile(/[\w\$_\.]/); return "meta"; }
|
|
68 if (state.lastToken == ".") return "property";
|
|
69 if (stream.eat(":")) { curPunc = "proplabel"; return "property"; }
|
|
70 var cur = stream.current();
|
|
71 if (atoms.propertyIsEnumerable(cur)) { return "atom"; }
|
|
72 if (keywords.propertyIsEnumerable(cur)) {
|
|
73 if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
|
74 else if (standaloneKeywords.propertyIsEnumerable(cur)) curPunc = "standalone";
|
|
75 return "keyword";
|
|
76 }
|
|
77 return "variable";
|
|
78 }
|
|
79 tokenBase.isBase = true;
|
|
80
|
|
81 function startString(quote, stream, state) {
|
|
82 var tripleQuoted = false;
|
|
83 if (quote != "/" && stream.eat(quote)) {
|
|
84 if (stream.eat(quote)) tripleQuoted = true;
|
|
85 else return "string";
|
|
86 }
|
|
87 function t(stream, state) {
|
|
88 var escaped = false, next, end = !tripleQuoted;
|
|
89 while ((next = stream.next()) != null) {
|
|
90 if (next == quote && !escaped) {
|
|
91 if (!tripleQuoted) { break; }
|
|
92 if (stream.match(quote + quote)) { end = true; break; }
|
|
93 }
|
|
94 if (quote == '"' && next == "$" && !escaped) {
|
|
95 if (stream.eat("{")) {
|
|
96 state.tokenize.push(tokenBaseUntilBrace());
|
|
97 return "string";
|
|
98 } else if (stream.match(/^\w/, false)) {
|
|
99 state.tokenize.push(tokenVariableDeref);
|
|
100 return "string";
|
|
101 }
|
|
102 }
|
|
103 escaped = !escaped && next == "\\";
|
|
104 }
|
|
105 if (end) state.tokenize.pop();
|
|
106 return "string";
|
|
107 }
|
|
108 state.tokenize.push(t);
|
|
109 return t(stream, state);
|
|
110 }
|
|
111
|
|
112 function tokenBaseUntilBrace() {
|
|
113 var depth = 1;
|
|
114 function t(stream, state) {
|
|
115 if (stream.peek() == "}") {
|
|
116 depth--;
|
|
117 if (depth == 0) {
|
|
118 state.tokenize.pop();
|
|
119 return state.tokenize[state.tokenize.length-1](stream, state);
|
|
120 }
|
|
121 } else if (stream.peek() == "{") {
|
|
122 depth++;
|
|
123 }
|
|
124 return tokenBase(stream, state);
|
|
125 }
|
|
126 t.isBase = true;
|
|
127 return t;
|
|
128 }
|
|
129
|
|
130 function tokenVariableDeref(stream, state) {
|
|
131 var next = stream.match(/^(\.|[\w\$_]+)/)
|
|
132 if (!next || !stream.match(next[0] == "." ? /^[\w$_]/ : /^\./)) state.tokenize.pop()
|
|
133 if (!next) return state.tokenize[state.tokenize.length-1](stream, state)
|
|
134 return next[0] == "." ? null : "variable"
|
|
135 }
|
|
136
|
|
137 function tokenComment(stream, state) {
|
|
138 var maybeEnd = false, ch;
|
|
139 while (ch = stream.next()) {
|
|
140 if (ch == "/" && maybeEnd) {
|
|
141 state.tokenize.pop();
|
|
142 break;
|
|
143 }
|
|
144 maybeEnd = (ch == "*");
|
|
145 }
|
|
146 return "comment";
|
|
147 }
|
|
148
|
|
149 function expectExpression(last, newline) {
|
|
150 return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) ||
|
|
151 last == "newstatement" || last == "keyword" || last == "proplabel" ||
|
|
152 (last == "standalone" && !newline);
|
|
153 }
|
|
154
|
|
155 function Context(indented, column, type, align, prev) {
|
|
156 this.indented = indented;
|
|
157 this.column = column;
|
|
158 this.type = type;
|
|
159 this.align = align;
|
|
160 this.prev = prev;
|
|
161 }
|
|
162 function pushContext(state, col, type) {
|
|
163 return state.context = new Context(state.indented, col, type, null, state.context);
|
|
164 }
|
|
165 function popContext(state) {
|
|
166 var t = state.context.type;
|
|
167 if (t == ")" || t == "]" || t == "}")
|
|
168 state.indented = state.context.indented;
|
|
169 return state.context = state.context.prev;
|
|
170 }
|
|
171
|
|
172 // Interface
|
|
173
|
|
174 return {
|
|
175 startState: function(basecolumn) {
|
|
176 return {
|
|
177 tokenize: [tokenBase],
|
|
178 context: new Context((basecolumn || 0) - config.indentUnit, 0, "top", false),
|
|
179 indented: 0,
|
|
180 startOfLine: true,
|
|
181 lastToken: null
|
|
182 };
|
|
183 },
|
|
184
|
|
185 token: function(stream, state) {
|
|
186 var ctx = state.context;
|
|
187 if (stream.sol()) {
|
|
188 if (ctx.align == null) ctx.align = false;
|
|
189 state.indented = stream.indentation();
|
|
190 state.startOfLine = true;
|
|
191 // Automatic semicolon insertion
|
|
192 if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) {
|
|
193 popContext(state); ctx = state.context;
|
|
194 }
|
|
195 }
|
|
196 if (stream.eatSpace()) return null;
|
|
197 curPunc = null;
|
|
198 var style = state.tokenize[state.tokenize.length-1](stream, state);
|
|
199 if (style == "comment") return style;
|
|
200 if (ctx.align == null) ctx.align = true;
|
|
201
|
|
202 if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
|
|
203 // Handle indentation for {x -> \n ... }
|
|
204 else if (curPunc == "->" && ctx.type == "statement" && ctx.prev.type == "}") {
|
|
205 popContext(state);
|
|
206 state.context.align = false;
|
|
207 }
|
|
208 else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
|
209 else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
|
210 else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
|
211 else if (curPunc == "}") {
|
|
212 while (ctx.type == "statement") ctx = popContext(state);
|
|
213 if (ctx.type == "}") ctx = popContext(state);
|
|
214 while (ctx.type == "statement") ctx = popContext(state);
|
|
215 }
|
|
216 else if (curPunc == ctx.type) popContext(state);
|
|
217 else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
|
|
218 pushContext(state, stream.column(), "statement");
|
|
219 state.startOfLine = false;
|
|
220 state.lastToken = curPunc || style;
|
|
221 return style;
|
|
222 },
|
|
223
|
|
224 indent: function(state, textAfter) {
|
|
225 if (!state.tokenize[state.tokenize.length-1].isBase) return CodeMirror.Pass;
|
|
226 var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;
|
|
227 if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) ctx = ctx.prev;
|
|
228 var closing = firstChar == ctx.type;
|
|
229 if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit);
|
|
230 else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
|
231 else return ctx.indented + (closing ? 0 : config.indentUnit);
|
|
232 },
|
|
233
|
|
234 electricChars: "{}",
|
|
235 closeBrackets: {triples: "'\""},
|
|
236 fold: "brace",
|
|
237 blockCommentStart: "/*",
|
|
238 blockCommentEnd: "*/",
|
|
239 lineComment: "//"
|
|
240 };
|
|
241 });
|
|
242
|
|
243 CodeMirror.defineMIME("text/x-groovy", "groovy");
|
|
244
|
|
245 });
|