1 CodeMirror.defineMode("python", function(conf, parserConf) {
2 var ERRORCLASS = 'error';
4 function wordRegexp(words) {
5 return new RegExp("^((" + words.join(")|(") + "))\\b");
8 var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
9 var singleDelimiters = parserConf.singleDelimiters || new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
10 var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
11 var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
12 var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
13 var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
15 var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']);
16 var commonkeywords = ['as', 'assert', 'break', 'class', 'continue',
17 'def', 'del', 'elif', 'else', 'except', 'finally',
18 'for', 'from', 'global', 'if', 'import',
19 'lambda', 'pass', 'raise', 'return',
20 'try', 'while', 'with', 'yield'];
21 var commonBuiltins = ['abs', 'all', 'any', 'bin', 'bool', 'bytearray', 'callable', 'chr',
22 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
23 'enumerate', 'eval', 'filter', 'float', 'format', 'frozenset',
24 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id',
25 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len',
26 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next',
27 'object', 'oct', 'open', 'ord', 'pow', 'property', 'range',
28 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
29 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple',
30 'type', 'vars', 'zip', '__import__', 'NotImplemented',
31 'Ellipsis', '__debug__'];
32 var py2 = {'builtins': ['apply', 'basestring', 'buffer', 'cmp', 'coerce', 'execfile',
33 'file', 'intern', 'long', 'raw_input', 'reduce', 'reload',
34 'unichr', 'unicode', 'xrange', 'False', 'True', 'None'],
35 'keywords': ['exec', 'print']};
36 var py3 = {'builtins': ['ascii', 'bytes', 'exec', 'print'],
37 'keywords': ['nonlocal', 'False', 'True', 'None']};
39 if(parserConf.extra_keywords != undefined){
40 commonkeywords = commonkeywords.concat(parserConf.extra_keywords);
42 if(parserConf.extra_builtins != undefined){
43 commonBuiltins = commonBuiltins.concat(parserConf.extra_builtins);
45 if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) {
46 commonkeywords = commonkeywords.concat(py3.keywords);
47 commonBuiltins = commonBuiltins.concat(py3.builtins);
48 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
50 commonkeywords = commonkeywords.concat(py2.keywords);
51 commonBuiltins = commonBuiltins.concat(py2.builtins);
52 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
54 var keywords = wordRegexp(commonkeywords);
55 var builtins = wordRegexp(commonBuiltins);
57 var indentInfo = null;
60 function tokenBase(stream, state) {
61 // Handle scope changes
63 var scopeOffset = state.scopes[0].offset;
64 if (stream.eatSpace()) {
65 var lineOffset = stream.indentation();
66 if (lineOffset > scopeOffset) {
67 indentInfo = 'indent';
68 } else if (lineOffset < scopeOffset) {
69 indentInfo = 'dedent';
73 if (scopeOffset > 0) {
74 dedent(stream, state);
78 if (stream.eatSpace()) {
82 var ch = stream.peek();
90 // Handle Number Literals
91 if (stream.match(/^[0-9\.]/, false)) {
92 var floatLiteral = false;
94 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
95 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
96 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
98 // Float literals may be "imaginary"
103 var intLiteral = false;
105 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
107 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
109 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
111 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
112 // Decimal literals may be "imaginary"
114 // TODO - Can you have imaginary longs?
117 // Zero by itself with no other piece of number.
118 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
120 // Integer literals may be "long"
127 if (stream.match(stringPrefixes)) {
128 state.tokenize = tokenStringFactory(stream.current());
129 return state.tokenize(stream, state);
132 // Handle operators and Delimiters
133 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
136 if (stream.match(doubleOperators)
137 || stream.match(singleOperators)
138 || stream.match(wordOperators)) {
141 if (stream.match(singleDelimiters)) {
145 if (stream.match(keywords)) {
149 if (stream.match(builtins)) {
153 if (stream.match(identifiers)) {
157 // Handle non-detected items
162 function tokenStringFactory(delimiter) {
163 while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
164 delimiter = delimiter.substr(1);
166 var singleline = delimiter.length == 1;
167 var OUTCLASS = 'string';
169 function tokenString(stream, state) {
170 while (!stream.eol()) {
171 stream.eatWhile(/[^'"\\]/);
172 if (stream.eat('\\')) {
174 if (singleline && stream.eol()) {
177 } else if (stream.match(delimiter)) {
178 state.tokenize = tokenBase;
185 if (parserConf.singleLineStringErrors) {
188 state.tokenize = tokenBase;
193 tokenString.isString = true;
197 function indent(stream, state, type) {
201 if (state.scopes[0].type !== 'py') {
202 state.scopes[0].offset = stream.indentation();
205 for (var i = 0; i < state.scopes.length; ++i) {
206 if (state.scopes[i].type === 'py') {
207 indentUnit = state.scopes[i].offset + conf.indentUnit;
212 indentUnit = stream.column() + stream.current().length;
214 state.scopes.unshift({
220 function dedent(stream, state, type) {
222 if (state.scopes.length == 1) return;
223 if (state.scopes[0].type === 'py') {
224 var _indent = stream.indentation();
225 var _indent_index = -1;
226 for (var i = 0; i < state.scopes.length; ++i) {
227 if (_indent === state.scopes[i].offset) {
232 if (_indent_index === -1) {
235 while (state.scopes[0].offset !== _indent) {
236 state.scopes.shift();
241 state.scopes[0].offset = stream.indentation();
244 if (state.scopes[0].type != type) {
247 state.scopes.shift();
253 function tokenLexer(stream, state) {
255 var style = state.tokenize(stream, state);
256 var current = stream.current();
258 // Handle '.' connected identifiers
259 if (current === '.') {
260 style = stream.match(identifiers, false) ? null : ERRORCLASS;
261 if (style === null && state.lastToken === 'meta') {
262 // Apply 'meta' style to '.' connected identifiers when
270 if (current === '@') {
271 return stream.match(identifiers, false) ? 'meta' : ERRORCLASS;
274 if ((style === 'variable' || style === 'builtin')
275 && state.lastToken === 'meta') {
279 // Handle scope changes.
280 if (current === 'pass' || current === 'return') {
283 if (current === 'lambda') state.lambda = true;
284 if ((current === ':' && !state.lambda && state.scopes[0].type == 'py')
285 || indentInfo === 'indent') {
286 indent(stream, state);
288 var delimiter_index = '[({'.indexOf(current);
289 if (delimiter_index !== -1) {
290 indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
292 if (indentInfo === 'dedent') {
293 if (dedent(stream, state)) {
297 delimiter_index = '])}'.indexOf(current);
298 if (delimiter_index !== -1) {
299 if (dedent(stream, state, current)) {
303 if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'py') {
304 if (state.scopes.length > 1) state.scopes.shift();
312 startState: function(basecolumn) {
315 scopes: [{offset:basecolumn || 0, type:'py'}],
322 token: function(stream, state) {
323 var style = tokenLexer(stream, state);
325 state.lastToken = style;
327 if (stream.eol() && state.lambda) {
328 state.lambda = false;
334 indent: function(state) {
335 if (state.tokenize != tokenBase) {
336 return state.tokenize.isString ? CodeMirror.Pass : 0;
339 return state.scopes[0].offset;
348 CodeMirror.defineMIME("text/x-python", "python");
350 var words = function(str){return str.split(' ');};
353 CodeMirror.defineMIME("text/x-cython", {
355 extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
356 "extern gil include nogil property public"+
357 "readonly struct union DEF IF ELIF ELSE")