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