@jupyterlab/codemirror 4.0.0-rc.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,7 +32,13 @@
32
32
 
33
33
  import { ISearchMatch } from '@jupyterlab/documentsearch';
34
34
  import { CodeMirrorEditor } from './editor';
35
- import { StateEffect, StateEffectType, StateField } from '@codemirror/state';
35
+ import {
36
+ EditorSelection,
37
+ Extension,
38
+ StateEffect,
39
+ StateEffectType,
40
+ StateField
41
+ } from '@codemirror/state';
36
42
  import { Decoration, DecorationSet, EditorView } from '@codemirror/view';
37
43
  import { CodeEditor } from '@jupyterlab/codeeditor';
38
44
  import {
@@ -45,6 +51,43 @@ import {
45
51
  import { ISignal, Signal } from '@lumino/signaling';
46
52
  import { ISharedText, SourceChange } from '@jupyter/ydoc';
47
53
 
54
+ /**
55
+ * Defines from which position the search should be executed.
56
+ * - `'selection'` - search from selection head/anchor (depending on search direction)
57
+ * - `'previous-match'` - search from previous match
58
+ * - `'auto'` - search from selection if editor is focused or previous match otherwise
59
+ * - `'selection-start'` - search from selection head/anchor whichever is smaller
60
+ * - `'start'` - from start of the editor
61
+ */
62
+ type SearchStartAnchor =
63
+ | 'auto'
64
+ | 'selection'
65
+ | 'selection-start'
66
+ | 'previous-match'
67
+ | 'start';
68
+
69
+ interface IHighlightMatchOptions {
70
+ /**
71
+ * Whether the highlighted match should be scrolled into view.
72
+ * Defaults to `true`.
73
+ */
74
+ scroll?: boolean;
75
+ /**
76
+ * Whether the user cursor should be moved to select the match.
77
+ * `protectSelection` flag takes precedence over this option.
78
+ * Defaults to `true`.
79
+ */
80
+ select?: boolean;
81
+ }
82
+
83
+ export interface IHighlightAdjacentMatchOptions extends IHighlightMatchOptions {
84
+ /**
85
+ * What should be used as an anchor when searching for adjacent match.
86
+ * Defaults to `'auto'`.
87
+ */
88
+ from?: SearchStartAnchor;
89
+ }
90
+
48
91
  /**
49
92
  * Search provider for editors.
50
93
  */
@@ -167,7 +210,7 @@ export abstract class EditorSearchProvider<
167
210
  }
168
211
 
169
212
  /**
170
- * Set whether search should be limitted to specified selection.
213
+ * Set whether search should be limitted to specified text selection.
171
214
  */
172
215
  async setSearchSelection(selection: CodeEditor.IRange | null): Promise<void> {
173
216
  if (this._inSelection === selection) {
@@ -178,6 +221,20 @@ export abstract class EditorSearchProvider<
178
221
  this._stateChanged.emit();
179
222
  }
180
223
 
224
+ /**
225
+ * Set whether user selection should be protected from modifications.
226
+ *
227
+ * If disabled, the selection will be updated on search and on editor focus
228
+ * to cover the current match. We need to protect selection from modifications
229
+ * for both: search in text and search in cells; since `setSearchSelection`
230
+ * is only telling us about search in text, we need to have an additional
231
+ * way to signal that either search in text or in cells is active, or for
232
+ * any other reason selection range should be protected.
233
+ */
234
+ setProtectSelection(v: boolean) {
235
+ this.cmHandler.protectSelection = v;
236
+ }
237
+
181
238
  /**
182
239
  * Initialize the search using the provided options. Should update the UI
183
240
  * to highlight all matches and "select" the first match.
@@ -211,13 +268,12 @@ export abstract class EditorSearchProvider<
211
268
  */
212
269
  async highlightNext(
213
270
  loop = true,
214
- fromCursor = false
271
+ options?: IHighlightAdjacentMatchOptions
215
272
  ): Promise<ISearchMatch | undefined> {
216
273
  if (this.matchesCount === 0 || !this.isActive) {
217
274
  this.currentIndex = null;
218
275
  } else {
219
- // This starts from the cursor position if `fromCursor` is true
220
- let match = await this.cmHandler.highlightNext(fromCursor);
276
+ let match = await this.cmHandler.highlightNext(options);
221
277
  if (match) {
222
278
  this.currentIndex = this.cmHandler.currentIndex;
223
279
  } else {
@@ -239,13 +295,12 @@ export abstract class EditorSearchProvider<
239
295
  */
240
296
  async highlightPrevious(
241
297
  loop = true,
242
- fromCursor = false
298
+ options?: IHighlightAdjacentMatchOptions
243
299
  ): Promise<ISearchMatch | undefined> {
244
300
  if (this.matchesCount === 0 || !this.isActive) {
245
301
  this.currentIndex = null;
246
302
  } else {
247
- // This starts from the cursor position if `fromCursor` is true
248
- let match = await this.cmHandler.highlightPrevious(fromCursor);
303
+ let match = await this.cmHandler.highlightPrevious(options);
249
304
  if (match) {
250
305
  this.currentIndex = this.cmHandler.currentIndex;
251
306
  } else {
@@ -405,7 +460,11 @@ export abstract class EditorSearchProvider<
405
460
  this.cmHandler.currentIndex === null &&
406
461
  this.cmHandler.matches.length > 0
407
462
  ) {
408
- await this.cmHandler.highlightNext(true, false);
463
+ await this.cmHandler.highlightNext({
464
+ from: 'selection',
465
+ select: false,
466
+ scroll: false
467
+ });
409
468
  }
410
469
  this.currentIndex = this.cmHandler.currentIndex;
411
470
  } else {
@@ -452,9 +511,9 @@ interface IEffectValue {
452
511
  *
453
512
  * **NOTES:**
454
513
  * - to retain the selection visibility `drawSelection` extension is needed.
455
- * - highlighting starts from the cursor (if editor is focused, cursor moved,
456
- * or `fromCursor` argument is set to `true`), or from last "current" match
457
- * otherwise.
514
+ * - highlighting starts from the cursor (if editor is focused and `from` is set
515
+ * to `'auto'`, cursor moved, or `from` argument is set to `'selection'` or
516
+ * `'selection-start'`), or from last "current" match otherwise.
458
517
  * - `currentIndex` is the (readonly) source of truth for the current match.
459
518
  */
460
519
  export class CodeMirrorSearchHighlighter {
@@ -495,6 +554,8 @@ export class CodeMirrorSearchHighlighter {
495
554
  if (ef.is(this._highlightEffect)) {
496
555
  const e = ef as StateEffect<IEffectValue>;
497
556
  if (e.value.matches.length) {
557
+ // Note: nesting will vary; sometimes `.cm-searching` will be
558
+ // inside `.jp-current-match`, sometime the other way round.
498
559
  highlights = highlights.update({
499
560
  add: e.value.matches.map(m =>
500
561
  this._highlightMark.range(
@@ -525,6 +586,13 @@ export class CodeMirrorSearchHighlighter {
525
586
  },
526
587
  provide: f => EditorView.decorations.from(f)
527
588
  });
589
+
590
+ this._domEventHandlers = EditorView.domEventHandlers({
591
+ focus: () => {
592
+ // Set cursor on active match when editor gets focused.
593
+ this._selectCurrentMatch();
594
+ }
595
+ });
528
596
  }
529
597
 
530
598
  /**
@@ -548,11 +616,22 @@ export class CodeMirrorSearchHighlighter {
548
616
  ) {
549
617
  this._currentIndex = this._matches.length > 0 ? 0 : null;
550
618
  }
551
- this._highlightCurrentMatch(true);
619
+ this._highlightCurrentMatch({ select: false });
552
620
  }
553
621
 
554
622
  private _current: ISearchMatch | null = null;
555
623
 
624
+ /**
625
+ * Whether the cursor/selection should not be modified.
626
+ */
627
+ get protectSelection(): boolean {
628
+ return this._protectSelection;
629
+ }
630
+ set protectSelection(v: boolean) {
631
+ this._protectSelection = v;
632
+ }
633
+ private _protectSelection: boolean;
634
+
556
635
  /**
557
636
  * Clear all highlighted matches
558
637
  */
@@ -583,11 +662,10 @@ export class CodeMirrorSearchHighlighter {
583
662
  * @returns The next match if available
584
663
  */
585
664
  highlightNext(
586
- fromCursor = false,
587
- doNotModifySelection = false
665
+ options?: IHighlightAdjacentMatchOptions
588
666
  ): Promise<ISearchMatch | undefined> {
589
- this._currentIndex = this._findNext(false, fromCursor);
590
- this._highlightCurrentMatch(doNotModifySelection);
667
+ this._currentIndex = this._findNext(false, options?.from ?? 'auto');
668
+ this._highlightCurrentMatch(options);
591
669
  return Promise.resolve(
592
670
  this._currentIndex !== null
593
671
  ? this._matches[this._currentIndex]
@@ -600,9 +678,11 @@ export class CodeMirrorSearchHighlighter {
600
678
  *
601
679
  * @returns The previous match if available
602
680
  */
603
- highlightPrevious(fromCursor = false): Promise<ISearchMatch | undefined> {
604
- this._currentIndex = this._findNext(true, fromCursor);
605
- this._highlightCurrentMatch();
681
+ highlightPrevious(
682
+ options?: IHighlightAdjacentMatchOptions
683
+ ): Promise<ISearchMatch | undefined> {
684
+ this._currentIndex = this._findNext(true, options?.from ?? 'auto');
685
+ this._highlightCurrentMatch(options);
606
686
  return Promise.resolve(
607
687
  this._currentIndex !== null
608
688
  ? this._matches[this._currentIndex]
@@ -623,11 +703,24 @@ export class CodeMirrorSearchHighlighter {
623
703
  if (this._currentIndex !== null) {
624
704
  this._highlightCurrentMatch();
625
705
  }
706
+ this._cm.editor.dispatch({
707
+ effects: StateEffect.appendConfig.of(this._domEventHandlers)
708
+ });
626
709
  this._refresh();
627
710
  }
628
711
  }
629
712
 
630
- private _selectCurrentMatch(): void {
713
+ private _selectCurrentMatch(scroll = true): void {
714
+ // This method has two responsibilities:
715
+ // 1) Scroll the current match into the view - useful for long lines,
716
+ // and file editors with more lines that fit on the screen
717
+ // 2) When user has focus on the editor (not search box) and presses
718
+ // ctrl + g/ctrl + shift + g to jump to next match they want their
719
+ // cursor to jump too.
720
+ // We execute (1) and (2) together as CodeMirror has a special code path
721
+ // to handle both in a single dispatch.
722
+ // The (2) case is inapplicable to search in selection mode, as it would
723
+ // invalidate the query selection, so in that case we only execute (1).
631
724
  const match = this._current;
632
725
  if (!match) {
633
726
  return;
@@ -635,24 +728,35 @@ export class CodeMirrorSearchHighlighter {
635
728
  if (!this._cm) {
636
729
  return;
637
730
  }
638
- const selection = this._cm.editor.state.selection.main;
639
- if (
640
- selection.from === match.position &&
641
- selection.to === match.position + match.text.length
642
- ) {
643
- return;
644
- }
645
731
  const cursor = {
646
732
  anchor: match.position,
647
733
  head: match.position + match.text.length
648
734
  };
649
- this._cm.editor.dispatch({
650
- selection: cursor,
651
- scrollIntoView: true
652
- });
735
+ const selection = this._cm.editor.state.selection.main;
736
+ if (
737
+ (selection.from === match.position &&
738
+ selection.to === match.position + match.text.length) ||
739
+ this._protectSelection
740
+ ) {
741
+ // Correct selection is already set or search is restricted to selection:
742
+ // scroll without changing the selection.
743
+ if (scroll) {
744
+ this._cm.editor.dispatch({
745
+ effects: EditorView.scrollIntoView(
746
+ EditorSelection.range(cursor.anchor, cursor.head)
747
+ )
748
+ });
749
+ return;
750
+ }
751
+ } else {
752
+ this._cm.editor.dispatch({
753
+ selection: cursor,
754
+ scrollIntoView: scroll
755
+ });
756
+ }
653
757
  }
654
758
 
655
- private _highlightCurrentMatch(doNotModifySelection = false): void {
759
+ private _highlightCurrentMatch(options?: IHighlightMatchOptions): void {
656
760
  if (!this._cm) {
657
761
  // no-op
658
762
  return;
@@ -662,12 +766,14 @@ export class CodeMirrorSearchHighlighter {
662
766
  if (this._currentIndex !== null) {
663
767
  const match = this.matches[this._currentIndex];
664
768
  this._current = match;
665
- // Do not change selection/scroll if user is selecting
666
- if (!doNotModifySelection) {
769
+ // We do not change selection nor scroll if:
770
+ // - user is selecting text,
771
+ // - document was modified
772
+ if (options?.select ?? true) {
667
773
  if (this._cm.hasFocus()) {
668
774
  // If editor is focused we actually set the cursor on the match.
669
- this._selectCurrentMatch();
670
- } else {
775
+ this._selectCurrentMatch(options?.scroll ?? true);
776
+ } else if (options?.scroll ?? true) {
671
777
  // otherwise we just scroll to preserve the selection.
672
778
  this._cm.editor.dispatch({
673
779
  effects: EditorView.scrollIntoView(match.position)
@@ -694,27 +800,28 @@ export class CodeMirrorSearchHighlighter {
694
800
 
695
801
  if (!this._cm!.state.field(this._highlightField, false)) {
696
802
  effects.push(StateEffect.appendConfig.of([this._highlightField]));
697
- // set cursor on active match when editor gets focused
698
- const focusExtension = EditorView.domEventHandlers({
699
- focus: () => {
700
- this._selectCurrentMatch();
701
- }
702
- });
703
- effects.push(StateEffect.appendConfig.of([focusExtension]));
704
803
  }
705
804
  this._cm!.editor.dispatch({ effects });
706
805
  }
707
806
 
708
- private _findNext(reverse: boolean, fromCursor = false): number | null {
807
+ private _findNext(
808
+ reverse: boolean,
809
+ from: SearchStartAnchor = 'auto'
810
+ ): number | null {
709
811
  if (this._matches.length === 0) {
710
812
  // No-op
711
813
  return null;
712
814
  }
713
815
 
714
816
  let lastPosition = 0;
715
- if (this._cm!.hasFocus() || fromCursor) {
817
+ if ((from === 'auto' && this._cm!.hasFocus()) || from === 'selection') {
716
818
  const cursor = this._cm!.state.selection.main;
717
819
  lastPosition = reverse ? cursor.anchor : cursor.head;
820
+ } else if (from === 'selection-start') {
821
+ const cursor = this._cm!.state.selection.main;
822
+ lastPosition = Math.min(cursor.anchor, cursor.head);
823
+ } else if (from === 'start') {
824
+ lastPosition = 0;
718
825
  } else if (this._current) {
719
826
  lastPosition = reverse
720
827
  ? this._current.position
@@ -757,6 +864,7 @@ export class CodeMirrorSearchHighlighter {
757
864
  private _highlightMark: Decoration;
758
865
  private _currentMark: Decoration;
759
866
  private _highlightField: StateField<DecorationSet>;
867
+ private _domEventHandlers: Extension;
760
868
  }
761
869
 
762
870
  /**
package/style/base.css CHANGED
@@ -59,24 +59,28 @@
59
59
 
60
60
  .cm-searching,
61
61
  .cm-searching span {
62
+ /* `.cm-searching span`: we need to override syntax highlighting */
62
63
  background-color: var(--jp-search-unselected-match-background-color);
63
64
  color: var(--jp-search-unselected-match-color);
64
65
  }
65
66
 
66
- .cm-content .cm-searching::selection,
67
- .cm-content .cm-searching span::selection {
67
+ .cm-searching::selection,
68
+ .cm-searching span::selection {
68
69
  background-color: var(--jp-search-unselected-match-background-color);
69
70
  color: var(--jp-search-unselected-match-color);
70
71
  }
71
72
 
72
- .jp-current-match .cm-searching,
73
- .jp-current-match .cm-searching span {
73
+ .jp-current-match > .cm-searching,
74
+ .jp-current-match > .cm-searching span,
75
+ .cm-searching > .jp-current-match,
76
+ .cm-searching > .jp-current-match span {
74
77
  background-color: var(--jp-search-selected-match-background-color);
75
78
  color: var(--jp-search-selected-match-color);
76
79
  }
77
80
 
78
- .cm-content .jp-current-match .cm-searching::selection,
79
- .cm-content .jp-current-match .cm-searching span::selection {
81
+ .jp-current-match > .cm-searching::selection,
82
+ .cm-searching > .jp-current-match::selection,
83
+ .jp-current-match > .cm-searching span::selection {
80
84
  background-color: var(--jp-search-selected-match-background-color);
81
85
  color: var(--jp-search-selected-match-color);
82
86
  }
@@ -1,9 +0,0 @@
1
- /**
2
- * Define an IPython GFM (GitHub Flavored Markdown) mode.
3
- *
4
- * Is just a slightly altered GFM Mode with support for LaTeX.
5
- * LaTeX support was supported by Codemirror GFM as of
6
- * https://github.com/codemirror/CodeMirror/pull/567
7
- * But was later removed in
8
- * https://github.com/codemirror/CodeMirror/commit/d9c9f1b1ffe984aee41307f3e927f80d1f23590c
9
- */
@@ -1,108 +0,0 @@
1
- // Copyright (c) Jupyter Development Team.
2
- // Distributed under the terms of the Modified BSD License.
3
- // TODO: add support for LaTeX
4
- // This should be coded as an extension for https://github.com/lezer-parser/markdown
5
- // see https://github.com/lezer-parser/markdown/blob/main/src/extension.ts for examples
6
- // Then this extension can be passed to the markdown function above:
7
- //
8
- // load: () => {
9
- // return import('@codemirror/lang-markdown').then(m => m.markdown({extensions: latex}));
10
- // }
11
- /*import CodeMirror from 'codemirror';
12
- import 'codemirror/addon/mode/multiplex';
13
- import 'codemirror/mode/gfm/gfm';
14
- import 'codemirror/mode/stex/stex';
15
- */
16
- /**
17
- * Define an IPython GFM (GitHub Flavored Markdown) mode.
18
- *
19
- * Is just a slightly altered GFM Mode with support for LaTeX.
20
- * LaTeX support was supported by Codemirror GFM as of
21
- * https://github.com/codemirror/CodeMirror/pull/567
22
- * But was later removed in
23
- * https://github.com/codemirror/CodeMirror/commit/d9c9f1b1ffe984aee41307f3e927f80d1f23590c
24
- */
25
- /*CodeMirror.defineMode(
26
- 'ipythongfm',
27
- (config: CodeMirror.EditorConfiguration, modeOptions?: any) => {
28
- const gfmMode = CodeMirror.getMode(config, {
29
- name: 'gfm',
30
- // Override list3 with an under-used token, rather than `keyword`
31
- tokenTypeOverrides: { list3: 'string-2' }
32
- });
33
- const texMode = CodeMirror.getMode(config, {
34
- name: 'stex',
35
- inMathMode: true
36
- });
37
-
38
- return CodeMirror.multiplexingMode(
39
- gfmMode,
40
- // force parsing inline code and code blocks with gfmMode to prevent
41
- // parsing them as tex see:
42
- // https://github.com/jupyterlab/jupyterlab/issues/6774
43
- {
44
- open: '<code>',
45
- close: '</code>',
46
- mode: gfmMode,
47
- parseDelimiters: true
48
- },
49
- {
50
- open: '<pre>',
51
- close: '</pre>',
52
- mode: gfmMode,
53
- parseDelimiters: true
54
- },
55
- {
56
- open: '```',
57
- close: '```',
58
- mode: gfmMode,
59
- parseDelimiters: true
60
- },
61
- {
62
- open: '`',
63
- close: '`',
64
- mode: gfmMode,
65
- parseDelimiters: true
66
- },
67
- // only if we did not match a code element or block,
68
- // then try do parse using the tex mode
69
- {
70
- open: '$$',
71
- close: '$$',
72
- mode: texMode,
73
- delimStyle: 'delimit'
74
- },
75
- {
76
- // `$math mode$` is only matched if both opening
77
- // and closing $ are in the same line
78
- open: /\$(?=.*\$)/,
79
- close: '$',
80
- mode: texMode,
81
- delimStyle: 'delimit'
82
- },
83
- {
84
- open: '\\(',
85
- close: '\\)',
86
- mode: texMode,
87
- delimStyle: 'delimit'
88
- },
89
- {
90
- open: '\\[',
91
- close: '\\]',
92
- mode: texMode,
93
- delimStyle: 'delimit'
94
- }
95
- // .. more multiplexed styles can follow here
96
- );
97
- },
98
- 'gfm'
99
- );
100
-
101
- CodeMirror.defineMIME('text/x-ipythongfm', 'ipythongfm');
102
- CodeMirror.modeInfo.push({
103
- ext: [],
104
- mime: 'text/x-ipythongfm',
105
- mode: 'ipythongfm',
106
- name: 'ipythongfm'
107
- });*/
108
- //# sourceMappingURL=codemirror-ipythongfm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"codemirror-ipythongfm.js","sourceRoot":"","sources":["../src/codemirror-ipythongfm.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,2DAA2D;AAE3D,8BAA8B;AAC9B,oFAAoF;AACpF,uFAAuF;AACvF,oEAAoE;AACpE,EAAE;AACF,gBAAgB;AAChB,2FAA2F;AAC3F,IAAI;AAEJ;;;;EAIE;AACF;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkFK"}
@@ -1,109 +0,0 @@
1
- // Copyright (c) Jupyter Development Team.
2
- // Distributed under the terms of the Modified BSD License.
3
-
4
- // TODO: add support for LaTeX
5
- // This should be coded as an extension for https://github.com/lezer-parser/markdown
6
- // see https://github.com/lezer-parser/markdown/blob/main/src/extension.ts for examples
7
- // Then this extension can be passed to the markdown function above:
8
- //
9
- // load: () => {
10
- // return import('@codemirror/lang-markdown').then(m => m.markdown({extensions: latex}));
11
- // }
12
-
13
- /*import CodeMirror from 'codemirror';
14
- import 'codemirror/addon/mode/multiplex';
15
- import 'codemirror/mode/gfm/gfm';
16
- import 'codemirror/mode/stex/stex';
17
- */
18
- /**
19
- * Define an IPython GFM (GitHub Flavored Markdown) mode.
20
- *
21
- * Is just a slightly altered GFM Mode with support for LaTeX.
22
- * LaTeX support was supported by Codemirror GFM as of
23
- * https://github.com/codemirror/CodeMirror/pull/567
24
- * But was later removed in
25
- * https://github.com/codemirror/CodeMirror/commit/d9c9f1b1ffe984aee41307f3e927f80d1f23590c
26
- */
27
- /*CodeMirror.defineMode(
28
- 'ipythongfm',
29
- (config: CodeMirror.EditorConfiguration, modeOptions?: any) => {
30
- const gfmMode = CodeMirror.getMode(config, {
31
- name: 'gfm',
32
- // Override list3 with an under-used token, rather than `keyword`
33
- tokenTypeOverrides: { list3: 'string-2' }
34
- });
35
- const texMode = CodeMirror.getMode(config, {
36
- name: 'stex',
37
- inMathMode: true
38
- });
39
-
40
- return CodeMirror.multiplexingMode(
41
- gfmMode,
42
- // force parsing inline code and code blocks with gfmMode to prevent
43
- // parsing them as tex see:
44
- // https://github.com/jupyterlab/jupyterlab/issues/6774
45
- {
46
- open: '<code>',
47
- close: '</code>',
48
- mode: gfmMode,
49
- parseDelimiters: true
50
- },
51
- {
52
- open: '<pre>',
53
- close: '</pre>',
54
- mode: gfmMode,
55
- parseDelimiters: true
56
- },
57
- {
58
- open: '```',
59
- close: '```',
60
- mode: gfmMode,
61
- parseDelimiters: true
62
- },
63
- {
64
- open: '`',
65
- close: '`',
66
- mode: gfmMode,
67
- parseDelimiters: true
68
- },
69
- // only if we did not match a code element or block,
70
- // then try do parse using the tex mode
71
- {
72
- open: '$$',
73
- close: '$$',
74
- mode: texMode,
75
- delimStyle: 'delimit'
76
- },
77
- {
78
- // `$math mode$` is only matched if both opening
79
- // and closing $ are in the same line
80
- open: /\$(?=.*\$)/,
81
- close: '$',
82
- mode: texMode,
83
- delimStyle: 'delimit'
84
- },
85
- {
86
- open: '\\(',
87
- close: '\\)',
88
- mode: texMode,
89
- delimStyle: 'delimit'
90
- },
91
- {
92
- open: '\\[',
93
- close: '\\]',
94
- mode: texMode,
95
- delimStyle: 'delimit'
96
- }
97
- // .. more multiplexed styles can follow here
98
- );
99
- },
100
- 'gfm'
101
- );
102
-
103
- CodeMirror.defineMIME('text/x-ipythongfm', 'ipythongfm');
104
- CodeMirror.modeInfo.push({
105
- ext: [],
106
- mime: 'text/x-ipythongfm',
107
- mode: 'ipythongfm',
108
- name: 'ipythongfm'
109
- });*/