@difizen/libro-lsp 0.1.2
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/LICENSE +21 -0
- package/README.md +0 -0
- package/es/adapters/adapter.d.ts +255 -0
- package/es/adapters/adapter.d.ts.map +1 -0
- package/es/adapters/adapter.js +647 -0
- package/es/adapters/notebook-adapter.d.ts +150 -0
- package/es/adapters/notebook-adapter.d.ts.map +1 -0
- package/es/adapters/notebook-adapter.js +588 -0
- package/es/adapters/status-message.d.ts +48 -0
- package/es/adapters/status-message.d.ts.map +1 -0
- package/es/adapters/status-message.js +110 -0
- package/es/connection-manager.d.ts +199 -0
- package/es/connection-manager.d.ts.map +1 -0
- package/es/connection-manager.js +685 -0
- package/es/connection.d.ts +149 -0
- package/es/connection.d.ts.map +1 -0
- package/es/connection.js +591 -0
- package/es/extractors/index.d.ts +4 -0
- package/es/extractors/index.d.ts.map +1 -0
- package/es/extractors/index.js +6 -0
- package/es/extractors/manager.d.ts +31 -0
- package/es/extractors/manager.d.ts.map +1 -0
- package/es/extractors/manager.js +90 -0
- package/es/extractors/text-extractor.d.ts +56 -0
- package/es/extractors/text-extractor.d.ts.map +1 -0
- package/es/extractors/text-extractor.js +72 -0
- package/es/extractors/types.d.ts +68 -0
- package/es/extractors/types.d.ts.map +1 -0
- package/es/extractors/types.js +1 -0
- package/es/feature.d.ts +29 -0
- package/es/feature.d.ts.map +1 -0
- package/es/feature.js +85 -0
- package/es/index.d.ts +19 -0
- package/es/index.d.ts.map +1 -0
- package/es/index.js +21 -0
- package/es/lsp-app-contribution.d.ts +19 -0
- package/es/lsp-app-contribution.d.ts.map +1 -0
- package/es/lsp-app-contribution.js +155 -0
- package/es/lsp-protocol.d.ts +10 -0
- package/es/lsp-protocol.d.ts.map +1 -0
- package/es/lsp-protocol.js +1 -0
- package/es/lsp.d.ts +136 -0
- package/es/lsp.d.ts.map +1 -0
- package/es/lsp.js +141 -0
- package/es/manager.d.ts +142 -0
- package/es/manager.d.ts.map +1 -0
- package/es/manager.js +423 -0
- package/es/module.d.ts +3 -0
- package/es/module.d.ts.map +1 -0
- package/es/module.js +21 -0
- package/es/plugin.d.ts +56 -0
- package/es/plugin.d.ts.map +1 -0
- package/es/plugin.js +0 -0
- package/es/positioning.d.ts +66 -0
- package/es/positioning.d.ts.map +1 -0
- package/es/positioning.js +96 -0
- package/es/schema.d.ts +240 -0
- package/es/schema.d.ts.map +1 -0
- package/es/schema.js +0 -0
- package/es/tokens.d.ts +677 -0
- package/es/tokens.d.ts.map +1 -0
- package/es/tokens.js +183 -0
- package/es/utils.d.ts +33 -0
- package/es/utils.d.ts.map +1 -0
- package/es/utils.js +168 -0
- package/es/virtual/document.d.ts +546 -0
- package/es/virtual/document.d.ts.map +1 -0
- package/es/virtual/document.js +1263 -0
- package/es/ws-connection/server-capability-registration.d.ts +19 -0
- package/es/ws-connection/server-capability-registration.d.ts.map +1 -0
- package/es/ws-connection/server-capability-registration.js +51 -0
- package/es/ws-connection/types.d.ts +76 -0
- package/es/ws-connection/types.d.ts.map +1 -0
- package/es/ws-connection/types.js +1 -0
- package/es/ws-connection/ws-connection.d.ts +105 -0
- package/es/ws-connection/ws-connection.d.ts.map +1 -0
- package/es/ws-connection/ws-connection.js +301 -0
- package/package.json +67 -0
- package/src/adapters/adapter.ts +611 -0
- package/src/adapters/notebook-adapter.ts +463 -0
- package/src/adapters/status-message.ts +93 -0
- package/src/connection-manager.ts +626 -0
- package/src/connection.ts +570 -0
- package/src/extractors/index.ts +6 -0
- package/src/extractors/manager.ts +82 -0
- package/src/extractors/text-extractor.ts +94 -0
- package/src/extractors/types.ts +78 -0
- package/src/feature.ts +60 -0
- package/src/index.spec.ts +10 -0
- package/src/index.ts +21 -0
- package/src/lsp-app-contribution.ts +83 -0
- package/src/lsp-protocol.ts +10 -0
- package/src/lsp.ts +160 -0
- package/src/manager.ts +358 -0
- package/src/module.ts +32 -0
- package/src/plugin.ts +62 -0
- package/src/positioning.ts +121 -0
- package/src/schema.ts +249 -0
- package/src/tokens.ts +843 -0
- package/src/utils.ts +109 -0
- package/src/virtual/document.ts +1250 -0
- package/src/ws-connection/server-capability-registration.ts +77 -0
- package/src/ws-connection/types.ts +102 -0
- package/src/ws-connection/ws-connection.ts +320 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-parameter-properties */
|
|
2
|
+
/* eslint-disable @typescript-eslint/parameter-properties */
|
|
3
|
+
// Copyright (c) Jupyter Development Team.
|
|
4
|
+
// Distributed under the terms of the Modified BSD License.
|
|
5
|
+
|
|
6
|
+
import type * as nbformat from '@difizen/libro-common';
|
|
7
|
+
import type {
|
|
8
|
+
CellView,
|
|
9
|
+
CellViewChange,
|
|
10
|
+
LibroModel,
|
|
11
|
+
LibroView,
|
|
12
|
+
} from '@difizen/libro-core';
|
|
13
|
+
import { EditorCellView } from '@difizen/libro-core';
|
|
14
|
+
import type { ExecutableNotebookModel } from '@difizen/libro-kernel';
|
|
15
|
+
import {} from '@difizen/mana-app';
|
|
16
|
+
import { watch, Deferred } from '@difizen/mana-app';
|
|
17
|
+
|
|
18
|
+
import type { IVirtualPosition } from '../positioning.js';
|
|
19
|
+
import type { Document } from '../tokens.js';
|
|
20
|
+
import { untilReady } from '../utils.js';
|
|
21
|
+
import { VirtualDocument } from '../virtual/document.js';
|
|
22
|
+
|
|
23
|
+
import type { IAdapterOptions } from './adapter.js';
|
|
24
|
+
import { WidgetLSPAdapter } from './adapter.js';
|
|
25
|
+
|
|
26
|
+
type ILanguageInfoMetadata = nbformat.ILanguageInfoMetadata;
|
|
27
|
+
|
|
28
|
+
export class NotebookAdapter extends WidgetLSPAdapter<LibroView> {
|
|
29
|
+
constructor(
|
|
30
|
+
public editorWidget: LibroView,
|
|
31
|
+
protected override options: IAdapterOptions,
|
|
32
|
+
) {
|
|
33
|
+
super(editorWidget, options);
|
|
34
|
+
this._editorToCell = new Map();
|
|
35
|
+
this.editor = editorWidget;
|
|
36
|
+
this._cellToEditor = new WeakMap();
|
|
37
|
+
Promise.all([this.notebookModel.kcReady, this.connectionManager.ready])
|
|
38
|
+
.then(async () => {
|
|
39
|
+
await this.initOnceReady();
|
|
40
|
+
this._readyDelegate.resolve();
|
|
41
|
+
return;
|
|
42
|
+
})
|
|
43
|
+
.catch(console.error);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The wrapped `Notebook` widget.
|
|
48
|
+
*/
|
|
49
|
+
readonly editor: LibroView;
|
|
50
|
+
|
|
51
|
+
get notebookModel() {
|
|
52
|
+
return this.widget.model as ExecutableNotebookModel;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get fileContents() {
|
|
56
|
+
return this.notebookModel.currentFileContents;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get current path of the document.
|
|
61
|
+
*/
|
|
62
|
+
get documentPath(): string {
|
|
63
|
+
return this.fileContents.path;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the mime type of the document.
|
|
68
|
+
*/
|
|
69
|
+
get mimeType(): string {
|
|
70
|
+
let mimeType: string | string[];
|
|
71
|
+
const languageMetadata = this.language_info();
|
|
72
|
+
if (!languageMetadata || !languageMetadata.mimetype) {
|
|
73
|
+
// fallback to the code cell mime type if no kernel in use
|
|
74
|
+
mimeType = this.fileContents.mimetype!;
|
|
75
|
+
} else {
|
|
76
|
+
mimeType = languageMetadata.mimetype;
|
|
77
|
+
}
|
|
78
|
+
return Array.isArray(mimeType) ? mimeType[0] ?? 'text/plain' : mimeType;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the file extension of the document.
|
|
83
|
+
*/
|
|
84
|
+
get languageFileExtension(): string | undefined {
|
|
85
|
+
const languageMetadata = this.language_info();
|
|
86
|
+
if (!languageMetadata || !languageMetadata.file_extension) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
return languageMetadata.file_extension.replace('.', '');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the inner HTMLElement of the document widget.
|
|
94
|
+
*/
|
|
95
|
+
get wrapperElement(): HTMLElement {
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
|
97
|
+
return this.widget.container?.current!;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the list of CM editor with its type in the document,
|
|
102
|
+
*/
|
|
103
|
+
get editors(): Document.ICodeBlockOptions[] {
|
|
104
|
+
if (this.disposed) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this._editorToCell.clear();
|
|
109
|
+
|
|
110
|
+
return this.editor.model.cells
|
|
111
|
+
.filter(
|
|
112
|
+
(item) => EditorCellView.is(item) && item.model.mimeType === 'text/x-python',
|
|
113
|
+
)
|
|
114
|
+
.map((cell) => {
|
|
115
|
+
return {
|
|
116
|
+
ceEditor: this.getCellEditor(cell)!,
|
|
117
|
+
type: cell.model.type,
|
|
118
|
+
value: cell.model.value,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get the activated CM editor.
|
|
125
|
+
*/
|
|
126
|
+
get activeEditor(): Document.IEditor | undefined {
|
|
127
|
+
return this.editor.activeCell
|
|
128
|
+
? this.getCellEditor(this.editor.activeCell)
|
|
129
|
+
: undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Promise that resolves once the adapter is initialized
|
|
134
|
+
*/
|
|
135
|
+
get ready(): Promise<void> {
|
|
136
|
+
return this._readyDelegate.promise;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the index of editor from the cursor position in the virtual
|
|
141
|
+
* document.
|
|
142
|
+
*
|
|
143
|
+
* @param position - the position of cursor in the virtual document.
|
|
144
|
+
*/
|
|
145
|
+
getEditorIndexAt(position: IVirtualPosition): number {
|
|
146
|
+
const cell = this._getCellAt(position);
|
|
147
|
+
return this.editor.model.cells.findIndex((otherCell) => {
|
|
148
|
+
return cell === otherCell;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get the index of input editor
|
|
154
|
+
*
|
|
155
|
+
* @param ceEditor - instance of the code editor
|
|
156
|
+
*/
|
|
157
|
+
getEditorIndex(ceEditor: Document.IEditor): number {
|
|
158
|
+
const cell = this._editorToCell.get(ceEditor)!;
|
|
159
|
+
return this.editor.model.cells.findIndex((otherCell) => {
|
|
160
|
+
return cell === otherCell;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get the wrapper of input editor.
|
|
166
|
+
*
|
|
167
|
+
* @param ceEditor - instance of the code editor
|
|
168
|
+
*/
|
|
169
|
+
getEditorWrapper(ceEditor: Document.IEditor): HTMLElement | undefined {
|
|
170
|
+
const cell = this._editorToCell.get(ceEditor)!;
|
|
171
|
+
return EditorCellView.is(cell) ? cell.editor?.host : undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Callback on kernel changed event, it will disconnect the
|
|
176
|
+
* document with the language server and then reconnect.
|
|
177
|
+
*
|
|
178
|
+
* @param _session - Session context of changed kernel
|
|
179
|
+
* @param change - Changed data
|
|
180
|
+
*/
|
|
181
|
+
async onKernelChanged(): Promise<void> {
|
|
182
|
+
try {
|
|
183
|
+
// note: we need to wait until ready before updating language info
|
|
184
|
+
const oldLanguageInfo = this._languageInfo;
|
|
185
|
+
await untilReady(this.isReady, -1);
|
|
186
|
+
await this._updateLanguageInfo();
|
|
187
|
+
const newLanguageInfo = this._languageInfo;
|
|
188
|
+
if (
|
|
189
|
+
oldLanguageInfo?.name !== newLanguageInfo.name ||
|
|
190
|
+
oldLanguageInfo?.mimetype !== newLanguageInfo?.mimetype ||
|
|
191
|
+
oldLanguageInfo?.file_extension !== newLanguageInfo?.file_extension
|
|
192
|
+
) {
|
|
193
|
+
console.warn(`Changed to ${this._languageInfo.name} kernel, reconnecting`);
|
|
194
|
+
this.reloadConnection();
|
|
195
|
+
} else {
|
|
196
|
+
console.warn(
|
|
197
|
+
'Keeping old LSP connection as the new kernel uses the same langauge',
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.warn(err);
|
|
202
|
+
// try to reconnect anyway
|
|
203
|
+
this.reloadConnection();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Dispose the widget.
|
|
209
|
+
*/
|
|
210
|
+
override dispose(): void {
|
|
211
|
+
if (this.disposed) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
super.dispose();
|
|
216
|
+
|
|
217
|
+
// editors are needed for the parent dispose() to unbind signals, so they are the last to go
|
|
218
|
+
this._editorToCell.clear();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Method to check if the notebook context is ready.
|
|
223
|
+
*/
|
|
224
|
+
isReady(): boolean {
|
|
225
|
+
return (
|
|
226
|
+
!this.widget.isDisposed &&
|
|
227
|
+
// this.notebookModel.currentKernelStatus !== undefined &&
|
|
228
|
+
this.widget.isVisible &&
|
|
229
|
+
this.notebookModel.cells.length > 0 &&
|
|
230
|
+
this.notebookModel.kernelConnection !== null
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
handleCellSourceChange = async () => {
|
|
235
|
+
await this.updateDocuments();
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Update the virtual document on cell changing event.
|
|
240
|
+
*
|
|
241
|
+
* @param cells - Observable list of changed cells
|
|
242
|
+
* @param change - Changed data
|
|
243
|
+
*/
|
|
244
|
+
handleCellChange = async (change: CellViewChange): Promise<void> => {
|
|
245
|
+
// const cellsAdded: ICellModel[] = [];
|
|
246
|
+
// const cellsRemoved: ICellModel[] = [];
|
|
247
|
+
// const type = this._type;
|
|
248
|
+
// if (change.type === 'set') {
|
|
249
|
+
// // handling of conversions is important, because the editors get re-used and their handlers inherited,
|
|
250
|
+
// // so we need to clear our handlers from editors of e.g. markdown cells which previously were code cells.
|
|
251
|
+
// const convertedToMarkdownOrRaw = [];
|
|
252
|
+
// const convertedToCode = [];
|
|
253
|
+
|
|
254
|
+
// if (change.newValues.length === change.oldValues.length) {
|
|
255
|
+
// // during conversion the cells should not get deleted nor added
|
|
256
|
+
// for (let i = 0; i < change.newValues.length; i++) {
|
|
257
|
+
// if (change.oldValues[i].type === type && change.newValues[i].type !== type) {
|
|
258
|
+
// convertedToMarkdownOrRaw.push(change.newValues[i]);
|
|
259
|
+
// } else if (change.oldValues[i].type !== type && change.newValues[i].type === type) {
|
|
260
|
+
// convertedToCode.push(change.newValues[i]);
|
|
261
|
+
// }
|
|
262
|
+
// }
|
|
263
|
+
// cellsAdded = convertedToCode;
|
|
264
|
+
// cellsRemoved = convertedToMarkdownOrRaw;
|
|
265
|
+
// }
|
|
266
|
+
// } else if (change.type == 'add') {
|
|
267
|
+
// cellsAdded = change.newValues.filter(cellModel => cellModel.type === type);
|
|
268
|
+
// }
|
|
269
|
+
// note: editorRemoved is not emitted for removal of cells by change of type 'remove' (but only during cell type conversion)
|
|
270
|
+
// because there is no easy way to get the widget associated with the removed cell(s) - because it is no
|
|
271
|
+
// longer in the notebook widget list! It would need to be tracked on our side, but it is not necessary
|
|
272
|
+
// as (except for a tiny memory leak) it should not impact the functionality in any way
|
|
273
|
+
|
|
274
|
+
// if (
|
|
275
|
+
// cellsRemoved.length ||
|
|
276
|
+
// cellsAdded.length ||
|
|
277
|
+
// change.type === 'set' ||
|
|
278
|
+
// change.type === 'move' ||
|
|
279
|
+
// change.type === 'remove'
|
|
280
|
+
// ) {
|
|
281
|
+
// in contrast to the file editor document which can be only changed by the modification of the editor content,
|
|
282
|
+
// the notebook document cna also get modified by a change in the number or arrangement of editors themselves;
|
|
283
|
+
// for this reason each change has to trigger documents update (so that LSP mirror is in sync).
|
|
284
|
+
await this.updateDocuments();
|
|
285
|
+
// }
|
|
286
|
+
|
|
287
|
+
for (const cellView of change.insert?.cells ?? []) {
|
|
288
|
+
// const cellWidget = this.widget.content.widgets.find(cell => cell.model.id === cellModel.id);
|
|
289
|
+
// if (!cellWidget) {
|
|
290
|
+
// console.warn(`Widget for added cell with ID: ${cellModel.id} not found!`);
|
|
291
|
+
// continue;
|
|
292
|
+
// }
|
|
293
|
+
|
|
294
|
+
// Add editor to the mapping if needed
|
|
295
|
+
this.getCellEditor(cellView);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Generate the virtual document associated with the document.
|
|
301
|
+
*/
|
|
302
|
+
createVirtualDocument(): VirtualDocument {
|
|
303
|
+
return new VirtualDocument({
|
|
304
|
+
language: this.language,
|
|
305
|
+
foreignCodeExtractors: this.options.foreignCodeExtractorsManager,
|
|
306
|
+
path: this.documentPath,
|
|
307
|
+
fileExtension: this.languageFileExtension,
|
|
308
|
+
// notebooks are continuous, each cell is dependent on the previous one
|
|
309
|
+
standalone: false,
|
|
310
|
+
// notebooks are not supported by LSP servers
|
|
311
|
+
hasLspSupportedFile: false,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get the metadata of notebook.
|
|
317
|
+
*/
|
|
318
|
+
protected language_info(): ILanguageInfoMetadata {
|
|
319
|
+
return this._languageInfo;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Initialization function called once the editor and the LSP connection
|
|
323
|
+
* manager is ready. This function will create the virtual document and
|
|
324
|
+
* connect various signals.
|
|
325
|
+
*/
|
|
326
|
+
protected initOnceReady = async (): Promise<void> => {
|
|
327
|
+
await untilReady(this.isReady.bind(this), -1);
|
|
328
|
+
await this._updateLanguageInfo();
|
|
329
|
+
this.initVirtual();
|
|
330
|
+
|
|
331
|
+
// connect the document, but do not open it as the adapter will handle this
|
|
332
|
+
// after registering all features
|
|
333
|
+
this.connectDocument(this.virtualDocument!, false).catch((error) => {
|
|
334
|
+
console.warn(error);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// this.widget.context.sessionContext.kernelChanged.connect(this.onKernelChanged, this);
|
|
338
|
+
watch(this.notebookModel, 'kernelConnection', this.onKernelChanged);
|
|
339
|
+
|
|
340
|
+
watch(this.notebookModel, 'active', ({ target }) =>
|
|
341
|
+
this._activeCellChanged(target),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
this._connectModelSignals(this.widget);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Connect the cell changed event to its handler
|
|
349
|
+
*
|
|
350
|
+
* @param notebook - The notebook that emitted event.
|
|
351
|
+
*/
|
|
352
|
+
protected _connectModelSignals(notebook: LibroView) {
|
|
353
|
+
if (notebook.model === null) {
|
|
354
|
+
console.warn(
|
|
355
|
+
`Model is missing for notebook ${notebook}, cannot connect cell changed signal!`,
|
|
356
|
+
);
|
|
357
|
+
} else {
|
|
358
|
+
notebook.model.onSourceChanged(this.handleCellSourceChange);
|
|
359
|
+
notebook.model.onCellViewChanged(this.handleCellChange);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Update the stored language info with the one from the notebook.
|
|
365
|
+
*/
|
|
366
|
+
protected async _updateLanguageInfo(): Promise<void> {
|
|
367
|
+
const language_info = (await this.notebookModel.kernelConnection?.info)
|
|
368
|
+
?.language_info;
|
|
369
|
+
// const language_info = (await this.widget.context.sessionContext?.session?.kernel?.info)
|
|
370
|
+
// ?.language_info;
|
|
371
|
+
if (language_info) {
|
|
372
|
+
this._languageInfo = language_info;
|
|
373
|
+
} else {
|
|
374
|
+
throw new Error(
|
|
375
|
+
'Language info update failed (no session, kernel, or info available)',
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Handle the cell changed event
|
|
382
|
+
* @param notebook - The notebook that emitted event
|
|
383
|
+
* @param cell - Changed cell.
|
|
384
|
+
*/
|
|
385
|
+
protected _activeCellChanged(libroModel: LibroModel | null) {
|
|
386
|
+
if (!libroModel || libroModel.active?.model.type !== this._type) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
this._activeEditorChanged.fire({
|
|
391
|
+
editor: this.getCellEditor(libroModel.active)!,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Get the cell at the cursor position of the virtual document.
|
|
397
|
+
* @param pos - Position in the virtual document.
|
|
398
|
+
*/
|
|
399
|
+
protected _getCellAt(pos: IVirtualPosition): CellView {
|
|
400
|
+
const editor = this.virtualDocument!.getEditorAtVirtualLine(pos);
|
|
401
|
+
return this._editorToCell.get(editor)!;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Get the cell editor and add new ones to the mappings.
|
|
406
|
+
*
|
|
407
|
+
* @param cell Cell widget
|
|
408
|
+
* @returns Cell editor accessor
|
|
409
|
+
*/
|
|
410
|
+
getCellEditor(cell: CellView): Document.IEditor | undefined {
|
|
411
|
+
if (!EditorCellView.is(cell)) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (!this._cellToEditor.has(cell)) {
|
|
415
|
+
const editor = Object.freeze({
|
|
416
|
+
getEditor: () => cell.editor!,
|
|
417
|
+
ready: async () => {
|
|
418
|
+
// await cell.ready;
|
|
419
|
+
return cell.editor!;
|
|
420
|
+
},
|
|
421
|
+
reveal: async () => {
|
|
422
|
+
// await this.editor.scrollToCell(cell);
|
|
423
|
+
return cell.editor!;
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
this._cellToEditor.set(cell, editor);
|
|
428
|
+
this._editorToCell.set(editor, cell);
|
|
429
|
+
cell.onDisposed(() => {
|
|
430
|
+
this._cellToEditor.delete(cell);
|
|
431
|
+
this._editorToCell.delete(editor);
|
|
432
|
+
this._editorRemoved.fire({
|
|
433
|
+
editor,
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
this._editorAdded.fire({
|
|
438
|
+
editor,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return this._cellToEditor.get(cell)!;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* A map between the editor accessor and the containing cell
|
|
447
|
+
*/
|
|
448
|
+
protected _editorToCell: Map<Document.IEditor, CellView>;
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Mapping of cell to editor accessor to ensure accessor uniqueness.
|
|
452
|
+
*/
|
|
453
|
+
protected _cellToEditor: WeakMap<CellView, Document.IEditor>;
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Metadata of the notebook
|
|
457
|
+
*/
|
|
458
|
+
protected _languageInfo: ILanguageInfoMetadata;
|
|
459
|
+
|
|
460
|
+
protected _type: nbformat.CellType = 'code';
|
|
461
|
+
|
|
462
|
+
protected _readyDelegate = new Deferred<void>();
|
|
463
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { Emitter } from '@difizen/mana-app';
|
|
5
|
+
import type { Disposable } from '@difizen/mana-app';
|
|
6
|
+
|
|
7
|
+
export class StatusMessage implements Disposable {
|
|
8
|
+
/**
|
|
9
|
+
* Timeout reference used to clear the previous `setTimeout` call.
|
|
10
|
+
*/
|
|
11
|
+
protected _timer: number | null;
|
|
12
|
+
/**
|
|
13
|
+
* The text message to be shown on the statusbar
|
|
14
|
+
*/
|
|
15
|
+
protected _message: string;
|
|
16
|
+
|
|
17
|
+
protected _changed = new Emitter<void>();
|
|
18
|
+
|
|
19
|
+
protected _isDisposed = false;
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
this._message = '';
|
|
23
|
+
this._timer = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Signal emitted on status changed event.
|
|
28
|
+
*/
|
|
29
|
+
get changed(): Emitter<void> {
|
|
30
|
+
return this._changed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Test whether the object is disposed.
|
|
35
|
+
*/
|
|
36
|
+
get isDisposed(): boolean {
|
|
37
|
+
return this._isDisposed;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Dispose the object.
|
|
42
|
+
*/
|
|
43
|
+
dispose(): void {
|
|
44
|
+
if (this.isDisposed) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this._isDisposed = true;
|
|
48
|
+
if (this._timer) {
|
|
49
|
+
window.clearTimeout(this._timer);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The text message to be shown on the statusbar.
|
|
55
|
+
*/
|
|
56
|
+
get message(): string {
|
|
57
|
+
return this._message;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Set the text message and (optionally) the timeout to remove it.
|
|
62
|
+
* @param message
|
|
63
|
+
* @param timeout - number of ms to until the message is cleaned;
|
|
64
|
+
* -1 if the message should stay up indefinitely;
|
|
65
|
+
* defaults to 3000ms (3 seconds)
|
|
66
|
+
*/
|
|
67
|
+
set(message: string, timeout: number = 1000 * 3): void {
|
|
68
|
+
this._expireTimer();
|
|
69
|
+
this._message = message;
|
|
70
|
+
this._changed.fire();
|
|
71
|
+
if (timeout !== -1) {
|
|
72
|
+
this._timer = window.setTimeout(this.clear.bind(this), timeout);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Clear the status message.
|
|
78
|
+
*/
|
|
79
|
+
clear(): void {
|
|
80
|
+
this._message = '';
|
|
81
|
+
this._changed.fire();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Clear the previous `setTimeout` call.
|
|
86
|
+
*/
|
|
87
|
+
protected _expireTimer(): void {
|
|
88
|
+
if (this._timer !== null) {
|
|
89
|
+
window.clearTimeout(this._timer);
|
|
90
|
+
this._timer = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|