@difizen/libro-codemirror 0.1.1 → 0.1.2

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.
Files changed (84) hide show
  1. package/es/auto-complete/filter.d.ts.map +1 -1
  2. package/es/auto-complete/filter.js +1 -7
  3. package/es/auto-complete/index.d.ts.map +1 -1
  4. package/es/auto-complete/index.js +5 -9
  5. package/es/auto-complete/snippet.d.ts.map +1 -1
  6. package/es/auto-complete/snippet.js +2 -2
  7. package/es/auto-complete/state.d.ts.map +1 -1
  8. package/es/auto-complete/state.js +5 -16
  9. package/es/auto-complete/view.d.ts +5 -0
  10. package/es/auto-complete/view.d.ts.map +1 -1
  11. package/es/auto-complete/view.js +2 -13
  12. package/es/completion.js +1 -1
  13. package/es/config.d.ts +15 -10
  14. package/es/config.d.ts.map +1 -1
  15. package/es/config.js +15 -6
  16. package/es/editor-contribution.d.ts +8 -0
  17. package/es/editor-contribution.d.ts.map +1 -0
  18. package/es/editor-contribution.js +30 -0
  19. package/es/editor.d.ts +40 -10
  20. package/es/editor.d.ts.map +1 -1
  21. package/es/editor.js +241 -45
  22. package/es/indentation-markers/map.d.ts +9 -9
  23. package/es/indentation-markers/map.d.ts.map +1 -1
  24. package/es/index.d.ts +3 -1
  25. package/es/index.d.ts.map +1 -1
  26. package/es/index.js +3 -1
  27. package/es/libro-icon.d.ts +2 -2
  28. package/es/libro-icon.d.ts.map +1 -1
  29. package/es/libro-icon.js +2 -2
  30. package/es/lsp/completion.d.ts +5 -0
  31. package/es/lsp/completion.d.ts.map +1 -0
  32. package/es/lsp/completion.js +245 -0
  33. package/es/lsp/format.d.ts +7 -0
  34. package/es/lsp/format.d.ts.map +1 -0
  35. package/es/lsp/format.js +193 -0
  36. package/es/lsp/index.d.ts +7 -0
  37. package/es/lsp/index.d.ts.map +1 -0
  38. package/es/lsp/index.js +6 -0
  39. package/es/lsp/lint.d.ts +3 -0
  40. package/es/lsp/lint.d.ts.map +1 -0
  41. package/es/lsp/lint.js +114 -0
  42. package/es/lsp/protocol.d.ts +7 -0
  43. package/es/lsp/protocol.d.ts.map +1 -0
  44. package/es/lsp/protocol.js +1 -0
  45. package/es/lsp/tooltip.d.ts +3 -0
  46. package/es/lsp/tooltip.d.ts.map +1 -0
  47. package/es/lsp/tooltip.js +113 -0
  48. package/es/lsp/util.d.ts +15 -0
  49. package/es/lsp/util.d.ts.map +1 -0
  50. package/es/lsp/util.js +58 -0
  51. package/es/mode.d.ts.map +1 -1
  52. package/es/module.d.ts +3 -0
  53. package/es/module.d.ts.map +1 -0
  54. package/es/module.js +4 -0
  55. package/es/theme.js +4 -5
  56. package/es/tooltip.d.ts.map +1 -1
  57. package/es/tooltip.js +2 -4
  58. package/package.json +7 -5
  59. package/src/auto-complete/filter.ts +5 -7
  60. package/src/auto-complete/index.ts +6 -7
  61. package/src/auto-complete/snippet.ts +8 -2
  62. package/src/auto-complete/state.ts +13 -18
  63. package/src/auto-complete/view.ts +7 -13
  64. package/src/completion.ts +2 -2
  65. package/src/config.ts +40 -28
  66. package/src/editor-contribution.ts +17 -0
  67. package/src/editor.ts +226 -50
  68. package/src/hyperlink.ts +1 -1
  69. package/src/indentation-markers/index.ts +3 -3
  70. package/src/indentation-markers/map.ts +9 -9
  71. package/src/index.ts +4 -1
  72. package/src/libro-icon.tsx +4 -0
  73. package/src/lsp/completion.ts +175 -0
  74. package/src/lsp/format.ts +144 -0
  75. package/src/lsp/index.ts +6 -0
  76. package/src/lsp/lint.ts +125 -0
  77. package/src/lsp/protocol.ts +8 -0
  78. package/src/lsp/tooltip.ts +76 -0
  79. package/src/lsp/util.ts +69 -0
  80. package/src/mode.ts +1 -1
  81. package/src/module.ts +8 -0
  82. package/src/theme.ts +4 -4
  83. package/src/tooltip.ts +2 -4
  84. package/src/libro-icon.ts +0 -4
package/src/config.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { defaultKeymap, historyKeymap, history } from '@codemirror/commands';
1
+ import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
2
2
  import { pythonLanguage } from '@codemirror/lang-python';
3
3
  import {
4
4
  bracketMatching,
@@ -17,23 +17,23 @@ import { Compartment, EditorState, StateEffect } from '@codemirror/state';
17
17
  import type { KeyBinding } from '@codemirror/view';
18
18
  import {
19
19
  crosshairCursor,
20
- rectangularSelection,
21
- dropCursor,
22
20
  drawSelection,
23
- highlightSpecialChars,
24
- highlightActiveLineGutter,
21
+ dropCursor,
25
22
  EditorView,
26
23
  highlightActiveLine,
24
+ highlightActiveLineGutter,
25
+ highlightSpecialChars,
27
26
  keymap,
28
27
  lineNumbers,
29
28
  placeholder,
29
+ rectangularSelection,
30
30
  } from '@codemirror/view';
31
31
  import type { IEditorConfig } from '@difizen/libro-code-editor';
32
32
 
33
33
  import {
34
+ autocompletion,
34
35
  closeBrackets,
35
36
  closeBracketsKeymap,
36
- autocompletion,
37
37
  completionKeymap,
38
38
  } from './auto-complete/index.js';
39
39
  import { kernelCompletions } from './completion.js';
@@ -41,6 +41,9 @@ import type { IOptions } from './editor.js';
41
41
  import { hyperLink } from './hyperlink.js';
42
42
  import { indentationMarkers } from './indentation-markers/index.js';
43
43
  import { FoldIcon, UnFoldIcon } from './libro-icon.js';
44
+ import { lspPythonCompletion } from './lsp/completion.js';
45
+ import { formatKeymap } from './lsp/format.js';
46
+ import { lspLint, lspTooltip } from './lsp/index.js';
44
47
  import { ensure } from './mode.js';
45
48
  import { getTheme, defaultTheme } from './theme.js';
46
49
  import { tabTooltip, tooltipKeymap } from './tooltip.js';
@@ -59,12 +62,6 @@ export interface CodeMirrorConfig extends IEditorConfig {
59
62
  */
60
63
  mimetype?: string;
61
64
 
62
- /**
63
- * The theme to style the editor with. see editortheme.ts for an example
64
- * of how to design a theme for CodeMirror 6.
65
- */
66
- theme?: string;
67
-
68
65
  // FIXME-TRANS: Handle theme localizable names
69
66
  // themeDisplayName?: string
70
67
 
@@ -257,7 +254,7 @@ class FacetWrapper<T, U> extends ExtensionBuilder<T> {
257
254
  return this._facet.of(value);
258
255
  }
259
256
 
260
- private _facet: Facet<T, U>;
257
+ protected _facet: Facet<T, U>;
261
258
  }
262
259
  /**
263
260
  * Extension builder that provides an extension depending
@@ -274,8 +271,8 @@ class ConditionalExtension extends ExtensionBuilder<boolean> {
274
271
  return value ? this._truthy : this._falsy;
275
272
  }
276
273
 
277
- private _truthy: Extension;
278
- private _falsy: Extension;
274
+ protected _truthy: Extension;
275
+ protected _falsy: Extension;
279
276
  }
280
277
 
281
278
  /**
@@ -293,8 +290,8 @@ class GenConditionalExtension<T> extends ExtensionBuilder<T> {
293
290
  return this._builder.of(this._fn(value));
294
291
  }
295
292
 
296
- private _fn: (a: T) => boolean;
297
- private _builder: ConditionalExtension;
293
+ protected _fn: (a: T) => boolean;
294
+ protected _builder: ConditionalExtension;
298
295
  }
299
296
 
300
297
  /**
@@ -325,8 +322,8 @@ class ConfigurableBuilder implements IConfigurableBuilder {
325
322
  );
326
323
  }
327
324
 
328
- private _compartment: Compartment;
329
- private _builder: IExtensionBuilder;
325
+ protected _compartment: Compartment;
326
+ protected _builder: IExtensionBuilder;
330
327
  }
331
328
 
332
329
  /*
@@ -348,7 +345,7 @@ class ThemeBuilder implements IConfigurableBuilder {
348
345
  return this._compartment.reconfigure(getTheme(v));
349
346
  }
350
347
 
351
- private _compartment: Compartment;
348
+ protected _compartment: Compartment;
352
349
  }
353
350
 
354
351
  /*
@@ -370,7 +367,7 @@ class PlaceHolderBuilder implements IConfigurableBuilder {
370
367
  return this._compartment.reconfigure(placeholder(v));
371
368
  }
372
369
 
373
- private _compartment: Compartment;
370
+ protected _compartment: Compartment;
374
371
  }
375
372
 
376
373
  /**
@@ -508,17 +505,31 @@ export class EditorConfiguration {
508
505
  'jupyterKernelCompletion',
509
506
  createConditionalBuilder(
510
507
  pythonLanguage.data.of({
511
- autocomplete: kernelCompletions(options['completionProvider']),
508
+ autocomplete: kernelCompletions(options.completionProvider),
512
509
  }),
513
510
  ),
514
511
  ],
515
512
  [
516
513
  'jupyterKernelTooltip',
517
- createConditionalBuilder(tabTooltip(options['tooltipProvider'])),
514
+ createConditionalBuilder(tabTooltip(options.tooltipProvider)),
518
515
  ],
519
516
  ['indentationMarkers', createConditionalBuilder(indentationMarkers())],
520
517
  ['hyperLink', createConditionalBuilder(hyperLink)],
521
518
  ['placeholder', createPlaceHolderBuilder()],
519
+ [
520
+ 'lspTooltip',
521
+ createConditionalBuilder(lspTooltip({ lspProvider: options.lspProvider })),
522
+ ],
523
+ [
524
+ 'lspLint',
525
+ createConditionalBuilder(lspLint({ lspProvider: options.lspProvider })),
526
+ ],
527
+ [
528
+ 'lspCompletion',
529
+ createConditionalBuilder(
530
+ lspPythonCompletion({ lspProvider: options.lspProvider }),
531
+ ),
532
+ ],
522
533
  ]);
523
534
  this._themeOverloaderSpec = { '&': {}, '.cm-line': {} };
524
535
  this._themeOverloader = new Compartment();
@@ -607,6 +618,7 @@ export class EditorConfiguration {
607
618
  ...completionKeymap,
608
619
  ...lintKeymap,
609
620
  ...tooltipKeymap,
621
+ ...formatKeymap,
610
622
  ];
611
623
 
612
624
  const keymapExt = builder!.of(
@@ -635,7 +647,7 @@ export class EditorConfiguration {
635
647
  return extensions;
636
648
  }
637
649
 
638
- private updateThemeOverload(
650
+ protected updateThemeOverload(
639
651
  config: Partial<CodeMirrorConfig> | Record<string, any>,
640
652
  ): Extension {
641
653
  const { fontFamily, fontSize, lineHeight, lineWrap, wordWrapColumn } = config;
@@ -679,11 +691,11 @@ export class EditorConfiguration {
679
691
  return EditorView.theme(this._themeOverloaderSpec);
680
692
  }
681
693
 
682
- private get(key: string): IConfigurableBuilder | undefined {
694
+ protected get(key: string): IConfigurableBuilder | undefined {
683
695
  return this._configurableBuilderMap.get(key);
684
696
  }
685
697
 
686
- private _configurableBuilderMap: Map<string, IConfigurableBuilder>;
687
- private _themeOverloaderSpec: Record<string, Record<string, string>>;
688
- private _themeOverloader: Compartment;
698
+ protected _configurableBuilderMap: Map<string, IConfigurableBuilder>;
699
+ protected _themeOverloaderSpec: Record<string, Record<string, string>>;
700
+ protected _themeOverloader: Compartment;
689
701
  }
@@ -0,0 +1,17 @@
1
+ import type { CodeEditorFactory } from '@difizen/libro-code-editor';
2
+ import { CodeEditorContribution } from '@difizen/libro-code-editor';
3
+ import { singleton } from '@difizen/mana-app';
4
+
5
+ import { codeMirrorDefaultConfig } from './editor.js';
6
+ import { codeMirrorEditorFactory } from './factory.js';
7
+
8
+ @singleton({ contrib: [CodeEditorContribution] })
9
+ export class CodeMirrorEditorContribution implements CodeEditorContribution {
10
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
+ canHandle(mime: string): number {
12
+ // default editor
13
+ return 50;
14
+ }
15
+ factory: CodeEditorFactory = codeMirrorEditorFactory;
16
+ defaultConfig = codeMirrorDefaultConfig;
17
+ }
package/src/editor.ts CHANGED
@@ -4,34 +4,36 @@ import type {
4
4
  ChangeSet,
5
5
  Extension,
6
6
  Range,
7
- StateEffectType,
8
7
  StateCommand,
8
+ StateEffectType,
9
9
  Text,
10
10
  } from '@codemirror/state';
11
11
  import {
12
- Prec,
13
- EditorState,
14
12
  EditorSelection,
13
+ EditorState,
14
+ Prec,
15
15
  StateEffect,
16
16
  StateField,
17
17
  } from '@codemirror/state';
18
- import { Decoration, EditorView } from '@codemirror/view';
19
18
  import type { Command, DecorationSet, ViewUpdate } from '@codemirror/view';
19
+ import { Decoration, EditorView } from '@codemirror/view';
20
+ import { defaultConfig, defaultSelectionStyle } from '@difizen/libro-code-editor';
20
21
  import type {
22
+ ICoordinate,
21
23
  IEditor,
22
24
  IEditorConfig,
23
- IEditorSelectionStyle,
24
- KeydownHandler,
25
25
  IEditorOptions,
26
+ IEditorSelectionStyle,
26
27
  IModel,
27
28
  IPosition,
28
29
  IRange,
29
- ICoordinate,
30
30
  ITextSelection,
31
31
  IToken,
32
+ KeydownHandler,
33
+ SearchMatch,
32
34
  } from '@difizen/libro-code-editor';
33
- import { defaultSelectionStyle, defaultConfig } from '@difizen/libro-code-editor';
34
35
  import { findFirstArrayIndex, removeAllWhereFromArray } from '@difizen/libro-common';
36
+ import type { LSPProvider } from '@difizen/libro-lsp';
35
37
  import { Disposable, Emitter } from '@difizen/mana-app';
36
38
  import { getOrigin, watch } from '@difizen/mana-app';
37
39
  import type { SyntaxNodeRef } from '@lezer/common';
@@ -90,7 +92,7 @@ export const codeMirrorDefaultConfig: Required<CodeMirrorConfig> = {
90
92
  ...defaultConfig,
91
93
  mode: 'null',
92
94
  mimetype: 'text/x-python',
93
- theme: 'jupyter',
95
+ theme: { light: 'jupyter', dark: 'jupyter', hc: 'jupyter' },
94
96
  smartIndent: true,
95
97
  electricChars: true,
96
98
  keyMap: 'default',
@@ -108,7 +110,7 @@ export const codeMirrorDefaultConfig: Required<CodeMirrorConfig> = {
108
110
  styleSelectedText: true,
109
111
  selectionPointer: false,
110
112
  handlePaste: true,
111
- lineWrap: 'off',
113
+ scrollBarHeight: 8,
112
114
 
113
115
  //
114
116
  highlightActiveLineGutter: false,
@@ -135,12 +137,21 @@ export const codeMirrorDefaultConfig: Required<CodeMirrorConfig> = {
135
137
  };
136
138
 
137
139
  export class CodeMirrorEditor implements IEditor {
140
+ // highlight
141
+ protected highlightEffect: StateEffectType<{
142
+ matches: SearchMatch[];
143
+ currentIndex: number | undefined;
144
+ }>;
145
+ protected highlightMark: Decoration;
146
+ protected selectedMatchMark: Decoration;
147
+ protected highlightField: StateField<DecorationSet>;
148
+
138
149
  /**
139
150
  * Construct a CodeMirror editor.
140
151
  */
141
152
  constructor(options: IOptions) {
142
153
  this._editorConfig = new EditorConfiguration(options);
143
- const host = (this.host = options['host']);
154
+ const host = (this.host = options.host);
144
155
 
145
156
  host.classList.add(EDITOR_CLASS);
146
157
  host.classList.add('jp-Editor');
@@ -148,7 +159,7 @@ export class CodeMirrorEditor implements IEditor {
148
159
  host.addEventListener('blur', this, true);
149
160
  host.addEventListener('scroll', this, true);
150
161
 
151
- this._uuid = options['uuid'] || v4();
162
+ this._uuid = options.uuid || v4();
152
163
 
153
164
  // State and effects for handling the selection marks
154
165
  this._addMark = StateEffect.define<ICollabSelectionText>();
@@ -186,20 +197,84 @@ export class CodeMirrorEditor implements IEditor {
186
197
  provide: (f) => EditorView.decorations.from(f),
187
198
  });
188
199
 
200
+ // handle highlight
201
+ this.highlightEffect = StateEffect.define<{
202
+ matches: SearchMatch[];
203
+ currentIndex: number | undefined;
204
+ }>({
205
+ map: (value, mapping) => ({
206
+ matches: value.matches.map((v) => ({
207
+ text: v.text,
208
+ position: mapping.mapPos(v.position),
209
+ })),
210
+ currentIndex: value.currentIndex,
211
+ }),
212
+ });
213
+ this.highlightMark = Decoration.mark({ class: 'cm-searchMatch' });
214
+ this.selectedMatchMark = Decoration.mark({
215
+ class: 'cm-searchMatch cm-searchMatch-selected libro-selectedtext',
216
+ });
217
+ this.highlightField = StateField.define<DecorationSet>({
218
+ create: () => {
219
+ return Decoration.none;
220
+ },
221
+ update: (highlights, transaction) => {
222
+ // eslint-disable-next-line no-param-reassign
223
+ highlights = highlights.map(transaction.changes);
224
+ for (const ef of transaction.effects) {
225
+ if (ef.is(this.highlightEffect)) {
226
+ const e = ef as StateEffect<{
227
+ matches: SearchMatch[];
228
+ currentIndex: number | undefined;
229
+ }>;
230
+ if (e.value.matches.length) {
231
+ // eslint-disable-next-line no-param-reassign
232
+ highlights = highlights.update({
233
+ add: e.value.matches.map((m, index) => {
234
+ if (index === e.value.currentIndex) {
235
+ return this.selectedMatchMark.range(
236
+ m.position,
237
+ m.position + m.text.length,
238
+ );
239
+ }
240
+ return this.highlightMark.range(
241
+ m.position,
242
+ m.position + m.text.length,
243
+ );
244
+ }),
245
+ filter: (from, to) => {
246
+ return (
247
+ !e.value.matches.some(
248
+ (m) => m.position >= from && m.position + m.text.length <= to,
249
+ ) || from === to
250
+ );
251
+ },
252
+ });
253
+ } else {
254
+ // eslint-disable-next-line no-param-reassign
255
+ highlights = Decoration.none;
256
+ }
257
+ }
258
+ }
259
+ return highlights;
260
+ },
261
+ provide: (f) => EditorView.decorations.from(f),
262
+ });
263
+
189
264
  // Handle selection style.
190
- const style = options['selectionStyle'] || {};
265
+ const style = options.selectionStyle || {};
191
266
  this._selectionStyle = {
192
267
  ...defaultSelectionStyle,
193
268
  ...(style as IEditorSelectionStyle),
194
269
  };
195
270
 
196
- const model = (this._model = options['model']);
271
+ const model = (this._model = options.model);
197
272
 
198
273
  const config = options.config || {};
199
274
  const fullConfig = (this._config = {
200
275
  ...codeMirrorDefaultConfig,
201
276
  ...config,
202
- mimetype: options['model'].mimeType,
277
+ mimetype: options.model.mimeType,
203
278
  });
204
279
 
205
280
  // this._initializeEditorBinding();
@@ -207,16 +282,13 @@ export class CodeMirrorEditor implements IEditor {
207
282
  // Extension for handling DOM events
208
283
  const domEventHandlers = EditorView.domEventHandlers({
209
284
  keydown: (event: KeyboardEvent) => {
210
- const index = findFirstArrayIndex(
211
- this._keydownHandlers,
212
- (handler: KeydownHandler) => {
213
- if (handler(this, event) === true) {
214
- event.preventDefault();
215
- return true;
216
- }
217
- return false;
218
- },
219
- );
285
+ const index = findFirstArrayIndex(this._keydownHandlers, (handler) => {
286
+ if (handler(this, event) === true) {
287
+ event.preventDefault();
288
+ return true;
289
+ }
290
+ return false;
291
+ });
220
292
  if (index === -1) {
221
293
  return this.onKeydown(event);
222
294
  }
@@ -261,10 +333,6 @@ export class CodeMirrorEditor implements IEditor {
261
333
  watch(model, 'mimeType', this._onMimeTypeChanged);
262
334
  }
263
335
 
264
- handleTooltipChange = (val: boolean) => {
265
- this.modalChangeEmitter.fire(val);
266
- };
267
-
268
336
  /**
269
337
  * Initialize the editor binding.
270
338
  */
@@ -277,9 +345,7 @@ export class CodeMirrorEditor implements IEditor {
277
345
  // };
278
346
  // }
279
347
 
280
- save: () => void = () => {
281
- //
282
- };
348
+ save: () => void;
283
349
  /**
284
350
  * A signal emitted when either the top or bottom edge is requested.
285
351
  */
@@ -360,7 +426,7 @@ export class CodeMirrorEditor implements IEditor {
360
426
  /**
361
427
  * Tests whether the editor is disposed.
362
428
  */
363
- get isDisposed(): boolean {
429
+ get disposed(): boolean {
364
430
  return this._isDisposed;
365
431
  }
366
432
 
@@ -368,7 +434,7 @@ export class CodeMirrorEditor implements IEditor {
368
434
  * Dispose of the resources held by the widget.
369
435
  */
370
436
  dispose(): void {
371
- if (this.isDisposed) {
437
+ if (this.disposed) {
372
438
  return;
373
439
  }
374
440
  this._isDisposed = true;
@@ -394,7 +460,7 @@ export class CodeMirrorEditor implements IEditor {
394
460
  // Don't bother setting the option if it is already the same.
395
461
  if (this._config[option] !== value) {
396
462
  this._config[option] = value;
397
- this._editorConfig.reconfigureExtension(this._editor, option as string, value);
463
+ this._editorConfig.reconfigureExtension(this._editor, option, value);
398
464
  }
399
465
 
400
466
  if (option === 'readOnly') {
@@ -427,8 +493,7 @@ export class CodeMirrorEditor implements IEditor {
427
493
  */
428
494
  getLine(line: number): string | undefined {
429
495
  // TODO: CM6 remove +1 when CM6 first line number has propagated
430
- const _line = line + 1;
431
- return _line <= this.doc.lines ? this.doc.line(_line).text : undefined;
496
+ return line <= this.doc.lines ? this.doc.line(line).text : undefined;
432
497
  }
433
498
 
434
499
  /**
@@ -436,7 +501,7 @@ export class CodeMirrorEditor implements IEditor {
436
501
  */
437
502
  getOffsetAt(position: IPosition): number {
438
503
  // TODO: CM6 remove +1 when CM6 first line number has propagated
439
- return this.doc.line(position.line + 1).from + position.column;
504
+ return this.doc.line(position.line).from + position.column - 1;
440
505
  }
441
506
 
442
507
  /**
@@ -537,6 +602,16 @@ export class CodeMirrorEditor implements IEditor {
537
602
  return this.state.sliceDoc(fromOffset, toOffset);
538
603
  }
539
604
 
605
+ getSelectionValue(range?: IRange) {
606
+ const fromOffset = range
607
+ ? this.getOffsetAt(range.start)
608
+ : this.editor.state.selection.main.from;
609
+ const toOffset = range
610
+ ? this.getOffsetAt(range.end)
611
+ : this.editor.state.selection.main.to;
612
+ return this.state.sliceDoc(fromOffset, toOffset);
613
+ }
614
+
540
615
  /**
541
616
  * Add a keydown handler to the editor.
542
617
  *
@@ -547,10 +622,7 @@ export class CodeMirrorEditor implements IEditor {
547
622
  addKeydownHandler(handler: KeydownHandler): Disposable {
548
623
  this._keydownHandlers.push(handler);
549
624
  return Disposable.create(() => {
550
- removeAllWhereFromArray(
551
- this._keydownHandlers,
552
- (val: KeydownHandler) => val === handler,
553
- );
625
+ removeAllWhereFromArray(this._keydownHandlers, (val) => val === handler);
554
626
  });
555
627
  }
556
628
 
@@ -682,10 +754,41 @@ export class CodeMirrorEditor implements IEditor {
682
754
  *
683
755
  * @param text The text to be inserted.
684
756
  */
685
- replaceSelection(text: string): void {
686
- this.editor.dispatch(this.state.replaceSelection(text));
757
+ replaceSelection(text: string, range: IRange): void {
758
+ this.editor.dispatch({
759
+ changes: {
760
+ from: this.getOffsetAt(range.start),
761
+ to: this.getOffsetAt(range.end),
762
+ insert: text,
763
+ },
764
+ });
765
+ }
766
+
767
+ replaceSelections(edits: { text: string; range: IRange }[]): void {
768
+ // const trans = this.state.replaceSelection(text);
769
+ this.editor.dispatch({
770
+ changes: edits.map((item) => ({
771
+ from: this.getOffsetAt(item.range.start),
772
+ to: this.getOffsetAt(item.range.end),
773
+ insert: item.text,
774
+ })),
775
+ });
687
776
  }
688
777
 
778
+ highlightMatches(matches: SearchMatch[], currentIndex: number | undefined) {
779
+ const effects: StateEffect<unknown>[] = [
780
+ this.highlightEffect.of({ matches: matches, currentIndex: currentIndex }),
781
+ ];
782
+ if (!this.state.field(this.highlightField, false)) {
783
+ effects.push(StateEffect.appendConfig.of([this.highlightField]));
784
+ }
785
+ this.editor.dispatch({ effects });
786
+ }
787
+
788
+ handleTooltipChange = (val: boolean) => {
789
+ this.modalChangeEmitter.fire(val);
790
+ };
791
+
689
792
  /**
690
793
  * Get a list of tokens for the current editor text content.
691
794
  */
@@ -806,10 +909,36 @@ export class CodeMirrorEditor implements IEditor {
806
909
  });
807
910
  }
808
911
 
912
+ /**
913
+ * Handles a selections change.
914
+ */
915
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
916
+ protected _onSelectionsChanged(args: ITextSelection[]): void {
917
+ // const uuid = args.key;
918
+ // if (uuid !== this.uuid) {
919
+ // this._cleanSelections(uuid);
920
+ // if (args.type !== 'remove' && args.newValue) {
921
+ // this._markSelections(uuid, args.newValue);
922
+ // }
923
+ // }
924
+ }
925
+
926
+ /**
927
+ * Clean selections for the given uuid.
928
+ */
929
+ protected _cleanSelections(uuid: string) {
930
+ this.editor.dispatch({
931
+ effects: this._removeMark.of({
932
+ uuid: uuid,
933
+ decorations: this._selectionMarkers[uuid],
934
+ }),
935
+ });
936
+ }
937
+
809
938
  protected _buildMarkDecoration(
810
939
  uuid: string,
811
940
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
812
- _selections: ISelectionText[],
941
+ selections: ISelectionText[],
813
942
  ) {
814
943
  const decorations: Range<Decoration>[] = [];
815
944
 
@@ -886,6 +1015,20 @@ export class CodeMirrorEditor implements IEditor {
886
1015
  // });
887
1016
  // }
888
1017
 
1018
+ /**
1019
+ * Marks selections.
1020
+ */
1021
+ protected _markSelections(uuid: string, selections: ITextSelection[]) {
1022
+ const sel = selections.map((selection) => ({
1023
+ from: this.getOffsetAt(selection.start),
1024
+ to: this.getOffsetAt(selection.end),
1025
+ style: selection.style,
1026
+ }));
1027
+ this.editor.dispatch({
1028
+ effects: this._addMark.of({ uuid: uuid, selections: sel }),
1029
+ });
1030
+ }
1031
+
889
1032
  /**
890
1033
  * Handles a cursor activity event.
891
1034
  */
@@ -943,7 +1086,6 @@ export class CodeMirrorEditor implements IEditor {
943
1086
  if (update.docChanged) {
944
1087
  this._lastChange = update.changes;
945
1088
  }
946
-
947
1089
  this.model.value = update.state.doc.toJSON().join('\n');
948
1090
  }
949
1091
  /**
@@ -1013,13 +1155,47 @@ export class CodeMirrorEditor implements IEditor {
1013
1155
  this._caretHover = null;
1014
1156
  }
1015
1157
  }
1158
+ /**
1159
+ * Check for an out of sync editor.
1160
+ */
1161
+ protected _checkSync(): void {
1162
+ const change = this._lastChange;
1163
+ if (!change) {
1164
+ return;
1165
+ }
1166
+ this._lastChange = null;
1167
+ const doc = this.doc;
1168
+ if (doc.toString() === this._model.value) {
1169
+ return;
1170
+ }
1171
+
1172
+ // void showDialog({
1173
+ // title: this._trans.__('Code Editor out of Sync'),
1174
+ // body: this._trans.__(
1175
+ // 'Please open your browser JavaScript console for bug report instructions',
1176
+ // ),
1177
+ // });
1178
+ console.warn(
1179
+ 'If you are able and willing to publicly share the text or code in your editor, you can help us debug the "Code Editor out of Sync" message by pasting the following to the public issue at https://github.com/jupyterlab/jupyterlab/issues/2951. Please note that the data below includes the text/code in your editor.',
1180
+ );
1181
+ console.warn(
1182
+ JSON.stringify({
1183
+ model: this._model.value,
1184
+ view: doc.toString(),
1185
+ selections: this.getSelections(),
1186
+ cursor: this.getCursorPosition(),
1187
+ lineSep: this.state.facet(EditorState.lineSeparator),
1188
+ change,
1189
+ }),
1190
+ );
1191
+ }
1016
1192
  protected _model: IModel;
1017
1193
  protected _editor: EditorView;
1018
1194
  protected _selectionMarkers: Record<string, Range<Decoration>[]> = {};
1019
- protected _caretHover: HTMLElement | null = null;
1195
+ protected _caretHover: HTMLElement | null;
1020
1196
  protected _config: IConfig;
1021
- protected _hoverTimeout: number | undefined = undefined;
1022
- protected _hoverId: string | undefined = undefined;
1197
+ protected _hoverTimeout: number;
1198
+ protected _hoverId: string;
1023
1199
  protected _keydownHandlers = new Array<KeydownHandler>();
1024
1200
  protected _selectionStyle: IEditorSelectionStyle;
1025
1201
  protected _uuid = '';
@@ -1036,7 +1212,7 @@ export class CodeMirrorEditor implements IEditor {
1036
1212
  export type IConfig = CodeMirrorConfig;
1037
1213
 
1038
1214
  export interface IOptions extends IEditorOptions {
1039
- [x: string]: any;
1215
+ lspProvider?: LSPProvider;
1040
1216
  /**
1041
1217
  * The configuration options for the editor.
1042
1218
  */
package/src/hyperlink.ts CHANGED
@@ -12,7 +12,7 @@ export interface HyperLinkState {
12
12
  }
13
13
 
14
14
  class HyperLink extends WidgetType {
15
- private readonly state: HyperLinkState;
15
+ protected readonly state: HyperLinkState;
16
16
  constructor(state: HyperLinkState) {
17
17
  super();
18
18
  this.state = state;
@@ -117,8 +117,8 @@ class IndentMarkersClass implements PluginValue {
117
117
  view: EditorView;
118
118
  decorations!: DecorationSet;
119
119
 
120
- private unitWidth: number;
121
- private currentLineNumber: number;
120
+ protected unitWidth: number;
121
+ protected currentLineNumber: number;
122
122
 
123
123
  constructor(view: EditorView) {
124
124
  this.view = view;
@@ -149,7 +149,7 @@ class IndentMarkersClass implements PluginValue {
149
149
  }
150
150
  }
151
151
 
152
- private generate(state: EditorState) {
152
+ protected generate(state: EditorState) {
153
153
  const builder = new RangeSetBuilder<Decoration>();
154
154
 
155
155
  const lines = getVisibleLines(this.view, state);