@difizen/libro-codemirror 0.0.0-snapshot-20241017072317

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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/es/auto-complete/closebrackets.d.ts +12 -0
  4. package/es/auto-complete/closebrackets.d.ts.map +1 -0
  5. package/es/auto-complete/closebrackets.js +409 -0
  6. package/es/auto-complete/completion.d.ts +57 -0
  7. package/es/auto-complete/completion.d.ts.map +1 -0
  8. package/es/auto-complete/completion.js +267 -0
  9. package/es/auto-complete/config.d.ts +22 -0
  10. package/es/auto-complete/config.d.ts.map +1 -0
  11. package/es/auto-complete/config.js +44 -0
  12. package/es/auto-complete/filter.d.ts +13 -0
  13. package/es/auto-complete/filter.d.ts.map +1 -0
  14. package/es/auto-complete/filter.js +190 -0
  15. package/es/auto-complete/index.d.ts +17 -0
  16. package/es/auto-complete/index.d.ts.map +1 -0
  17. package/es/auto-complete/index.js +107 -0
  18. package/es/auto-complete/snippet.d.ts +14 -0
  19. package/es/auto-complete/snippet.d.ts.map +1 -0
  20. package/es/auto-complete/snippet.js +445 -0
  21. package/es/auto-complete/state.d.ts +63 -0
  22. package/es/auto-complete/state.d.ts.map +1 -0
  23. package/es/auto-complete/state.js +448 -0
  24. package/es/auto-complete/theme.d.ts +6 -0
  25. package/es/auto-complete/theme.d.ts.map +1 -0
  26. package/es/auto-complete/theme.js +150 -0
  27. package/es/auto-complete/tooltip.d.ts +5 -0
  28. package/es/auto-complete/tooltip.d.ts.map +1 -0
  29. package/es/auto-complete/tooltip.js +365 -0
  30. package/es/auto-complete/view.d.ts +38 -0
  31. package/es/auto-complete/view.d.ts.map +1 -0
  32. package/es/auto-complete/view.js +368 -0
  33. package/es/auto-complete/word.d.ts +3 -0
  34. package/es/auto-complete/word.d.ts.map +1 -0
  35. package/es/auto-complete/word.js +119 -0
  36. package/es/completion.d.ts +6 -0
  37. package/es/completion.d.ts.map +1 -0
  38. package/es/completion.js +84 -0
  39. package/es/config.d.ts +189 -0
  40. package/es/config.d.ts.map +1 -0
  41. package/es/config.js +482 -0
  42. package/es/editor-contribution.d.ts +9 -0
  43. package/es/editor-contribution.d.ts.map +1 -0
  44. package/es/editor-contribution.js +35 -0
  45. package/es/editor.d.ts +397 -0
  46. package/es/editor.d.ts.map +1 -0
  47. package/es/editor.js +1338 -0
  48. package/es/factory.d.ts +4 -0
  49. package/es/factory.d.ts.map +1 -0
  50. package/es/factory.js +24 -0
  51. package/es/hyperlink.d.ts +15 -0
  52. package/es/hyperlink.d.ts.map +1 -0
  53. package/es/hyperlink.js +120 -0
  54. package/es/indent.d.ts +8 -0
  55. package/es/indent.d.ts.map +1 -0
  56. package/es/indent.js +58 -0
  57. package/es/indentation-markers/config.d.ts +17 -0
  58. package/es/indentation-markers/config.d.ts.map +1 -0
  59. package/es/indentation-markers/config.js +10 -0
  60. package/es/indentation-markers/index.d.ts +3 -0
  61. package/es/indentation-markers/index.d.ts.map +1 -0
  62. package/es/indentation-markers/index.js +160 -0
  63. package/es/indentation-markers/map.d.ts +77 -0
  64. package/es/indentation-markers/map.d.ts.map +1 -0
  65. package/es/indentation-markers/map.js +265 -0
  66. package/es/indentation-markers/utils.d.ts +27 -0
  67. package/es/indentation-markers/utils.d.ts.map +1 -0
  68. package/es/indentation-markers/utils.js +91 -0
  69. package/es/index.d.ts +13 -0
  70. package/es/index.d.ts.map +1 -0
  71. package/es/index.js +12 -0
  72. package/es/libro-icon.d.ts +3 -0
  73. package/es/libro-icon.d.ts.map +1 -0
  74. package/es/libro-icon.js +2 -0
  75. package/es/lsp/completion.d.ts +5 -0
  76. package/es/lsp/completion.d.ts.map +1 -0
  77. package/es/lsp/completion.js +245 -0
  78. package/es/lsp/format.d.ts +7 -0
  79. package/es/lsp/format.d.ts.map +1 -0
  80. package/es/lsp/format.js +193 -0
  81. package/es/lsp/index.d.ts +7 -0
  82. package/es/lsp/index.d.ts.map +1 -0
  83. package/es/lsp/index.js +6 -0
  84. package/es/lsp/lint.d.ts +3 -0
  85. package/es/lsp/lint.d.ts.map +1 -0
  86. package/es/lsp/lint.js +113 -0
  87. package/es/lsp/protocol.d.ts +7 -0
  88. package/es/lsp/protocol.d.ts.map +1 -0
  89. package/es/lsp/protocol.js +1 -0
  90. package/es/lsp/tooltip.d.ts +3 -0
  91. package/es/lsp/tooltip.d.ts.map +1 -0
  92. package/es/lsp/tooltip.js +113 -0
  93. package/es/lsp/util.d.ts +15 -0
  94. package/es/lsp/util.d.ts.map +1 -0
  95. package/es/lsp/util.js +57 -0
  96. package/es/mimetype.d.ts +22 -0
  97. package/es/mimetype.d.ts.map +1 -0
  98. package/es/mimetype.js +59 -0
  99. package/es/mode.d.ts +86 -0
  100. package/es/mode.d.ts.map +1 -0
  101. package/es/mode.js +280 -0
  102. package/es/module.d.ts +3 -0
  103. package/es/module.d.ts.map +1 -0
  104. package/es/module.js +4 -0
  105. package/es/monitor.d.ts +32 -0
  106. package/es/monitor.d.ts.map +1 -0
  107. package/es/monitor.js +129 -0
  108. package/es/python-lang.d.ts +3 -0
  109. package/es/python-lang.d.ts.map +1 -0
  110. package/es/python-lang.js +7 -0
  111. package/es/style/base.css +129 -0
  112. package/es/style/theme.css +12 -0
  113. package/es/style/variables.css +405 -0
  114. package/es/theme.d.ts +35 -0
  115. package/es/theme.d.ts.map +1 -0
  116. package/es/theme.js +223 -0
  117. package/es/tooltip.d.ts +10 -0
  118. package/es/tooltip.d.ts.map +1 -0
  119. package/es/tooltip.js +168 -0
  120. package/package.json +74 -0
  121. package/src/auto-complete/README.md +71 -0
  122. package/src/auto-complete/closebrackets.ts +423 -0
  123. package/src/auto-complete/completion.ts +345 -0
  124. package/src/auto-complete/config.ts +101 -0
  125. package/src/auto-complete/filter.ts +214 -0
  126. package/src/auto-complete/index.ts +112 -0
  127. package/src/auto-complete/snippet.ts +392 -0
  128. package/src/auto-complete/state.ts +465 -0
  129. package/src/auto-complete/theme.ts +127 -0
  130. package/src/auto-complete/tooltip.ts +386 -0
  131. package/src/auto-complete/view.ts +339 -0
  132. package/src/auto-complete/word.ts +118 -0
  133. package/src/completion.ts +61 -0
  134. package/src/config.ts +701 -0
  135. package/src/editor-contribution.ts +22 -0
  136. package/src/editor.ts +1287 -0
  137. package/src/factory.ts +31 -0
  138. package/src/hyperlink.ts +95 -0
  139. package/src/indent.ts +69 -0
  140. package/src/indentation-markers/config.ts +31 -0
  141. package/src/indentation-markers/index.ts +192 -0
  142. package/src/indentation-markers/map.ts +273 -0
  143. package/src/indentation-markers/utils.ts +84 -0
  144. package/src/index.spec.ts +12 -0
  145. package/src/index.ts +14 -0
  146. package/src/libro-icon.tsx +4 -0
  147. package/src/lsp/completion.ts +175 -0
  148. package/src/lsp/format.ts +144 -0
  149. package/src/lsp/index.ts +6 -0
  150. package/src/lsp/lint.ts +125 -0
  151. package/src/lsp/protocol.ts +8 -0
  152. package/src/lsp/tooltip.ts +76 -0
  153. package/src/lsp/util.ts +69 -0
  154. package/src/mimetype.ts +49 -0
  155. package/src/mode.ts +265 -0
  156. package/src/module.ts +8 -0
  157. package/src/monitor.ts +105 -0
  158. package/src/python-lang.ts +7 -0
  159. package/src/style/base.css +129 -0
  160. package/src/style/theme.css +12 -0
  161. package/src/style/variables.css +405 -0
  162. package/src/theme.ts +231 -0
  163. package/src/tooltip.ts +143 -0
package/src/config.ts ADDED
@@ -0,0 +1,701 @@
1
+ import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
2
+ import { pythonLanguage } from '@codemirror/lang-python';
3
+ import {
4
+ bracketMatching,
5
+ defaultHighlightStyle,
6
+ foldGutter,
7
+ foldKeymap,
8
+ indentOnInput,
9
+ indentUnit,
10
+ syntaxHighlighting,
11
+ } from '@codemirror/language';
12
+ import type { LanguageSupport } from '@codemirror/language';
13
+ import { lintKeymap } from '@codemirror/lint';
14
+ import { highlightSelectionMatches } from '@codemirror/search';
15
+ import type { Extension, Facet } from '@codemirror/state';
16
+ import { Compartment, EditorState, StateEffect } from '@codemirror/state';
17
+ import type { KeyBinding } from '@codemirror/view';
18
+ import {
19
+ crosshairCursor,
20
+ drawSelection,
21
+ dropCursor,
22
+ EditorView,
23
+ highlightActiveLine,
24
+ highlightActiveLineGutter,
25
+ highlightSpecialChars,
26
+ keymap,
27
+ lineNumbers,
28
+ placeholder,
29
+ rectangularSelection,
30
+ } from '@codemirror/view';
31
+ import type { IEditorConfig } from '@difizen/libro-code-editor';
32
+
33
+ import {
34
+ autocompletion,
35
+ closeBrackets,
36
+ closeBracketsKeymap,
37
+ completionKeymap,
38
+ } from './auto-complete/index.js';
39
+ import { kernelCompletions } from './completion.js';
40
+ import type { IOptions } from './editor.js';
41
+ import { hyperLink } from './hyperlink.js';
42
+ import { indentationMarkers } from './indentation-markers/index.js';
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';
47
+ import { ensure } from './mode.js';
48
+ import { getTheme, defaultTheme } from './theme.js';
49
+ import { tabTooltip, tooltipKeymap } from './tooltip.js';
50
+
51
+ /**
52
+ * The configuration options for a codemirror editor.
53
+ */
54
+ export interface CodeMirrorConfig extends IEditorConfig {
55
+ /**
56
+ * The mode to use.
57
+ */
58
+ mode?: string;
59
+
60
+ /**
61
+ * content mimetype
62
+ */
63
+ mimetype?: string;
64
+
65
+ // FIXME-TRANS: Handle theme localizable names
66
+ // themeDisplayName?: string
67
+
68
+ /**
69
+ * Whether to use the context-sensitive indentation that the mode provides
70
+ * (or just indent the same as the line before).
71
+ */
72
+ smartIndent?: boolean;
73
+
74
+ /**
75
+ * Configures whether the editor should re-indent the current line when a
76
+ * character is typed that might change its proper indentation
77
+ * (only works if the mode supports indentation).
78
+ */
79
+ electricChars?: boolean;
80
+
81
+ /**
82
+ * Configures the keymap to use. The default is "default", which is the
83
+ * only keymap defined in codemirror.js itself.
84
+ * Extra keymaps are found in the CodeMirror keymap directory.
85
+ */
86
+ keyMap?: string;
87
+
88
+ /**
89
+ * Can be used to specify extra keybindings for the editor, alongside the
90
+ * ones defined by keyMap. Should be either null, or a valid keymap value.
91
+ */
92
+ extraKeys?: KeyBinding[] | null;
93
+
94
+ /**
95
+ * Can be used to add extra gutters (beyond or instead of the line number
96
+ * gutter).
97
+ * Should be an array of CSS class names, each of which defines a width
98
+ * (and optionally a background),
99
+ * and which will be used to draw the background of the gutters.
100
+ * May include the CodeMirror-linenumbers class, in order to explicitly
101
+ * set the position of the line number gutter
102
+ * (it will default to be to the right of all other gutters).
103
+ * These class names are the keys passed to setGutterMarker.
104
+ */
105
+ gutters?: string[];
106
+
107
+ /**
108
+ * Determines whether the gutter scrolls along with the content
109
+ * horizontally (false)
110
+ * or whether it stays fixed during horizontal scrolling (true,
111
+ * the default).
112
+ */
113
+ fixedGutter?: boolean;
114
+
115
+ /**
116
+ * Whether the cursor should be drawn when a selection is active.
117
+ */
118
+ showCursorWhenSelecting?: boolean;
119
+
120
+ /**
121
+ * When fixedGutter is on, and there is a horizontal scrollbar, by default
122
+ * the gutter will be visible to the left of this scrollbar. If this
123
+ * option is set to true, it will be covered by an element with class
124
+ * CodeMirror-gutter-filler.
125
+ */
126
+ coverGutterNextToScrollbar?: boolean;
127
+
128
+ /**
129
+ * Controls whether drag-and-drop is enabled.
130
+ */
131
+ dragDrop?: boolean;
132
+
133
+ /**
134
+ * Explicitly set the line separator for the editor.
135
+ * By default (value null), the document will be split on CRLFs as well as
136
+ * lone CRs and LFs, and a single LF will be used as line separator in all
137
+ * output (such as getValue). When a specific string is given, lines will
138
+ * only be split on that string, and output will, by default, use that
139
+ * same separator.
140
+ */
141
+ lineSeparator?: string | null;
142
+
143
+ /**
144
+ * Chooses a scrollbar implementation. The default is "native", showing
145
+ * native scrollbars. The core library also provides the "null" style,
146
+ * which completely hides the scrollbars. Addons can implement additional
147
+ * scrollbar models.
148
+ */
149
+ scrollbarStyle?: string;
150
+
151
+ /**
152
+ * When enabled, which is the default, doing copy or cut when there is no
153
+ * selection will copy or cut the whole lines that have cursors on them.
154
+ */
155
+ lineWiseCopyCut?: boolean;
156
+
157
+ /**
158
+ * Whether to scroll past the end of the buffer.
159
+ */
160
+ scrollPastEnd?: boolean;
161
+
162
+ /**
163
+ * Whether to give the wrapper of the line that contains the cursor the class
164
+ * cm-activeLine.
165
+ */
166
+ styleActiveLine?: boolean;
167
+
168
+ /**
169
+ * Whether to causes the selected text to be marked with the CSS class
170
+ * CodeMirror-selectedtext. Useful to change the colour of the selection
171
+ * (in addition to the background).
172
+ */
173
+ styleSelectedText?: boolean;
174
+
175
+ /**
176
+ * Defines the mouse cursor appearance when hovering over the selection.
177
+ * It can be set to a string, like "pointer", or to true,
178
+ * in which case the "default" (arrow) cursor will be used.
179
+ */
180
+ selectionPointer?: boolean | string;
181
+
182
+ //
183
+ highlightActiveLineGutter?: boolean;
184
+ highlightSpecialChars?: boolean;
185
+ history?: boolean;
186
+ drawSelection?: boolean;
187
+ dropCursor?: boolean;
188
+ allowMultipleSelections?: boolean;
189
+ autocompletion?: boolean;
190
+ rectangularSelection?: boolean;
191
+ crosshairCursor?: boolean;
192
+ highlightSelectionMatches?: boolean;
193
+ foldGutter?: boolean;
194
+ syntaxHighlighting?: boolean;
195
+ /**
196
+ * 是否从kernel获取completion
197
+ */
198
+ jupyterKernelCompletion?: boolean;
199
+ /**
200
+ * 是否从kernel获取tooltip
201
+ */
202
+ jupyterKernelTooltip?: boolean;
203
+ indentationMarkers?: boolean;
204
+ hyperLink?: boolean;
205
+ /**
206
+ * 是否开启tab触发completion和tooltip
207
+ */
208
+ tabEditorFunction?: boolean;
209
+
210
+ lspCompletion?: boolean;
211
+
212
+ lspTooltip?: boolean;
213
+
214
+ lspLint?: boolean;
215
+
216
+ placeholder?: HTMLElement | string;
217
+ }
218
+ /**
219
+ * Extension builder (we use the same trick as in CM Extension
220
+ * to emulate empty interface)
221
+ */
222
+ interface IExtensionBuilder {
223
+ extensionBuilder: IExtensionBuilder;
224
+ }
225
+
226
+ /**
227
+ * Builder for extensions from value of type T
228
+ */
229
+ abstract class ExtensionBuilder<T> implements IExtensionBuilder {
230
+ abstract of(value: T): Extension;
231
+
232
+ get extensionBuilder(): IExtensionBuilder {
233
+ return this;
234
+ }
235
+ }
236
+ /**
237
+ * Extension builder that simply forwards a value as an extension.
238
+ */
239
+ class ExtensionForwarder<T extends Extension> extends ExtensionBuilder<T> {
240
+ of(value: T): Extension {
241
+ return value;
242
+ }
243
+ }
244
+ /**
245
+ * Extension builder that builds an extension from a facet.
246
+ */
247
+ class FacetWrapper<T, U> extends ExtensionBuilder<T> {
248
+ constructor(facet: Facet<T, U>) {
249
+ super();
250
+ this._facet = facet;
251
+ }
252
+
253
+ of(value: T): Extension {
254
+ return this._facet.of(value);
255
+ }
256
+
257
+ protected _facet: Facet<T, U>;
258
+ }
259
+ /**
260
+ * Extension builder that provides an extension depending
261
+ * on a boolean value.
262
+ */
263
+ class ConditionalExtension extends ExtensionBuilder<boolean> {
264
+ constructor(truthy: Extension, falsy: Extension = []) {
265
+ super();
266
+ this._truthy = truthy;
267
+ this._falsy = falsy;
268
+ }
269
+
270
+ of(value: boolean): Extension {
271
+ return value ? this._truthy : this._falsy;
272
+ }
273
+
274
+ protected _truthy: Extension;
275
+ protected _falsy: Extension;
276
+ }
277
+
278
+ /**
279
+ * Extension builds that provides an extension depending on
280
+ * conditional function operating on a value.
281
+ */
282
+ class GenConditionalExtension<T> extends ExtensionBuilder<T> {
283
+ constructor(fn: (a: T) => boolean, truthy: Extension, falsy: Extension = []) {
284
+ super();
285
+ this._fn = fn;
286
+ this._builder = new ConditionalExtension(truthy, falsy);
287
+ }
288
+
289
+ of(value: T): Extension {
290
+ return this._builder.of(this._fn(value));
291
+ }
292
+
293
+ protected _fn: (a: T) => boolean;
294
+ protected _builder: ConditionalExtension;
295
+ }
296
+
297
+ /**
298
+ * Builds an extension in a compartment that can
299
+ * be reconfigured.
300
+ */
301
+ interface IConfigurableBuilder {
302
+ of: <T>(value: T) => Extension;
303
+ reconfigure: <T>(value: T) => StateEffect<unknown>;
304
+ }
305
+
306
+ /**
307
+ * IConfigurableBuilder implementation *
308
+ */
309
+ class ConfigurableBuilder implements IConfigurableBuilder {
310
+ constructor(builder: IExtensionBuilder) {
311
+ this._compartment = new Compartment();
312
+ this._builder = builder;
313
+ }
314
+
315
+ of<T>(value: T): Extension {
316
+ return this._compartment.of((this._builder as ExtensionBuilder<T>).of(value));
317
+ }
318
+
319
+ reconfigure<T>(value: T): StateEffect<unknown> {
320
+ return this._compartment.reconfigure(
321
+ (this._builder as ExtensionBuilder<T>).of(value),
322
+ );
323
+ }
324
+
325
+ protected _compartment: Compartment;
326
+ protected _builder: IExtensionBuilder;
327
+ }
328
+
329
+ /*
330
+ * Specific builder for themes. Provide a default theme and
331
+ * allows to register new themes.
332
+ */
333
+ class ThemeBuilder implements IConfigurableBuilder {
334
+ constructor() {
335
+ this._compartment = new Compartment();
336
+ }
337
+
338
+ of<T>(value: T): Extension {
339
+ const v = value as unknown as string;
340
+ return this._compartment.of(getTheme(v));
341
+ }
342
+
343
+ reconfigure<T>(value: T): StateEffect<unknown> {
344
+ const v = value as unknown as string;
345
+ return this._compartment.reconfigure(getTheme(v));
346
+ }
347
+
348
+ protected _compartment: Compartment;
349
+ }
350
+
351
+ /*
352
+ * Specific builder for themes. Provide a default theme and
353
+ * allows to register new themes.
354
+ */
355
+ class PlaceHolderBuilder implements IConfigurableBuilder {
356
+ constructor() {
357
+ this._compartment = new Compartment();
358
+ }
359
+
360
+ of<T>(value: T): Extension {
361
+ const v = value as unknown as HTMLElement | string;
362
+ return this._compartment.of(placeholder(v));
363
+ }
364
+
365
+ reconfigure<T>(value: T): StateEffect<unknown> {
366
+ const v = value as unknown as HTMLElement | string;
367
+ return this._compartment.reconfigure(placeholder(v));
368
+ }
369
+
370
+ protected _compartment: Compartment;
371
+ }
372
+
373
+ /**
374
+ * Creates a ConfigurableBuilder based on an ExtensionForwarder.
375
+ */
376
+ function createForwarderBuilder<T extends Extension>(): IConfigurableBuilder {
377
+ return new ConfigurableBuilder(new ExtensionForwarder<T>());
378
+ }
379
+
380
+ /**
381
+ * Creates a ConfigurableBuilder based on a Facet.
382
+ */
383
+ function createConfigurableBuilder<T, U>(facet: Facet<T, U>): IConfigurableBuilder {
384
+ return new ConfigurableBuilder(new FacetWrapper<T, U>(facet));
385
+ }
386
+
387
+ /**
388
+ * Creates a ConditionalBuilder from two extensions.
389
+ */
390
+ function createConditionalBuilder(
391
+ truthy: Extension,
392
+ falsy: Extension = [],
393
+ ): IConfigurableBuilder {
394
+ return new ConfigurableBuilder(new ConditionalExtension(truthy, falsy));
395
+ }
396
+
397
+ /**
398
+ * Creates a ConditionalBuilder based on two extensions and a
399
+ * conditional function.
400
+ */
401
+ function createGenConditionalBuilder<T>(
402
+ fn: (value: T) => boolean,
403
+ truthy: Extension,
404
+ falsy: Extension = [],
405
+ ): IConfigurableBuilder {
406
+ return new ConfigurableBuilder(new GenConditionalExtension<T>(fn, truthy, falsy));
407
+ }
408
+
409
+ /**
410
+ * Creates a theme builder.
411
+ */
412
+ function createThemeBuilder() {
413
+ return new ThemeBuilder();
414
+ }
415
+
416
+ /**
417
+ * Creates a theme builder.
418
+ */
419
+ function createPlaceHolderBuilder() {
420
+ return new PlaceHolderBuilder();
421
+ }
422
+
423
+ /**
424
+ * Editor configuration: provides APIs to get and reconfigure CodeMirror 6
425
+ * extensions from option names. Also allows to register new themes and
426
+ * inject ne extensions in already configured editor instances.
427
+ */
428
+ export class EditorConfiguration {
429
+ constructor(options: IOptions) {
430
+ // Order does not matter
431
+ this._configurableBuilderMap = new Map<string, IConfigurableBuilder>([
432
+ ['tabSize', createConfigurableBuilder(EditorState.tabSize)],
433
+ ['readOnly', createConfigurableBuilder(EditorState.readOnly)],
434
+ ['editable', createConfigurableBuilder(EditorView.editable)],
435
+ ['keymap', createConfigurableBuilder(keymap)],
436
+ ['indentUnit', createConfigurableBuilder(indentUnit)],
437
+ ['smartIndent', createConditionalBuilder(indentOnInput())],
438
+ ['autoClosingBrackets', createConditionalBuilder(closeBrackets())],
439
+ ['matchBrackets', createConditionalBuilder(bracketMatching())],
440
+ ['styleActiveLine', createConditionalBuilder(highlightActiveLine())],
441
+ ['lineNumbers', createConditionalBuilder(lineNumbers())],
442
+ [
443
+ 'lineWrap',
444
+ createGenConditionalBuilder(
445
+ (a: string) => a !== 'off',
446
+ EditorView.lineWrapping,
447
+ ),
448
+ ],
449
+ ['language', createForwarderBuilder<LanguageSupport>()],
450
+ ['theme', createThemeBuilder()],
451
+
452
+ // addtional
453
+ [
454
+ 'highlightActiveLineGutter',
455
+ createConditionalBuilder(highlightActiveLineGutter()),
456
+ ],
457
+ ['highlightSpecialChars', createConditionalBuilder(highlightSpecialChars())],
458
+ ['history', createConditionalBuilder(history())],
459
+ ['drawSelection', createConditionalBuilder(drawSelection())],
460
+ ['dropCursor', createConditionalBuilder(dropCursor())],
461
+ [
462
+ 'allowMultipleSelections',
463
+ createConfigurableBuilder(EditorState.allowMultipleSelections),
464
+ ],
465
+ [
466
+ 'autocompletion',
467
+ createConditionalBuilder(
468
+ autocompletion({ activateOnTyping: options.config?.lspCompletion === true }),
469
+ ),
470
+ ],
471
+ ['rectangularSelection', createConditionalBuilder(rectangularSelection())],
472
+ ['crosshairCursor', createConditionalBuilder(crosshairCursor())],
473
+ [
474
+ 'highlightSelectionMatches',
475
+ createConditionalBuilder(highlightSelectionMatches()),
476
+ ],
477
+ [
478
+ 'foldGutter',
479
+ createConditionalBuilder(
480
+ foldGutter({
481
+ markerDOM: (open) => {
482
+ if (open) {
483
+ const iconElement = document.createElement('div');
484
+ iconElement.innerHTML = FoldIcon;
485
+ iconElement.style.display = 'flex';
486
+ iconElement.style.cursor = 'pointer';
487
+ return iconElement;
488
+ } else {
489
+ const iconElement = document.createElement('div');
490
+ iconElement.innerHTML = UnFoldIcon;
491
+ iconElement.style.cursor = 'pointer';
492
+ return iconElement;
493
+ }
494
+ },
495
+ }),
496
+ ),
497
+ ],
498
+ [
499
+ 'syntaxHighlighting',
500
+ createConditionalBuilder(
501
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
502
+ ),
503
+ ],
504
+ [
505
+ 'jupyterKernelCompletion',
506
+ createConditionalBuilder(
507
+ pythonLanguage.data.of({
508
+ autocomplete: kernelCompletions(options.completionProvider),
509
+ }),
510
+ ),
511
+ ],
512
+ [
513
+ 'jupyterKernelTooltip',
514
+ createConditionalBuilder(tabTooltip(options.tooltipProvider)),
515
+ ],
516
+ ['indentationMarkers', createConditionalBuilder(indentationMarkers())],
517
+ ['hyperLink', createConditionalBuilder(hyperLink)],
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
+ ],
533
+ ]);
534
+ this._themeOverloaderSpec = { '&': {}, '.cm-line': {} };
535
+ this._themeOverloader = new Compartment();
536
+ }
537
+
538
+ /**
539
+ * Reconfigures the extension mapped with key with the provided value.
540
+ */
541
+ reconfigureExtension<T>(view: EditorView, key: string, value: T): void {
542
+ const builder = this.get(key);
543
+ if (builder) {
544
+ view.dispatch({
545
+ effects: builder.reconfigure(value),
546
+ });
547
+ } else {
548
+ const config: Record<string, any> = {};
549
+ config[key] = value;
550
+ const themeOverload = this.updateThemeOverload(config);
551
+ view.dispatch({
552
+ effects: this._themeOverloader.reconfigure(themeOverload),
553
+ });
554
+ }
555
+ }
556
+
557
+ /**
558
+ * Reconfigures all the extensions mapped with the options from the
559
+ * provided partial configuration.
560
+ */
561
+ reconfigureExtensions(view: EditorView, config: Partial<CodeMirrorConfig>): void {
562
+ const eff = [];
563
+ for (const [key, value] of Object.entries(config)) {
564
+ const builder = this.get(key);
565
+ if (builder) {
566
+ eff.push(builder.reconfigure(value));
567
+ }
568
+ }
569
+
570
+ const themeOverload = this.updateThemeOverload(config);
571
+ eff.push(this._themeOverloader.reconfigure(themeOverload));
572
+
573
+ view.dispatch({
574
+ effects: eff,
575
+ });
576
+ }
577
+
578
+ /**
579
+ * Appends extensions to the top-level configuration of the
580
+ * editor.
581
+ */
582
+ injectExtension(view: EditorView, ext: Extension): void {
583
+ view.dispatch({
584
+ effects: StateEffect.appendConfig.of(ext),
585
+ });
586
+ }
587
+
588
+ /**
589
+ * Returns the list of initial extensions of an editor
590
+ * based on the provided configuration.
591
+ */
592
+ getInitialExtensions(config: CodeMirrorConfig): Extension[] {
593
+ const keys = Object.keys(config).filter(
594
+ (v) => v !== 'insertSpaces' && v !== 'extraKeys',
595
+ );
596
+ const extensions = [];
597
+ for (const k of keys) {
598
+ const builder = this.get(k);
599
+ if (builder) {
600
+ const value = config[k as keyof CodeMirrorConfig];
601
+ extensions.push(builder.of(value));
602
+ }
603
+ }
604
+ const libroDefaultKeymap = defaultKeymap.filter((item) => item.key !== 'Mod-Enter');
605
+
606
+ if (!config.theme) {
607
+ extensions.push(defaultTheme());
608
+ }
609
+
610
+ const builder = this.get('keymap');
611
+
612
+ const initialKeymap = [
613
+ ...libroDefaultKeymap,
614
+ ...closeBracketsKeymap,
615
+ // ...searchKeymap,
616
+ ...historyKeymap,
617
+ ...foldKeymap,
618
+ ...completionKeymap,
619
+ ...lintKeymap,
620
+ ...tooltipKeymap,
621
+ ...formatKeymap,
622
+ ];
623
+
624
+ const keymapExt = builder!.of(
625
+ config.extraKeys ? [initialKeymap, ...config.extraKeys] : [...initialKeymap],
626
+ );
627
+ const indentBuilder = this.get('indentUnit');
628
+ const insertExt = indentBuilder!.of(
629
+ config.insertSpaces ? ' '.repeat(config.tabSize) : '\t',
630
+ );
631
+
632
+ const themeOverload = this.updateThemeOverload(config);
633
+ extensions.push(this._themeOverloader.of(themeOverload), insertExt, keymapExt);
634
+
635
+ ensure(config?.mimetype ?? 'text/x-python')
636
+ .then((spec) => {
637
+ if (spec) {
638
+ extensions.push(this.get('language')?.of(spec.support!));
639
+ }
640
+ return undefined;
641
+ })
642
+ .catch((error) => {
643
+ console.error('Could not load language parser:');
644
+ console.error(error);
645
+ });
646
+
647
+ return extensions;
648
+ }
649
+
650
+ protected updateThemeOverload(
651
+ config: Partial<CodeMirrorConfig> | Record<string, any>,
652
+ ): Extension {
653
+ const { fontFamily, fontSize, lineHeight, lineWrap, wordWrapColumn } = config;
654
+
655
+ let needUpdate = false;
656
+
657
+ const parentStyle: Record<string, any> = {};
658
+ if (fontSize) {
659
+ parentStyle['fontSize'] = fontSize + 'px';
660
+ needUpdate = true;
661
+ }
662
+ if (fontFamily) {
663
+ parentStyle['fontFamily'] = fontFamily;
664
+ needUpdate = true;
665
+ }
666
+ if (lineHeight) {
667
+ parentStyle['lineHeight'] = lineHeight.toString();
668
+ needUpdate = true;
669
+ }
670
+
671
+ const lineStyle: Record<string, any> = {};
672
+ if (lineWrap === 'wordWrapColumn') {
673
+ lineStyle['width'] = wordWrapColumn + 'ch';
674
+ needUpdate = true;
675
+ } else if (lineWrap === 'bounded') {
676
+ lineStyle['maxWidth'] = wordWrapColumn + 'ch';
677
+ needUpdate = true;
678
+ }
679
+
680
+ if (needUpdate) {
681
+ const newThemeSpec = {
682
+ '&': parentStyle,
683
+ '.cm-line': lineStyle,
684
+ };
685
+ this._themeOverloaderSpec = {
686
+ ...this._themeOverloaderSpec,
687
+ ...newThemeSpec,
688
+ };
689
+ }
690
+
691
+ return EditorView.theme(this._themeOverloaderSpec);
692
+ }
693
+
694
+ protected get(key: string): IConfigurableBuilder | undefined {
695
+ return this._configurableBuilderMap.get(key);
696
+ }
697
+
698
+ protected _configurableBuilderMap: Map<string, IConfigurableBuilder>;
699
+ protected _themeOverloaderSpec: Record<string, Record<string, string>>;
700
+ protected _themeOverloader: Compartment;
701
+ }
@@ -0,0 +1,22 @@
1
+ import type { CodeEditorFactory } from '@difizen/libro-code-editor';
2
+ import { CodeEditorContribution } from '@difizen/libro-code-editor';
3
+ import { MIME } from '@difizen/libro-common';
4
+ import { singleton } from '@difizen/mana-app';
5
+
6
+ import { codeMirrorDefaultConfig } from './editor.js';
7
+ import { codeMirrorEditorFactory, stateFactory } from './factory.js';
8
+
9
+ @singleton({ contrib: [CodeEditorContribution] })
10
+ export class CodeMirrorEditorContribution implements CodeEditorContribution {
11
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
+ canHandle(mime: string): number {
13
+ const mimes = [MIME.codemirror, MIME.text] as string[];
14
+ if (mimes.includes(mime)) {
15
+ return 50 + 1;
16
+ }
17
+ return 10;
18
+ }
19
+ factory: CodeEditorFactory = codeMirrorEditorFactory;
20
+ stateFactory = stateFactory;
21
+ defaultConfig = codeMirrorDefaultConfig;
22
+ }