0
# Extension System
1
2
The extension system provides a comprehensive registry and configuration management for 25+ configurable extensions that customize editor behavior and appearance.
3
4
## Capabilities
5
6
### EditorExtensionRegistry
7
8
Main registry for managing editor extensions and their configurations.
9
10
```typescript { .api }
11
/**
12
* CodeMirror extensions registry
13
* Manages 25+ configurable extensions for editor customization
14
*/
15
class EditorExtensionRegistry implements IEditorExtensionRegistry {
16
constructor();
17
18
/**
19
* Base editor configuration (default config optionally modified by user)
20
*/
21
baseConfiguration: Record<string, any>;
22
23
/**
24
* Default editor configuration as defined when extensions are registered
25
*/
26
readonly defaultConfiguration: Record<string, any>;
27
28
/**
29
* Editor configuration JSON schema for validation
30
*/
31
readonly settingsSchema: ReadonlyJSONObject;
32
33
/**
34
* Add a default editor extension
35
*/
36
addExtension<T>(factory: IEditorExtensionFactory<T>): void;
37
38
/**
39
* Create a new extensions handler for an editor
40
*/
41
createNew(options: IEditorHandlerOptions): IExtensionsHandler;
42
}
43
44
interface IEditorExtensionRegistry {
45
readonly baseConfiguration: Record<string, any>;
46
addExtension<T>(factory: IEditorExtensionFactory<T>): void;
47
createNew(options: IEditorHandlerOptions): IExtensionsHandler;
48
}
49
```
50
51
**Usage Examples:**
52
53
```typescript
54
import { EditorExtensionRegistry } from "@jupyterlab/codemirror";
55
56
// Create extension registry
57
const registry = new EditorExtensionRegistry();
58
59
// Add custom extension
60
registry.addExtension({
61
name: 'myExtension',
62
factory: (options) => ({
63
instance: (value) => myCustomExtension(value),
64
reconfigure: (value) => myExtension.reconfigure(value)
65
}),
66
default: true,
67
schema: {
68
type: 'boolean',
69
description: 'Enable my custom extension'
70
}
71
});
72
73
// Create extensions handler for editor
74
const handler = registry.createNew({
75
baseConfiguration: { lineNumbers: true },
76
config: { myExtension: true }
77
});
78
```
79
80
### ExtensionsHandler
81
82
Manages individual editor configuration and extension lifecycle.
83
84
```typescript { .api }
85
/**
86
* Editor configuration handler
87
* Stores editor configuration and manages extension reconfiguration
88
*/
89
class ExtensionsHandler implements IExtensionsHandler, IObservableDisposable {
90
constructor(options?: IEditorHandlerOptions);
91
92
/**
93
* Signal triggered when editor configuration changes
94
*/
95
readonly configChanged: ISignal<this, Record<string, any>>;
96
97
/**
98
* Signal emitted when object is disposed
99
*/
100
readonly disposed: ISignal<this, void>;
101
102
/**
103
* Whether the object is disposed
104
*/
105
readonly isDisposed: boolean;
106
107
// Lifecycle
108
dispose(): void;
109
110
// Configuration management
111
getOption(option: string): unknown;
112
hasOption(option: string): boolean;
113
setOption(option: string, value: unknown): void;
114
setBaseOptions(options: Record<string, any>): void;
115
setOptions(options: Record<string, any>): void;
116
117
// Extension management
118
reconfigureExtension<T>(view: EditorView, key: string, value: T): void;
119
reconfigureExtensions(view: EditorView, configuration: Record<string, any>): void;
120
injectExtension(view: EditorView, extension: Extension): void;
121
getInitialExtensions(): Extension[];
122
}
123
124
interface IEditorHandlerOptions {
125
baseConfiguration?: Record<string, any>;
126
config?: Record<string, any>;
127
defaultExtensions?: [string, IConfigurableExtension<any>][];
128
}
129
```
130
131
**Usage Examples:**
132
133
```typescript
134
import { ExtensionsHandler } from "@jupyterlab/codemirror";
135
import { EditorView } from "@codemirror/view";
136
137
// Create handler with configuration
138
const handler = new ExtensionsHandler({
139
baseConfiguration: {
140
lineNumbers: true,
141
tabSize: 4
142
},
143
config: {
144
lineWrap: true,
145
theme: 'dark'
146
}
147
});
148
149
// Listen for configuration changes
150
handler.configChanged.connect((sender, changes) => {
151
console.log('Configuration changed:', changes);
152
});
153
154
// Update single option
155
handler.setOption('fontSize', 14);
156
157
// Update multiple options
158
handler.setOptions({
159
lineHeight: 1.5,
160
fontFamily: 'Monaco'
161
});
162
163
// Get current option value
164
const lineNumbers = handler.getOption('lineNumbers'); // true
165
166
// Reconfigure extensions on editor view
167
const view = new EditorView({...});
168
handler.reconfigureExtensions(view, {
169
lineNumbers: false,
170
lineWrap: false
171
});
172
```
173
174
### Default Extensions
175
176
The registry comes with 25+ built-in extensions for comprehensive editor customization.
177
178
```typescript { .api }
179
/**
180
* Get default editor extensions
181
* Returns array of 25+ configurable extension factories
182
*/
183
static getDefaultExtensions(options?: {
184
themes?: IEditorThemeRegistry;
185
translator?: ITranslator | null;
186
}): ReadonlyArray<Readonly<IEditorExtensionFactory<any>>>;
187
```
188
189
**Built-in Extensions:**
190
191
1. **autoClosingBrackets** - Automatically close brackets and quotes
192
2. **codeFolding** - Code folding functionality
193
3. **cursorBlinkRate** - Cursor blinking rate configuration
194
4. **highlightActiveLine** - Highlight the current line
195
5. **highlightSpecialCharacters** - Highlight special/invisible characters
196
6. **highlightTrailingWhitespace** - Highlight trailing whitespace
197
7. **highlightWhitespace** - Highlight all whitespace characters
198
8. **indentUnit** - Indentation unit size
199
9. **keymap** - Keyboard shortcuts and key bindings
200
10. **lineNumbers** - Line number display
201
11. **lineWrap** - Line wrapping behavior
202
12. **dropCursor** - Drop cursor for drag and drop
203
13. **matchBrackets** - Bracket matching
204
14. **rectangularSelection** - Rectangular/block selection
205
15. **readOnly** - Read-only mode
206
16. **rulers** - Ruler lines at specified columns
207
17. **extendSelection** - Selection extension behavior
208
18. **searchWithCM** - Native CodeMirror search panel
209
19. **scrollPastEnd** - Allow scrolling past document end
210
20. **smartIndent** - Smart indentation
211
21. **tabFocusable** - Tab key focus behavior
212
22. **tabSize** - Tab size configuration
213
23. **tooltips** - Tooltip support
214
24. **allowMultipleSelections** - Multiple cursor support
215
25. **customStyles** - Custom editor styling
216
26. **theme** - Theme support (when theme registry provided)
217
27. **translation** - Internationalization (when translator provided)
218
219
**Usage Examples:**
220
221
```typescript
222
import { EditorExtensionRegistry } from "@jupyterlab/codemirror";
223
224
// Get default extensions
225
const extensions = EditorExtensionRegistry.getDefaultExtensions();
226
227
// Get extensions with theme and translation support
228
const extensionsWithThemes = EditorExtensionRegistry.getDefaultExtensions({
229
themes: themeRegistry,
230
translator: app.translator
231
});
232
233
// Create registry with default extensions
234
const registry = new EditorExtensionRegistry();
235
extensions.forEach(extension => registry.addExtension(extension));
236
```
237
238
### Extension Factory Interface
239
240
Interface for creating configurable extensions.
241
242
```typescript { .api }
243
/**
244
* Editor extension factory interface
245
*/
246
interface IEditorExtensionFactory<T> {
247
/**
248
* Extension unique identifier
249
*/
250
readonly name: string;
251
252
/**
253
* Extension factory function
254
*/
255
readonly factory: (options: IEditorExtensionFactory.IOptions) => IConfigurableExtension<T> | null;
256
257
/**
258
* Extension default value
259
*/
260
readonly default?: T;
261
262
/**
263
* JSON schema for configurable option
264
*/
265
readonly schema?: ReadonlyJSONObject;
266
}
267
268
interface IEditorExtensionFactory.IOptions {
269
inline: boolean;
270
model: CodeEditor.IModel;
271
}
272
273
/**
274
* Dynamically configurable editor extension
275
*/
276
interface IConfigurableExtension<T> {
277
/**
278
* Create an editor extension for the provided value
279
*/
280
instance(value: T): Extension;
281
282
/**
283
* Reconfigure an editor extension with new value
284
*/
285
reconfigure(value: T): StateEffect<T> | null;
286
}
287
```
288
289
### Extension Creation Utilities
290
291
Helper functions for creating different types of extensions.
292
293
```typescript { .api }
294
/**
295
* Creates a dynamically configurable editor extension
296
*/
297
static createConfigurableExtension<T>(
298
builder: (value: T) => Extension
299
): IConfigurableExtension<T>;
300
301
/**
302
* Creates a configurable extension returning one of two extensions
303
* based on a boolean value
304
*/
305
static createConditionalExtension(
306
truthy: Extension,
307
falsy?: Extension
308
): IConfigurableExtension<boolean>;
309
310
/**
311
* Creates an immutable extension (cannot be reconfigured)
312
*/
313
static createImmutableExtension(
314
extension: Extension
315
): IConfigurableExtension<undefined>;
316
```
317
318
**Usage Examples:**
319
320
```typescript
321
import { EditorExtensionRegistry } from "@jupyterlab/codemirror";
322
import { lineNumbers } from "@codemirror/view";
323
import { keymap } from "@codemirror/view";
324
import { defaultKeymap } from "@codemirror/commands";
325
326
// Create configurable extension
327
const configurableLineNumbers = EditorExtensionRegistry.createConfigurableExtension<boolean>(
328
(show) => show ? lineNumbers() : []
329
);
330
331
// Create conditional extension
332
const conditionalKeymap = EditorExtensionRegistry.createConditionalExtension(
333
keymap.of(defaultKeymap),
334
[] // No keymap when false
335
);
336
337
// Create immutable extension
338
const immutableTheme = EditorExtensionRegistry.createImmutableExtension(
339
EditorView.theme({
340
'.cm-editor': { fontSize: '14px' }
341
})
342
);
343
344
// Add to registry
345
registry.addExtension({
346
name: 'myLineNumbers',
347
factory: () => configurableLineNumbers,
348
default: true
349
});
350
```
351
352
### Advanced Configuration
353
354
Complex configuration scenarios and extension interactions.
355
356
```typescript
357
// Create handler with complex configuration
358
const handler = new ExtensionsHandler({
359
baseConfiguration: {
360
// Base settings applied to all editors
361
tabSize: 4,
362
indentUnit: ' ',
363
lineNumbers: true
364
},
365
config: {
366
// Specific settings for this editor
367
theme: 'jupyterlab',
368
lineWrap: true,
369
rulers: [80, 120]
370
},
371
defaultExtensions: [
372
// Custom extensions specific to this handler
373
['myCustomExtension', myCustomExtensionFactory.factory()]
374
]
375
});
376
377
// Dynamic reconfiguration based on context
378
handler.configChanged.connect((sender, changes) => {
379
if ('theme' in changes) {
380
// React to theme changes
381
updateEditorStyling(changes.theme);
382
}
383
384
if ('language' in changes) {
385
// Update language-specific extensions
386
handler.setOptions({
387
pythonBuiltin: changes.language === 'python',
388
jsxHighlighting: changes.language === 'jsx'
389
});
390
}
391
});
392
393
// Conditional extension loading
394
if (isCollaborativeMode) {
395
handler.setOptions({
396
yBinding: true,
397
collaborativeCursors: true,
398
conflictResolution: 'merge'
399
});
400
}
401
```
402
403
## Types
404
405
```typescript { .api }
406
interface IExtensionsHandler extends IDisposable {
407
readonly configChanged: ISignal<this, Record<string, any>>;
408
409
getOption(option: string): unknown;
410
hasOption(option: string): boolean;
411
setOption(option: string, value: unknown): void;
412
setBaseOptions(options: Record<string, any>): void;
413
setOptions(options: Record<string, any>): void;
414
reconfigureExtension<T>(view: EditorView, key: string, value: T): void;
415
reconfigureExtensions(view: EditorView, configuration: Record<string, any>): void;
416
injectExtension(view: EditorView, extension: Extension): void;
417
getInitialExtensions(): Extension[];
418
}
419
420
interface IEditorHandlerOptions {
421
baseConfiguration?: Record<string, any>;
422
config?: Record<string, any>;
423
defaultExtensions?: [string, IConfigurableExtension<any>][];
424
}
425
```