CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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