@jupyterlab/codemirror 4.0.0-alpha.9 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/lib/codemirror-ipythongfm.d.ts +9 -3
  2. package/lib/codemirror-ipythongfm.js +52 -30
  3. package/lib/codemirror-ipythongfm.js.map +1 -1
  4. package/lib/commands.d.ts +13 -0
  5. package/lib/commands.js +32 -0
  6. package/lib/commands.js.map +1 -0
  7. package/lib/editor.d.ts +48 -273
  8. package/lib/editor.js +220 -819
  9. package/lib/editor.js.map +1 -1
  10. package/lib/extension.d.ts +236 -0
  11. package/lib/extension.js +696 -0
  12. package/lib/extension.js.map +1 -0
  13. package/lib/extensions/customStyle.d.ts +25 -0
  14. package/lib/extensions/customStyle.js +51 -0
  15. package/lib/extensions/customStyle.js.map +1 -0
  16. package/lib/extensions/index.d.ts +3 -0
  17. package/lib/extensions/index.js +8 -0
  18. package/lib/extensions/index.js.map +1 -0
  19. package/lib/extensions/rulers.d.ts +8 -0
  20. package/lib/extensions/rulers.js +85 -0
  21. package/lib/extensions/rulers.js.map +1 -0
  22. package/lib/extensions/ybinding.d.ts +129 -0
  23. package/lib/extensions/ybinding.js +229 -0
  24. package/lib/extensions/ybinding.js.map +1 -0
  25. package/lib/factory.d.ts +15 -5
  26. package/lib/factory.js +53 -24
  27. package/lib/factory.js.map +1 -1
  28. package/lib/index.d.ts +7 -19
  29. package/lib/index.js +7 -23
  30. package/lib/index.js.map +1 -1
  31. package/lib/language.d.ts +96 -0
  32. package/lib/language.js +1687 -0
  33. package/lib/language.js.map +1 -0
  34. package/lib/mimetype.d.ts +3 -0
  35. package/lib/mimetype.js +16 -5
  36. package/lib/mimetype.js.map +1 -1
  37. package/lib/searchprovider.d.ts +219 -0
  38. package/lib/searchprovider.js +640 -0
  39. package/lib/searchprovider.js.map +1 -0
  40. package/lib/theme.d.ts +57 -0
  41. package/lib/theme.js +194 -0
  42. package/lib/theme.js.map +1 -0
  43. package/lib/token.d.ts +375 -0
  44. package/lib/token.js +18 -0
  45. package/lib/token.js.map +1 -0
  46. package/package.json +45 -41
  47. package/src/codemirror-ipythongfm.ts +109 -0
  48. package/src/commands.ts +34 -0
  49. package/src/editor.ts +783 -0
  50. package/src/extension.ts +890 -0
  51. package/src/extensions/customStyle.ts +79 -0
  52. package/src/extensions/index.ts +8 -0
  53. package/src/extensions/rulers.ts +112 -0
  54. package/src/extensions/ybinding.ts +295 -0
  55. package/src/factory.ts +100 -0
  56. package/src/index.ts +17 -0
  57. package/src/language.ts +1726 -0
  58. package/src/mimetype.ts +54 -0
  59. package/src/searchprovider.ts +813 -0
  60. package/src/theme.ts +219 -0
  61. package/src/token.ts +436 -0
  62. package/src/typings.d.ts +13 -0
  63. package/style/base.css +33 -187
  64. package/style/index.css +1 -7
  65. package/style/index.js +1 -7
  66. package/lib/codemirror-ipython.d.ts +0 -2
  67. package/lib/codemirror-ipython.js +0 -30
  68. package/lib/codemirror-ipython.js.map +0 -1
  69. package/lib/mode.d.ts +0 -82
  70. package/lib/mode.js +0 -150
  71. package/lib/mode.js.map +0 -1
  72. package/lib/syntaxstatus.d.ts +0 -67
  73. package/lib/syntaxstatus.js +0 -140
  74. package/lib/syntaxstatus.js.map +0 -1
  75. package/lib/tokens.d.ts +0 -18
  76. package/lib/tokens.js +0 -8
  77. package/lib/tokens.js.map +0 -1
  78. package/typings/codemirror/codemirror.d.ts +0 -227
@@ -0,0 +1,890 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ import { closeBrackets } from '@codemirror/autocomplete';
5
+ import { defaultKeymap, indentLess } from '@codemirror/commands';
6
+ import {
7
+ bracketMatching,
8
+ foldGutter,
9
+ indentOnInput,
10
+ indentUnit
11
+ } from '@codemirror/language';
12
+ import {
13
+ Compartment,
14
+ EditorState,
15
+ Extension,
16
+ StateEffect
17
+ } from '@codemirror/state';
18
+ import {
19
+ drawSelection,
20
+ EditorView,
21
+ highlightActiveLine,
22
+ highlightTrailingWhitespace,
23
+ highlightWhitespace,
24
+ KeyBinding,
25
+ keymap,
26
+ lineNumbers,
27
+ scrollPastEnd
28
+ } from '@codemirror/view';
29
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
30
+ import { JSONExt, ReadonlyJSONObject } from '@lumino/coreutils';
31
+ import { IObservableDisposable } from '@lumino/disposable';
32
+ import { ISignal, Signal } from '@lumino/signaling';
33
+ import { StateCommands } from './commands';
34
+ import { customTheme, CustomTheme, rulers } from './extensions';
35
+ import {
36
+ IConfigurableExtension,
37
+ IEditorExtensionFactory,
38
+ IEditorExtensionRegistry,
39
+ IEditorThemeRegistry,
40
+ IExtensionsHandler
41
+ } from './token';
42
+
43
+ /**
44
+ * The class name added to read only editor widgets.
45
+ */
46
+ const READ_ONLY_CLASS = 'jp-mod-readOnly';
47
+
48
+ /**
49
+ * Editor configuration handler options
50
+ */
51
+ export interface IEditorHandlerOptions {
52
+ /**
53
+ * The base configuration options for all editor.
54
+ */
55
+ baseConfiguration?: Record<string, any>;
56
+ /**
57
+ * The configuration options for the editor.
58
+ *
59
+ * They take precendence over the base configuration.
60
+ */
61
+ config?: Record<string, any>;
62
+ /**
63
+ * Editor default extensions.
64
+ *
65
+ * Extension defined in the mapping without a base configuration
66
+ * will not be configurable.
67
+ */
68
+ defaultExtensions?: [string, IConfigurableExtension<any>][];
69
+ }
70
+
71
+ /**
72
+ * Editor configuration handler
73
+ *
74
+ * It stores the editor configuration and the editor extensions.
75
+ * It also allows to inject new extensions into an editor.
76
+ */
77
+ export class ExtensionsHandler
78
+ implements IExtensionsHandler, IObservableDisposable
79
+ {
80
+ constructor({
81
+ baseConfiguration,
82
+ config,
83
+ defaultExtensions
84
+ }: IEditorHandlerOptions = {}) {
85
+ this._baseConfig = baseConfiguration ?? {};
86
+ this._config = config ?? {};
87
+
88
+ this._configurableBuilderMap = new Map<string, IConfigurableExtension<any>>(
89
+ defaultExtensions
90
+ );
91
+
92
+ const configurables = Object.keys(this._config).concat(
93
+ Object.keys(this._baseConfig)
94
+ );
95
+ this._immutables = new Set(
96
+ [...this._configurableBuilderMap.keys()].filter(
97
+ key => !configurables.includes(key)
98
+ )
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Signal triggered when the editor configuration changes.
104
+ * It provides the mapping of the new configuration (only those that changed).
105
+ *
106
+ * It should result in a call to `IExtensionsHandler.reconfigureExtensions`.
107
+ */
108
+ get configChanged(): ISignal<this, Record<string, any>> {
109
+ return this._configChanged;
110
+ }
111
+
112
+ /**
113
+ * A signal emitted when the object is disposed.
114
+ */
115
+ get disposed(): ISignal<this, void> {
116
+ return this._disposed;
117
+ }
118
+
119
+ /**
120
+ * Tests whether the object is disposed.
121
+ */
122
+ get isDisposed(): boolean {
123
+ return this._isDisposed;
124
+ }
125
+
126
+ /**
127
+ * Dispose of the resources held by the object.
128
+ */
129
+ dispose(): void {
130
+ if (this.isDisposed) {
131
+ return;
132
+ }
133
+ this._isDisposed = true;
134
+ this._disposed.emit();
135
+ Signal.clearData(this);
136
+ }
137
+
138
+ /**
139
+ * Get a config option for the editor.
140
+ */
141
+ getOption(option: string): unknown {
142
+ return this._config[option] ?? this._baseConfig[option];
143
+ }
144
+
145
+ /**
146
+ * Whether the option exists or not.
147
+ */
148
+ hasOption(option: string): boolean {
149
+ return (
150
+ Object.keys(this._config).includes(option) ||
151
+ Object.keys(this._baseConfig).includes(option)
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Set a config option for the editor.
157
+ *
158
+ * You will need to reconfigure the editor extensions by listening
159
+ * to `IExtensionsHandler.configChanged`.
160
+ */
161
+ setOption(option: string, value: unknown): void {
162
+ // Don't bother setting the option if it is already the same.
163
+ if (this._config[option] !== value) {
164
+ this._config[option] = value;
165
+ this._configChanged.emit({ [option]: value });
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Set a base config option for the editor.
171
+ *
172
+ * You will need to reconfigure the editor extensions by listening
173
+ * to `IExtensionsHandler.configChanged`.
174
+ */
175
+ setBaseOptions(options: Record<string, any>): void {
176
+ const changed = this._getChangedOptions(options, this._baseConfig);
177
+ if (changed.length > 0) {
178
+ this._baseConfig = options;
179
+ const customizedKeys = Object.keys(this._config);
180
+ const notOverridden = changed.filter(k => !customizedKeys.includes(k));
181
+ if (notOverridden.length > 0) {
182
+ this._configChanged.emit(
183
+ notOverridden.reduce<Record<string, any>>((agg, key) => {
184
+ agg[key] = this._baseConfig[key];
185
+ return agg;
186
+ }, {})
187
+ );
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Set config options for the editor.
194
+ *
195
+ * You will need to reconfigure the editor extensions by listening
196
+ * to `EditorHandler.configChanged`.
197
+ *
198
+ * This method is preferred when setting several options. The
199
+ * options are set within an operation, which only performs
200
+ * the costly update at the end, and not after every option
201
+ * is set.
202
+ */
203
+ setOptions(options: Record<string, any>): void {
204
+ const changed = this._getChangedOptions(options, this._config);
205
+ if (changed.length > 0) {
206
+ this._config = { ...options };
207
+
208
+ this._configChanged.emit(
209
+ changed.reduce<Record<string, any>>((agg, key) => {
210
+ agg[key] = this._config[key] ?? this._baseConfig[key];
211
+ return agg;
212
+ }, {})
213
+ );
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Reconfigures the extension mapped with key with the provided value.
219
+ *
220
+ * @param view Editor view
221
+ * @param key Parameter unique key
222
+ * @param value Parameter value to be applied
223
+ */
224
+ reconfigureExtension<T>(view: EditorView, key: string, value: T): void {
225
+ const effect = this.getEffect<T>(view.state, key, value);
226
+
227
+ if (effect) {
228
+ view.dispatch({
229
+ effects: [effect]
230
+ });
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Reconfigures all the extensions mapped with the options from the
236
+ * provided partial configuration.
237
+ *
238
+ * @param view Editor view
239
+ * @param configuration Editor configuration
240
+ */
241
+ reconfigureExtensions(
242
+ view: EditorView,
243
+ configuration: Record<string, any>
244
+ ): void {
245
+ const effects = Object.keys(configuration)
246
+ .filter(key => this.has(key))
247
+ .map(key => this.getEffect(view.state, key, configuration[key]));
248
+
249
+ view.dispatch({
250
+ effects: effects.filter(
251
+ effect => effect !== null
252
+ ) as StateEffect<unknown>[]
253
+ });
254
+ }
255
+
256
+ /**
257
+ * Appends extensions to the top-level configuration of the
258
+ * editor.
259
+ *
260
+ * Injected extension cannot be removed.
261
+ *
262
+ * ### Notes
263
+ * You should prefer registering a IEditorExtensionFactory instead
264
+ * of this feature.
265
+ *
266
+ * @alpha
267
+ * @param view Editor view
268
+ * @param extension Editor extension to inject
269
+ */
270
+ injectExtension(view: EditorView, extension: Extension): void {
271
+ view.dispatch({
272
+ effects: StateEffect.appendConfig.of(extension)
273
+ });
274
+ }
275
+
276
+ /**
277
+ * Returns the list of initial extensions of an editor
278
+ * based on the configuration.
279
+ *
280
+ * @returns The initial editor extensions
281
+ */
282
+ getInitialExtensions(): Extension[] {
283
+ const configuration = { ...this._baseConfig, ...this._config };
284
+
285
+ const extensions = [...this._immutables]
286
+ .map(key => this.get(key)?.instance(undefined))
287
+ .filter(ext => ext) as Extension[];
288
+ for (const k of Object.keys(configuration)) {
289
+ const builder = this.get(k);
290
+ if (builder) {
291
+ const value = configuration[k];
292
+ extensions.push(builder.instance(value));
293
+ }
294
+ }
295
+
296
+ return extensions;
297
+ }
298
+
299
+ /**
300
+ * Get a extension builder
301
+ * @param key Extension unique identifier
302
+ * @returns The extension builder
303
+ */
304
+ protected get(key: string): IConfigurableExtension<any> | undefined {
305
+ return this._configurableBuilderMap.get(key);
306
+ }
307
+
308
+ /**
309
+ * Whether the editor has an extension for the identifier.
310
+ *
311
+ * @param key Extension unique identifier
312
+ * @returns Extension existence
313
+ */
314
+ protected has(key: string): boolean {
315
+ return this._configurableBuilderMap.has(key);
316
+ }
317
+
318
+ protected getEffect<T>(
319
+ state: EditorState,
320
+ key: string,
321
+ value: T
322
+ ): StateEffect<unknown> | null {
323
+ const builder = this.get(key);
324
+ return builder?.reconfigure(value) ?? null;
325
+ }
326
+
327
+ private _getChangedOptions(
328
+ newConfig: Record<string, any>,
329
+ oldConfig: Record<string, any>
330
+ ): string[] {
331
+ const changed = new Array<string>();
332
+ const newKeys = new Array<string>();
333
+ for (const [key, value] of Object.entries(newConfig)) {
334
+ newKeys.push(key);
335
+ if (oldConfig[key] !== value) {
336
+ changed.push(key);
337
+ }
338
+ }
339
+ // Add removed old keys
340
+ changed.push(...Object.keys(oldConfig).filter(k => !newKeys.includes(k)));
341
+
342
+ return changed;
343
+ }
344
+
345
+ private _baseConfig: Record<string, any>;
346
+ private _config: Record<string, any>;
347
+ private _configChanged = new Signal<this, Record<string, any>>(this);
348
+ private _configurableBuilderMap: Map<string, IConfigurableExtension<any>>;
349
+ private _disposed = new Signal<this, void>(this);
350
+ private _isDisposed = false;
351
+ private _immutables = new Set<string>();
352
+ }
353
+
354
+ /**
355
+ * CodeMirror extensions registry
356
+ */
357
+ export class EditorExtensionRegistry implements IEditorExtensionRegistry {
358
+ /**
359
+ * Base editor configuration
360
+ *
361
+ * This is the default configuration optionally modified by the user;
362
+ * e.g. through user settings.
363
+ */
364
+ get baseConfiguration(): Record<string, any> {
365
+ return { ...this.defaultOptions, ...this._baseConfiguration };
366
+ }
367
+ set baseConfiguration(v: Record<string, any>) {
368
+ if (!JSONExt.deepEqual(v, this._baseConfiguration)) {
369
+ this._baseConfiguration = v;
370
+ for (const handler of this.handlers) {
371
+ handler.setBaseOptions(this.baseConfiguration);
372
+ }
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Default editor configuration
378
+ *
379
+ * This is the default configuration as defined when extensions
380
+ * are registered.
381
+ */
382
+ get defaultConfiguration(): Record<string, any> {
383
+ // Only options with schema should be JSON serializable
384
+ // So we cannot use `JSONExt.deepCopy` on the default options.
385
+ return Object.freeze({ ...this.defaultOptions });
386
+ }
387
+
388
+ /**
389
+ * Editor configuration JSON schema
390
+ */
391
+ get settingsSchema(): ReadonlyJSONObject {
392
+ return Object.freeze(JSONExt.deepCopy(this.configurationSchema));
393
+ }
394
+
395
+ /**
396
+ * Add a default editor extension
397
+ *
398
+ * @template T Extension parameter type
399
+ * @param factory Extension factory
400
+ */
401
+ addExtension<T>(factory: IEditorExtensionFactory<T>): void {
402
+ if (this.configurationBuilder.has(factory.name)) {
403
+ throw new Error(`Extension named ${factory.name} is already registered.`);
404
+ }
405
+
406
+ this.configurationBuilder.set(factory.name, factory);
407
+ if (typeof factory.default != 'undefined') {
408
+ this.defaultOptions[factory.name] = factory.default;
409
+ }
410
+ if (factory.schema) {
411
+ this.configurationSchema[factory.name] = {
412
+ default: factory.default ?? null,
413
+ ...factory.schema
414
+ };
415
+ this.defaultOptions[factory.name] =
416
+ this.configurationSchema[factory.name].default;
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Create a new extensions handler for an editor
422
+ *
423
+ * @param options Extensions options and initial editor configuration
424
+ */
425
+ createNew(
426
+ options: IEditorExtensionFactory.IOptions & {
427
+ /**
428
+ * The configuration options for the editor.
429
+ */
430
+ config?: Record<string, any>;
431
+ }
432
+ ): IExtensionsHandler {
433
+ const configuration = new Array<[string, IConfigurableExtension<any>]>();
434
+ for (const [key, builder] of this.configurationBuilder.entries()) {
435
+ const extension = builder.factory(options);
436
+ if (extension) {
437
+ configuration.push([key, extension]);
438
+ }
439
+ }
440
+
441
+ const handler = new ExtensionsHandler({
442
+ baseConfiguration: this.baseConfiguration,
443
+ config: options.config,
444
+ defaultExtensions: configuration
445
+ });
446
+
447
+ this.handlers.add(handler);
448
+ handler.disposed.connect(() => {
449
+ this.handlers.delete(handler);
450
+ });
451
+
452
+ return handler;
453
+ }
454
+ protected configurationBuilder = new Map<
455
+ string,
456
+ IEditorExtensionFactory<any>
457
+ >();
458
+ protected configurationSchema: Record<string, any> = {};
459
+
460
+ protected defaultOptions: Record<string, any> = {};
461
+ protected handlers = new Set<ExtensionsHandler>();
462
+ protected immutableExtensions = new Set<string>();
463
+ private _baseConfiguration: Record<string, any> = {};
464
+ }
465
+
466
+ /**
467
+ * Editor extension registry namespace
468
+ */
469
+ export namespace EditorExtensionRegistry {
470
+ /**
471
+ * Dynamically configurable editor extension.
472
+ */
473
+ class ConfigurableExtension<T> implements IConfigurableExtension<T> {
474
+ /**
475
+ * Create a dynamic editor extension.
476
+ *
477
+ * @param builder Extension builder
478
+ */
479
+ constructor(builder: (value: T) => Extension) {
480
+ this._compartment = new Compartment();
481
+ this._builder = builder;
482
+ }
483
+
484
+ /**
485
+ * Create an editor extension for the provided value.
486
+ *
487
+ * @param value Editor extension parameter value
488
+ * @returns The editor extension
489
+ */
490
+ instance(value: T): Extension {
491
+ return this._compartment.of(this._builder(value));
492
+ }
493
+
494
+ /**
495
+ * Reconfigure an editor extension.
496
+ *
497
+ * @param value Editor extension value
498
+ * @returns Editor state effect
499
+ */
500
+ reconfigure(value: T): StateEffect<T> {
501
+ return this._compartment.reconfigure(
502
+ this._builder(value)
503
+ ) as StateEffect<T>;
504
+ }
505
+
506
+ private _compartment: Compartment;
507
+ private _builder: (value: T) => Extension;
508
+ }
509
+
510
+ /**
511
+ * Immutable editor extension class
512
+ */
513
+ class ImmutableExtension implements IConfigurableExtension<undefined> {
514
+ /**
515
+ * Create an immutable editor extension.
516
+ *
517
+ * @param extension Extension
518
+ */
519
+ constructor(extension: Extension) {
520
+ this._extension = extension;
521
+ }
522
+
523
+ /**
524
+ * Create an editor extension.
525
+ *
526
+ * @returns The editor extension
527
+ */
528
+ instance(): Extension {
529
+ return this._extension;
530
+ }
531
+
532
+ /**
533
+ * Reconfigure an editor extension.
534
+ *
535
+ * This is a no-op
536
+ */
537
+ reconfigure(): null {
538
+ // This is a no-op
539
+ return null;
540
+ }
541
+
542
+ private _extension: Extension;
543
+ }
544
+
545
+ /**
546
+ * Creates a dynamically configurable editor extension.
547
+ *
548
+ * @param builder Extension builder
549
+ * @return The extension
550
+ */
551
+ export function createConfigurableExtension<T>(
552
+ builder: (value: T) => Extension
553
+ ): IConfigurableExtension<T> {
554
+ return new ConfigurableExtension<T>(builder);
555
+ }
556
+
557
+ /**
558
+ * Creates a configurable extension returning
559
+ * one of two extensions depending on a boolean value.
560
+ *
561
+ * @param truthy Extension to apply when the parameter is true
562
+ * @param falsy Extension to apply when the parameter is false
563
+ * @return The extension
564
+ */
565
+ export function createConditionalExtension(
566
+ truthy: Extension,
567
+ falsy: Extension = []
568
+ ): IConfigurableExtension<boolean> {
569
+ return new ConfigurableExtension<boolean>(value =>
570
+ value ? truthy : falsy
571
+ );
572
+ }
573
+
574
+ /**
575
+ * Creates an immutable extension.
576
+ *
577
+ * @param extension Immutable extension
578
+ * @return The extension
579
+ */
580
+ export function createImmutableExtension(
581
+ extension: Extension
582
+ ): IConfigurableExtension<undefined> {
583
+ return new ImmutableExtension(extension);
584
+ }
585
+
586
+ /**
587
+ * Get the default editor extensions.
588
+ *
589
+ * @returns CodeMirror 6 extension factories
590
+ */
591
+ export function getDefaultExtensions(
592
+ options: {
593
+ themes?: IEditorThemeRegistry;
594
+ translator?: ITranslator | null;
595
+ } = {}
596
+ ): ReadonlyArray<Readonly<IEditorExtensionFactory<any>>> {
597
+ const { themes, translator } = options;
598
+ const trans = (translator ?? nullTranslator).load('jupyter');
599
+ const extensions: IEditorExtensionFactory<any>[] = [
600
+ Object.freeze({
601
+ name: 'autoClosingBrackets',
602
+ default: false,
603
+ factory: () => createConditionalExtension(closeBrackets()),
604
+ schema: {
605
+ type: 'boolean',
606
+ title: trans.__('Auto Closing Brackets')
607
+ }
608
+ }),
609
+ Object.freeze({
610
+ name: 'codeFolding',
611
+ default: false,
612
+ factory: () => createConditionalExtension(foldGutter()),
613
+ schema: {
614
+ type: 'boolean',
615
+ title: trans.__('Code Folding')
616
+ }
617
+ }),
618
+ Object.freeze({
619
+ name: 'cursorBlinkRate',
620
+ default: 1200,
621
+ factory: () =>
622
+ createConfigurableExtension((value: number) =>
623
+ drawSelection({ cursorBlinkRate: value })
624
+ ),
625
+ schema: {
626
+ type: 'number',
627
+ title: trans.__('Cursor blinking rate'),
628
+ description: trans.__(
629
+ 'Half-period in milliseconds used for cursor blinking. The default blink rate is 1200ms. By setting this to zero, blinking can be disabled.'
630
+ )
631
+ }
632
+ }),
633
+ Object.freeze({
634
+ name: 'highlightActiveLine',
635
+ default: false,
636
+ factory: () => createConditionalExtension(highlightActiveLine()),
637
+ schema: {
638
+ type: 'boolean',
639
+ title: trans.__('Highlight the active line')
640
+ }
641
+ }),
642
+ Object.freeze({
643
+ name: 'highlightTrailingWhitespace',
644
+ default: false,
645
+ factory: () =>
646
+ createConditionalExtension(highlightTrailingWhitespace()),
647
+ schema: {
648
+ type: 'boolean',
649
+ title: trans.__('Highlight trailing white spaces')
650
+ }
651
+ }),
652
+ Object.freeze({
653
+ name: 'highlightWhitespace',
654
+ default: false,
655
+ factory: () => createConditionalExtension(highlightWhitespace()),
656
+ schema: {
657
+ type: 'boolean',
658
+ title: trans.__('Highlight white spaces')
659
+ }
660
+ }),
661
+ Object.freeze({
662
+ name: 'indentUnit',
663
+ default: '2',
664
+ factory: () =>
665
+ createConfigurableExtension<string>((value: string) =>
666
+ value == 'Tab'
667
+ ? indentUnit.of('\t')
668
+ : indentUnit.of(' '.repeat(parseInt(value, 10)))
669
+ ),
670
+ schema: {
671
+ type: 'string',
672
+ title: trans.__('Indentation unit'),
673
+ description: trans.__(
674
+ 'The indentation is a `Tab` or the number of spaces. This defaults to 2 spaces.'
675
+ ),
676
+ enum: ['Tab', '1', '2', '4', '8']
677
+ }
678
+ }),
679
+ // Default keyboard shortcuts
680
+ // TODO at some point we may want to get this configurable
681
+ Object.freeze({
682
+ name: 'keymap',
683
+ default: [
684
+ ...defaultKeymap,
685
+ {
686
+ key: 'Tab',
687
+ run: StateCommands.indentMoreOrInsertTab,
688
+ shift: indentLess
689
+ }
690
+ ],
691
+ factory: () =>
692
+ createConfigurableExtension<KeyBinding[]>(value => keymap.of(value))
693
+ }),
694
+ Object.freeze({
695
+ name: 'lineNumbers',
696
+ default: true,
697
+ factory: () => createConditionalExtension(lineNumbers()),
698
+ schema: {
699
+ type: 'boolean',
700
+ title: trans.__('Line Numbers')
701
+ }
702
+ }),
703
+ Object.freeze({
704
+ name: 'lineWrap',
705
+ factory: () => createConditionalExtension(EditorView.lineWrapping),
706
+ default: true,
707
+ schema: {
708
+ type: 'boolean',
709
+ title: trans.__('Line Wrap')
710
+ }
711
+ }),
712
+ Object.freeze({
713
+ name: 'matchBrackets',
714
+ default: true,
715
+ factory: () => createConditionalExtension(bracketMatching()),
716
+ schema: {
717
+ type: 'boolean',
718
+ title: trans.__('Match Brackets')
719
+ }
720
+ }),
721
+ Object.freeze({
722
+ name: 'readOnly',
723
+ default: false,
724
+ factory: () =>
725
+ createConfigurableExtension((value: boolean) => [
726
+ EditorState.readOnly.of(value),
727
+ value
728
+ ? EditorView.editorAttributes.of({ class: READ_ONLY_CLASS })
729
+ : []
730
+ ])
731
+ }),
732
+ Object.freeze({
733
+ name: 'rulers',
734
+ default: [],
735
+ factory: () =>
736
+ createConfigurableExtension((value: number[]) =>
737
+ value.length > 0 ? rulers(value) : []
738
+ ),
739
+ schema: {
740
+ type: 'array',
741
+ title: trans.__('Rulers'),
742
+ items: {
743
+ type: 'number',
744
+ minimum: 0
745
+ }
746
+ }
747
+ }),
748
+ Object.freeze({
749
+ name: 'scrollPastEnd',
750
+ default: false,
751
+ factory: (options: IEditorExtensionFactory.IOptions) =>
752
+ options.inline ? null : createConditionalExtension(scrollPastEnd())
753
+ }),
754
+ Object.freeze({
755
+ name: 'smartIndent',
756
+ default: true,
757
+ factory: () => createConditionalExtension(indentOnInput()),
758
+ schema: {
759
+ type: 'boolean',
760
+ title: trans.__('Smart Indentation')
761
+ }
762
+ }),
763
+ Object.freeze({
764
+ name: 'tabSize',
765
+ default: 4,
766
+ factory: () =>
767
+ createConfigurableExtension((value: number) =>
768
+ EditorState.tabSize.of(value)
769
+ ),
770
+ schema: {
771
+ type: 'number',
772
+ title: trans.__('Tab size')
773
+ }
774
+ }),
775
+ Object.freeze({
776
+ name: 'allowMultipleSelections',
777
+ default: true,
778
+ factory: () =>
779
+ createConfigurableExtension((value: boolean) =>
780
+ EditorState.allowMultipleSelections.of(value)
781
+ ),
782
+ schema: {
783
+ type: 'boolean',
784
+ title: trans.__('Multiple selections')
785
+ }
786
+ }),
787
+ Object.freeze({
788
+ name: 'customStyles',
789
+ factory: () =>
790
+ createConfigurableExtension<CustomTheme>(config =>
791
+ customTheme(config)
792
+ ),
793
+ default: {
794
+ fontFamily: null,
795
+ fontSize: null,
796
+ lineHeight: null
797
+ },
798
+ schema: {
799
+ title: trans.__('Custom editor styles'),
800
+ type: 'object',
801
+ properties: {
802
+ fontFamily: {
803
+ type: ['string', 'null'],
804
+ title: trans.__('Font Family')
805
+ },
806
+ fontSize: {
807
+ type: ['number', 'null'],
808
+ minimum: 1,
809
+ maximum: 100,
810
+ title: trans.__('Font Size')
811
+ },
812
+ lineHeight: {
813
+ type: ['number', 'null'],
814
+ title: trans.__('Line Height')
815
+ }
816
+ },
817
+ additionalProperties: false
818
+ }
819
+ })
820
+ ];
821
+
822
+ if (themes) {
823
+ extensions.push(
824
+ Object.freeze({
825
+ name: 'theme',
826
+ default: 'jupyter',
827
+ factory: () =>
828
+ createConfigurableExtension<string>(value =>
829
+ themes.getTheme(value)
830
+ ),
831
+ schema: {
832
+ type: 'string',
833
+ title: trans.__('Theme'),
834
+ description: trans.__('CodeMirror theme')
835
+ }
836
+ })
837
+ );
838
+ }
839
+
840
+ if (translator) {
841
+ extensions.push(
842
+ Object.freeze({
843
+ name: 'translation',
844
+ // The list of internal strings is available at https://codemirror.net/examples/translate/
845
+ default: {
846
+ // @codemirror/view
847
+ 'Control character': trans.__('Control character'),
848
+ // @codemirror/commands
849
+ 'Selection deleted': trans.__('Selection deleted'),
850
+ // @codemirror/language
851
+ 'Folded lines': trans.__('Folded lines'),
852
+ 'Unfolded lines': trans.__('Unfolded lines'),
853
+ to: trans.__('to'),
854
+ 'folded code': trans.__('folded code'),
855
+ unfold: trans.__('unfold'),
856
+ 'Fold line': trans.__('Fold line'),
857
+ 'Unfold line': trans.__('Unfold line'),
858
+ // @codemirror/search
859
+ 'Go to line': trans.__('Go to line'),
860
+ go: trans.__('go'),
861
+ Find: trans.__('Find'),
862
+ Replace: trans.__('Replace'),
863
+ next: trans.__('next'),
864
+ previous: trans.__('previous'),
865
+ all: trans.__('all'),
866
+ 'match case': trans.__('match case'),
867
+ replace: trans.__('replace'),
868
+ 'replace all': trans.__('replace all'),
869
+ close: trans.__('close'),
870
+ 'current match': trans.__('current match'),
871
+ 'replaced $ matches': trans.__('replaced $ matches'),
872
+ 'replaced match on line $': trans.__('replaced match on line $'),
873
+ 'on line': trans.__('on line'),
874
+ // @codemirror/autocomplete
875
+ Completions: trans.__('Completions'),
876
+ // @codemirror/lint
877
+ Diagnostics: trans.__('Diagnostics'),
878
+ 'No diagnostics': trans.__('No diagnostics')
879
+ },
880
+ factory: () =>
881
+ createConfigurableExtension<Record<string, string>>(value =>
882
+ EditorState.phrases.of(value)
883
+ )
884
+ })
885
+ );
886
+ }
887
+
888
+ return extensions;
889
+ }
890
+ }