@jupyterlab/lsp 4.0.0-alpha.11 → 4.0.0-alpha.12
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/adapters/adapter.d.ts +184 -54
- package/lib/adapters/adapter.js +177 -132
- package/lib/adapters/adapter.js.map +1 -1
- package/lib/adapters/statusmessage.d.ts +47 -0
- package/lib/adapters/statusmessage.js +74 -0
- package/lib/adapters/statusmessage.js.map +1 -0
- package/lib/connection.d.ts +113 -26
- package/lib/connection.js +166 -105
- package/lib/connection.js.map +1 -1
- package/lib/connection_manager.d.ts +143 -18
- package/lib/connection_manager.js +163 -49
- package/lib/connection_manager.js.map +1 -1
- package/lib/extractors/index.d.ts +3 -0
- package/lib/extractors/index.js +6 -0
- package/lib/extractors/index.js.map +1 -0
- package/lib/extractors/manager.d.ts +21 -4
- package/lib/extractors/manager.js +22 -5
- package/lib/extractors/manager.js.map +1 -1
- package/lib/extractors/text_extractor.d.ts +34 -7
- package/lib/extractors/text_extractor.js +11 -0
- package/lib/extractors/text_extractor.js.map +1 -1
- package/lib/extractors/types.d.ts +8 -6
- package/lib/extractors/types.js +2 -0
- package/lib/extractors/types.js.map +1 -1
- package/lib/feature.d.ts +18 -3
- package/lib/feature.js +20 -3
- package/lib/feature.js.map +1 -1
- package/lib/index.d.ts +6 -9
- package/lib/index.js +8 -13
- package/lib/index.js.map +1 -1
- package/lib/lsp.d.ts +10 -4
- package/lib/lsp.js +2 -0
- package/lib/lsp.js.map +1 -1
- package/lib/manager.d.ts +103 -16
- package/lib/manager.js +118 -31
- package/lib/manager.js.map +1 -1
- package/lib/plugin.d.ts +18 -13
- package/lib/plugin.js +2 -1
- package/lib/plugin.js.map +1 -1
- package/lib/positioning.d.ts +43 -6
- package/lib/positioning.js +28 -2
- package/lib/positioning.js.map +1 -1
- package/lib/schema.js +2 -1
- package/lib/schema.js.map +1 -1
- package/lib/tokens.d.ts +275 -45
- package/lib/tokens.js +28 -0
- package/lib/tokens.js.map +1 -1
- package/lib/utils.d.ts +15 -1
- package/lib/utils.js +18 -2
- package/lib/utils.js.map +1 -1
- package/lib/virtual/document.d.ts +374 -78
- package/lib/virtual/document.js +375 -148
- package/lib/virtual/document.js.map +1 -1
- package/lib/ws-connection/server-capability-registration.d.ts +18 -0
- package/lib/ws-connection/server-capability-registration.js +46 -0
- package/lib/ws-connection/server-capability-registration.js.map +1 -0
- package/lib/ws-connection/types.d.ts +75 -0
- package/lib/ws-connection/types.js +7 -0
- package/lib/ws-connection/types.js.map +1 -0
- package/lib/ws-connection/ws-connection.d.ts +82 -0
- package/lib/ws-connection/ws-connection.js +201 -0
- package/lib/ws-connection/ws-connection.js.map +1 -0
- package/package.json +15 -16
- package/style/index.css +0 -2
- package/style/index.js +0 -2
package/lib/virtual/document.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
1
3
|
import { Signal } from '@lumino/signaling';
|
|
2
4
|
import { DocumentConnectionManager } from '../connection_manager';
|
|
3
|
-
import { PositionError } from '../positioning';
|
|
4
5
|
import { DefaultMap, untilReady } from '../utils';
|
|
5
6
|
/**
|
|
6
7
|
* Check if given position is within range.
|
|
@@ -23,25 +24,48 @@ export function isWithinRange(position, range) {
|
|
|
23
24
|
(position.line > range.start.line && position.line < range.end.line));
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
+
* A virtual implementation of IDocumentInfo
|
|
27
28
|
*/
|
|
28
29
|
export class VirtualDocumentInfo {
|
|
30
|
+
/**
|
|
31
|
+
* Creates an instance of VirtualDocumentInfo.
|
|
32
|
+
* @param document - the virtual document need to
|
|
33
|
+
* be wrapped.
|
|
34
|
+
*/
|
|
29
35
|
constructor(document) {
|
|
36
|
+
/**
|
|
37
|
+
* Current version of the virtual document.
|
|
38
|
+
*/
|
|
30
39
|
this.version = 0;
|
|
31
40
|
this._document = document;
|
|
32
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Get the text content of the virtual document.
|
|
44
|
+
*/
|
|
33
45
|
get text() {
|
|
34
46
|
return this._document.value;
|
|
35
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the uri of the virtual document, if the document is not available,
|
|
50
|
+
* it returns an empty string, users need to check for the length of returned
|
|
51
|
+
* value before using it.
|
|
52
|
+
*/
|
|
36
53
|
get uri() {
|
|
37
54
|
const uris = DocumentConnectionManager.solveUris(this._document, this.languageId);
|
|
55
|
+
if (!uris) {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
38
58
|
return uris.document;
|
|
39
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the language identifier of the document.
|
|
62
|
+
*/
|
|
40
63
|
get languageId() {
|
|
41
64
|
return this._document.language;
|
|
42
65
|
}
|
|
43
66
|
}
|
|
44
67
|
/**
|
|
68
|
+
*
|
|
45
69
|
* A notebook can hold one or more virtual documents; there is always one,
|
|
46
70
|
* "root" document, corresponding to the language of the kernel. All other
|
|
47
71
|
* virtual documents are extracted out of the notebook, based on magics,
|
|
@@ -63,8 +87,15 @@ export class VirtualDocumentInfo {
|
|
|
63
87
|
*/
|
|
64
88
|
export class VirtualDocument {
|
|
65
89
|
constructor(options) {
|
|
66
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Number of blank lines appended to the virtual document between
|
|
92
|
+
* each cell.
|
|
93
|
+
*/
|
|
67
94
|
this.blankLinesBetweenCells = 2;
|
|
95
|
+
this._isDisposed = false;
|
|
96
|
+
this._foreignDocumentClosed = new Signal(this);
|
|
97
|
+
this._foreignDocumentOpened = new Signal(this);
|
|
98
|
+
this._changed = new Signal(this);
|
|
68
99
|
this.options = options;
|
|
69
100
|
this.path = this.options.path;
|
|
70
101
|
this.fileExtension = options.fileExtension;
|
|
@@ -81,9 +112,6 @@ export class VirtualDocument {
|
|
|
81
112
|
VirtualDocument.instancesCount += 1;
|
|
82
113
|
this.unusedStandaloneDocuments = new DefaultMap(() => new Array());
|
|
83
114
|
this._remainingLifetime = 6;
|
|
84
|
-
this.foreignDocumentClosed = new Signal(this);
|
|
85
|
-
this.foreignDocumentOpened = new Signal(this);
|
|
86
|
-
this.changed = new Signal(this);
|
|
87
115
|
this.unusedDocuments = new Set();
|
|
88
116
|
this.documentInfo = new VirtualDocumentInfo(this);
|
|
89
117
|
this.updateManager = new UpdateManager(this);
|
|
@@ -98,11 +126,104 @@ export class VirtualDocument {
|
|
|
98
126
|
}, this);
|
|
99
127
|
this.clear();
|
|
100
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Convert from code editor position into code mirror position.
|
|
131
|
+
*/
|
|
132
|
+
static ceToCm(position) {
|
|
133
|
+
return { line: position.line, ch: position.column };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Test whether the document is disposed.
|
|
137
|
+
*/
|
|
138
|
+
get isDisposed() {
|
|
139
|
+
return this._isDisposed;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Signal emitted when the foreign document is closed
|
|
143
|
+
*/
|
|
144
|
+
get foreignDocumentClosed() {
|
|
145
|
+
return this._foreignDocumentClosed;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Signal emitted when the foreign document is opened
|
|
149
|
+
*/
|
|
150
|
+
get foreignDocumentOpened() {
|
|
151
|
+
return this._foreignDocumentOpened;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Signal emitted when the foreign document is changed
|
|
155
|
+
*/
|
|
156
|
+
get changed() {
|
|
157
|
+
return this._changed;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Id of the virtual document.
|
|
161
|
+
*/
|
|
162
|
+
get virtualId() {
|
|
163
|
+
// for easier debugging, the language information is included in the ID:
|
|
164
|
+
return this.standalone
|
|
165
|
+
? this.instanceId + '(' + this.language + ')'
|
|
166
|
+
: this.language;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Return the ancestry to this document.
|
|
170
|
+
*/
|
|
171
|
+
get ancestry() {
|
|
172
|
+
if (!this.parent) {
|
|
173
|
+
return [this];
|
|
174
|
+
}
|
|
175
|
+
return this.parent.ancestry.concat([this]);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Return the id path to the virtual document.
|
|
179
|
+
*/
|
|
180
|
+
get idPath() {
|
|
181
|
+
if (!this.parent) {
|
|
182
|
+
return this.virtualId;
|
|
183
|
+
}
|
|
184
|
+
return this.parent.idPath + '-' + this.virtualId;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the uri of the virtual document.
|
|
188
|
+
*/
|
|
189
|
+
get uri() {
|
|
190
|
+
const encodedPath = encodeURI(this.path);
|
|
191
|
+
if (!this.parent) {
|
|
192
|
+
return encodedPath;
|
|
193
|
+
}
|
|
194
|
+
return encodedPath + '.' + this.idPath + '.' + this.fileExtension;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get the text value of the document
|
|
198
|
+
*/
|
|
199
|
+
get value() {
|
|
200
|
+
let linesPadding = '\n'.repeat(this.blankLinesBetweenCells);
|
|
201
|
+
return this.lineBlocks.join(linesPadding);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get the last line in the virtual document
|
|
205
|
+
*/
|
|
206
|
+
get lastLine() {
|
|
207
|
+
const linesInLastBlock = this.lineBlocks[this.lineBlocks.length - 1].split('\n');
|
|
208
|
+
return linesInLastBlock[linesInLastBlock.length - 1];
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get the root document of current virtual document.
|
|
212
|
+
*/
|
|
213
|
+
get root() {
|
|
214
|
+
if (this.parent == null) {
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
return this.parent.root;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Dispose the virtual document.
|
|
221
|
+
*/
|
|
101
222
|
dispose() {
|
|
102
|
-
if (this.
|
|
223
|
+
if (this._isDisposed) {
|
|
103
224
|
return;
|
|
104
225
|
}
|
|
105
|
-
this.
|
|
226
|
+
this._isDisposed = true;
|
|
106
227
|
this.parent = null;
|
|
107
228
|
this.closeAllForeignDocuments();
|
|
108
229
|
this.updateManager.dispose();
|
|
@@ -113,29 +234,14 @@ export class VirtualDocument {
|
|
|
113
234
|
this.unusedStandaloneDocuments.clear();
|
|
114
235
|
this.virtualLines.clear();
|
|
115
236
|
// just to be sure - if anything is accessed after disposal (it should not) we
|
|
116
|
-
// will get
|
|
237
|
+
// will get altered by errors in the console AND this will limit memory leaks
|
|
117
238
|
this.documentInfo = null;
|
|
118
239
|
this.lineBlocks = null;
|
|
240
|
+
Signal.clearData(this);
|
|
119
241
|
}
|
|
120
242
|
/**
|
|
121
|
-
*
|
|
122
|
-
* This is meant to reduce the number of open connections when a a foreign code snippet was removed from the document.
|
|
123
|
-
*
|
|
124
|
-
* Note: top level virtual documents are currently immortal (unless killed by other means); it might be worth
|
|
125
|
-
* implementing culling of unused documents, but if and only if JupyterLab will also implement culling of
|
|
126
|
-
* idle kernels - otherwise the user experience could be a bit inconsistent, and we would need to invent our own rules.
|
|
243
|
+
* Clear the virtual document and all related stuffs
|
|
127
244
|
*/
|
|
128
|
-
get remainingLifetime() {
|
|
129
|
-
if (!this.parent) {
|
|
130
|
-
return Infinity;
|
|
131
|
-
}
|
|
132
|
-
return this._remainingLifetime;
|
|
133
|
-
}
|
|
134
|
-
set remainingLifetime(value) {
|
|
135
|
-
if (this.parent) {
|
|
136
|
-
this._remainingLifetime = value;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
245
|
clear() {
|
|
140
246
|
for (let document of this.foreignDocuments.values()) {
|
|
141
247
|
document.clear();
|
|
@@ -149,6 +255,11 @@ export class VirtualDocument {
|
|
|
149
255
|
this.lastSourceLine = 0;
|
|
150
256
|
this.lineBlocks = [];
|
|
151
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Get the virtual document from the cursor position of the source
|
|
260
|
+
* document
|
|
261
|
+
* @param position - position in source document
|
|
262
|
+
*/
|
|
152
263
|
documentAtSourcePosition(position) {
|
|
153
264
|
let sourceLine = this.sourceLines.get(position.line);
|
|
154
265
|
if (sourceLine == null) {
|
|
@@ -169,6 +280,12 @@ export class VirtualDocument {
|
|
|
169
280
|
}
|
|
170
281
|
return this;
|
|
171
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Detect if the input source position is belong to the current
|
|
285
|
+
* virtual document.
|
|
286
|
+
*
|
|
287
|
+
* @param sourcePosition - position in the source document
|
|
288
|
+
*/
|
|
172
289
|
isWithinForeign(sourcePosition) {
|
|
173
290
|
let sourceLine = this.sourceLines.get(sourcePosition.line);
|
|
174
291
|
let sourcePositionCe = {
|
|
@@ -182,6 +299,13 @@ export class VirtualDocument {
|
|
|
182
299
|
}
|
|
183
300
|
return false;
|
|
184
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Compute the position in root document from the position of
|
|
304
|
+
* a child editor.
|
|
305
|
+
*
|
|
306
|
+
* @param editor - the active editor.
|
|
307
|
+
* @param position - position in the active editor.
|
|
308
|
+
*/
|
|
185
309
|
transformFromEditorToRoot(editor, position) {
|
|
186
310
|
if (!this._editorToSourceLine.has(editor)) {
|
|
187
311
|
console.log('Editor not found in _editorToSourceLine map');
|
|
@@ -190,10 +314,16 @@ export class VirtualDocument {
|
|
|
190
314
|
let shift = this._editorToSourceLine.get(editor);
|
|
191
315
|
return Object.assign(Object.assign({}, position), { line: position.line + shift });
|
|
192
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Compute the position in the virtual document from the position
|
|
319
|
+
* if the source document.
|
|
320
|
+
*
|
|
321
|
+
* @param sourcePosition - position in source document
|
|
322
|
+
*/
|
|
193
323
|
virtualPositionAtDocument(sourcePosition) {
|
|
194
324
|
let sourceLine = this.sourceLines.get(sourcePosition.line);
|
|
195
325
|
if (sourceLine == null) {
|
|
196
|
-
throw new
|
|
326
|
+
throw new Error('Source line not mapped to virtual position');
|
|
197
327
|
}
|
|
198
328
|
let virtualLine = sourceLine.virtualLine;
|
|
199
329
|
// position inside the cell (block)
|
|
@@ -224,6 +354,15 @@ export class VirtualDocument {
|
|
|
224
354
|
line: virtualLine
|
|
225
355
|
};
|
|
226
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Append a code block to the end of the virtual document.
|
|
359
|
+
*
|
|
360
|
+
* @param block - block to be appended
|
|
361
|
+
* @param editorShift - position shift in source
|
|
362
|
+
* document
|
|
363
|
+
* @param [virtualShift] - position shift in
|
|
364
|
+
* virtual document.
|
|
365
|
+
*/
|
|
227
366
|
appendCodeBlock(block, editorShift = { line: 0, column: 0 }, virtualShift) {
|
|
228
367
|
let cellCode = block.value;
|
|
229
368
|
let ceEditor = block.ceEditor;
|
|
@@ -270,11 +409,23 @@ export class VirtualDocument {
|
|
|
270
409
|
this.lastVirtualLine += this.blankLinesBetweenCells;
|
|
271
410
|
this.lastSourceLine += sourceCellLines.length;
|
|
272
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Extract a code block into list of string in supported language and
|
|
414
|
+
* a map of foreign document if any.
|
|
415
|
+
* @param block - block to be appended
|
|
416
|
+
* @param editorShift - position shift in source document
|
|
417
|
+
*/
|
|
273
418
|
prepareCodeBlock(block, editorShift = { line: 0, column: 0 }) {
|
|
274
419
|
let { cellCodeKept, foreignDocumentsMap } = this.extractForeignCode(block, editorShift);
|
|
275
420
|
let lines = cellCodeKept.split('\n');
|
|
276
421
|
return { lines, foreignDocumentsMap };
|
|
277
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Extract the foreign code from input block by using the registered
|
|
425
|
+
* extractors.
|
|
426
|
+
* @param block - block to be appended
|
|
427
|
+
* @param editorShift - position shift in source document
|
|
428
|
+
*/
|
|
278
429
|
extractForeignCode(block, editorShift) {
|
|
279
430
|
let foreignDocumentsMap = new Map();
|
|
280
431
|
let cellCode = block.value;
|
|
@@ -322,41 +473,11 @@ export class VirtualDocument {
|
|
|
322
473
|
}
|
|
323
474
|
return { cellCodeKept: cellCode, foreignDocumentsMap };
|
|
324
475
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
let foreignExists = this.foreignDocuments.has(extractor.language);
|
|
329
|
-
if (!extractor.standalone && foreignExists) {
|
|
330
|
-
foreignDocument = this.foreignDocuments.get(extractor.language);
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
// if (previous document does not exists) or (extractor produces standalone documents
|
|
334
|
-
// and no old standalone document could be reused): create a new document
|
|
335
|
-
foreignDocument = this.openForeign(extractor.language, extractor.standalone, extractor.fileExtension);
|
|
336
|
-
}
|
|
337
|
-
return foreignDocument;
|
|
338
|
-
}
|
|
339
|
-
openForeign(language, standalone, fileExtension) {
|
|
340
|
-
let document = new VirtualDocument(Object.assign(Object.assign({}, this.options), { parent: this, standalone: standalone, fileExtension: fileExtension, language: language }));
|
|
341
|
-
const context = {
|
|
342
|
-
foreignDocument: document,
|
|
343
|
-
parentHost: this
|
|
344
|
-
};
|
|
345
|
-
this.foreignDocumentOpened.emit(context);
|
|
346
|
-
// pass through any future signals
|
|
347
|
-
document.foreignDocumentClosed.connect(this.forwardClosedSignal, this);
|
|
348
|
-
document.foreignDocumentOpened.connect(this.forwardOpenedSignal, this);
|
|
349
|
-
this.foreignDocuments.set(document.virtualId, document);
|
|
350
|
-
return document;
|
|
351
|
-
}
|
|
352
|
-
forwardClosedSignal(host, context) {
|
|
353
|
-
this.foreignDocumentClosed.emit(context);
|
|
354
|
-
}
|
|
355
|
-
forwardOpenedSignal(host, context) {
|
|
356
|
-
this.foreignDocumentOpened.emit(context);
|
|
357
|
-
}
|
|
476
|
+
/**
|
|
477
|
+
* Close a foreign document and disconnect all associated signals
|
|
478
|
+
*/
|
|
358
479
|
closeForeign(document) {
|
|
359
|
-
this.
|
|
480
|
+
this._foreignDocumentClosed.emit({
|
|
360
481
|
foreignDocument: document,
|
|
361
482
|
parentHost: this
|
|
362
483
|
});
|
|
@@ -368,52 +489,32 @@ export class VirtualDocument {
|
|
|
368
489
|
document.foreignDocumentOpened.disconnect(this.forwardOpenedSignal, this);
|
|
369
490
|
document.dispose();
|
|
370
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Close all foreign documents.
|
|
494
|
+
*/
|
|
371
495
|
closeAllForeignDocuments() {
|
|
372
496
|
for (let document of this.foreignDocuments.values()) {
|
|
373
497
|
this.closeForeign(document);
|
|
374
498
|
}
|
|
375
499
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
get lastLine() {
|
|
381
|
-
const linesInLastBlock = this.lineBlocks[this.lineBlocks.length - 1].split('\n');
|
|
382
|
-
return linesInLastBlock[linesInLastBlock.length - 1];
|
|
383
|
-
}
|
|
500
|
+
/**
|
|
501
|
+
* Close all expired documents.
|
|
502
|
+
*/
|
|
384
503
|
closeExpiredDocuments() {
|
|
385
504
|
for (let document of this.unusedDocuments.values()) {
|
|
386
505
|
document.remainingLifetime -= 1;
|
|
387
506
|
if (document.remainingLifetime <= 0) {
|
|
388
|
-
|
|
507
|
+
document.dispose();
|
|
389
508
|
}
|
|
390
509
|
}
|
|
391
510
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (!this.parent) {
|
|
400
|
-
return [this];
|
|
401
|
-
}
|
|
402
|
-
return this.parent.ancestry.concat([this]);
|
|
403
|
-
}
|
|
404
|
-
get idPath() {
|
|
405
|
-
if (!this.parent) {
|
|
406
|
-
return this.virtualId;
|
|
407
|
-
}
|
|
408
|
-
return this.parent.idPath + '-' + this.virtualId;
|
|
409
|
-
}
|
|
410
|
-
get uri() {
|
|
411
|
-
const encodedPath = encodeURI(this.path);
|
|
412
|
-
if (!this.parent) {
|
|
413
|
-
return encodedPath;
|
|
414
|
-
}
|
|
415
|
-
return encodedPath + '.' + this.idPath + '.' + this.fileExtension;
|
|
416
|
-
}
|
|
511
|
+
/**
|
|
512
|
+
* Transform the position of the source to the editor
|
|
513
|
+
* position.
|
|
514
|
+
*
|
|
515
|
+
* @param pos - position in the source document
|
|
516
|
+
* @return position in the editor.
|
|
517
|
+
*/
|
|
417
518
|
transformSourceToEditor(pos) {
|
|
418
519
|
let sourceLine = this.sourceLines.get(pos.line);
|
|
419
520
|
let editorLine = sourceLine.editorLine;
|
|
@@ -427,10 +528,12 @@ export class VirtualDocument {
|
|
|
427
528
|
};
|
|
428
529
|
}
|
|
429
530
|
/**
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
531
|
+
* Transform the position in the virtual document to the
|
|
532
|
+
* editor position.
|
|
533
|
+
* Can be null because some lines are added as padding/anchors
|
|
534
|
+
* to the virtual document and those do not exist in the source document
|
|
535
|
+
* and thus they are absent in the editor.
|
|
536
|
+
*/
|
|
434
537
|
transformVirtualToEditor(virtualPosition) {
|
|
435
538
|
let sourcePosition = this.transformVirtualToSource(virtualPosition);
|
|
436
539
|
if (sourcePosition == null) {
|
|
@@ -439,9 +542,10 @@ export class VirtualDocument {
|
|
|
439
542
|
return this.transformSourceToEditor(sourcePosition);
|
|
440
543
|
}
|
|
441
544
|
/**
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
545
|
+
* Transform the position in the virtual document to the source.
|
|
546
|
+
* Can be null because some lines are added as padding/anchors
|
|
547
|
+
* to the virtual document and those do not exist in the source document.
|
|
548
|
+
*/
|
|
445
549
|
transformVirtualToSource(position) {
|
|
446
550
|
const line = this.virtualLines.get(position.line).sourceLine;
|
|
447
551
|
if (line == null) {
|
|
@@ -452,12 +556,9 @@ export class VirtualDocument {
|
|
|
452
556
|
line: line
|
|
453
557
|
};
|
|
454
558
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
return this.parent.root;
|
|
460
|
-
}
|
|
559
|
+
/**
|
|
560
|
+
* Get the corresponding editor of the virtual line.
|
|
561
|
+
*/
|
|
461
562
|
getEditorAtVirtualLine(pos) {
|
|
462
563
|
let line = pos.line;
|
|
463
564
|
// tolerate overshot by one (the hanging blank line at the end)
|
|
@@ -466,6 +567,9 @@ export class VirtualDocument {
|
|
|
466
567
|
}
|
|
467
568
|
return this.virtualLines.get(line).editor;
|
|
468
569
|
}
|
|
570
|
+
/**
|
|
571
|
+
* Get the corresponding editor of the source line
|
|
572
|
+
*/
|
|
469
573
|
getEditorAtSourceLine(pos) {
|
|
470
574
|
return this.sourceLines.get(pos.line).editor;
|
|
471
575
|
}
|
|
@@ -474,18 +578,90 @@ export class VirtualDocument {
|
|
|
474
578
|
*/
|
|
475
579
|
maybeEmitChanged() {
|
|
476
580
|
if (this.value !== this.previousValue) {
|
|
477
|
-
this.
|
|
581
|
+
this._changed.emit(this);
|
|
478
582
|
}
|
|
479
583
|
this.previousValue = this.value;
|
|
480
584
|
for (let document of this.foreignDocuments.values()) {
|
|
481
585
|
document.maybeEmitChanged();
|
|
482
586
|
}
|
|
483
587
|
}
|
|
484
|
-
|
|
485
|
-
|
|
588
|
+
/**
|
|
589
|
+
* When this counter goes down to 0, the document will be destroyed and the associated connection will be closed;
|
|
590
|
+
* This is meant to reduce the number of open connections when a a foreign code snippet was removed from the document.
|
|
591
|
+
*
|
|
592
|
+
* Note: top level virtual documents are currently immortal (unless killed by other means); it might be worth
|
|
593
|
+
* implementing culling of unused documents, but if and only if JupyterLab will also implement culling of
|
|
594
|
+
* idle kernels - otherwise the user experience could be a bit inconsistent, and we would need to invent our own rules.
|
|
595
|
+
*/
|
|
596
|
+
get remainingLifetime() {
|
|
597
|
+
if (!this.parent) {
|
|
598
|
+
return Infinity;
|
|
599
|
+
}
|
|
600
|
+
return this._remainingLifetime;
|
|
601
|
+
}
|
|
602
|
+
set remainingLifetime(value) {
|
|
603
|
+
if (this.parent) {
|
|
604
|
+
this._remainingLifetime = value;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Get the foreign document that can be opened with the input extractor.
|
|
609
|
+
*/
|
|
610
|
+
chooseForeignDocument(extractor) {
|
|
611
|
+
let foreignDocument;
|
|
612
|
+
// if not standalone, try to append to existing document
|
|
613
|
+
let foreignExists = this.foreignDocuments.has(extractor.language);
|
|
614
|
+
if (!extractor.standalone && foreignExists) {
|
|
615
|
+
foreignDocument = this.foreignDocuments.get(extractor.language);
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
// if (previous document does not exists) or (extractor produces standalone documents
|
|
619
|
+
// and no old standalone document could be reused): create a new document
|
|
620
|
+
foreignDocument = this.openForeign(extractor.language, extractor.standalone, extractor.fileExtension);
|
|
621
|
+
}
|
|
622
|
+
return foreignDocument;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Create a foreign document from input language and file extension.
|
|
626
|
+
*
|
|
627
|
+
* @param language - the required language
|
|
628
|
+
* @param standalone - the document type is supported natively by LSP?
|
|
629
|
+
* @param fileExtension - File extension.
|
|
630
|
+
*/
|
|
631
|
+
openForeign(language, standalone, fileExtension) {
|
|
632
|
+
let document = new VirtualDocument(Object.assign(Object.assign({}, this.options), { parent: this, standalone: standalone, fileExtension: fileExtension, language: language }));
|
|
633
|
+
const context = {
|
|
634
|
+
foreignDocument: document,
|
|
635
|
+
parentHost: this
|
|
636
|
+
};
|
|
637
|
+
this._foreignDocumentOpened.emit(context);
|
|
638
|
+
// pass through any future signals
|
|
639
|
+
document.foreignDocumentClosed.connect(this.forwardClosedSignal, this);
|
|
640
|
+
document.foreignDocumentOpened.connect(this.forwardOpenedSignal, this);
|
|
641
|
+
this.foreignDocuments.set(document.virtualId, document);
|
|
642
|
+
return document;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Forward the closed signal from the foreign document to the host document's
|
|
646
|
+
* signal
|
|
647
|
+
*/
|
|
648
|
+
forwardClosedSignal(host, context) {
|
|
649
|
+
this._foreignDocumentClosed.emit(context);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Forward the opened signal from the foreign document to the host document's
|
|
653
|
+
* signal
|
|
654
|
+
*/
|
|
655
|
+
forwardOpenedSignal(host, context) {
|
|
656
|
+
this._foreignDocumentOpened.emit(context);
|
|
486
657
|
}
|
|
487
658
|
}
|
|
488
659
|
VirtualDocument.instancesCount = 0;
|
|
660
|
+
/**
|
|
661
|
+
* Create foreign documents if available from input virtual documents.
|
|
662
|
+
* @param virtualDocument - the virtual document to be collected
|
|
663
|
+
* @return - Set of generated foreign documents
|
|
664
|
+
*/
|
|
489
665
|
export function collectDocuments(virtualDocument) {
|
|
490
666
|
let collected = new Set();
|
|
491
667
|
collected.add(virtualDocument);
|
|
@@ -498,41 +674,74 @@ export function collectDocuments(virtualDocument) {
|
|
|
498
674
|
export class UpdateManager {
|
|
499
675
|
constructor(virtualDocument) {
|
|
500
676
|
this.virtualDocument = virtualDocument;
|
|
677
|
+
this._isDisposed = false;
|
|
501
678
|
/**
|
|
502
|
-
*
|
|
679
|
+
* Promise resolved when the updating process finishes.
|
|
503
680
|
*/
|
|
504
|
-
this.
|
|
505
|
-
this.updateLock = false;
|
|
506
|
-
this.isDisposed = false;
|
|
507
|
-
this.updateDone = new Promise(resolve => {
|
|
681
|
+
this._updateDone = new Promise(resolve => {
|
|
508
682
|
resolve();
|
|
509
683
|
});
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
this.
|
|
514
|
-
|
|
684
|
+
/**
|
|
685
|
+
* Virtual documents update guard.
|
|
686
|
+
*/
|
|
687
|
+
this._isUpdateInProgress = false;
|
|
688
|
+
/**
|
|
689
|
+
* Update lock to prevent multiple updates are applied at the same time.
|
|
690
|
+
*/
|
|
691
|
+
this._updateLock = false;
|
|
692
|
+
this._blockAdded = new Signal(this);
|
|
693
|
+
this._documentUpdated = new Signal(this);
|
|
694
|
+
this._updateBegan = new Signal(this);
|
|
695
|
+
this._updateFinished = new Signal(this);
|
|
696
|
+
this.documentUpdated.connect(this._onUpdated, this);
|
|
515
697
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
this.
|
|
698
|
+
/**
|
|
699
|
+
* Promise resolved when the updating process finishes.
|
|
700
|
+
*/
|
|
701
|
+
get updateDone() {
|
|
702
|
+
return this._updateDone;
|
|
521
703
|
}
|
|
522
704
|
/**
|
|
523
|
-
*
|
|
524
|
-
* should be terminated if their lifetime has expired.
|
|
705
|
+
* Test whether the document is disposed.
|
|
525
706
|
*/
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
707
|
+
get isDisposed() {
|
|
708
|
+
return this._isDisposed;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Signal emitted when a code block is added to the document.
|
|
712
|
+
*/
|
|
713
|
+
get blockAdded() {
|
|
714
|
+
return this._blockAdded;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Signal emitted by the editor that triggered the update,
|
|
718
|
+
* providing the root document of the updated documents.
|
|
719
|
+
*/
|
|
720
|
+
get documentUpdated() {
|
|
721
|
+
return this._documentUpdated;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Signal emitted when the update is started
|
|
725
|
+
*/
|
|
726
|
+
get updateBegan() {
|
|
727
|
+
return this._updateBegan;
|
|
533
728
|
}
|
|
534
|
-
|
|
535
|
-
|
|
729
|
+
/**
|
|
730
|
+
* Signal emitted when the update is finished
|
|
731
|
+
*/
|
|
732
|
+
get updateFinished() {
|
|
733
|
+
return this._updateFinished;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Dispose the class
|
|
737
|
+
*/
|
|
738
|
+
dispose() {
|
|
739
|
+
if (this._isDisposed) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
this._isDisposed = true;
|
|
743
|
+
Signal.clearData(this);
|
|
744
|
+
this.documentUpdated.disconnect(this._onUpdated, this);
|
|
536
745
|
}
|
|
537
746
|
/**
|
|
538
747
|
* Execute provided callback within an update-locked context, which guarantees that:
|
|
@@ -541,13 +750,13 @@ export class UpdateManager {
|
|
|
541
750
|
* @param fn - the callback to execute in update lock
|
|
542
751
|
*/
|
|
543
752
|
async withUpdateLock(fn) {
|
|
544
|
-
await untilReady(() => this.
|
|
753
|
+
await untilReady(() => this._canUpdate(), 12, 10).then(() => {
|
|
545
754
|
try {
|
|
546
|
-
this.
|
|
755
|
+
this._updateLock = true;
|
|
547
756
|
fn();
|
|
548
757
|
}
|
|
549
758
|
finally {
|
|
550
|
-
this.
|
|
759
|
+
this._updateLock = false;
|
|
551
760
|
}
|
|
552
761
|
});
|
|
553
762
|
}
|
|
@@ -560,25 +769,25 @@ export class UpdateManager {
|
|
|
560
769
|
let update = new Promise((resolve, reject) => {
|
|
561
770
|
// defer the update by up to 50 ms (10 retrials * 5 ms break),
|
|
562
771
|
// awaiting for the previous update to complete.
|
|
563
|
-
untilReady(() => this.
|
|
772
|
+
untilReady(() => this._canUpdate(), 10, 5)
|
|
564
773
|
.then(() => {
|
|
565
774
|
if (this.isDisposed || !this.virtualDocument) {
|
|
566
775
|
resolve();
|
|
567
776
|
}
|
|
568
777
|
try {
|
|
569
|
-
this.
|
|
570
|
-
this.
|
|
778
|
+
this._isUpdateInProgress = true;
|
|
779
|
+
this._updateBegan.emit(blocks);
|
|
571
780
|
this.virtualDocument.clear();
|
|
572
781
|
for (let codeBlock of blocks) {
|
|
573
|
-
this.
|
|
782
|
+
this._blockAdded.emit({
|
|
574
783
|
block: codeBlock,
|
|
575
784
|
virtualDocument: this.virtualDocument
|
|
576
785
|
});
|
|
577
786
|
this.virtualDocument.appendCodeBlock(codeBlock);
|
|
578
787
|
}
|
|
579
|
-
this.
|
|
788
|
+
this._updateFinished.emit(blocks);
|
|
580
789
|
if (this.virtualDocument) {
|
|
581
|
-
this.
|
|
790
|
+
this._documentUpdated.emit(this.virtualDocument);
|
|
582
791
|
this.virtualDocument.maybeEmitChanged();
|
|
583
792
|
}
|
|
584
793
|
resolve();
|
|
@@ -588,13 +797,31 @@ export class UpdateManager {
|
|
|
588
797
|
reject(e);
|
|
589
798
|
}
|
|
590
799
|
finally {
|
|
591
|
-
this.
|
|
800
|
+
this._isUpdateInProgress = false;
|
|
592
801
|
}
|
|
593
802
|
})
|
|
594
803
|
.catch(console.error);
|
|
595
804
|
});
|
|
596
|
-
this.
|
|
805
|
+
this._updateDone = update;
|
|
597
806
|
return update;
|
|
598
807
|
}
|
|
808
|
+
/**
|
|
809
|
+
* Once all the foreign documents were refreshed, the unused documents (and their connections)
|
|
810
|
+
* should be terminated if their lifetime has expired.
|
|
811
|
+
*/
|
|
812
|
+
_onUpdated(manager, rootDocument) {
|
|
813
|
+
try {
|
|
814
|
+
rootDocument.closeExpiredDocuments();
|
|
815
|
+
}
|
|
816
|
+
catch (e) {
|
|
817
|
+
console.warn('Failed to close expired documents');
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Check if the document can be updated.
|
|
822
|
+
*/
|
|
823
|
+
_canUpdate() {
|
|
824
|
+
return !this.isDisposed && !this._isUpdateInProgress && !this._updateLock;
|
|
825
|
+
}
|
|
599
826
|
}
|
|
600
827
|
//# sourceMappingURL=document.js.map
|