@difizen/libro-cofine-editor 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 (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/es/editor-contribution.d.ts +10 -0
  4. package/es/editor-contribution.d.ts.map +1 -0
  5. package/es/editor-contribution.js +31 -0
  6. package/es/index.d.ts +7 -0
  7. package/es/index.d.ts.map +1 -0
  8. package/es/index.js +6 -0
  9. package/es/index.less +32 -0
  10. package/es/language/lsp/completion-provider.d.ts +13 -0
  11. package/es/language/lsp/completion-provider.d.ts.map +1 -0
  12. package/es/language/lsp/completion-provider.js +290 -0
  13. package/es/language/lsp/diagnostic-provider.d.ts +22 -0
  14. package/es/language/lsp/diagnostic-provider.d.ts.map +1 -0
  15. package/es/language/lsp/diagnostic-provider.js +161 -0
  16. package/es/language/lsp/format-provider.d.ts +9 -0
  17. package/es/language/lsp/format-provider.d.ts.map +1 -0
  18. package/es/language/lsp/format-provider.js +139 -0
  19. package/es/language/lsp/hover-provider.d.ts +8 -0
  20. package/es/language/lsp/hover-provider.d.ts.map +1 -0
  21. package/es/language/lsp/hover-provider.js +136 -0
  22. package/es/language/lsp/language-feature-provider.d.ts +17 -0
  23. package/es/language/lsp/language-feature-provider.d.ts.map +1 -0
  24. package/es/language/lsp/language-feature-provider.js +89 -0
  25. package/es/language/lsp/lsp-contribution.d.ts +25 -0
  26. package/es/language/lsp/lsp-contribution.d.ts.map +1 -0
  27. package/es/language/lsp/lsp-contribution.js +213 -0
  28. package/es/language/lsp/module.d.ts +2 -0
  29. package/es/language/lsp/module.d.ts.map +1 -0
  30. package/es/language/lsp/module.js +3 -0
  31. package/es/language/lsp/semantic-highlight-provider.d.ts +9 -0
  32. package/es/language/lsp/semantic-highlight-provider.d.ts.map +1 -0
  33. package/es/language/lsp/semantic-highlight-provider.js +41 -0
  34. package/es/language/lsp/signature-help-provider.d.ts +8 -0
  35. package/es/language/lsp/signature-help-provider.d.ts.map +1 -0
  36. package/es/language/lsp/signature-help-provider.js +108 -0
  37. package/es/language/lsp/type-concerters.d.ts +7 -0
  38. package/es/language/lsp/type-concerters.d.ts.map +1 -0
  39. package/es/language/lsp/type-concerters.js +37 -0
  40. package/es/language/python/data/MagicPython.tmLanguage.json +4213 -0
  41. package/es/language/python/data/MagicRegExp.tmLanguage.json +497 -0
  42. package/es/language/python/data/configurations/python.json +135 -0
  43. package/es/language/python/data/snippets/python.snippets.json +223 -0
  44. package/es/language/python/module.d.ts +2 -0
  45. package/es/language/python/module.d.ts.map +1 -0
  46. package/es/language/python/module.js +3 -0
  47. package/es/language/python/python-builtin.d.ts +86 -0
  48. package/es/language/python/python-builtin.d.ts.map +1 -0
  49. package/es/language/python/python-builtin.js +569 -0
  50. package/es/language/python/python-language-feature.d.ts +26 -0
  51. package/es/language/python/python-language-feature.d.ts.map +1 -0
  52. package/es/language/python/python-language-feature.js +174 -0
  53. package/es/language-specs.d.ts +33 -0
  54. package/es/language-specs.d.ts.map +1 -0
  55. package/es/language-specs.js +115 -0
  56. package/es/libro-e2-editor.d.ts +277 -0
  57. package/es/libro-e2-editor.d.ts.map +1 -0
  58. package/es/libro-e2-editor.js +720 -0
  59. package/es/libro-e2-preload.d.ts +5 -0
  60. package/es/libro-e2-preload.d.ts.map +1 -0
  61. package/es/libro-e2-preload.js +57 -0
  62. package/es/libro-sql-api.d.ts +8 -0
  63. package/es/libro-sql-api.d.ts.map +1 -0
  64. package/es/libro-sql-api.js +26 -0
  65. package/es/module.d.ts +3 -0
  66. package/es/module.d.ts.map +1 -0
  67. package/es/module.js +44 -0
  68. package/es/placeholder.d.ts +31 -0
  69. package/es/placeholder.d.ts.map +1 -0
  70. package/es/placeholder.js +101 -0
  71. package/es/theme/data/jupyter_dark.json +406 -0
  72. package/es/theme/data/jupyter_hc_dark.json +385 -0
  73. package/es/theme/data/jupyter_hc_light.json +386 -0
  74. package/es/theme/data/jupyter_light.json +386 -0
  75. package/es/theme/data/libro_dark.json +186 -0
  76. package/es/theme/data/libro_light.json +170 -0
  77. package/es/theme/libro-python-theme-contribution.d.ts +6 -0
  78. package/es/theme/libro-python-theme-contribution.d.ts.map +1 -0
  79. package/es/theme/libro-python-theme-contribution.js +49 -0
  80. package/es/theme/module.d.ts +2 -0
  81. package/es/theme/module.d.ts.map +1 -0
  82. package/es/theme/module.js +3 -0
  83. package/es/types.d.ts +10 -0
  84. package/es/types.d.ts.map +1 -0
  85. package/es/types.js +3 -0
  86. package/package.json +67 -0
  87. package/src/editor-contribution.ts +30 -0
  88. package/src/index.less +32 -0
  89. package/src/index.spec.ts +8 -0
  90. package/src/index.ts +6 -0
  91. package/src/language/lsp/completion-provider.ts +214 -0
  92. package/src/language/lsp/diagnostic-provider.ts +148 -0
  93. package/src/language/lsp/format-provider.ts +87 -0
  94. package/src/language/lsp/hover-provider.ts +98 -0
  95. package/src/language/lsp/language-feature-provider.ts +69 -0
  96. package/src/language/lsp/lsp-contribution.ts +159 -0
  97. package/src/language/lsp/module.ts +5 -0
  98. package/src/language/lsp/semantic-highlight-provider.ts +26 -0
  99. package/src/language/lsp/signature-help-provider.ts +65 -0
  100. package/src/language/lsp/type-concerters.ts +72 -0
  101. package/src/language/python/data/MagicPython.tmLanguage.json +4213 -0
  102. package/src/language/python/data/MagicRegExp.tmLanguage.json +497 -0
  103. package/src/language/python/data/configurations/python.json +135 -0
  104. package/src/language/python/data/snippets/python.snippets.json +223 -0
  105. package/src/language/python/module.ts +5 -0
  106. package/src/language/python/python-builtin.ts +891 -0
  107. package/src/language/python/python-language-feature.ts +163 -0
  108. package/src/language-specs.ts +99 -0
  109. package/src/libro-e2-editor.ts +922 -0
  110. package/src/libro-e2-preload.ts +23 -0
  111. package/src/libro-sql-api.ts +20 -0
  112. package/src/module.ts +44 -0
  113. package/src/placeholder.ts +100 -0
  114. package/src/theme/data/jupyter_dark.json +406 -0
  115. package/src/theme/data/jupyter_hc_dark.json +385 -0
  116. package/src/theme/data/jupyter_hc_light.json +386 -0
  117. package/src/theme/data/jupyter_light.json +386 -0
  118. package/src/theme/data/libro_dark.json +186 -0
  119. package/src/theme/data/libro_light.json +170 -0
  120. package/src/theme/libro-python-theme-contribution.ts +36 -0
  121. package/src/theme/module.ts +5 -0
  122. package/src/types.ts +11 -0
@@ -0,0 +1,922 @@
1
+ import type {
2
+ CodeEditorFactory,
3
+ CompletionProvider,
4
+ ICoordinate,
5
+ IEditor,
6
+ IEditorConfig,
7
+ IEditorOptions,
8
+ IModel,
9
+ IPosition,
10
+ IRange,
11
+ SearchMatch,
12
+ TooltipProvider,
13
+ } from '@difizen/libro-code-editor';
14
+ import { defaultConfig } from '@difizen/libro-code-editor';
15
+ import type { E2Editor } from '@difizen/libro-cofine-editor-core';
16
+ import { EditorProvider, MonacoEnvironment } from '@difizen/libro-cofine-editor-core';
17
+ import { NotebookCommands } from '@difizen/libro-core';
18
+ import type { LSPProvider } from '@difizen/libro-lsp';
19
+ import {
20
+ CommandRegistry,
21
+ inject,
22
+ ThemeService,
23
+ transient,
24
+ watch,
25
+ } from '@difizen/mana-app';
26
+ import { Disposable, DisposableCollection, Emitter } from '@difizen/mana-app';
27
+ import { editor, Selection } from '@difizen/monaco-editor-core';
28
+ import 'resize-observer-polyfill';
29
+ import { v4 } from 'uuid';
30
+
31
+ import { LSPContribution } from './language/lsp/lsp-contribution.js';
32
+ import { LanguageSpecRegistry } from './language-specs.js';
33
+ import { PlaceholderContentWidget } from './placeholder.js';
34
+ import type { MonacoEditorOptions, MonacoEditorType, MonacoMatch } from './types.js';
35
+ import { MonacoRange, MonacoUri } from './types.js';
36
+ import './index.less';
37
+
38
+ export interface LibroE2EditorConfig extends IEditorConfig {
39
+ /**
40
+ * The mode to use.
41
+ */
42
+ mode?: string;
43
+
44
+ /**
45
+ * content mimetype
46
+ */
47
+ mimetype?: string;
48
+
49
+ // FIXME-TRANS: Handle theme localizable names
50
+ // themeDisplayName?: string
51
+
52
+ /**
53
+ * Whether to use the context-sensitive indentation that the mode provides
54
+ * (or just indent the same as the line before).
55
+ */
56
+ smartIndent?: boolean;
57
+
58
+ /**
59
+ * Configures whether the editor should re-indent the current line when a
60
+ * character is typed that might change its proper indentation
61
+ * (only works if the mode supports indentation).
62
+ */
63
+ electricChars?: boolean;
64
+
65
+ /**
66
+ * Configures the keymap to use. The default is "default", which is the
67
+ * only keymap defined in codemirror.js itself.
68
+ * Extra keymaps are found in the CodeMirror keymap directory.
69
+ */
70
+ keyMap?: string;
71
+
72
+ /**
73
+ * Can be used to specify extra keybindings for the editor, alongside the
74
+ * ones defined by keyMap. Should be either null, or a valid keymap value.
75
+ */
76
+ // extraKeys?: KeyBinding[] | null;
77
+
78
+ /**
79
+ * Can be used to add extra gutters (beyond or instead of the line number
80
+ * gutter).
81
+ * Should be an array of CSS class names, each of which defines a width
82
+ * (and optionally a background),
83
+ * and which will be used to draw the background of the gutters.
84
+ * May include the CodeMirror-linenumbers class, in order to explicitly
85
+ * set the position of the line number gutter
86
+ * (it will default to be to the right of all other gutters).
87
+ * These class names are the keys passed to setGutterMarker.
88
+ */
89
+ gutters?: string[];
90
+
91
+ /**
92
+ * Determines whether the gutter scrolls along with the content
93
+ * horizontally (false)
94
+ * or whether it stays fixed during horizontal scrolling (true,
95
+ * the default).
96
+ */
97
+ fixedGutter?: boolean;
98
+
99
+ /**
100
+ * Whether the cursor should be drawn when a selection is active.
101
+ */
102
+ showCursorWhenSelecting?: boolean;
103
+
104
+ /**
105
+ * When fixedGutter is on, and there is a horizontal scrollbar, by default
106
+ * the gutter will be visible to the left of this scrollbar. If this
107
+ * option is set to true, it will be covered by an element with class
108
+ * CodeMirror-gutter-filler.
109
+ */
110
+ coverGutterNextToScrollbar?: boolean;
111
+
112
+ /**
113
+ * Controls whether drag-and-drop is enabled.
114
+ */
115
+ dragDrop?: boolean;
116
+
117
+ /**
118
+ * Explicitly set the line separator for the editor.
119
+ * By default (value null), the document will be split on CRLFs as well as
120
+ * lone CRs and LFs, and a single LF will be used as line separator in all
121
+ * output (such as getValue). When a specific string is given, lines will
122
+ * only be split on that string, and output will, by default, use that
123
+ * same separator.
124
+ */
125
+ lineSeparator?: string | null;
126
+
127
+ /**
128
+ * Chooses a scrollbar implementation. The default is "native", showing
129
+ * native scrollbars. The core library also provides the "null" style,
130
+ * which completely hides the scrollbars. Addons can implement additional
131
+ * scrollbar models.
132
+ */
133
+ scrollbarStyle?: string;
134
+
135
+ /**
136
+ * When enabled, which is the default, doing copy or cut when there is no
137
+ * selection will copy or cut the whole lines that have cursors on them.
138
+ */
139
+ lineWiseCopyCut?: boolean;
140
+
141
+ /**
142
+ * Whether to scroll past the end of the buffer.
143
+ */
144
+ scrollPastEnd?: boolean;
145
+
146
+ /**
147
+ * Whether to give the wrapper of the line that contains the cursor the class
148
+ * cm-activeLine.
149
+ */
150
+ styleActiveLine?: boolean;
151
+
152
+ /**
153
+ * Whether to causes the selected text to be marked with the CSS class
154
+ * CodeMirror-selectedtext. Useful to change the colour of the selection
155
+ * (in addition to the background).
156
+ */
157
+ styleSelectedText?: boolean;
158
+
159
+ /**
160
+ * Defines the mouse cursor appearance when hovering over the selection.
161
+ * It can be set to a string, like "pointer", or to true,
162
+ * in which case the "default" (arrow) cursor will be used.
163
+ */
164
+ selectionPointer?: boolean | string;
165
+
166
+ //
167
+ highlightActiveLineGutter?: boolean;
168
+ highlightSpecialChars?: boolean;
169
+ history?: boolean;
170
+ drawSelection?: boolean;
171
+ dropCursor?: boolean;
172
+ allowMultipleSelections?: boolean;
173
+ autocompletion?: boolean;
174
+ rectangularSelection?: boolean;
175
+ crosshairCursor?: boolean;
176
+ highlightSelectionMatches?: boolean;
177
+ foldGutter?: boolean;
178
+ syntaxHighlighting?: boolean;
179
+ /**
180
+ * 是否从kernel获取completion
181
+ */
182
+ jupyterKernelCompletion?: boolean;
183
+ /**
184
+ * 是否从kernel获取tooltip
185
+ */
186
+ jupyterKernelTooltip?: boolean;
187
+ indentationMarkers?: boolean;
188
+ hyperLink?: boolean;
189
+ /**
190
+ * 是否开启tab触发completion和tooltip
191
+ */
192
+ tabEditorFunction?: boolean;
193
+
194
+ lspCompletion?: boolean;
195
+
196
+ lspTooltip?: boolean;
197
+
198
+ lspLint?: boolean;
199
+
200
+ placeholder?: HTMLElement | string;
201
+ }
202
+
203
+ export const LibroE2EditorOptions = Symbol('LibroE2EditorOptions');
204
+
205
+ export interface LibroE2EditorOptions extends IEditorOptions {
206
+ lspProvider?: LSPProvider;
207
+
208
+ /**
209
+ * The configuration options for the editor.
210
+ */
211
+ config?: Partial<LibroE2EditorConfig>;
212
+ }
213
+
214
+ export const libroE2DefaultConfig: Required<LibroE2EditorConfig> = {
215
+ ...defaultConfig,
216
+ theme: {
217
+ dark: 'libro-dark',
218
+ light: 'libro-light',
219
+ hc: 'e2-hc',
220
+ },
221
+ scrollBarHeight: 12,
222
+ mode: 'null',
223
+ mimetype: 'text/x-python',
224
+ smartIndent: true,
225
+ electricChars: true,
226
+ keyMap: 'default',
227
+ // extraKeys: null,
228
+ gutters: [],
229
+ fixedGutter: true,
230
+ showCursorWhenSelecting: false,
231
+ coverGutterNextToScrollbar: false,
232
+ dragDrop: true,
233
+ lineSeparator: null,
234
+ scrollbarStyle: 'native',
235
+ lineWiseCopyCut: true,
236
+ scrollPastEnd: false,
237
+ styleActiveLine: false,
238
+ styleSelectedText: true,
239
+ selectionPointer: false,
240
+ handlePaste: true,
241
+ lineWrap: 'off',
242
+ lspEnabled: true,
243
+
244
+ //
245
+ highlightActiveLineGutter: false,
246
+ highlightSpecialChars: true,
247
+ history: true,
248
+ drawSelection: true,
249
+ dropCursor: true,
250
+ allowMultipleSelections: true,
251
+ autocompletion: true,
252
+ rectangularSelection: true,
253
+ crosshairCursor: true,
254
+ highlightSelectionMatches: true,
255
+ foldGutter: true,
256
+ syntaxHighlighting: true,
257
+ jupyterKernelCompletion: true,
258
+ indentationMarkers: true,
259
+ hyperLink: true,
260
+ jupyterKernelTooltip: true,
261
+ tabEditorFunction: true,
262
+ lspCompletion: true,
263
+ lspTooltip: true,
264
+ lspLint: true,
265
+ placeholder: '',
266
+ };
267
+
268
+ export const LibroE2EditorFactory = Symbol('LibroE2EditorFactory');
269
+ export type LibroE2EditorFactory = CodeEditorFactory;
270
+
271
+ export const E2EditorClassname = 'libro-e2-editor';
272
+
273
+ export const LibroE2URIScheme = 'libro-e2';
274
+
275
+ @transient()
276
+ export class LibroE2Editor implements IEditor {
277
+ protected readonly themeService: ThemeService;
278
+
279
+ protected readonly languageSpecRegistry: LanguageSpecRegistry;
280
+ @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
281
+ @inject(LSPContribution) protected lspContribution: LSPContribution;
282
+
283
+ protected defaultLineHeight = 20;
284
+
285
+ protected toDispose = new DisposableCollection();
286
+
287
+ /**
288
+ * The DOM node that hosts the editor.
289
+ */
290
+ readonly host: HTMLElement;
291
+ /**
292
+ * The DOM node that hosts the monaco editor.
293
+ */
294
+ readonly editorHost: HTMLElement;
295
+
296
+ protected placeholder: PlaceholderContentWidget;
297
+
298
+ protected oldDeltaDecorations: string[] = [];
299
+
300
+ protected _config: LibroE2EditorConfig;
301
+
302
+ private resizeObserver: ResizeObserver;
303
+
304
+ protected _uuid = '';
305
+ /**
306
+ * The uuid of this editor;
307
+ */
308
+ get uuid(): string {
309
+ return this._uuid;
310
+ }
311
+ set uuid(value: string) {
312
+ this._uuid = value;
313
+ }
314
+
315
+ protected _model: IModel;
316
+ /**
317
+ * Returns a model for this editor.
318
+ */
319
+ get model(): IModel {
320
+ return this._model;
321
+ }
322
+
323
+ protected _editor?: E2Editor<MonacoEditorType>;
324
+ get editor(): E2Editor<MonacoEditorType> | undefined {
325
+ return this?._editor;
326
+ }
327
+
328
+ get monacoEditor(): MonacoEditorType | undefined {
329
+ return this?._editor?.codeEditor;
330
+ }
331
+
332
+ get lineCount(): number {
333
+ return this.monacoEditor?.getModel()?.getLineCount() || 0;
334
+ }
335
+
336
+ lspProvider?: LSPProvider;
337
+
338
+ completionProvider?: CompletionProvider;
339
+
340
+ tooltipProvider?: TooltipProvider;
341
+
342
+ protected isLayouting = false;
343
+ constructor(
344
+ @inject(LibroE2EditorOptions) options: LibroE2EditorOptions,
345
+ @inject(ThemeService) themeService: ThemeService,
346
+ @inject(LanguageSpecRegistry)
347
+ languageSpecRegistry: LanguageSpecRegistry,
348
+ ) {
349
+ this.themeService = themeService;
350
+ this.languageSpecRegistry = languageSpecRegistry;
351
+ this.host = options.host;
352
+ this.host.classList.add('libro-e2-editor-container');
353
+ this._uuid = options.uuid || v4();
354
+
355
+ this._model = options.model;
356
+
357
+ const config = options.config || {};
358
+ const fullConfig = (this._config = {
359
+ ...libroE2DefaultConfig,
360
+ ...config,
361
+ mimetype: options.model.mimeType,
362
+ });
363
+
364
+ this.completionProvider = options.completionProvider;
365
+ this.tooltipProvider = options.tooltipProvider;
366
+ this.lspProvider = options.lspProvider;
367
+
368
+ this.editorHost = document.createElement('div');
369
+ this.host.append(this.editorHost);
370
+
371
+ this.createEditor(this.editorHost, fullConfig);
372
+
373
+ this.onMimeTypeChanged();
374
+ this.onCursorActivity();
375
+
376
+ this.toDispose.push(watch(this._model, 'mimeType', this.onMimeTypeChanged));
377
+ // this.toDispose.push(watch(this._model, 'source', this.onValueChange));
378
+ this.toDispose.push(watch(this._model, 'selections', this.onSelectionChange));
379
+ this.toDispose.push(this.themeService.onDidColorThemeChange(this.onThemeChange));
380
+ }
381
+
382
+ get languageSpec() {
383
+ return this.languageSpecRegistry.languageSpecs.find(
384
+ (item) => item.mime === this.model.mimeType,
385
+ );
386
+ }
387
+
388
+ get theme(): string {
389
+ const themetype = this.themeService.getActiveTheme().type;
390
+ return this._config.theme[themetype];
391
+ }
392
+
393
+ protected toMonacoOptions(
394
+ editorConfig: Partial<LibroE2EditorConfig>,
395
+ ): MonacoEditorOptions {
396
+ return {
397
+ minimap: {
398
+ enabled: false,
399
+ },
400
+ lineHeight: editorConfig.lineHeight ?? this.defaultLineHeight,
401
+ fontSize: editorConfig.fontSize ?? 13,
402
+ lineNumbers: editorConfig.lineNumbers ? 'on' : 'off',
403
+ folding: editorConfig.codeFolding,
404
+ wordWrap: editorConfig.lineWrap,
405
+ lineDecorationsWidth: 15,
406
+ lineNumbersMinChars: 3,
407
+ suggestSelection: 'first',
408
+ wordBasedSuggestions: false,
409
+ scrollBeyondLastLine: false,
410
+ /**
411
+ * 使用该选项可以让modal widget出现在正确的范围,而不是被遮挡,解决z-index问题,但是会导致hover组件之类的无法被选中
412
+ * 根据 https://github.com/microsoft/monaco-editor/issues/2156,0.34.x 版本修复了这个问题
413
+ * TODO: 当前0.31.1 无法开启此选项,升级 E2 3.x 可以解决(monaco 0.39)
414
+ *
415
+ * ```html
416
+ * <div id="monaco-editor-overflow-widgets-root" class="monaco-editor" style="z-index: 999;"></div>
417
+ * ```
418
+ *
419
+ */
420
+ // overflowWidgetsDomNode: document.getElementById('monaco-editor-overflow-widgets-root')!,
421
+ // fixedOverflowWidgets: true,
422
+ suggest: { snippetsPreventQuickSuggestions: false },
423
+ autoClosingQuotes: editorConfig.autoClosingBrackets ? 'always' : 'never',
424
+ autoDetectHighContrast: false,
425
+ scrollbar: {
426
+ alwaysConsumeMouseWheel: false,
427
+ verticalScrollbarSize: 0,
428
+ },
429
+ extraEditorClassName: E2EditorClassname,
430
+ renderLineHighlight: 'all',
431
+ renderLineHighlightOnlyWhenFocus: true,
432
+ readOnly: editorConfig.readOnly,
433
+ cursorWidth: 1,
434
+ tabSize: editorConfig.tabSize,
435
+ insertSpaces: editorConfig.insertSpaces,
436
+ matchBrackets: editorConfig.matchBrackets ? 'always' : 'never',
437
+ rulers: editorConfig.rulers,
438
+ wordWrapColumn: editorConfig.wordWrapColumn,
439
+ 'semanticHighlighting.enabled': true,
440
+ };
441
+ }
442
+
443
+ protected async createEditor(host: HTMLElement, config: LibroE2EditorConfig) {
444
+ if (!this.languageSpec) {
445
+ return;
446
+ }
447
+ const editorConfig: LibroE2EditorConfig = {
448
+ ...config,
449
+ ...this.languageSpec.editorConfig,
450
+ };
451
+ this._config = editorConfig;
452
+ // await this.languageSpec.loadModule()
453
+ await MonacoEnvironment.init();
454
+ await this.languageSpec?.beforeEditorInit?.();
455
+ const editorPorvider =
456
+ MonacoEnvironment.container.get<EditorProvider>(EditorProvider);
457
+
458
+ const uri = MonacoUri.from({
459
+ scheme: LibroE2URIScheme,
460
+ path: `${this.uuid}${this.languageSpec.ext[0]}`,
461
+ });
462
+
463
+ const options: MonacoEditorOptions = {
464
+ ...this.toMonacoOptions(editorConfig),
465
+ /**
466
+ * language ia an uri:
467
+ */
468
+ language: this.languageSpec.language,
469
+ uri,
470
+ theme: this.theme,
471
+ value: this.model.value,
472
+ };
473
+
474
+ const e2Editor = editorPorvider.create(host, options);
475
+ this._editor = e2Editor;
476
+ this.toDispose.push(
477
+ this.monacoEditor?.onDidChangeModelContent(() => {
478
+ const value = this.monacoEditor?.getValue();
479
+ this.model.value = value ?? '';
480
+ this.updateEditorSize();
481
+ }) ?? Disposable.NONE,
482
+ );
483
+ this.toDispose.push(
484
+ this.monacoEditor?.onDidContentSizeChange(() => {
485
+ this.updateEditorSize();
486
+ }) ?? Disposable.NONE,
487
+ );
488
+ this.toDispose.push(
489
+ this.monacoEditor?.onDidBlurEditorText(() => {
490
+ this.blur();
491
+ }) ?? Disposable.NONE,
492
+ );
493
+
494
+ this.updateEditorSize();
495
+ this.inspectResize();
496
+ this.handleCommand(this.commandRegistry);
497
+ await this.languageSpec?.afterEditorInit?.(this);
498
+ this.placeholder = new PlaceholderContentWidget(
499
+ config.placeholder,
500
+ this.monacoEditor!,
501
+ );
502
+
503
+ // console.log(
504
+ // 'editor._themeService.getColorTheme()',
505
+ // this.monacoEditor._themeService,
506
+ // this.monacoEditor._themeService.getColorTheme(),
507
+ // );
508
+
509
+ // setTimeout(() => {
510
+ // this.monacoEditor?.trigger('editor', 'editor.action.formatDocument');
511
+ // console.log('trigger format');
512
+ // }, 5000);
513
+ }
514
+
515
+ protected inspectResize() {
516
+ this.resizeObserver = new ResizeObserver((entries) => {
517
+ entries.forEach((entry) => {
518
+ const isVisible =
519
+ entry.contentRect.width !== 0 && entry.contentRect.height !== 0;
520
+
521
+ if (isVisible) {
522
+ this.updateEditorSize();
523
+ }
524
+ });
525
+ });
526
+
527
+ this.resizeObserver.observe(this.host);
528
+ }
529
+
530
+ protected getEditorNode() {
531
+ return Array.from(
532
+ this.host.getElementsByClassName(E2EditorClassname),
533
+ )[0] as HTMLDivElement;
534
+ }
535
+
536
+ protected updateEditorSize() {
537
+ const contentHeight = this.monacoEditor?.getContentHeight() ?? 20;
538
+ this.host.style.height = `${contentHeight + 30}px`;
539
+ try {
540
+ this.isLayouting = true;
541
+ this.monacoEditor?.layout({
542
+ width: this.host.offsetWidth,
543
+ height: contentHeight,
544
+ });
545
+ } finally {
546
+ this.isLayouting = false;
547
+ }
548
+ }
549
+
550
+ /**
551
+ * 解决e2与libro快捷键冲突
552
+ * @param commandRegistry
553
+ */
554
+ protected handleCommand(commandRegistry: CommandRegistry) {
555
+ // need monaco 0.34
556
+ // editor.addKeybindingRules([
557
+ // {
558
+ // // disable show command center
559
+ // keybinding: KeyCode.F1,
560
+ // command: null,
561
+ // },
562
+ // {
563
+ // // disable show error command
564
+ // keybinding: KeyCode.F8,
565
+ // command: null,
566
+ // },
567
+ // {
568
+ // // disable toggle debugger breakpoint
569
+ // keybinding: KeyCode.F9,
570
+ // command: null,
571
+ // },
572
+ this.monacoEditor?.addCommand(
573
+ 9,
574
+ () => {
575
+ commandRegistry.executeCommand(NotebookCommands['EnterCommandMode'].id);
576
+ },
577
+ '!editorHasSelection && !editorHasSelection && !editorHasMultipleSelections',
578
+ );
579
+ this.monacoEditor?.addCommand(
580
+ 2048 | 3,
581
+ () => {
582
+ commandRegistry.executeCommand(NotebookCommands['RunCell'].id);
583
+ },
584
+ '!findWidgetVisible && !referenceSearchVisible',
585
+ );
586
+ this.monacoEditor?.addCommand(
587
+ 1024 | 3,
588
+ () => {
589
+ commandRegistry.executeCommand(NotebookCommands['RunCellAndSelectNext'].id);
590
+ },
591
+ '!findInputFocussed',
592
+ );
593
+ this.monacoEditor?.addCommand(
594
+ 512 | 3,
595
+ () => {
596
+ commandRegistry.executeCommand(NotebookCommands['RunCellAndInsertBelow'].id);
597
+ },
598
+ '!findWidgetVisible',
599
+ );
600
+
601
+ this.monacoEditor?.addCommand(
602
+ 2048 | 1024 | 83,
603
+ () => {
604
+ commandRegistry.executeCommand(NotebookCommands['SplitCellAntCursor'].id);
605
+ },
606
+ // '!findWidgetVisible',
607
+ );
608
+
609
+ this.monacoEditor?.addCommand(
610
+ 2048 | 36,
611
+ () => {
612
+ commandRegistry.executeCommand('libro-search:toggle');
613
+ },
614
+ // '!findWidgetVisible',
615
+ );
616
+ }
617
+
618
+ protected onValueChange() {
619
+ // this.editor?.codeEditor.setValue(this.model.value);
620
+ }
621
+
622
+ protected onSelectionChange() {
623
+ this.setSelections(this.model.selections);
624
+ }
625
+
626
+ protected onThemeChange = () => {
627
+ this.monacoEditor?.updateOptions({ theme: this.theme });
628
+ };
629
+
630
+ /**
631
+ * Handles a mime type change.
632
+ * 切换语言
633
+ * cell 切换没走这里
634
+ */
635
+ protected onMimeTypeChanged(): void {
636
+ const model = this.monacoEditor?.getModel();
637
+ if (this.languageSpec && model) {
638
+ editor.setModelLanguage(model, this.languageSpec.language);
639
+ }
640
+ }
641
+
642
+ /**
643
+ * Handles a cursor activity event.
644
+ */
645
+ protected onCursorActivity(): void {
646
+ // Only add selections if the editor has focus. This avoids unwanted
647
+ // triggering of cursor activity due to collaborator actions.
648
+ if (this.hasFocus()) {
649
+ // const selections = this.getSelections();
650
+ // this.model.selections = selections;
651
+ }
652
+ }
653
+
654
+ getOption<K extends keyof LibroE2EditorConfig>(option: K) {
655
+ return this._config[option];
656
+ }
657
+
658
+ /**
659
+ *
660
+ * @param option
661
+ * @param value
662
+ */
663
+ setOption = <K extends keyof LibroE2EditorConfig>(
664
+ option: K,
665
+ value: LibroE2EditorConfig[K],
666
+ ) => {
667
+ if (value === null || value === undefined) {
668
+ return;
669
+ }
670
+
671
+ if (option === 'theme') {
672
+ this._config.theme = value as NonNullable<LibroE2EditorConfig['theme']>;
673
+ this.monacoEditor?.updateOptions({
674
+ theme: this.theme,
675
+ });
676
+ }
677
+
678
+ if (option === 'placeholder') {
679
+ this._config.placeholder = value as NonNullable<
680
+ LibroE2EditorConfig['placeholder']
681
+ >;
682
+ this.placeholder.update(value as NonNullable<LibroE2EditorConfig['placeholder']>);
683
+ }
684
+
685
+ if (option === 'lspEnabled') {
686
+ this._config.lspEnabled = value as NonNullable<LibroE2EditorConfig['lspEnabled']>;
687
+ }
688
+
689
+ const sizeKeys = [
690
+ 'fontFamily',
691
+ 'fontSize',
692
+ 'lineHeight',
693
+ 'wordWrapColumn',
694
+ 'lineWrap',
695
+ ];
696
+ const monacoOptionkeys = sizeKeys.concat(['readOnly', 'insertSpaces', 'tabSize']);
697
+
698
+ if (monacoOptionkeys.includes(option)) {
699
+ this._config = { ...this._config, [option]: value };
700
+
701
+ this.monacoEditor?.updateOptions(this.toMonacoOptions(this._config));
702
+ }
703
+
704
+ if (sizeKeys.includes(option)) {
705
+ this.updateEditorSize();
706
+ }
707
+ };
708
+
709
+ getLine = (line: number) => {
710
+ return this.monacoEditor?.getModel()?.getLineContent(line);
711
+ };
712
+ getOffsetAt = (position: IPosition) => {
713
+ return (
714
+ this.monacoEditor
715
+ ?.getModel()
716
+ ?.getOffsetAt({ lineNumber: position.line, column: position.column }) || 0
717
+ );
718
+ };
719
+ undo = () => {
720
+ this.monacoEditor?.trigger('source', 'undo', {});
721
+ };
722
+
723
+ redo = () => {
724
+ this.monacoEditor?.trigger('source', 'redo', {});
725
+ };
726
+ focus = () => {
727
+ this.monacoEditor?.focus();
728
+ };
729
+ hasFocus = () => {
730
+ return this.monacoEditor?.hasWidgetFocus() ?? false;
731
+ };
732
+ blur = () => {
733
+ document.documentElement.focus();
734
+ };
735
+ resizeToFit = () => {
736
+ this.monacoEditor?.layout();
737
+ };
738
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
739
+ getPositionForCoordinate = (coordinate: ICoordinate) => {
740
+ return null;
741
+ };
742
+
743
+ protected modalChangeEmitter = new Emitter<boolean>();
744
+ get onModalChange() {
745
+ return this.modalChangeEmitter.event;
746
+ }
747
+
748
+ protected toMonacoRange(range: IRange) {
749
+ const selection = range ?? this.getSelection();
750
+ const monacoSelection = {
751
+ startLineNumber: selection.start.line || 1,
752
+ startColumn: selection.start.column || 1,
753
+ endLineNumber: selection.end.line || 1,
754
+ endColumn: selection.end.column || 1,
755
+ };
756
+ return monacoSelection;
757
+ }
758
+
759
+ getSelectionValue = (range?: IRange | undefined) => {
760
+ const selection = range ?? this.getSelection();
761
+ return this.monacoEditor
762
+ ?.getModel()
763
+ ?.getValueInRange(this.toMonacoRange(selection));
764
+ };
765
+
766
+ getPositionAt = (offset: number): IPosition | undefined => {
767
+ const position = this.monacoEditor?.getModel()?.getPositionAt(offset);
768
+ return position ? { line: position.lineNumber, column: position.column } : position;
769
+ };
770
+
771
+ protected toMonacoMatch(match: SearchMatch): MonacoMatch {
772
+ const start = this.getPositionAt(match.position);
773
+ const end = this.getPositionAt(match.position + match.text.length);
774
+ return {
775
+ range: new MonacoRange(
776
+ start?.line ?? 1,
777
+ start?.column ?? 1,
778
+ end?.line ?? 1,
779
+ end?.column ?? 1,
780
+ ),
781
+ matches: [match.text],
782
+ _findMatchBrand: undefined,
783
+ };
784
+ }
785
+
786
+ replaceSelection = (text: string, range: IRange) => {
787
+ this.monacoEditor?.executeEdits(undefined, [
788
+ {
789
+ range: this.toMonacoRange(range),
790
+ text,
791
+ },
792
+ ]);
793
+ this.monacoEditor?.pushUndoStop();
794
+ };
795
+
796
+ replaceSelections = (edits: { text: string; range: IRange }[]) => {
797
+ this.monacoEditor?.executeEdits(
798
+ undefined,
799
+ edits.map((item) => ({ range: this.toMonacoRange(item.range), text: item.text })),
800
+ );
801
+ this.monacoEditor?.pushUndoStop();
802
+ };
803
+
804
+ getCursorPosition = () => {
805
+ const position: IPosition = {
806
+ line: this.monacoEditor?.getPosition()?.lineNumber || 1,
807
+ column: this.monacoEditor?.getPosition()?.column || 1,
808
+ };
809
+
810
+ return position;
811
+ };
812
+ setCursorPosition = (position: IPosition) => {
813
+ this.monacoEditor?.setPosition({
814
+ column: position.column,
815
+ lineNumber: position.line,
816
+ });
817
+ };
818
+ getSelection = () => {
819
+ const selection = {
820
+ start: {
821
+ line: this.monacoEditor?.getSelection()?.startLineNumber || 1,
822
+ column: this.monacoEditor?.getSelection()?.startColumn || 1,
823
+ } as IPosition,
824
+ end: {
825
+ line: this.monacoEditor?.getSelection()?.endLineNumber || 1,
826
+ column: this.monacoEditor?.getSelection()?.endColumn || 1,
827
+ } as IPosition,
828
+ };
829
+ return selection;
830
+ };
831
+ setSelection = (selection: IRange) => {
832
+ this.monacoEditor?.setSelection(this.toMonacoRange(selection));
833
+ };
834
+ getSelections = () => {
835
+ const selections: IRange[] = [];
836
+ this.monacoEditor?.getSelections()?.map((selection: any) =>
837
+ selections.push({
838
+ start: {
839
+ line: selection.startLineNumber || 1,
840
+ column: selection.startColumn || 1,
841
+ } as IPosition,
842
+ end: {
843
+ line: selection.endLineNumber || 1,
844
+ column: selection.endColumn || 1,
845
+ } as IPosition,
846
+ }),
847
+ );
848
+ return selections;
849
+ };
850
+ setSelections = (selections: IRange[]) => {
851
+ this.monacoEditor?.setSelections(
852
+ selections.map<Selection>(
853
+ (item) =>
854
+ new Selection(
855
+ item.start.line,
856
+ item.start.column,
857
+ item.end.line,
858
+ item.end.column,
859
+ ),
860
+ ),
861
+ );
862
+ };
863
+
864
+ revealSelection = (selection: IRange) => {
865
+ this.monacoEditor?.revealRange(this.toMonacoRange(selection));
866
+ };
867
+ highlightMatches = (matches: SearchMatch[], currentIndex: number | undefined) => {
868
+ let currentMatch: SearchMatch | undefined;
869
+ const _matches = matches
870
+ .map((item, index) => {
871
+ if (index === currentIndex) {
872
+ currentMatch = item;
873
+ return {
874
+ range: item,
875
+ options: {
876
+ className: `currentFindMatch`, // 当前高亮
877
+ },
878
+ };
879
+ }
880
+ return {
881
+ range: item,
882
+ options: {
883
+ className: `findMatch`, // 匹配高亮
884
+ },
885
+ };
886
+ })
887
+ .map((item) => ({
888
+ ...item,
889
+ range: this.toMonacoMatch(item.range).range,
890
+ }));
891
+ this.oldDeltaDecorations =
892
+ this.monacoEditor?.deltaDecorations(this.oldDeltaDecorations, _matches) || [];
893
+ if (currentMatch) {
894
+ const start = this.getPositionAt(currentMatch.position);
895
+ const end = this.getPositionAt(currentMatch.position + currentMatch.text.length);
896
+ if (start && end) {
897
+ this.revealSelection({ end, start });
898
+ }
899
+ }
900
+ };
901
+
902
+ protected _isDisposed = false;
903
+ /**
904
+ * Tests whether the editor is disposed.
905
+ */
906
+ get disposed(): boolean {
907
+ return this._isDisposed;
908
+ }
909
+ dispose() {
910
+ if (this.disposed) {
911
+ return;
912
+ }
913
+ this.placeholder.dispose();
914
+ this.toDispose.dispose();
915
+ this.disposeResizeObserver();
916
+ this.lspContribution.beforeDestory();
917
+ this._isDisposed = true;
918
+ }
919
+ protected disposeResizeObserver = () => {
920
+ this.resizeObserver.disconnect();
921
+ };
922
+ }