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