OLD | NEW |
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others |
2 // Distributed under an MIT license: http://codemirror.net/LICENSE | 2 // Distributed under an MIT license: http://codemirror.net/LICENSE |
3 | 3 |
4 (function(mod) { | 4 (function(mod) { |
5 if (typeof exports == "object" && typeof module == "object") // CommonJS | 5 if (typeof exports == "object" && typeof module == "object") // CommonJS |
6 mod(require("../../lib/codemirror")); | 6 mod(require("../../lib/codemirror")); |
7 else if (typeof define == "function" && define.amd) // AMD | 7 else if (typeof define == "function" && define.amd) // AMD |
8 define(["../../lib/codemirror"], mod); | 8 define(["../../lib/codemirror"], mod); |
9 else // Plain browser env | 9 else // Plain browser env |
10 mod(CodeMirror); | 10 mod(CodeMirror); |
11 })(function(CodeMirror) { | 11 })(function(CodeMirror) { |
12 "use strict"; | 12 "use strict"; |
13 | 13 |
14 function wordRegexp(words) { | 14 function wordRegexp(words) { |
15 return new RegExp("^((" + words.join(")|(") + "))\\b"); | 15 return new RegExp("^((" + words.join(")|(") + "))\\b"); |
16 } | 16 } |
17 | 17 |
18 var wordOperators = wordRegexp(["and", "or", "not", "is", "in"]); | 18 var wordOperators = wordRegexp(["and", "or", "not", "is"]); |
19 var commonKeywords = ["as", "assert", "break", "class", "continue", | 19 var commonKeywords = ["as", "assert", "break", "class", "continue", |
20 "def", "del", "elif", "else", "except", "finally", | 20 "def", "del", "elif", "else", "except", "finally", |
21 "for", "from", "global", "if", "import", | 21 "for", "from", "global", "if", "import", |
22 "lambda", "pass", "raise", "return", | 22 "lambda", "pass", "raise", "return", |
23 "try", "while", "with", "yield"]; | 23 "try", "while", "with", "yield", "in"]; |
24 var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callab
le", "chr", | 24 var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callab
le", "chr", |
25 "classmethod", "compile", "complex", "delattr", "dict",
"dir", "divmod", | 25 "classmethod", "compile", "complex", "delattr", "dict",
"dir", "divmod", |
26 "enumerate", "eval", "filter", "float", "format", "froze
nset", | 26 "enumerate", "eval", "filter", "float", "format", "froze
nset", |
27 "getattr", "globals", "hasattr", "hash", "help", "hex",
"id", | 27 "getattr", "globals", "hasattr", "hash", "help", "hex",
"id", |
28 "input", "int", "isinstance", "issubclass", "iter", "len
", | 28 "input", "int", "isinstance", "issubclass", "iter", "len
", |
29 "list", "locals", "map", "max", "memoryview", "min", "ne
xt", | 29 "list", "locals", "map", "max", "memoryview", "min", "ne
xt", |
30 "object", "oct", "open", "ord", "pow", "property", "rang
e", | 30 "object", "oct", "open", "ord", "pow", "property", "rang
e", |
31 "repr", "reversed", "round", "set", "setattr", "slice", | 31 "repr", "reversed", "round", "set", "setattr", "slice", |
32 "sorted", "staticmethod", "str", "sum", "super", "tuple"
, | 32 "sorted", "staticmethod", "str", "sum", "super", "tuple"
, |
33 "type", "vars", "zip", "__import__", "NotImplemented", | 33 "type", "vars", "zip", "__import__", "NotImplemented", |
34 "Ellipsis", "__debug__"]; | 34 "Ellipsis", "__debug__"]; |
35 var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execf
ile", | |
36 "file", "intern", "long", "raw_input", "reduce", "reload
", | |
37 "unichr", "unicode", "xrange", "False", "True", "None"], | |
38 keywords: ["exec", "print"]}; | |
39 var py3 = {builtins: ["ascii", "bytes", "exec", "print"], | |
40 keywords: ["nonlocal", "False", "True", "None"]}; | |
41 | |
42 CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonB
uiltins)); | 35 CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonB
uiltins)); |
43 | 36 |
44 function top(state) { | 37 function top(state) { |
45 return state.scopes[state.scopes.length - 1]; | 38 return state.scopes[state.scopes.length - 1]; |
46 } | 39 } |
47 | 40 |
48 CodeMirror.defineMode("python", function(conf, parserConf) { | 41 CodeMirror.defineMode("python", function(conf, parserConf) { |
49 var ERRORCLASS = "error"; | 42 var ERRORCLASS = "error"; |
50 | 43 |
51 var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/
%&|\\^~<>!]"); | 44 var singleDelimiters = parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\
.]/; |
52 var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\
[\\]\\{\\}@,:`=;\\.]"); | 45 var doubleOperators = parserConf.doubleOperators || /^([!<>]==|<>|<<|>>|\/\/
|\*\*)/; |
53 var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|
(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); | 46 var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|
&=|\|=|\^=)/; |
54 var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(
\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); | 47 var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=
)/; |
55 var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>
>=)|(<<=)|(\\*\\*=))"); | 48 |
56 var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9
]*"); | |
57 var hangingIndent = parserConf.hangingIndent || conf.indentUnit; | 49 var hangingIndent = parserConf.hangingIndent || conf.indentUnit; |
58 | 50 |
59 var myKeywords = commonKeywords, myBuiltins = commonBuiltins; | 51 var myKeywords = commonKeywords, myBuiltins = commonBuiltins; |
60 if(parserConf.extra_keywords != undefined){ | 52 if (parserConf.extra_keywords != undefined) |
61 myKeywords = myKeywords.concat(parserConf.extra_keywords); | 53 myKeywords = myKeywords.concat(parserConf.extra_keywords); |
62 } | 54 |
63 if(parserConf.extra_builtins != undefined){ | 55 if (parserConf.extra_builtins != undefined) |
64 myBuiltins = myBuiltins.concat(parserConf.extra_builtins); | 56 myBuiltins = myBuiltins.concat(parserConf.extra_builtins); |
65 } | 57 |
66 if (parserConf.version && parseInt(parserConf.version, 10) == 3) { | 58 var py3 = parserConf.version && parseInt(parserConf.version, 10) == 3 |
67 myKeywords = myKeywords.concat(py3.keywords); | 59 if (py3) { |
68 myBuiltins = myBuiltins.concat(py3.builtins); | 60 // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator |
69 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i"); | 61 var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]
/; |
| 62 var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-
z0-9\u00A1-\uFFFF]*/; |
| 63 myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "asyn
c", "await"]); |
| 64 myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]); |
| 65 var stringPrefixes = new RegExp("^(([rbuf]|(br))?('{3}|\"{3}|['\"]))", "i"
); |
70 } else { | 66 } else { |
71 myKeywords = myKeywords.concat(py2.keywords); | 67 var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/
; |
72 myBuiltins = myBuiltins.concat(py2.builtins); | 68 var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; |
| 69 myKeywords = myKeywords.concat(["exec", "print"]); |
| 70 myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "c
oerce", "execfile", |
| 71 "file", "intern", "long", "raw_input", "re
duce", "reload", |
| 72 "unichr", "unicode", "xrange", "False", "T
rue", "None"]); |
73 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))",
"i"); | 73 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))",
"i"); |
74 } | 74 } |
75 var keywords = wordRegexp(myKeywords); | 75 var keywords = wordRegexp(myKeywords); |
76 var builtins = wordRegexp(myBuiltins); | 76 var builtins = wordRegexp(myBuiltins); |
77 | 77 |
78 // tokenizers | 78 // tokenizers |
79 function tokenBase(stream, state) { | 79 function tokenBase(stream, state) { |
| 80 if (stream.sol()) state.indent = stream.indentation() |
80 // Handle scope changes | 81 // Handle scope changes |
81 if (stream.sol() && top(state).type == "py") { | 82 if (stream.sol() && top(state).type == "py") { |
82 var scopeOffset = top(state).offset; | 83 var scopeOffset = top(state).offset; |
83 if (stream.eatSpace()) { | 84 if (stream.eatSpace()) { |
84 var lineOffset = stream.indentation(); | 85 var lineOffset = stream.indentation(); |
85 if (lineOffset > scopeOffset) | 86 if (lineOffset > scopeOffset) |
86 pushScope(stream, state, "py"); | 87 pushPyScope(state); |
87 else if (lineOffset < scopeOffset && dedent(stream, state)) | 88 else if (lineOffset < scopeOffset && dedent(stream, state)) |
88 state.errorToken = true; | 89 state.errorToken = true; |
89 return null; | 90 return null; |
90 } else { | 91 } else { |
91 var style = tokenBaseInner(stream, state); | 92 var style = tokenBaseInner(stream, state); |
92 if (scopeOffset > 0 && dedent(stream, state)) | 93 if (scopeOffset > 0 && dedent(stream, state)) |
93 style += " " + ERRORCLASS; | 94 style += " " + ERRORCLASS; |
94 return style; | 95 return style; |
95 } | 96 } |
96 } | 97 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 } | 146 } |
146 | 147 |
147 // Handle Strings | 148 // Handle Strings |
148 if (stream.match(stringPrefixes)) { | 149 if (stream.match(stringPrefixes)) { |
149 state.tokenize = tokenStringFactory(stream.current()); | 150 state.tokenize = tokenStringFactory(stream.current()); |
150 return state.tokenize(stream, state); | 151 return state.tokenize(stream, state); |
151 } | 152 } |
152 | 153 |
153 // Handle operators and Delimiters | 154 // Handle operators and Delimiters |
154 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) | 155 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) |
155 return null; | 156 return "punctuation"; |
156 | 157 |
157 if (stream.match(doubleOperators) | 158 if (stream.match(doubleOperators) || stream.match(singleOperators)) |
158 || stream.match(singleOperators) | |
159 || stream.match(wordOperators)) | |
160 return "operator"; | 159 return "operator"; |
161 | 160 |
162 if (stream.match(singleDelimiters)) | 161 if (stream.match(singleDelimiters)) |
163 return null; | 162 return "punctuation"; |
164 | 163 |
165 if (stream.match(keywords)) | 164 if (state.lastToken == "." && stream.match(identifiers)) |
| 165 return "property"; |
| 166 |
| 167 if (stream.match(keywords) || stream.match(wordOperators)) |
166 return "keyword"; | 168 return "keyword"; |
167 | 169 |
168 if (stream.match(builtins)) | 170 if (stream.match(builtins)) |
169 return "builtin"; | 171 return "builtin"; |
170 | 172 |
171 if (stream.match(/^(self|cls)\b/)) | 173 if (stream.match(/^(self|cls)\b/)) |
172 return "variable-2"; | 174 return "variable-2"; |
173 | 175 |
174 if (stream.match(identifiers)) { | 176 if (stream.match(identifiers)) { |
175 if (state.lastToken == "def" || state.lastToken == "class") | 177 if (state.lastToken == "def" || state.lastToken == "class") |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 return ERRORCLASS; | 210 return ERRORCLASS; |
209 else | 211 else |
210 state.tokenize = tokenBase; | 212 state.tokenize = tokenBase; |
211 } | 213 } |
212 return OUTCLASS; | 214 return OUTCLASS; |
213 } | 215 } |
214 tokenString.isString = true; | 216 tokenString.isString = true; |
215 return tokenString; | 217 return tokenString; |
216 } | 218 } |
217 | 219 |
218 function pushScope(stream, state, type) { | 220 function pushPyScope(state) { |
219 var offset = 0, align = null; | 221 while (top(state).type != "py") state.scopes.pop() |
220 if (type == "py") { | 222 state.scopes.push({offset: top(state).offset + conf.indentUnit, |
221 while (top(state).type != "py") | 223 type: "py", |
222 state.scopes.pop(); | 224 align: null}) |
223 } | 225 } |
224 offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingInde
nt); | 226 |
225 if (type != "py" && !stream.match(/^(\s|#.*)*$/, false)) | 227 function pushBracketScope(stream, state, type) { |
226 align = stream.column() + 1; | 228 var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.col
umn() + 1 |
227 state.scopes.push({offset: offset, type: type, align: align}); | 229 state.scopes.push({offset: state.indent + hangingIndent, |
| 230 type: type, |
| 231 align: align}) |
228 } | 232 } |
229 | 233 |
230 function dedent(stream, state) { | 234 function dedent(stream, state) { |
231 var indented = stream.indentation(); | 235 var indented = stream.indentation(); |
232 while (top(state).offset > indented) { | 236 while (state.scopes.length > 1 && top(state).offset > indented) { |
233 if (top(state).type != "py") return true; | 237 if (top(state).type != "py") return true; |
234 state.scopes.pop(); | 238 state.scopes.pop(); |
235 } | 239 } |
236 return top(state).offset != indented; | 240 return top(state).offset != indented; |
237 } | 241 } |
238 | 242 |
239 function tokenLexer(stream, state) { | 243 function tokenLexer(stream, state) { |
| 244 if (stream.sol()) state.beginningOfLine = true; |
| 245 |
240 var style = state.tokenize(stream, state); | 246 var style = state.tokenize(stream, state); |
241 var current = stream.current(); | 247 var current = stream.current(); |
242 | 248 |
243 // Handle '.' connected identifiers | 249 // Handle decorators |
244 if (current == ".") { | 250 if (state.beginningOfLine && current == "@") |
245 style = stream.match(identifiers, false) ? null : ERRORCLASS; | 251 return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ER
RORCLASS; |
246 if (style == null && state.lastStyle == "meta") { | |
247 // Apply 'meta' style to '.' connected identifiers when | |
248 // appropriate. | |
249 style = "meta"; | |
250 } | |
251 return style; | |
252 } | |
253 | 252 |
254 // Handle decorators | 253 if (/\S/.test(current)) state.beginningOfLine = false; |
255 if (current == "@") | |
256 return stream.match(identifiers, false) ? "meta" : ERRORCLASS; | |
257 | 254 |
258 if ((style == "variable" || style == "builtin") | 255 if ((style == "variable" || style == "builtin") |
259 && state.lastStyle == "meta") | 256 && state.lastToken == "meta") |
260 style = "meta"; | 257 style = "meta"; |
261 | 258 |
262 // Handle scope changes. | 259 // Handle scope changes. |
263 if (current == "pass" || current == "return") | 260 if (current == "pass" || current == "return") |
264 state.dedent += 1; | 261 state.dedent += 1; |
265 | 262 |
266 if (current == "lambda") state.lambda = true; | 263 if (current == "lambda") state.lambda = true; |
267 if (current == ":" && !state.lambda && top(state).type == "py") | 264 if (current == ":" && !state.lambda && top(state).type == "py") |
268 pushScope(stream, state, "py"); | 265 pushPyScope(state); |
269 | 266 |
270 var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1; | 267 var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1; |
271 if (delimiter_index != -1) | 268 if (delimiter_index != -1) |
272 pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1)
); | 269 pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_i
ndex+1)); |
273 | 270 |
274 delimiter_index = "])}".indexOf(current); | 271 delimiter_index = "])}".indexOf(current); |
275 if (delimiter_index != -1) { | 272 if (delimiter_index != -1) { |
276 if (top(state).type == current) state.scopes.pop(); | 273 if (top(state).type == current) state.indent = state.scopes.pop().offset
- hangingIndent |
277 else return ERRORCLASS; | 274 else return ERRORCLASS; |
278 } | 275 } |
279 if (state.dedent > 0 && stream.eol() && top(state).type == "py") { | 276 if (state.dedent > 0 && stream.eol() && top(state).type == "py") { |
280 if (state.scopes.length > 1) state.scopes.pop(); | 277 if (state.scopes.length > 1) state.scopes.pop(); |
281 state.dedent -= 1; | 278 state.dedent -= 1; |
282 } | 279 } |
283 | 280 |
284 return style; | 281 return style; |
285 } | 282 } |
286 | 283 |
287 var external = { | 284 var external = { |
288 startState: function(basecolumn) { | 285 startState: function(basecolumn) { |
289 return { | 286 return { |
290 tokenize: tokenBase, | 287 tokenize: tokenBase, |
291 scopes: [{offset: basecolumn || 0, type: "py", align: null}], | 288 scopes: [{offset: basecolumn || 0, type: "py", align: null}], |
292 lastStyle: null, | 289 indent: basecolumn || 0, |
293 lastToken: null, | 290 lastToken: null, |
294 lambda: false, | 291 lambda: false, |
295 dedent: 0 | 292 dedent: 0 |
296 }; | 293 }; |
297 }, | 294 }, |
298 | 295 |
299 token: function(stream, state) { | 296 token: function(stream, state) { |
300 var addErr = state.errorToken; | 297 var addErr = state.errorToken; |
301 if (addErr) state.errorToken = false; | 298 if (addErr) state.errorToken = false; |
302 var style = tokenLexer(stream, state); | 299 var style = tokenLexer(stream, state); |
303 | 300 |
304 state.lastStyle = style; | 301 if (style && style != "comment") |
305 | 302 state.lastToken = (style == "keyword" || style == "punctuation") ? str
eam.current() : style; |
306 var current = stream.current(); | 303 if (style == "punctuation") style = null; |
307 if (current && style) | |
308 state.lastToken = current; | |
309 | 304 |
310 if (stream.eol() && state.lambda) | 305 if (stream.eol() && state.lambda) |
311 state.lambda = false; | 306 state.lambda = false; |
312 return addErr ? style + " " + ERRORCLASS : style; | 307 return addErr ? style + " " + ERRORCLASS : style; |
313 }, | 308 }, |
314 | 309 |
315 indent: function(state, textAfter) { | 310 indent: function(state, textAfter) { |
316 if (state.tokenize != tokenBase) | 311 if (state.tokenize != tokenBase) |
317 return state.tokenize.isString ? CodeMirror.Pass : 0; | 312 return state.tokenize.isString ? CodeMirror.Pass : 0; |
318 | 313 |
319 var scope = top(state); | 314 var scope = top(state), closing = scope.type == textAfter.charAt(0) |
320 var closing = textAfter && textAfter.charAt(0) == scope.type; | |
321 if (scope.align != null) | 315 if (scope.align != null) |
322 return scope.align - (closing ? 1 : 0); | 316 return scope.align - (closing ? 1 : 0) |
323 else if (closing && state.scopes.length > 1) | |
324 return state.scopes[state.scopes.length - 2].offset; | |
325 else | 317 else |
326 return scope.offset; | 318 return scope.offset - (closing ? hangingIndent : 0) |
327 }, | 319 }, |
328 | 320 |
| 321 electricInput: /^\s*[\}\]\)]$/, |
| 322 closeBrackets: {triples: "'\""}, |
329 lineComment: "#", | 323 lineComment: "#", |
330 fold: "indent" | 324 fold: "indent" |
331 }; | 325 }; |
332 return external; | 326 return external; |
333 }); | 327 }); |
334 | 328 |
335 CodeMirror.defineMIME("text/x-python", "python"); | 329 CodeMirror.defineMIME("text/x-python", "python"); |
336 | 330 |
337 var words = function(str) { return str.split(" "); }; | 331 var words = function(str) { return str.split(" "); }; |
338 | 332 |
339 CodeMirror.defineMIME("text/x-cython", { | 333 CodeMirror.defineMIME("text/x-cython", { |
340 name: "python", | 334 name: "python", |
341 extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ | 335 extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ |
342 "extern gil include nogil property public"+ | 336 "extern gil include nogil property public"+ |
343 "readonly struct union DEF IF ELIF ELSE") | 337 "readonly struct union DEF IF ELIF ELSE") |
344 }); | 338 }); |
345 | 339 |
346 }); | 340 }); |
OLD | NEW |