1 |
|
2 |
|
3 |
|
4 | (function(mod) {
|
5 | if (typeof exports == "object" && typeof module == "object")
|
6 | mod(require("../../lib/codemirror"));
|
7 | else if (typeof define == "function" && define.amd)
|
8 | define(["../../lib/codemirror"], mod);
|
9 | else
|
10 | mod(CodeMirror);
|
11 | })(function(CodeMirror) {
|
12 | "use strict";
|
13 |
|
14 | function wordRegexp(words) {
|
15 | return new RegExp("^((" + words.join(")|(") + "))\\b");
|
16 | }
|
17 |
|
18 | var wordOperators = wordRegexp(["and", "or", "not", "is"]);
|
19 | var commonKeywords = ["as", "assert", "break", "class", "continue",
|
20 | "def", "del", "elif", "else", "except", "finally",
|
21 | "for", "from", "global", "if", "import",
|
22 | "lambda", "pass", "raise", "return",
|
23 | "try", "while", "with", "yield", "in"];
|
24 | var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
|
25 | "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
|
26 | "enumerate", "eval", "filter", "float", "format", "frozenset",
|
27 | "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
|
28 | "input", "int", "isinstance", "issubclass", "iter", "len",
|
29 | "list", "locals", "map", "max", "memoryview", "min", "next",
|
30 | "object", "oct", "open", "ord", "pow", "property", "range",
|
31 | "repr", "reversed", "round", "set", "setattr", "slice",
|
32 | "sorted", "staticmethod", "str", "sum", "super", "tuple",
|
33 | "type", "vars", "zip", "__import__", "NotImplemented",
|
34 | "Ellipsis", "__debug__"];
|
35 | CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
|
36 |
|
37 | function top(state) {
|
38 | return state.scopes[state.scopes.length - 1];
|
39 | }
|
40 |
|
41 | CodeMirror.defineMode("python", function(conf, parserConf) {
|
42 | var ERRORCLASS = "error";
|
43 |
|
44 | var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
|
45 |
|
46 | var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
|
47 | parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/]
|
48 | for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)
|
49 |
|
50 | var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
|
51 |
|
52 | var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
|
53 | if (parserConf.extra_keywords != undefined)
|
54 | myKeywords = myKeywords.concat(parserConf.extra_keywords);
|
55 |
|
56 | if (parserConf.extra_builtins != undefined)
|
57 | myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
|
58 |
|
59 | var py3 = !(parserConf.version && Number(parserConf.version) < 3)
|
60 | if (py3) {
|
61 |
|
62 | var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
|
63 | myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
|
64 | myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
|
65 | var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
|
66 | } else {
|
67 | var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
|
68 | myKeywords = myKeywords.concat(["exec", "print"]);
|
69 | myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
|
70 | "file", "intern", "long", "raw_input", "reduce", "reload",
|
71 | "unichr", "unicode", "xrange", "False", "True", "None"]);
|
72 | var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
|
73 | }
|
74 | var keywords = wordRegexp(myKeywords);
|
75 | var builtins = wordRegexp(myBuiltins);
|
76 |
|
77 |
|
78 | function tokenBase(stream, state) {
|
79 | var sol = stream.sol() && state.lastToken != "\\"
|
80 | if (sol) state.indent = stream.indentation()
|
81 |
|
82 | if (sol && top(state).type == "py") {
|
83 | var scopeOffset = top(state).offset;
|
84 | if (stream.eatSpace()) {
|
85 | var lineOffset = stream.indentation();
|
86 | if (lineOffset > scopeOffset)
|
87 | pushPyScope(state);
|
88 | else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#")
|
89 | state.errorToken = true;
|
90 | return null;
|
91 | } else {
|
92 | var style = tokenBaseInner(stream, state);
|
93 | if (scopeOffset > 0 && dedent(stream, state))
|
94 | style += " " + ERRORCLASS;
|
95 | return style;
|
96 | }
|
97 | }
|
98 | return tokenBaseInner(stream, state);
|
99 | }
|
100 |
|
101 | function tokenBaseInner(stream, state, inFormat) {
|
102 | if (stream.eatSpace()) return null;
|
103 |
|
104 |
|
105 | if (!inFormat && stream.match(/^#.*/)) return "comment";
|
106 |
|
107 |
|
108 | if (stream.match(/^[0-9\.]/, false)) {
|
109 | var floatLiteral = false;
|
110 |
|
111 | if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
|
112 | if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; }
|
113 | if (stream.match(/^\.\d+/)) { floatLiteral = true; }
|
114 | if (floatLiteral) {
|
115 |
|
116 | stream.eat(/J/i);
|
117 | return "number";
|
118 | }
|
119 |
|
120 | var intLiteral = false;
|
121 |
|
122 | if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;
|
123 |
|
124 | if (stream.match(/^0b[01_]+/i)) intLiteral = true;
|
125 |
|
126 | if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;
|
127 |
|
128 | if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) {
|
129 |
|
130 | stream.eat(/J/i);
|
131 |
|
132 | intLiteral = true;
|
133 | }
|
134 |
|
135 | if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
|
136 | if (intLiteral) {
|
137 |
|
138 | stream.eat(/L/i);
|
139 | return "number";
|
140 | }
|
141 | }
|
142 |
|
143 |
|
144 | if (stream.match(stringPrefixes)) {
|
145 | var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
|
146 | if (!isFmtString) {
|
147 | state.tokenize = tokenStringFactory(stream.current(), state.tokenize);
|
148 | return state.tokenize(stream, state);
|
149 | } else {
|
150 | state.tokenize = formatStringFactory(stream.current(), state.tokenize);
|
151 | return state.tokenize(stream, state);
|
152 | }
|
153 | }
|
154 |
|
155 | for (var i = 0; i < operators.length; i++)
|
156 | if (stream.match(operators[i])) return "operator"
|
157 |
|
158 | if (stream.match(delimiters)) return "punctuation";
|
159 |
|
160 | if (state.lastToken == "." && stream.match(identifiers))
|
161 | return "property";
|
162 |
|
163 | if (stream.match(keywords) || stream.match(wordOperators))
|
164 | return "keyword";
|
165 |
|
166 | if (stream.match(builtins))
|
167 | return "builtin";
|
168 |
|
169 | if (stream.match(/^(self|cls)\b/))
|
170 | return "variable-2";
|
171 |
|
172 | if (stream.match(identifiers)) {
|
173 | if (state.lastToken == "def" || state.lastToken == "class")
|
174 | return "def";
|
175 | return "variable";
|
176 | }
|
177 |
|
178 |
|
179 | stream.next();
|
180 | return inFormat ? null :ERRORCLASS;
|
181 | }
|
182 |
|
183 | function formatStringFactory(delimiter, tokenOuter) {
|
184 | while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
|
185 | delimiter = delimiter.substr(1);
|
186 |
|
187 | var singleline = delimiter.length == 1;
|
188 | var OUTCLASS = "string";
|
189 |
|
190 | function tokenNestedExpr(depth) {
|
191 | return function(stream, state) {
|
192 | var inner = tokenBaseInner(stream, state, true)
|
193 | if (inner == "punctuation") {
|
194 | if (stream.current() == "{") {
|
195 | state.tokenize = tokenNestedExpr(depth + 1)
|
196 | } else if (stream.current() == "}") {
|
197 | if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)
|
198 | else state.tokenize = tokenString
|
199 | }
|
200 | }
|
201 | return inner
|
202 | }
|
203 | }
|
204 |
|
205 | function tokenString(stream, state) {
|
206 | while (!stream.eol()) {
|
207 | stream.eatWhile(/[^'"\{\}\\]/);
|
208 | if (stream.eat("\\")) {
|
209 | stream.next();
|
210 | if (singleline && stream.eol())
|
211 | return OUTCLASS;
|
212 | } else if (stream.match(delimiter)) {
|
213 | state.tokenize = tokenOuter;
|
214 | return OUTCLASS;
|
215 | } else if (stream.match('{{')) {
|
216 |
|
217 | return OUTCLASS;
|
218 | } else if (stream.match('{', false)) {
|
219 |
|
220 | state.tokenize = tokenNestedExpr(0)
|
221 | if (stream.current()) return OUTCLASS;
|
222 | else return state.tokenize(stream, state)
|
223 | } else if (stream.match('}}')) {
|
224 | return OUTCLASS;
|
225 | } else if (stream.match('}')) {
|
226 |
|
227 | return ERRORCLASS;
|
228 | } else {
|
229 | stream.eat(/['"]/);
|
230 | }
|
231 | }
|
232 | if (singleline) {
|
233 | if (parserConf.singleLineStringErrors)
|
234 | return ERRORCLASS;
|
235 | else
|
236 | state.tokenize = tokenOuter;
|
237 | }
|
238 | return OUTCLASS;
|
239 | }
|
240 | tokenString.isString = true;
|
241 | return tokenString;
|
242 | }
|
243 |
|
244 | function tokenStringFactory(delimiter, tokenOuter) {
|
245 | while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
|
246 | delimiter = delimiter.substr(1);
|
247 |
|
248 | var singleline = delimiter.length == 1;
|
249 | var OUTCLASS = "string";
|
250 |
|
251 | function tokenString(stream, state) {
|
252 | while (!stream.eol()) {
|
253 | stream.eatWhile(/[^'"\\]/);
|
254 | if (stream.eat("\\")) {
|
255 | stream.next();
|
256 | if (singleline && stream.eol())
|
257 | return OUTCLASS;
|
258 | } else if (stream.match(delimiter)) {
|
259 | state.tokenize = tokenOuter;
|
260 | return OUTCLASS;
|
261 | } else {
|
262 | stream.eat(/['"]/);
|
263 | }
|
264 | }
|
265 | if (singleline) {
|
266 | if (parserConf.singleLineStringErrors)
|
267 | return ERRORCLASS;
|
268 | else
|
269 | state.tokenize = tokenOuter;
|
270 | }
|
271 | return OUTCLASS;
|
272 | }
|
273 | tokenString.isString = true;
|
274 | return tokenString;
|
275 | }
|
276 |
|
277 | function pushPyScope(state) {
|
278 | while (top(state).type != "py") state.scopes.pop()
|
279 | state.scopes.push({offset: top(state).offset + conf.indentUnit,
|
280 | type: "py",
|
281 | align: null})
|
282 | }
|
283 |
|
284 | function pushBracketScope(stream, state, type) {
|
285 | var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1
|
286 | state.scopes.push({offset: state.indent + hangingIndent,
|
287 | type: type,
|
288 | align: align})
|
289 | }
|
290 |
|
291 | function dedent(stream, state) {
|
292 | var indented = stream.indentation();
|
293 | while (state.scopes.length > 1 && top(state).offset > indented) {
|
294 | if (top(state).type != "py") return true;
|
295 | state.scopes.pop();
|
296 | }
|
297 | return top(state).offset != indented;
|
298 | }
|
299 |
|
300 | function tokenLexer(stream, state) {
|
301 | if (stream.sol()) state.beginningOfLine = true;
|
302 |
|
303 | var style = state.tokenize(stream, state);
|
304 | var current = stream.current();
|
305 |
|
306 |
|
307 | if (state.beginningOfLine && current == "@")
|
308 | return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS;
|
309 |
|
310 | if (/\S/.test(current)) state.beginningOfLine = false;
|
311 |
|
312 | if ((style == "variable" || style == "builtin")
|
313 | && state.lastToken == "meta")
|
314 | style = "meta";
|
315 |
|
316 |
|
317 | if (current == "pass" || current == "return")
|
318 | state.dedent += 1;
|
319 |
|
320 | if (current == "lambda") state.lambda = true;
|
321 | if (current == ":" && !state.lambda && top(state).type == "py")
|
322 | pushPyScope(state);
|
323 |
|
324 | if (current.length == 1 && !/string|comment/.test(style)) {
|
325 | var delimiter_index = "[({".indexOf(current);
|
326 | if (delimiter_index != -1)
|
327 | pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
|
328 |
|
329 | delimiter_index = "])}".indexOf(current);
|
330 | if (delimiter_index != -1) {
|
331 | if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
|
332 | else return ERRORCLASS;
|
333 | }
|
334 | }
|
335 | if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
|
336 | if (state.scopes.length > 1) state.scopes.pop();
|
337 | state.dedent -= 1;
|
338 | }
|
339 |
|
340 | return style;
|
341 | }
|
342 |
|
343 | var external = {
|
344 | startState: function(basecolumn) {
|
345 | return {
|
346 | tokenize: tokenBase,
|
347 | scopes: [{offset: basecolumn || 0, type: "py", align: null}],
|
348 | indent: basecolumn || 0,
|
349 | lastToken: null,
|
350 | lambda: false,
|
351 | dedent: 0
|
352 | };
|
353 | },
|
354 |
|
355 | token: function(stream, state) {
|
356 | var addErr = state.errorToken;
|
357 | if (addErr) state.errorToken = false;
|
358 | var style = tokenLexer(stream, state);
|
359 |
|
360 | if (style && style != "comment")
|
361 | state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style;
|
362 | if (style == "punctuation") style = null;
|
363 |
|
364 | if (stream.eol() && state.lambda)
|
365 | state.lambda = false;
|
366 | return addErr ? style + " " + ERRORCLASS : style;
|
367 | },
|
368 |
|
369 | indent: function(state, textAfter) {
|
370 | if (state.tokenize != tokenBase)
|
371 | return state.tokenize.isString ? CodeMirror.Pass : 0;
|
372 |
|
373 | var scope = top(state), closing = scope.type == textAfter.charAt(0)
|
374 | if (scope.align != null)
|
375 | return scope.align - (closing ? 1 : 0)
|
376 | else
|
377 | return scope.offset - (closing ? hangingIndent : 0)
|
378 | },
|
379 |
|
380 | electricInput: /^\s*[\}\]\)]$/,
|
381 | closeBrackets: {triples: "'\""},
|
382 | lineComment: "#",
|
383 | fold: "indent"
|
384 | };
|
385 | return external;
|
386 | });
|
387 |
|
388 | CodeMirror.defineMIME("text/x-python", "python");
|
389 |
|
390 | var words = function(str) { return str.split(" "); };
|
391 |
|
392 | CodeMirror.defineMIME("text/x-cython", {
|
393 | name: "python",
|
394 | extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+
|
395 | "extern gil include nogil property public "+
|
396 | "readonly struct union DEF IF ELIF ELSE")
|
397 | });
|
398 |
|
399 | });
|