rst.js 18 KB

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