python.js 17 KB

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