@jupyterlab/fileeditor-extension 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.
package/src/index.ts ADDED
@@ -0,0 +1,673 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+ /**
4
+ * @packageDocumentation
5
+ * @module fileeditor-extension
6
+ */
7
+
8
+ import {
9
+ ILayoutRestorer,
10
+ JupyterFrontEnd,
11
+ JupyterFrontEndPlugin
12
+ } from '@jupyterlab/application';
13
+ import {
14
+ createToolbarFactory,
15
+ ICommandPalette,
16
+ ISanitizer,
17
+ ISessionContextDialogs,
18
+ IToolbarWidgetRegistry,
19
+ MainAreaWidget,
20
+ Sanitizer,
21
+ SessionContextDialogs,
22
+ WidgetTracker
23
+ } from '@jupyterlab/apputils';
24
+ import {
25
+ CodeViewerWidget,
26
+ IEditorServices,
27
+ IPositionModel
28
+ } from '@jupyterlab/codeeditor';
29
+ import {
30
+ IEditorExtensionRegistry,
31
+ IEditorLanguageRegistry,
32
+ IEditorThemeRegistry
33
+ } from '@jupyterlab/codemirror';
34
+ import { ICompletionProviderManager } from '@jupyterlab/completer';
35
+ import { IConsoleTracker } from '@jupyterlab/console';
36
+ import { DocumentRegistry, IDocumentWidget } from '@jupyterlab/docregistry';
37
+ import { ISearchProviderRegistry } from '@jupyterlab/documentsearch';
38
+ import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
39
+ import {
40
+ FileEditor,
41
+ FileEditorAdapter,
42
+ FileEditorFactory,
43
+ FileEditorSearchProvider,
44
+ IEditorTracker,
45
+ LaTeXTableOfContentsFactory,
46
+ MarkdownTableOfContentsFactory,
47
+ PythonTableOfContentsFactory,
48
+ TabSpaceStatus
49
+ } from '@jupyterlab/fileeditor';
50
+ import { ILauncher } from '@jupyterlab/launcher';
51
+ import {
52
+ ILSPCodeExtractorsManager,
53
+ ILSPDocumentConnectionManager,
54
+ ILSPFeatureManager
55
+ } from '@jupyterlab/lsp';
56
+ import { IMainMenu } from '@jupyterlab/mainmenu';
57
+ import { IObservableList } from '@jupyterlab/observables';
58
+ import { IRenderMime } from '@jupyterlab/rendermime-interfaces';
59
+ import { Session } from '@jupyterlab/services';
60
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
61
+ import { IStatusBar } from '@jupyterlab/statusbar';
62
+ import { ITableOfContentsRegistry } from '@jupyterlab/toc';
63
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
64
+ import { IFormRendererRegistry, MenuSvg } from '@jupyterlab/ui-components';
65
+ import { find } from '@lumino/algorithm';
66
+ import { JSONObject } from '@lumino/coreutils';
67
+ import { Widget } from '@lumino/widgets';
68
+
69
+ import { CommandIDs, Commands, FACTORY, IFileTypeData } from './commands';
70
+ import { editorSyntaxStatus } from './syntaxstatus';
71
+
72
+ export { Commands } from './commands';
73
+
74
+ /**
75
+ * The editor tracker extension.
76
+ */
77
+ const plugin: JupyterFrontEndPlugin<IEditorTracker> = {
78
+ activate,
79
+ id: '@jupyterlab/fileeditor-extension:plugin',
80
+ requires: [
81
+ IEditorServices,
82
+ IEditorExtensionRegistry,
83
+ IEditorLanguageRegistry,
84
+ IEditorThemeRegistry,
85
+ IDefaultFileBrowser,
86
+ ISettingRegistry
87
+ ],
88
+ optional: [
89
+ IConsoleTracker,
90
+ ICommandPalette,
91
+ ILauncher,
92
+ IMainMenu,
93
+ ILayoutRestorer,
94
+ ISessionContextDialogs,
95
+ ITableOfContentsRegistry,
96
+ IToolbarWidgetRegistry,
97
+ ITranslator,
98
+ IFormRendererRegistry
99
+ ],
100
+ provides: IEditorTracker,
101
+ autoStart: true
102
+ };
103
+
104
+ /**
105
+ * A plugin that provides a status item allowing the user to
106
+ * switch tabs vs spaces and tab widths for text editors.
107
+ */
108
+ export const tabSpaceStatus: JupyterFrontEndPlugin<void> = {
109
+ id: '@jupyterlab/fileeditor-extension:tab-space-status',
110
+ autoStart: true,
111
+ requires: [
112
+ IEditorTracker,
113
+ IEditorExtensionRegistry,
114
+ ISettingRegistry,
115
+ ITranslator
116
+ ],
117
+ optional: [IStatusBar],
118
+ activate: (
119
+ app: JupyterFrontEnd,
120
+ editorTracker: IEditorTracker,
121
+ extensions: IEditorExtensionRegistry,
122
+ settingRegistry: ISettingRegistry,
123
+ translator: ITranslator,
124
+ statusBar: IStatusBar | null
125
+ ) => {
126
+ const trans = translator.load('jupyterlab');
127
+ if (!statusBar) {
128
+ // Automatically disable if statusbar missing
129
+ return;
130
+ }
131
+ // Create a menu for switching tabs vs spaces.
132
+ const menu = new MenuSvg({ commands: app.commands });
133
+ const command = 'fileeditor:change-tabs';
134
+ const { shell } = app;
135
+ const args: JSONObject = {
136
+ name: trans.__('Indent with Tab')
137
+ };
138
+ menu.addItem({ command, args });
139
+ for (const size of ['1', '2', '4', '8']) {
140
+ const args: JSONObject = {
141
+ size,
142
+ name: trans.__('Spaces: %1', size)
143
+ };
144
+ menu.addItem({ command, args });
145
+ }
146
+
147
+ // Create the status item.
148
+ const item = new TabSpaceStatus({ menu, translator });
149
+
150
+ // Keep a reference to the code editor config from the settings system.
151
+ const updateIndentUnit = (settings: ISettingRegistry.ISettings): void => {
152
+ item.model!.indentUnit =
153
+ (settings.get('editorConfig').composite as any)?.indentUnit ??
154
+ extensions.baseConfiguration.indentUnit ??
155
+ null;
156
+ };
157
+
158
+ void Promise.all([
159
+ settingRegistry.load('@jupyterlab/fileeditor-extension:plugin'),
160
+ app.restored
161
+ ]).then(([settings]) => {
162
+ updateIndentUnit(settings);
163
+ settings.changed.connect(updateIndentUnit);
164
+ });
165
+
166
+ // Add the status item.
167
+ statusBar.registerStatusItem(
168
+ '@jupyterlab/fileeditor-extension:tab-space-status',
169
+ {
170
+ item,
171
+ align: 'right',
172
+ rank: 1,
173
+ isActive: () => {
174
+ return (
175
+ !!shell.currentWidget && editorTracker.has(shell.currentWidget)
176
+ );
177
+ }
178
+ }
179
+ );
180
+ }
181
+ };
182
+
183
+ /**
184
+ * Cursor position.
185
+ */
186
+ const lineColStatus: JupyterFrontEndPlugin<void> = {
187
+ id: '@jupyterlab/fileeditor-extension:cursor-position',
188
+ activate: (
189
+ app: JupyterFrontEnd,
190
+ tracker: IEditorTracker,
191
+ positionModel: IPositionModel
192
+ ) => {
193
+ positionModel.addEditorProvider((widget: Widget | null) =>
194
+ Promise.resolve(
195
+ widget && tracker.has(widget)
196
+ ? (widget as IDocumentWidget<FileEditor>).content.editor
197
+ : null
198
+ )
199
+ );
200
+ },
201
+ requires: [IEditorTracker, IPositionModel],
202
+ autoStart: true
203
+ };
204
+
205
+ const completerPlugin: JupyterFrontEndPlugin<void> = {
206
+ id: '@jupyterlab/fileeditor-extension:completer',
207
+ requires: [IEditorTracker],
208
+ optional: [ICompletionProviderManager, ITranslator, ISanitizer],
209
+ activate: activateFileEditorCompleterService,
210
+ autoStart: true
211
+ };
212
+
213
+ /**
214
+ * A plugin to search file editors
215
+ */
216
+ const searchProvider: JupyterFrontEndPlugin<void> = {
217
+ id: '@jupyterlab/fileeditor-extension:search',
218
+ requires: [ISearchProviderRegistry],
219
+ autoStart: true,
220
+ activate: (app: JupyterFrontEnd, registry: ISearchProviderRegistry) => {
221
+ registry.add('jp-fileeditorSearchProvider', FileEditorSearchProvider);
222
+ }
223
+ };
224
+
225
+ const languageServerPlugin: JupyterFrontEndPlugin<void> = {
226
+ id: '@jupyterlab/fileeditor-extension:language-server',
227
+ requires: [
228
+ IEditorTracker,
229
+ ILSPDocumentConnectionManager,
230
+ ILSPFeatureManager,
231
+ ILSPCodeExtractorsManager
232
+ ],
233
+
234
+ activate: activateFileEditorLanguageServer,
235
+ autoStart: true
236
+ };
237
+
238
+ /**
239
+ * Export the plugins as default.
240
+ */
241
+ const plugins: JupyterFrontEndPlugin<any>[] = [
242
+ plugin,
243
+ lineColStatus,
244
+ completerPlugin,
245
+ languageServerPlugin,
246
+ searchProvider,
247
+ editorSyntaxStatus,
248
+ tabSpaceStatus
249
+ ];
250
+ export default plugins;
251
+
252
+ /**
253
+ * Activate the editor tracker plugin.
254
+ */
255
+ function activate(
256
+ app: JupyterFrontEnd,
257
+ editorServices: IEditorServices,
258
+ extensions: IEditorExtensionRegistry,
259
+ languages: IEditorLanguageRegistry,
260
+ themes: IEditorThemeRegistry,
261
+ fileBrowser: IDefaultFileBrowser,
262
+ settingRegistry: ISettingRegistry,
263
+ consoleTracker: IConsoleTracker | null,
264
+ palette: ICommandPalette | null,
265
+ launcher: ILauncher | null,
266
+ menu: IMainMenu | null,
267
+ restorer: ILayoutRestorer | null,
268
+ sessionDialogs_: ISessionContextDialogs | null,
269
+ tocRegistry: ITableOfContentsRegistry | null,
270
+ toolbarRegistry: IToolbarWidgetRegistry | null,
271
+ translator_: ITranslator | null,
272
+ formRegistry: IFormRendererRegistry | null
273
+ ): IEditorTracker {
274
+ const id = plugin.id;
275
+ const translator = translator_ ?? nullTranslator;
276
+ const sessionDialogs =
277
+ sessionDialogs_ ?? new SessionContextDialogs({ translator });
278
+ const trans = translator.load('jupyterlab');
279
+ const namespace = 'editor';
280
+ let toolbarFactory:
281
+ | ((
282
+ widget: IDocumentWidget<FileEditor>
283
+ ) => IObservableList<DocumentRegistry.IToolbarItem>)
284
+ | undefined;
285
+
286
+ if (toolbarRegistry) {
287
+ toolbarFactory = createToolbarFactory(
288
+ toolbarRegistry,
289
+ settingRegistry,
290
+ FACTORY,
291
+ id,
292
+ translator
293
+ );
294
+ }
295
+
296
+ const factory = new FileEditorFactory({
297
+ editorServices,
298
+ factoryOptions: {
299
+ name: FACTORY,
300
+ label: trans.__('Editor'),
301
+ fileTypes: ['markdown', '*'], // Explicitly add the markdown fileType so
302
+ defaultFor: ['markdown', '*'], // it outranks the defaultRendered viewer.
303
+ toolbarFactory,
304
+ translator
305
+ }
306
+ });
307
+ const { commands, restored, shell } = app;
308
+ const tracker = new WidgetTracker<IDocumentWidget<FileEditor>>({
309
+ namespace
310
+ });
311
+ const isEnabled = () =>
312
+ tracker.currentWidget !== null &&
313
+ tracker.currentWidget === shell.currentWidget;
314
+
315
+ const commonLanguageFileTypeData = new Map<string, IFileTypeData[]>([
316
+ [
317
+ 'python',
318
+ [
319
+ {
320
+ fileExt: 'py',
321
+ iconName: 'ui-components:python',
322
+ launcherLabel: trans.__('Python File'),
323
+ paletteLabel: trans.__('New Python File'),
324
+ caption: trans.__('Create a new Python file')
325
+ }
326
+ ]
327
+ ],
328
+ [
329
+ 'julia',
330
+ [
331
+ {
332
+ fileExt: 'jl',
333
+ iconName: 'ui-components:julia',
334
+ launcherLabel: trans.__('Julia File'),
335
+ paletteLabel: trans.__('New Julia File'),
336
+ caption: trans.__('Create a new Julia file')
337
+ }
338
+ ]
339
+ ],
340
+ [
341
+ 'R',
342
+ [
343
+ {
344
+ fileExt: 'r',
345
+ iconName: 'ui-components:r-kernel',
346
+ launcherLabel: trans.__('R File'),
347
+ paletteLabel: trans.__('New R File'),
348
+ caption: trans.__('Create a new R file')
349
+ }
350
+ ]
351
+ ]
352
+ ]);
353
+
354
+ // Use available kernels to determine which common file types should have 'Create New' options in the Launcher, File Editor palette, and File menu
355
+ const getAvailableKernelFileTypes = async (): Promise<Set<IFileTypeData>> => {
356
+ const specsManager = app.serviceManager.kernelspecs;
357
+ await specsManager.ready;
358
+ let fileTypes = new Set<IFileTypeData>();
359
+ const specs = specsManager.specs?.kernelspecs ?? {};
360
+ Object.keys(specs).forEach(spec => {
361
+ const specModel = specs[spec];
362
+ if (specModel) {
363
+ const exts = commonLanguageFileTypeData.get(specModel.language);
364
+ exts?.forEach(ext => fileTypes.add(ext));
365
+ }
366
+ });
367
+ return fileTypes;
368
+ };
369
+
370
+ // Handle state restoration.
371
+ if (restorer) {
372
+ void restorer.restore(tracker, {
373
+ command: 'docmanager:open',
374
+ args: widget => ({ path: widget.context.path, factory: FACTORY }),
375
+ name: widget => widget.context.path
376
+ });
377
+ }
378
+
379
+ // Add a console creator to the File menu
380
+ // Fetch the initial state of the settings.
381
+ Promise.all([settingRegistry.load(id), restored])
382
+ .then(([settings]) => {
383
+ // As the menu are defined in the settings we must ensure they are loaded
384
+ // before updating dynamically the submenu
385
+ if (menu) {
386
+ const languageMenu = menu.viewMenu.items.find(
387
+ item =>
388
+ item.type === 'submenu' &&
389
+ item.submenu?.id === 'jp-mainmenu-view-codemirror-language'
390
+ )?.submenu;
391
+
392
+ if (languageMenu) {
393
+ languages
394
+ .getLanguages()
395
+ .sort((a, b) => {
396
+ const aName = a.name;
397
+ const bName = b.name;
398
+ return aName.localeCompare(bName);
399
+ })
400
+ .forEach(spec => {
401
+ // Avoid mode name with a curse word.
402
+ if (spec.name.indexOf('brainf') === 0) {
403
+ return;
404
+ }
405
+ languageMenu.addItem({
406
+ command: CommandIDs.changeLanguage,
407
+ args: { ...spec } as any // TODO: Casting to `any` until lumino typings are fixed
408
+ });
409
+ });
410
+ }
411
+ const themeMenu = menu.settingsMenu.items.find(
412
+ item =>
413
+ item.type === 'submenu' &&
414
+ item.submenu?.id === 'jp-mainmenu-settings-codemirror-theme'
415
+ )?.submenu;
416
+
417
+ if (themeMenu) {
418
+ for (const theme of themes.themes) {
419
+ themeMenu.addItem({
420
+ command: CommandIDs.changeTheme,
421
+ args: {
422
+ theme: theme.name,
423
+ displayName: theme.displayName ?? theme.name
424
+ }
425
+ });
426
+ }
427
+ }
428
+
429
+ // Add go to line capabilities to the edit menu.
430
+ menu.editMenu.goToLiners.add({
431
+ id: CommandIDs.goToLine,
432
+ isEnabled: (w: Widget) =>
433
+ tracker.currentWidget !== null && tracker.has(w)
434
+ });
435
+ }
436
+
437
+ Commands.updateSettings(settings, commands);
438
+ Commands.updateTracker(tracker);
439
+ settings.changed.connect(() => {
440
+ Commands.updateSettings(settings, commands);
441
+ Commands.updateTracker(tracker);
442
+ });
443
+ })
444
+ .catch((reason: Error) => {
445
+ console.error(reason.message);
446
+ Commands.updateTracker(tracker);
447
+ });
448
+
449
+ if (formRegistry) {
450
+ const CMRenderer = formRegistry.getRenderer(
451
+ '@jupyterlab/codemirror-extension:plugin.defaultConfig'
452
+ );
453
+ if (CMRenderer) {
454
+ formRegistry.addRenderer(
455
+ '@jupyterlab/fileeditor-extension:plugin.editorConfig',
456
+ CMRenderer
457
+ );
458
+ }
459
+ }
460
+
461
+ factory.widgetCreated.connect((sender, widget) => {
462
+ // Notify the widget tracker if restore data needs to update.
463
+ widget.context.pathChanged.connect(() => {
464
+ void tracker.save(widget);
465
+ });
466
+ void tracker.add(widget);
467
+ Commands.updateWidget(widget.content);
468
+ });
469
+ app.docRegistry.addWidgetFactory(factory);
470
+
471
+ // Handle the settings of new widgets.
472
+ tracker.widgetAdded.connect((sender, widget) => {
473
+ Commands.updateWidget(widget.content);
474
+ });
475
+
476
+ Commands.addCommands(
477
+ app.commands,
478
+ settingRegistry,
479
+ trans,
480
+ id,
481
+ isEnabled,
482
+ tracker,
483
+ fileBrowser,
484
+ extensions,
485
+ languages,
486
+ consoleTracker,
487
+ sessionDialogs
488
+ );
489
+
490
+ const codeViewerTracker = new WidgetTracker<MainAreaWidget<CodeViewerWidget>>(
491
+ {
492
+ namespace: 'codeviewer'
493
+ }
494
+ );
495
+
496
+ // Handle state restoration for code viewers
497
+ if (restorer) {
498
+ void restorer.restore(codeViewerTracker, {
499
+ command: CommandIDs.openCodeViewer,
500
+ args: widget => ({
501
+ content: widget.content.content,
502
+ label: widget.content.title.label,
503
+ mimeType: widget.content.mimeType,
504
+ widgetId: widget.content.id
505
+ }),
506
+ name: widget => widget.content.id
507
+ });
508
+ }
509
+
510
+ Commands.addOpenCodeViewerCommand(
511
+ app,
512
+ editorServices,
513
+ codeViewerTracker,
514
+ trans
515
+ );
516
+
517
+ // Add a launcher item if the launcher is available.
518
+ if (launcher) {
519
+ Commands.addLauncherItems(launcher, trans);
520
+ }
521
+
522
+ if (palette) {
523
+ Commands.addPaletteItems(palette, trans);
524
+ }
525
+
526
+ if (menu) {
527
+ Commands.addMenuItems(menu, tracker, consoleTracker, isEnabled);
528
+ }
529
+
530
+ getAvailableKernelFileTypes()
531
+ .then(availableKernelFileTypes => {
532
+ if (launcher) {
533
+ Commands.addKernelLanguageLauncherItems(
534
+ launcher,
535
+ trans,
536
+ availableKernelFileTypes
537
+ );
538
+ }
539
+
540
+ if (palette) {
541
+ Commands.addKernelLanguagePaletteItems(
542
+ palette,
543
+ trans,
544
+ availableKernelFileTypes
545
+ );
546
+ }
547
+
548
+ if (menu) {
549
+ Commands.addKernelLanguageMenuItems(menu, availableKernelFileTypes);
550
+ }
551
+ })
552
+ .catch((reason: Error) => {
553
+ console.error(reason.message);
554
+ });
555
+
556
+ if (tocRegistry) {
557
+ tocRegistry.add(new LaTeXTableOfContentsFactory(tracker));
558
+ tocRegistry.add(new MarkdownTableOfContentsFactory(tracker));
559
+ tocRegistry.add(new PythonTableOfContentsFactory(tracker));
560
+ }
561
+
562
+ return tracker;
563
+ }
564
+
565
+ /**
566
+ * Activate the completer service for file editor.
567
+ */
568
+ function activateFileEditorCompleterService(
569
+ app: JupyterFrontEnd,
570
+ editorTracker: IEditorTracker,
571
+ manager: ICompletionProviderManager | null,
572
+ translator: ITranslator | null,
573
+ appSanitizer: IRenderMime.ISanitizer | null
574
+ ): void {
575
+ if (!manager) {
576
+ return;
577
+ }
578
+
579
+ Commands.addCompleterCommands(
580
+ app.commands,
581
+ editorTracker,
582
+ manager,
583
+ translator
584
+ );
585
+ const sessionManager = app.serviceManager.sessions;
586
+ const sanitizer = appSanitizer ?? new Sanitizer();
587
+ const _activeSessions = new Map<string, Session.ISessionConnection>();
588
+ const updateCompleter = async (
589
+ _: IEditorTracker,
590
+ widget: IDocumentWidget<FileEditor>
591
+ ) => {
592
+ const completerContext = {
593
+ editor: widget.content.editor,
594
+ widget
595
+ };
596
+
597
+ await manager.updateCompleter(completerContext);
598
+ const onRunningChanged = (
599
+ _: Session.IManager,
600
+ models: Session.IModel[]
601
+ ) => {
602
+ const oldSession = _activeSessions.get(widget.id);
603
+ // Search for a matching path.
604
+ const model = find(models, m => m.path === widget.context.path);
605
+ if (model) {
606
+ // If there is a matching path, but it is the same
607
+ // session as we previously had, do nothing.
608
+ if (oldSession && oldSession.id === model.id) {
609
+ return;
610
+ }
611
+ // Otherwise, dispose of the old session and reset to
612
+ // a new CompletionConnector.
613
+ if (oldSession) {
614
+ _activeSessions.delete(widget.id);
615
+ oldSession.dispose();
616
+ }
617
+ const session = sessionManager.connectTo({ model });
618
+ const newCompleterContext = {
619
+ editor: widget.content.editor,
620
+ widget,
621
+ session,
622
+ sanitizer
623
+ };
624
+ manager.updateCompleter(newCompleterContext).catch(console.error);
625
+ _activeSessions.set(widget.id, session);
626
+ } else {
627
+ // If we didn't find a match, make sure
628
+ // the connector is the contextConnector and
629
+ // dispose of any previous connection.
630
+ if (oldSession) {
631
+ _activeSessions.delete(widget.id);
632
+ oldSession.dispose();
633
+ }
634
+ }
635
+ };
636
+
637
+ onRunningChanged(sessionManager, Array.from(sessionManager.running()));
638
+ sessionManager.runningChanged.connect(onRunningChanged);
639
+
640
+ widget.disposed.connect(() => {
641
+ sessionManager.runningChanged.disconnect(onRunningChanged);
642
+ const session = _activeSessions.get(widget.id);
643
+ if (session) {
644
+ _activeSessions.delete(widget.id);
645
+ session.dispose();
646
+ }
647
+ });
648
+ };
649
+ editorTracker.widgetAdded.connect(updateCompleter);
650
+ manager.activeProvidersChanged.connect(() => {
651
+ editorTracker.forEach(editorWidget => {
652
+ updateCompleter(editorTracker, editorWidget).catch(console.error);
653
+ });
654
+ });
655
+ }
656
+
657
+ function activateFileEditorLanguageServer(
658
+ app: JupyterFrontEnd,
659
+ editors: IEditorTracker,
660
+ connectionManager: ILSPDocumentConnectionManager,
661
+ featureManager: ILSPFeatureManager,
662
+ extractorManager: ILSPCodeExtractorsManager
663
+ ): void {
664
+ editors.widgetAdded.connect(async (_, editor) => {
665
+ const adapter = new FileEditorAdapter(editor, {
666
+ connectionManager,
667
+ featureManager,
668
+ foreignCodeExtractorsManager: extractorManager,
669
+ docRegistry: app.docRegistry
670
+ });
671
+ connectionManager.registerAdapter(editor.context.path, adapter);
672
+ });
673
+ }