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