1CodeMirror.defineMode("python", function(conf, parserConf) { 2 var ERRORCLASS = 'error'; 3 4 function wordRegexp(words) { 5 return new RegExp("^((" + words.join(")|(") + "))\\b"); 6 } 7 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]*"); 14 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']}; 38 39 if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) { 40 commonkeywords = commonkeywords.concat(py3.keywords); 41 commonBuiltins = commonBuiltins.concat(py3.builtins); 42 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i"); 43 } else { 44 commonkeywords = commonkeywords.concat(py2.keywords); 45 commonBuiltins = commonBuiltins.concat(py2.builtins); 46 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); 47 } 48 var keywords = wordRegexp(commonkeywords); 49 var builtins = wordRegexp(commonBuiltins); 50 51 var indentInfo = null; 52 53 // tokenizers 54 function tokenBase(stream, state) { 55 // Handle scope changes 56 if (stream.sol()) { 57 var scopeOffset = state.scopes[0].offset; 58 if (stream.eatSpace()) { 59 var lineOffset = stream.indentation(); 60 if (lineOffset > scopeOffset) { 61 indentInfo = 'indent'; 62 } else if (lineOffset < scopeOffset) { 63 indentInfo = 'dedent'; 64 } 65 return null; 66 } else { 67 if (scopeOffset > 0) { 68 dedent(stream, state); 69 } 70 } 71 } 72 if (stream.eatSpace()) { 73 return null; 74 } 75 76 var ch = stream.peek(); 77 78 // Handle Comments 79 if (ch === '#') { 80 stream.skipToEnd(); 81 return 'comment'; 82 } 83 84 // Handle Number Literals 85 if (stream.match(/^[0-9\.]/, false)) { 86 var floatLiteral = false; 87 // Floats 88 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } 89 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } 90 if (stream.match(/^\.\d+/)) { floatLiteral = true; } 91 if (floatLiteral) { 92 // Float literals may be "imaginary" 93 stream.eat(/J/i); 94 return 'number'; 95 } 96 // Integers 97 var intLiteral = false; 98 // Hex 99 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; } 100 // Binary 101 if (stream.match(/^0b[01]+/i)) { intLiteral = true; } 102 // Octal 103 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } 104 // Decimal 105 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { 106 // Decimal literals may be "imaginary" 107 stream.eat(/J/i); 108 // TODO - Can you have imaginary longs? 109 intLiteral = true; 110 } 111 // Zero by itself with no other piece of number. 112 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } 113 if (intLiteral) { 114 // Integer literals may be "long" 115 stream.eat(/L/i); 116 return 'number'; 117 } 118 } 119 120 // Handle Strings 121 if (stream.match(stringPrefixes)) { 122 state.tokenize = tokenStringFactory(stream.current()); 123 return state.tokenize(stream, state); 124 } 125 126 // Handle operators and Delimiters 127 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) { 128 return null; 129 } 130 if (stream.match(doubleOperators) 131 || stream.match(singleOperators) 132 || stream.match(wordOperators)) { 133 return 'operator'; 134 } 135 if (stream.match(singleDelimiters)) { 136 return null; 137 } 138 139 if (stream.match(keywords)) { 140 return 'keyword'; 141 } 142 143 if (stream.match(builtins)) { 144 return 'builtin'; 145 } 146 147 if (stream.match(identifiers)) { 148 return 'variable'; 149 } 150 151 // Handle non-detected items 152 stream.next(); 153 return ERRORCLASS; 154 } 155 156 function tokenStringFactory(delimiter) { 157 while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) { 158 delimiter = delimiter.substr(1); 159 } 160 var singleline = delimiter.length == 1; 161 var OUTCLASS = 'string'; 162 163 function tokenString(stream, state) { 164 while (!stream.eol()) { 165 stream.eatWhile(/[^'"\\]/); 166 if (stream.eat('\\')) { 167 stream.next(); 168 if (singleline && stream.eol()) { 169 return OUTCLASS; 170 } 171 } else if (stream.match(delimiter)) { 172 state.tokenize = tokenBase; 173 return OUTCLASS; 174 } else { 175 stream.eat(/['"]/); 176 } 177 } 178 if (singleline) { 179 if (parserConf.singleLineStringErrors) { 180 return ERRORCLASS; 181 } else { 182 state.tokenize = tokenBase; 183 } 184 } 185 return OUTCLASS; 186 } 187 tokenString.isString = true; 188 return tokenString; 189 } 190 191 function indent(stream, state, type) { 192 type = type || 'py'; 193 var indentUnit = 0; 194 if (type === 'py') { 195 if (state.scopes[0].type !== 'py') { 196 state.scopes[0].offset = stream.indentation(); 197 return; 198 } 199 for (var i = 0; i < state.scopes.length; ++i) { 200 if (state.scopes[i].type === 'py') { 201 indentUnit = state.scopes[i].offset + conf.indentUnit; 202 break; 203 } 204 } 205 } else { 206 indentUnit = stream.column() + stream.current().length; 207 } 208 state.scopes.unshift({ 209 offset: indentUnit, 210 type: type 211 }); 212 } 213 214 function dedent(stream, state, type) { 215 type = type || 'py'; 216 if (state.scopes.length == 1) return; 217 if (state.scopes[0].type === 'py') { 218 var _indent = stream.indentation(); 219 var _indent_index = -1; 220 for (var i = 0; i < state.scopes.length; ++i) { 221 if (_indent === state.scopes[i].offset) { 222 _indent_index = i; 223 break; 224 } 225 } 226 if (_indent_index === -1) { 227 return true; 228 } 229 while (state.scopes[0].offset !== _indent) { 230 state.scopes.shift(); 231 } 232 return false; 233 } else { 234 if (type === 'py') { 235 state.scopes[0].offset = stream.indentation(); 236 return false; 237 } else { 238 if (state.scopes[0].type != type) { 239 return true; 240 } 241 state.scopes.shift(); 242 return false; 243 } 244 } 245 } 246 247 function tokenLexer(stream, state) { 248 indentInfo = null; 249 var style = state.tokenize(stream, state); 250 var current = stream.current(); 251 252 // Handle '.' connected identifiers 253 if (current === '.') { 254 style = stream.match(identifiers, false) ? null : ERRORCLASS; 255 if (style === null && state.lastToken === 'meta') { 256 // Apply 'meta' style to '.' connected identifiers when 257 // appropriate. 258 style = 'meta'; 259 } 260 return style; 261 } 262 263 // Handle decorators 264 if (current === '@') { 265 return stream.match(identifiers, false) ? 'meta' : ERRORCLASS; 266 } 267 268 if ((style === 'variable' || style === 'builtin') 269 && state.lastToken === 'meta') { 270 style = 'meta'; 271 } 272 273 // Handle scope changes. 274 if (current === 'pass' || current === 'return') { 275 state.dedent += 1; 276 } 277 if (current === 'lambda') state.lambda = true; 278 if ((current === ':' && !state.lambda && state.scopes[0].type == 'py') 279 || indentInfo === 'indent') { 280 indent(stream, state); 281 } 282 var delimiter_index = '[({'.indexOf(current); 283 if (delimiter_index !== -1) { 284 indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1)); 285 } 286 if (indentInfo === 'dedent') { 287 if (dedent(stream, state)) { 288 return ERRORCLASS; 289 } 290 } 291 delimiter_index = '])}'.indexOf(current); 292 if (delimiter_index !== -1) { 293 if (dedent(stream, state, current)) { 294 return ERRORCLASS; 295 } 296 } 297 if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'py') { 298 if (state.scopes.length > 1) state.scopes.shift(); 299 state.dedent -= 1; 300 } 301 302 return style; 303 } 304 305 var external = { 306 startState: function(basecolumn) { 307 return { 308 tokenize: tokenBase, 309 scopes: [{offset:basecolumn || 0, type:'py'}], 310 lastToken: null, 311 lambda: false, 312 dedent: 0 313 }; 314 }, 315 316 token: function(stream, state) { 317 var style = tokenLexer(stream, state); 318 319 state.lastToken = style; 320 321 if (stream.eol() && stream.lambda) { 322 state.lambda = false; 323 } 324 325 return style; 326 }, 327 328 indent: function(state) { 329 if (state.tokenize != tokenBase) { 330 return state.tokenize.isString ? CodeMirror.Pass : 0; 331 } 332 333 return state.scopes[0].offset; 334 }, 335 336 lineComment: "#" 337 }; 338 return external; 339}); 340 341CodeMirror.defineMIME("text/x-python", "python"); 342