rst.js 21 KB

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