2
0

rst.js 20 KB

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