@jupyterlab/fileeditor-extension 4.0.0-alpha.9 → 4.0.0-beta.1

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