rst.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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'), require('../python/python'), require('../stex/stex'), require('../../addon/mode/overlay'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror', '../python/python', '../stex/stex', '../../addon/mode/overlay'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.defineMode(
  15. 'rst',
  16. function (config, options) {
  17. var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/
  18. var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/
  19. var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/
  20. var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/
  21. var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/
  22. var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/
  23. var rx_uri_protocol = '[Hh][Tt][Tt][Pp][Ss]?://'
  24. var rx_uri_domain = '(?:[\\d\\w.-]+)\\.(?:\\w{2,6})'
  25. var rx_uri_path = '(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*'
  26. var rx_uri = new RegExp('^' + rx_uri_protocol + rx_uri_domain + rx_uri_path)
  27. var overlay = {
  28. token: function (stream) {
  29. if (stream.match(rx_strong) && stream.match(/\W+|$/, false)) return 'strong'
  30. if (stream.match(rx_emphasis) && stream.match(/\W+|$/, false)) return 'em'
  31. if (stream.match(rx_literal) && stream.match(/\W+|$/, false)) return 'string-2'
  32. if (stream.match(rx_number)) return 'number'
  33. if (stream.match(rx_positive)) return 'positive'
  34. if (stream.match(rx_negative)) return 'negative'
  35. if (stream.match(rx_uri)) return 'link'
  36. while (stream.next() != null) {
  37. if (stream.match(rx_strong, false)) break
  38. if (stream.match(rx_emphasis, false)) break
  39. if (stream.match(rx_literal, false)) break
  40. if (stream.match(rx_number, false)) break
  41. if (stream.match(rx_positive, false)) break
  42. if (stream.match(rx_negative, false)) break
  43. if (stream.match(rx_uri, false)) break
  44. }
  45. return null
  46. },
  47. }
  48. var mode = CodeMirror.getMode(config, options.backdrop || 'rst-base')
  49. return CodeMirror.overlayMode(mode, overlay, true) // combine
  50. },
  51. 'python',
  52. 'stex'
  53. )
  54. ///////////////////////////////////////////////////////////////////////////////
  55. ///////////////////////////////////////////////////////////////////////////////
  56. CodeMirror.defineMode(
  57. 'rst-base',
  58. function (config) {
  59. ///////////////////////////////////////////////////////////////////////////
  60. ///////////////////////////////////////////////////////////////////////////
  61. function format(string) {
  62. var args = Array.prototype.slice.call(arguments, 1)
  63. return string.replace(/{(\d+)}/g, function (match, n) {
  64. return typeof args[n] != 'undefined' ? args[n] : match
  65. })
  66. }
  67. ///////////////////////////////////////////////////////////////////////////
  68. ///////////////////////////////////////////////////////////////////////////
  69. var mode_python = CodeMirror.getMode(config, 'python')
  70. var mode_stex = CodeMirror.getMode(config, 'stex')
  71. ///////////////////////////////////////////////////////////////////////////
  72. ///////////////////////////////////////////////////////////////////////////
  73. var SEPA = '\\s+'
  74. var TAIL = '(?:\\s*|\\W|$)',
  75. rx_TAIL = new RegExp(format('^{0}', TAIL))
  76. var NAME = '(?:[^\\W\\d_](?:[\\w!"#$%&\'()\\*\\+,\\-\\./:;<=>\\?]*[^\\W_])?)',
  77. rx_NAME = new RegExp(format('^{0}', NAME))
  78. var NAME_WWS = '(?:[^\\W\\d_](?:[\\w\\s!"#$%&\'()\\*\\+,\\-\\./:;<=>\\?]*[^\\W_])?)'
  79. var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS)
  80. var TEXT1 = '(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)'
  81. var TEXT2 = '(?:[^\\`]+)',
  82. rx_TEXT2 = new RegExp(format('^{0}', TEXT2))
  83. var rx_section = new RegExp('^([!\'#$%&"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$')
  84. var rx_explicit = new RegExp(format('^\\.\\.{0}', SEPA))
  85. var rx_link = new RegExp(format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL))
  86. var rx_directive = new RegExp(format('^{0}::{1}', REF_NAME, TAIL))
  87. var rx_substitution = new RegExp(format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL))
  88. var rx_footnote = new RegExp(format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL))
  89. var rx_citation = new RegExp(format('^\\[{0}\\]{1}', REF_NAME, TAIL))
  90. var rx_substitution_ref = new RegExp(format('^\\|{0}\\|', TEXT1))
  91. var rx_footnote_ref = new RegExp(format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME))
  92. var rx_citation_ref = new RegExp(format('^\\[{0}\\]_', REF_NAME))
  93. var rx_link_ref1 = new RegExp(format('^{0}__?', REF_NAME))
  94. var rx_link_ref2 = new RegExp(format('^`{0}`_', TEXT2))
  95. var rx_role_pre = new RegExp(format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL))
  96. var rx_role_suf = new RegExp(format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL))
  97. var rx_role = new RegExp(format('^:{0}:{1}', NAME, TAIL))
  98. var rx_directive_name = new RegExp(format('^{0}', REF_NAME))
  99. var rx_directive_tail = new RegExp(format('^::{0}', TAIL))
  100. var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1))
  101. var rx_substitution_sepa = new RegExp(format('^{0}', SEPA))
  102. var rx_substitution_name = new RegExp(format('^{0}', REF_NAME))
  103. var rx_substitution_tail = new RegExp(format('^::{0}', TAIL))
  104. var rx_link_head = new RegExp('^_')
  105. var rx_link_name = new RegExp(format('^{0}|_', REF_NAME))
  106. var rx_link_tail = new RegExp(format('^:{0}', TAIL))
  107. var rx_verbatim = new RegExp('^::\\s*$')
  108. var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s')
  109. ///////////////////////////////////////////////////////////////////////////
  110. ///////////////////////////////////////////////////////////////////////////
  111. function to_normal(stream, state) {
  112. var token = null
  113. if (stream.sol() && stream.match(rx_examples, false)) {
  114. change(state, to_mode, {
  115. mode: mode_python,
  116. local: CodeMirror.startState(mode_python),
  117. })
  118. } else if (stream.sol() && stream.match(rx_explicit)) {
  119. change(state, to_explicit)
  120. token = 'meta'
  121. } else if (stream.sol() && stream.match(rx_section)) {
  122. change(state, to_normal)
  123. token = 'header'
  124. } else if (phase(state) == rx_role_pre || stream.match(rx_role_pre, false)) {
  125. switch (stage(state)) {
  126. case 0:
  127. change(state, to_normal, context(rx_role_pre, 1))
  128. stream.match(/^:/)
  129. token = 'meta'
  130. break
  131. case 1:
  132. change(state, to_normal, context(rx_role_pre, 2))
  133. stream.match(rx_NAME)
  134. token = 'keyword'
  135. if (stream.current().match(/^(?:math|latex)/)) {
  136. state.tmp_stex = true
  137. }
  138. break
  139. case 2:
  140. change(state, to_normal, context(rx_role_pre, 3))
  141. stream.match(/^:`/)
  142. token = 'meta'
  143. break
  144. case 3:
  145. if (state.tmp_stex) {
  146. state.tmp_stex = undefined
  147. state.tmp = {
  148. mode: mode_stex,
  149. local: CodeMirror.startState(mode_stex),
  150. }
  151. }
  152. if (state.tmp) {
  153. if (stream.peek() == '`') {
  154. change(state, to_normal, context(rx_role_pre, 4))
  155. state.tmp = undefined
  156. break
  157. }
  158. token = state.tmp.mode.token(stream, state.tmp.local)
  159. break
  160. }
  161. change(state, to_normal, context(rx_role_pre, 4))
  162. stream.match(rx_TEXT2)
  163. token = 'string'
  164. break
  165. case 4:
  166. change(state, to_normal, context(rx_role_pre, 5))
  167. stream.match(/^`/)
  168. token = 'meta'
  169. break
  170. case 5:
  171. change(state, to_normal, context(rx_role_pre, 6))
  172. stream.match(rx_TAIL)
  173. break
  174. default:
  175. change(state, to_normal)
  176. }
  177. } else if (phase(state) == rx_role_suf || stream.match(rx_role_suf, false)) {
  178. switch (stage(state)) {
  179. case 0:
  180. change(state, to_normal, context(rx_role_suf, 1))
  181. stream.match(/^`/)
  182. token = 'meta'
  183. break
  184. case 1:
  185. change(state, to_normal, context(rx_role_suf, 2))
  186. stream.match(rx_TEXT2)
  187. token = 'string'
  188. break
  189. case 2:
  190. change(state, to_normal, context(rx_role_suf, 3))
  191. stream.match(/^`:/)
  192. token = 'meta'
  193. break
  194. case 3:
  195. change(state, to_normal, context(rx_role_suf, 4))
  196. stream.match(rx_NAME)
  197. token = 'keyword'
  198. break
  199. case 4:
  200. change(state, to_normal, context(rx_role_suf, 5))
  201. stream.match(/^:/)
  202. token = 'meta'
  203. break
  204. case 5:
  205. change(state, to_normal, context(rx_role_suf, 6))
  206. stream.match(rx_TAIL)
  207. break
  208. default:
  209. change(state, to_normal)
  210. }
  211. } else if (phase(state) == rx_role || stream.match(rx_role, false)) {
  212. switch (stage(state)) {
  213. case 0:
  214. change(state, to_normal, context(rx_role, 1))
  215. stream.match(/^:/)
  216. token = 'meta'
  217. break
  218. case 1:
  219. change(state, to_normal, context(rx_role, 2))
  220. stream.match(rx_NAME)
  221. token = 'keyword'
  222. break
  223. case 2:
  224. change(state, to_normal, context(rx_role, 3))
  225. stream.match(/^:/)
  226. token = 'meta'
  227. break
  228. case 3:
  229. change(state, to_normal, context(rx_role, 4))
  230. stream.match(rx_TAIL)
  231. break
  232. default:
  233. change(state, to_normal)
  234. }
  235. } else if (phase(state) == rx_substitution_ref || stream.match(rx_substitution_ref, false)) {
  236. switch (stage(state)) {
  237. case 0:
  238. change(state, to_normal, context(rx_substitution_ref, 1))
  239. stream.match(rx_substitution_text)
  240. token = 'variable-2'
  241. break
  242. case 1:
  243. change(state, to_normal, context(rx_substitution_ref, 2))
  244. if (stream.match(/^_?_?/)) token = 'link'
  245. break
  246. default:
  247. change(state, to_normal)
  248. }
  249. } else if (stream.match(rx_footnote_ref)) {
  250. change(state, to_normal)
  251. token = 'quote'
  252. } else if (stream.match(rx_citation_ref)) {
  253. change(state, to_normal)
  254. token = 'quote'
  255. } else if (stream.match(rx_link_ref1)) {
  256. change(state, to_normal)
  257. if (!stream.peek() || stream.peek().match(/^\W$/)) {
  258. token = 'link'
  259. }
  260. } else if (phase(state) == rx_link_ref2 || stream.match(rx_link_ref2, false)) {
  261. switch (stage(state)) {
  262. case 0:
  263. if (!stream.peek() || stream.peek().match(/^\W$/)) {
  264. change(state, to_normal, context(rx_link_ref2, 1))
  265. } else {
  266. stream.match(rx_link_ref2)
  267. }
  268. break
  269. case 1:
  270. change(state, to_normal, context(rx_link_ref2, 2))
  271. stream.match(/^`/)
  272. token = 'link'
  273. break
  274. case 2:
  275. change(state, to_normal, context(rx_link_ref2, 3))
  276. stream.match(rx_TEXT2)
  277. break
  278. case 3:
  279. change(state, to_normal, context(rx_link_ref2, 4))
  280. stream.match(/^`_/)
  281. token = 'link'
  282. break
  283. default:
  284. change(state, to_normal)
  285. }
  286. } else if (stream.match(rx_verbatim)) {
  287. change(state, to_verbatim)
  288. } else {
  289. if (stream.next()) change(state, to_normal)
  290. }
  291. return token
  292. }
  293. ///////////////////////////////////////////////////////////////////////////
  294. ///////////////////////////////////////////////////////////////////////////
  295. function to_explicit(stream, state) {
  296. var token = null
  297. if (phase(state) == rx_substitution || stream.match(rx_substitution, false)) {
  298. switch (stage(state)) {
  299. case 0:
  300. change(state, to_explicit, context(rx_substitution, 1))
  301. stream.match(rx_substitution_text)
  302. token = 'variable-2'
  303. break
  304. case 1:
  305. change(state, to_explicit, context(rx_substitution, 2))
  306. stream.match(rx_substitution_sepa)
  307. break
  308. case 2:
  309. change(state, to_explicit, context(rx_substitution, 3))
  310. stream.match(rx_substitution_name)
  311. token = 'keyword'
  312. break
  313. case 3:
  314. change(state, to_explicit, context(rx_substitution, 4))
  315. stream.match(rx_substitution_tail)
  316. token = 'meta'
  317. break
  318. default:
  319. change(state, to_normal)
  320. }
  321. } else if (phase(state) == rx_directive || stream.match(rx_directive, false)) {
  322. switch (stage(state)) {
  323. case 0:
  324. change(state, to_explicit, context(rx_directive, 1))
  325. stream.match(rx_directive_name)
  326. token = 'keyword'
  327. if (stream.current().match(/^(?:math|latex)/)) state.tmp_stex = true
  328. else if (stream.current().match(/^python/)) state.tmp_py = true
  329. break
  330. case 1:
  331. change(state, to_explicit, context(rx_directive, 2))
  332. stream.match(rx_directive_tail)
  333. token = 'meta'
  334. if (stream.match(/^latex\s*$/) || state.tmp_stex) {
  335. state.tmp_stex = undefined
  336. change(state, to_mode, {
  337. mode: mode_stex,
  338. local: CodeMirror.startState(mode_stex),
  339. })
  340. }
  341. break
  342. case 2:
  343. change(state, to_explicit, context(rx_directive, 3))
  344. if (stream.match(/^python\s*$/) || state.tmp_py) {
  345. state.tmp_py = undefined
  346. change(state, to_mode, {
  347. mode: mode_python,
  348. local: CodeMirror.startState(mode_python),
  349. })
  350. }
  351. break
  352. default:
  353. change(state, to_normal)
  354. }
  355. } else if (phase(state) == rx_link || stream.match(rx_link, false)) {
  356. switch (stage(state)) {
  357. case 0:
  358. change(state, to_explicit, context(rx_link, 1))
  359. stream.match(rx_link_head)
  360. stream.match(rx_link_name)
  361. token = 'link'
  362. break
  363. case 1:
  364. change(state, to_explicit, context(rx_link, 2))
  365. stream.match(rx_link_tail)
  366. token = 'meta'
  367. break
  368. default:
  369. change(state, to_normal)
  370. }
  371. } else if (stream.match(rx_footnote)) {
  372. change(state, to_normal)
  373. token = 'quote'
  374. } else if (stream.match(rx_citation)) {
  375. change(state, to_normal)
  376. token = 'quote'
  377. } else {
  378. stream.eatSpace()
  379. if (stream.eol()) {
  380. change(state, to_normal)
  381. } else {
  382. stream.skipToEnd()
  383. change(state, to_comment)
  384. token = 'comment'
  385. }
  386. }
  387. return token
  388. }
  389. ///////////////////////////////////////////////////////////////////////////
  390. ///////////////////////////////////////////////////////////////////////////
  391. function to_comment(stream, state) {
  392. return as_block(stream, state, 'comment')
  393. }
  394. function to_verbatim(stream, state) {
  395. return as_block(stream, state, 'meta')
  396. }
  397. function as_block(stream, state, token) {
  398. if (stream.eol() || stream.eatSpace()) {
  399. stream.skipToEnd()
  400. return token
  401. } else {
  402. change(state, to_normal)
  403. return null
  404. }
  405. }
  406. ///////////////////////////////////////////////////////////////////////////
  407. ///////////////////////////////////////////////////////////////////////////
  408. function to_mode(stream, state) {
  409. if (state.ctx.mode && state.ctx.local) {
  410. if (stream.sol()) {
  411. if (!stream.eatSpace()) change(state, to_normal)
  412. return null
  413. }
  414. return state.ctx.mode.token(stream, state.ctx.local)
  415. }
  416. change(state, to_normal)
  417. return null
  418. }
  419. ///////////////////////////////////////////////////////////////////////////
  420. ///////////////////////////////////////////////////////////////////////////
  421. function context(phase, stage, mode, local) {
  422. return { phase: phase, stage: stage, mode: mode, local: local }
  423. }
  424. function change(state, tok, ctx) {
  425. state.tok = tok
  426. state.ctx = ctx || {}
  427. }
  428. function stage(state) {
  429. return state.ctx.stage || 0
  430. }
  431. function phase(state) {
  432. return state.ctx.phase
  433. }
  434. ///////////////////////////////////////////////////////////////////////////
  435. ///////////////////////////////////////////////////////////////////////////
  436. return {
  437. startState: function () {
  438. return { tok: to_normal, ctx: context(undefined, 0) }
  439. },
  440. copyState: function (state) {
  441. var ctx = state.ctx,
  442. tmp = state.tmp
  443. if (ctx.local) ctx = { mode: ctx.mode, local: CodeMirror.copyState(ctx.mode, ctx.local) }
  444. if (tmp) tmp = { mode: tmp.mode, local: CodeMirror.copyState(tmp.mode, tmp.local) }
  445. return { tok: state.tok, ctx: ctx, tmp: tmp }
  446. },
  447. innerMode: function (state) {
  448. return state.tmp ? { state: state.tmp.local, mode: state.tmp.mode } : state.ctx.mode ? { state: state.ctx.local, mode: state.ctx.mode } : null
  449. },
  450. token: function (stream, state) {
  451. return state.tok(stream, state)
  452. },
  453. }
  454. },
  455. 'python',
  456. 'stex'
  457. )
  458. ///////////////////////////////////////////////////////////////////////////////
  459. ///////////////////////////////////////////////////////////////////////////////
  460. CodeMirror.defineMIME('text/x-rst', 'rst')
  461. ///////////////////////////////////////////////////////////////////////////////
  462. ///////////////////////////////////////////////////////////////////////////////
  463. })