1 |
|
2 |
|
3 | import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
|
4 | import { untilReady, VirtualDocument, WidgetLSPAdapter } from '@jupyterlab/lsp';
|
5 | import { PromiseDelegate } from '@lumino/coreutils';
|
6 | import { Signal } from '@lumino/signaling';
|
7 | export class NotebookAdapter extends WidgetLSPAdapter {
|
8 | constructor(editorWidget, options) {
|
9 | super(editorWidget, options);
|
10 | this.editorWidget = editorWidget;
|
11 | this.options = options;
|
12 | this._type = 'code';
|
13 | this._readyDelegate = new PromiseDelegate();
|
14 | this._editorToCell = new Map();
|
15 | this.editor = editorWidget.content;
|
16 | this._cellToEditor = new WeakMap();
|
17 | Promise.all([
|
18 | this.widget.context.sessionContext.ready,
|
19 | this.connectionManager.ready
|
20 | ])
|
21 | .then(async () => {
|
22 | await this.initOnceReady();
|
23 | this._readyDelegate.resolve();
|
24 | })
|
25 | .catch(console.error);
|
26 | }
|
27 | |
28 |
|
29 |
|
30 | get documentPath() {
|
31 | return this.widget.context.path;
|
32 | }
|
33 | |
34 |
|
35 |
|
36 | get mimeType() {
|
37 | var _a;
|
38 | let mimeType;
|
39 | let languageMetadata = this.language_info();
|
40 | if (!languageMetadata || !languageMetadata.mimetype) {
|
41 |
|
42 | mimeType = this.widget.content.codeMimetype;
|
43 | }
|
44 | else {
|
45 | mimeType = languageMetadata.mimetype;
|
46 | }
|
47 | return Array.isArray(mimeType)
|
48 | ? (_a = mimeType[0]) !== null && _a !== void 0 ? _a : IEditorMimeTypeService.defaultMimeType
|
49 | : mimeType;
|
50 | }
|
51 | |
52 |
|
53 |
|
54 | get languageFileExtension() {
|
55 | let languageMetadata = this.language_info();
|
56 | if (!languageMetadata || !languageMetadata.file_extension) {
|
57 | return;
|
58 | }
|
59 | return languageMetadata.file_extension.replace('.', '');
|
60 | }
|
61 | |
62 |
|
63 |
|
64 | get wrapperElement() {
|
65 | return this.widget.node;
|
66 | }
|
67 | |
68 |
|
69 |
|
70 | get editors() {
|
71 | if (this.isDisposed) {
|
72 | return [];
|
73 | }
|
74 | let notebook = this.widget.content;
|
75 | this._editorToCell.clear();
|
76 | if (notebook.isDisposed) {
|
77 | return [];
|
78 | }
|
79 | return notebook.widgets.map(cell => {
|
80 | return {
|
81 | ceEditor: this._getCellEditor(cell),
|
82 | type: cell.model.type,
|
83 | value: cell.model.sharedModel.getSource()
|
84 | };
|
85 | });
|
86 | }
|
87 | |
88 |
|
89 |
|
90 | get activeEditor() {
|
91 | return this.editor.activeCell
|
92 | ? this._getCellEditor(this.editor.activeCell)
|
93 | : undefined;
|
94 | }
|
95 | |
96 |
|
97 |
|
98 | get ready() {
|
99 | return this._readyDelegate.promise;
|
100 | }
|
101 | |
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 | getEditorIndexAt(position) {
|
109 | let cell = this._getCellAt(position);
|
110 | let notebook = this.widget.content;
|
111 | return notebook.widgets.findIndex(otherCell => {
|
112 | return cell === otherCell;
|
113 | });
|
114 | }
|
115 | |
116 |
|
117 |
|
118 |
|
119 |
|
120 | getEditorIndex(ceEditor) {
|
121 | let cell = this._editorToCell.get(ceEditor);
|
122 | return this.editor.widgets.findIndex(otherCell => {
|
123 | return cell === otherCell;
|
124 | });
|
125 | }
|
126 | |
127 |
|
128 |
|
129 |
|
130 |
|
131 | getEditorWrapper(ceEditor) {
|
132 | let cell = this._editorToCell.get(ceEditor);
|
133 | return cell.node;
|
134 | }
|
135 | |
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 | async onKernelChanged(_session, change) {
|
143 | if (!change.newValue) {
|
144 | return;
|
145 | }
|
146 | try {
|
147 |
|
148 | const oldLanguageInfo = this._languageInfo;
|
149 | await untilReady(this.isReady, -1);
|
150 | await this._updateLanguageInfo();
|
151 | const newLanguageInfo = this._languageInfo;
|
152 | if ((oldLanguageInfo === null || oldLanguageInfo === void 0 ? void 0 : oldLanguageInfo.name) != newLanguageInfo.name ||
|
153 | (oldLanguageInfo === null || oldLanguageInfo === void 0 ? void 0 : oldLanguageInfo.mimetype) != (newLanguageInfo === null || newLanguageInfo === void 0 ? void 0 : newLanguageInfo.mimetype) ||
|
154 | (oldLanguageInfo === null || oldLanguageInfo === void 0 ? void 0 : oldLanguageInfo.file_extension) != (newLanguageInfo === null || newLanguageInfo === void 0 ? void 0 : newLanguageInfo.file_extension)) {
|
155 | console.log(`Changed to ${this._languageInfo.name} kernel, reconnecting`);
|
156 | this.reloadConnection();
|
157 | }
|
158 | else {
|
159 | console.log('Keeping old LSP connection as the new kernel uses the same langauge');
|
160 | }
|
161 | }
|
162 | catch (err) {
|
163 | console.warn(err);
|
164 |
|
165 | this.reloadConnection();
|
166 | }
|
167 | }
|
168 | |
169 |
|
170 |
|
171 | dispose() {
|
172 | if (this.isDisposed) {
|
173 | return;
|
174 | }
|
175 | this.widget.context.sessionContext.kernelChanged.disconnect(this.onKernelChanged, this);
|
176 | this.widget.content.activeCellChanged.disconnect(this._activeCellChanged, this);
|
177 | super.dispose();
|
178 |
|
179 | this._editorToCell.clear();
|
180 | Signal.clearData(this);
|
181 | }
|
182 | |
183 |
|
184 |
|
185 | isReady() {
|
186 | var _a;
|
187 | return (!this.widget.isDisposed &&
|
188 | this.widget.context.isReady &&
|
189 | this.widget.content.isVisible &&
|
190 | this.widget.content.widgets.length > 0 &&
|
191 | ((_a = this.widget.context.sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel) != null);
|
192 | }
|
193 | |
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 | async handleCellChange(cells, change) {
|
200 | let cellsAdded = [];
|
201 | let cellsRemoved = [];
|
202 | const type = this._type;
|
203 | if (change.type === 'set') {
|
204 |
|
205 |
|
206 | let convertedToMarkdownOrRaw = [];
|
207 | let convertedToCode = [];
|
208 | if (change.newValues.length === change.oldValues.length) {
|
209 |
|
210 | for (let i = 0; i < change.newValues.length; i++) {
|
211 | if (change.oldValues[i].type === type &&
|
212 | change.newValues[i].type !== type) {
|
213 | convertedToMarkdownOrRaw.push(change.newValues[i]);
|
214 | }
|
215 | else if (change.oldValues[i].type !== type &&
|
216 | change.newValues[i].type === type) {
|
217 | convertedToCode.push(change.newValues[i]);
|
218 | }
|
219 | }
|
220 | cellsAdded = convertedToCode;
|
221 | cellsRemoved = convertedToMarkdownOrRaw;
|
222 | }
|
223 | }
|
224 | else if (change.type == 'add') {
|
225 | cellsAdded = change.newValues.filter(cellModel => cellModel.type === type);
|
226 | }
|
227 |
|
228 |
|
229 |
|
230 |
|
231 | if (cellsRemoved.length ||
|
232 | cellsAdded.length ||
|
233 | change.type === 'set' ||
|
234 | change.type === 'move' ||
|
235 | change.type === 'remove') {
|
236 |
|
237 |
|
238 |
|
239 | await this.updateDocuments();
|
240 | }
|
241 | for (let cellModel of cellsAdded) {
|
242 | let cellWidget = this.widget.content.widgets.find(cell => cell.model.id === cellModel.id);
|
243 | if (!cellWidget) {
|
244 | console.warn(`Widget for added cell with ID: ${cellModel.id} not found!`);
|
245 | continue;
|
246 | }
|
247 |
|
248 | this._getCellEditor(cellWidget);
|
249 | }
|
250 | }
|
251 | |
252 |
|
253 |
|
254 | createVirtualDocument() {
|
255 | return new VirtualDocument({
|
256 | language: this.language,
|
257 | foreignCodeExtractors: this.options.foreignCodeExtractorsManager,
|
258 | path: this.documentPath,
|
259 | fileExtension: this.languageFileExtension,
|
260 |
|
261 | standalone: false,
|
262 |
|
263 | hasLspSupportedFile: false
|
264 | });
|
265 | }
|
266 | |
267 |
|
268 |
|
269 | language_info() {
|
270 | return this._languageInfo;
|
271 | }
|
272 | |
273 |
|
274 |
|
275 |
|
276 |
|
277 | async initOnceReady() {
|
278 | await untilReady(this.isReady.bind(this), -1);
|
279 | await this._updateLanguageInfo();
|
280 | this.initVirtual();
|
281 |
|
282 |
|
283 | this.connectDocument(this.virtualDocument, false).catch(console.warn);
|
284 | this.widget.context.sessionContext.kernelChanged.connect(this.onKernelChanged, this);
|
285 | this.widget.content.activeCellChanged.connect(this._activeCellChanged, this);
|
286 | this._connectModelSignals(this.widget);
|
287 | this.editor.modelChanged.connect(notebook => {
|
288 |
|
289 |
|
290 |
|
291 |
|
292 | console.warn('Model changed, connecting cell change handler; this is not something we were expecting');
|
293 | this._connectModelSignals(notebook);
|
294 | });
|
295 | }
|
296 | |
297 |
|
298 |
|
299 |
|
300 |
|
301 | _connectModelSignals(notebook) {
|
302 | if (notebook.model === null) {
|
303 | console.warn(`Model is missing for notebook ${notebook}, cannot connect cell changed signal!`);
|
304 | }
|
305 | else {
|
306 | notebook.model.cells.changed.connect(this.handleCellChange, this);
|
307 | }
|
308 | }
|
309 | |
310 |
|
311 |
|
312 | async _updateLanguageInfo() {
|
313 | var _a, _b, _c, _d;
|
314 | const language_info = (_d = (await ((_c = (_b = (_a = this.widget.context.sessionContext) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.kernel) === null || _c === void 0 ? void 0 : _c.info))) === null || _d === void 0 ? void 0 : _d.language_info;
|
315 | if (language_info) {
|
316 | this._languageInfo = language_info;
|
317 | }
|
318 | else {
|
319 | throw new Error('Language info update failed (no session, kernel, or info available)');
|
320 | }
|
321 | }
|
322 | |
323 |
|
324 |
|
325 |
|
326 |
|
327 | _activeCellChanged(notebook, cell) {
|
328 | if (!cell || cell.model.type !== this._type) {
|
329 | return;
|
330 | }
|
331 | this._activeEditorChanged.emit({
|
332 | editor: this._getCellEditor(cell)
|
333 | });
|
334 | }
|
335 | |
336 |
|
337 |
|
338 |
|
339 | _getCellAt(pos) {
|
340 | let editor = this.virtualDocument.getEditorAtVirtualLine(pos);
|
341 | return this._editorToCell.get(editor);
|
342 | }
|
343 | |
344 |
|
345 |
|
346 |
|
347 |
|
348 |
|
349 | _getCellEditor(cell) {
|
350 | if (!this._cellToEditor.has(cell)) {
|
351 | const editor = Object.freeze({
|
352 | getEditor: () => cell.editor,
|
353 | ready: async () => {
|
354 | await cell.ready;
|
355 | return cell.editor;
|
356 | },
|
357 | reveal: async () => {
|
358 | await this.editor.scrollToCell(cell);
|
359 | return cell.editor;
|
360 | }
|
361 | });
|
362 | this._cellToEditor.set(cell, editor);
|
363 | this._editorToCell.set(editor, cell);
|
364 | cell.disposed.connect(() => {
|
365 | this._cellToEditor.delete(cell);
|
366 | this._editorToCell.delete(editor);
|
367 | this._editorRemoved.emit({
|
368 | editor
|
369 | });
|
370 | });
|
371 | this._editorAdded.emit({
|
372 | editor
|
373 | });
|
374 | }
|
375 | return this._cellToEditor.get(cell);
|
376 | }
|
377 | }
|
378 |
|
\ | No newline at end of file |