1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
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
11 })(function(CodeMirror
) {
14 function wordRegexp(words
) {
15 return new RegExp("^((" + words
.join(")|(") + "))\\b");
18 var wordOperators
= wordRegexp(["and", "or", "not", "is", "in"]);
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"];
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 var py2
= {builtins
: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
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"]};
42 CodeMirror
.registerHelper("hintWords", "python", commonKeywords
.concat(commonBuiltins
));
45 return state
.scopes
[state
.scopes
.length
- 1];
48 CodeMirror
.defineMode("python", function(conf
, parserConf
) {
49 var ERRORCLASS
= "error";
51 var singleOperators
= parserConf
.singleOperators
|| new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
52 var singleDelimiters
= parserConf
.singleDelimiters
|| new RegExp("^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]");
53 var doubleOperators
= parserConf
.doubleOperators
|| new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
54 var doubleDelimiters
= parserConf
.doubleDelimiters
|| new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
55 var tripleDelimiters
= parserConf
.tripleDelimiters
|| new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
56 var identifiers
= parserConf
.identifiers
|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
57 var hangingIndent
= parserConf
.hangingIndent
|| conf
.indentUnit
;
59 var myKeywords
= commonKeywords
, myBuiltins
= commonBuiltins
;
60 if(parserConf
.extra_keywords
!= undefined){
61 myKeywords
= myKeywords
.concat(parserConf
.extra_keywords
);
63 if(parserConf
.extra_builtins
!= undefined){
64 myBuiltins
= myBuiltins
.concat(parserConf
.extra_builtins
);
66 if (parserConf
.version
&& parseInt(parserConf
.version
, 10) == 3) {
67 myKeywords
= myKeywords
.concat(py3
.keywords
);
68 myBuiltins
= myBuiltins
.concat(py3
.builtins
);
69 var stringPrefixes
= new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
71 myKeywords
= myKeywords
.concat(py2
.keywords
);
72 myBuiltins
= myBuiltins
.concat(py2
.builtins
);
73 var stringPrefixes
= new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
75 var keywords
= wordRegexp(myKeywords
);
76 var builtins
= wordRegexp(myBuiltins
);
79 function tokenBase(stream
, state
) {
80 // Handle scope changes
81 if (stream
.sol() && top(state
).type
== "py") {
82 var scopeOffset
= top(state
).offset
;
83 if (stream
.eatSpace()) {
84 var lineOffset
= stream
.indentation();
85 if (lineOffset
> scopeOffset
)
86 pushScope(stream
, state
, "py");
87 else if (lineOffset
< scopeOffset
&& dedent(stream
, state
))
88 state
.errorToken
= true;
91 var style
= tokenBaseInner(stream
, state
);
92 if (scopeOffset
> 0 && dedent(stream
, state
))
93 style
+= " " + ERRORCLASS
;
97 return tokenBaseInner(stream
, state
);
100 function tokenBaseInner(stream
, state
) {
101 if (stream
.eatSpace()) return null;
103 var ch
= stream
.peek();
111 // Handle Number Literals
112 if (stream
.match(/^[0-9\.]/, false)) {
113 var floatLiteral
= false;
115 if (stream
.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral
= true; }
116 if (stream
.match(/^\d+\.\d*/)) { floatLiteral
= true; }
117 if (stream
.match(/^\.\d+/)) { floatLiteral
= true; }
119 // Float literals may be "imaginary"
124 var intLiteral
= false;
126 if (stream
.match(/^0x[0-9a-f]+/i)) intLiteral
= true;
128 if (stream
.match(/^0b[01]+/i)) intLiteral
= true;
130 if (stream
.match(/^0o[0-7]+/i)) intLiteral
= true;
132 if (stream
.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
133 // Decimal literals may be "imaginary"
135 // TODO - Can you have imaginary longs?
138 // Zero by itself with no other piece of number.
139 if (stream
.match(/^0(?![\dx])/i)) intLiteral
= true;
141 // Integer literals may be "long"
148 if (stream
.match(stringPrefixes
)) {
149 state
.tokenize
= tokenStringFactory(stream
.current());
150 return state
.tokenize(stream
, state
);
153 // Handle operators and Delimiters
154 if (stream
.match(tripleDelimiters
) || stream
.match(doubleDelimiters
))
157 if (stream
.match(doubleOperators
)
158 || stream
.match(singleOperators
)
159 || stream
.match(wordOperators
))
162 if (stream
.match(singleDelimiters
))
165 if (stream
.match(keywords
))
168 if (stream
.match(builtins
))
171 if (stream
.match(/^(self|cls)\b/))
174 if (stream
.match(identifiers
)) {
175 if (state
.lastToken
== "def" || state
.lastToken
== "class")
180 // Handle non-detected items
185 function tokenStringFactory(delimiter
) {
186 while ("rub".indexOf(delimiter
.charAt(0).toLowerCase()) >= 0)
187 delimiter
= delimiter
.substr(1);
189 var singleline
= delimiter
.length
== 1;
190 var OUTCLASS
= "string";
192 function tokenString(stream
, state
) {
193 while (!stream
.eol()) {
194 stream
.eatWhile(/[^'"\\]/);
195 if (stream
.eat("\\")) {
197 if (singleline
&& stream
.eol())
199 } else if (stream
.match(delimiter
)) {
200 state
.tokenize
= tokenBase
;
207 if (parserConf
.singleLineStringErrors
)
210 state
.tokenize
= tokenBase
;
214 tokenString
.isString
= true;
218 function pushScope(stream
, state
, type
) {
219 var offset
= 0, align
= null;
221 while (top(state
).type
!= "py")
224 offset
= top(state
).offset
+ (type
== "py" ? conf
.indentUnit
: hangingIndent
);
225 if (type
!= "py" && !stream
.match(/^(\s|#.*)*$/, false))
226 align
= stream
.column() + 1;
227 state
.scopes
.push({offset
: offset
, type
: type
, align
: align
});
230 function dedent(stream
, state
) {
231 var indented
= stream
.indentation();
232 while (top(state
).offset
> indented
) {
233 if (top(state
).type
!= "py") return true;
236 return top(state
).offset
!= indented
;
239 function tokenLexer(stream
, state
) {
240 var style
= state
.tokenize(stream
, state
);
241 var current
= stream
.current();
243 // Handle '.' connected identifiers
244 if (current
== ".") {
245 style
= stream
.match(identifiers
, false) ? null : ERRORCLASS
;
246 if (style
== null && state
.lastStyle
== "meta") {
247 // Apply 'meta' style to '.' connected identifiers when
256 return stream
.match(identifiers
, false) ? "meta" : ERRORCLASS
;
258 if ((style
== "variable" || style
== "builtin")
259 && state
.lastStyle
== "meta")
262 // Handle scope changes.
263 if (current
== "pass" || current
== "return")
266 if (current
== "lambda") state
.lambda
= true;
267 if (current
== ":" && !state
.lambda
&& top(state
).type
== "py")
268 pushScope(stream
, state
, "py");
270 var delimiter_index
= current
.length
== 1 ? "[({".indexOf(current
) : -1;
271 if (delimiter_index
!= -1)
272 pushScope(stream
, state
, "])}".slice(delimiter_index
, delimiter_index
+1));
274 delimiter_index
= "])}".indexOf(current
);
275 if (delimiter_index
!= -1) {
276 if (top(state
).type
== current
) state
.scopes
.pop();
277 else return ERRORCLASS
;
279 if (state
.dedent
> 0 && stream
.eol() && top(state
).type
== "py") {
280 if (state
.scopes
.length
> 1) state
.scopes
.pop();
288 startState: function(basecolumn
) {
291 scopes
: [{offset
: basecolumn
|| 0, type
: "py", align
: null}],
299 token: function(stream
, state
) {
300 var addErr
= state
.errorToken
;
301 if (addErr
) state
.errorToken
= false;
302 var style
= tokenLexer(stream
, state
);
304 state
.lastStyle
= style
;
306 var current
= stream
.current();
307 if (current
&& style
)
308 state
.lastToken
= current
;
310 if (stream
.eol() && state
.lambda
)
311 state
.lambda
= false;
312 return addErr
? style
+ " " + ERRORCLASS
: style
;
315 indent: function(state
, textAfter
) {
316 if (state
.tokenize
!= tokenBase
)
317 return state
.tokenize
.isString
? CodeMirror
.Pass
: 0;
319 var scope
= top(state
);
320 var closing
= textAfter
&& textAfter
.charAt(0) == scope
.type
;
321 if (scope
.align
!= null)
322 return scope
.align
- (closing
? 1 : 0);
323 else if (closing
&& state
.scopes
.length
> 1)
324 return state
.scopes
[state
.scopes
.length
- 2].offset
;
335 CodeMirror
.defineMIME("text/x-python", "python");
337 var words = function(str
) { return str
.split(" "); };
339 CodeMirror
.defineMIME("text/x-cython", {
341 extra_keywords
: words("by cdef cimport cpdef ctypedef enum except"+
342 "extern gil include nogil property public"+
343 "readonly struct union DEF IF ELIF ELSE")