@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.
Files changed (65) hide show
  1. package/lib/adapters/adapter.d.ts +184 -54
  2. package/lib/adapters/adapter.js +177 -132
  3. package/lib/adapters/adapter.js.map +1 -1
  4. package/lib/adapters/statusmessage.d.ts +47 -0
  5. package/lib/adapters/statusmessage.js +74 -0
  6. package/lib/adapters/statusmessage.js.map +1 -0
  7. package/lib/connection.d.ts +113 -26
  8. package/lib/connection.js +166 -105
  9. package/lib/connection.js.map +1 -1
  10. package/lib/connection_manager.d.ts +143 -18
  11. package/lib/connection_manager.js +163 -49
  12. package/lib/connection_manager.js.map +1 -1
  13. package/lib/extractors/index.d.ts +3 -0
  14. package/lib/extractors/index.js +6 -0
  15. package/lib/extractors/index.js.map +1 -0
  16. package/lib/extractors/manager.d.ts +21 -4
  17. package/lib/extractors/manager.js +22 -5
  18. package/lib/extractors/manager.js.map +1 -1
  19. package/lib/extractors/text_extractor.d.ts +34 -7
  20. package/lib/extractors/text_extractor.js +11 -0
  21. package/lib/extractors/text_extractor.js.map +1 -1
  22. package/lib/extractors/types.d.ts +8 -6
  23. package/lib/extractors/types.js +2 -0
  24. package/lib/extractors/types.js.map +1 -1
  25. package/lib/feature.d.ts +18 -3
  26. package/lib/feature.js +20 -3
  27. package/lib/feature.js.map +1 -1
  28. package/lib/index.d.ts +6 -9
  29. package/lib/index.js +8 -13
  30. package/lib/index.js.map +1 -1
  31. package/lib/lsp.d.ts +10 -4
  32. package/lib/lsp.js +2 -0
  33. package/lib/lsp.js.map +1 -1
  34. package/lib/manager.d.ts +103 -16
  35. package/lib/manager.js +118 -31
  36. package/lib/manager.js.map +1 -1
  37. package/lib/plugin.d.ts +18 -13
  38. package/lib/plugin.js +2 -1
  39. package/lib/plugin.js.map +1 -1
  40. package/lib/positioning.d.ts +43 -6
  41. package/lib/positioning.js +28 -2
  42. package/lib/positioning.js.map +1 -1
  43. package/lib/schema.js +2 -1
  44. package/lib/schema.js.map +1 -1
  45. package/lib/tokens.d.ts +275 -45
  46. package/lib/tokens.js +28 -0
  47. package/lib/tokens.js.map +1 -1
  48. package/lib/utils.d.ts +15 -1
  49. package/lib/utils.js +18 -2
  50. package/lib/utils.js.map +1 -1
  51. package/lib/virtual/document.d.ts +374 -78
  52. package/lib/virtual/document.js +375 -148
  53. package/lib/virtual/document.js.map +1 -1
  54. package/lib/ws-connection/server-capability-registration.d.ts +18 -0
  55. package/lib/ws-connection/server-capability-registration.js +46 -0
  56. package/lib/ws-connection/server-capability-registration.js.map +1 -0
  57. package/lib/ws-connection/types.d.ts +75 -0
  58. package/lib/ws-connection/types.js +7 -0
  59. package/lib/ws-connection/types.js.map +1 -0
  60. package/lib/ws-connection/ws-connection.d.ts +82 -0
  61. package/lib/ws-connection/ws-connection.js +201 -0
  62. package/lib/ws-connection/ws-connection.js.map +1 -0
  63. package/package.json +15 -16
  64. package/style/index.css +0 -2
  65. package/style/index.js +0 -2
@@ -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
- * a virtual implementation of IDocumentInfo
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
- this.isDisposed = false;
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.isDisposed) {
223
+ if (this._isDisposed) {
103
224
  return;
104
225
  }
105
- this.isDisposed = true;
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 alterted by errors in the console AND this will limit memory leaks
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
- * When this counter goes down to 0, the document will be destroyed and the associated connection will be closed;
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 PositionError('Source line not mapped to virtual position');
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
- chooseForeignDocument(extractor) {
326
- let foreignDocument;
327
- // if not standalone, try to append to existing document
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.foreignDocumentClosed.emit({
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
- get value() {
377
- let linesPadding = '\n'.repeat(this.blankLinesBetweenCells);
378
- return this.lineBlocks.join(linesPadding);
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
- /** TODO */
507
+ document.dispose();
389
508
  }
390
509
  }
391
510
  }
392
- get virtualId() {
393
- // for easier debugging, the language information is included in the ID:
394
- return this.standalone
395
- ? this.instanceId + '(' + this.language + ')'
396
- : this.language;
397
- }
398
- get ancestry() {
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
- Can be null because some lines are added as padding/anchors
431
- to the virtual document and those do not exist in the source document
432
- and thus they are absent in the editor.
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
- Can be null because some lines are added as padding/anchors
443
- to the virtual document and those do not exist in the source document.
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
- get root() {
456
- if (this.parent == null) {
457
- return this;
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.changed.emit(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
- static ceToCm(position) {
485
- return { line: position.line, ch: position.column };
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
- * Virtual documents update guard.
679
+ * Promise resolved when the updating process finishes.
503
680
  */
504
- this.isUpdateInProgress = false;
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
- this.documentUpdated = new Signal(this);
511
- this.blockAdded = new Signal(this);
512
- this.updateBegan = new Signal(this);
513
- this.updateFinished = new Signal(this);
514
- this.documentUpdated.connect(this.onUpdated, this);
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
- dispose() {
517
- if (this.isDisposed) {
518
- return;
519
- }
520
- this.documentUpdated.disconnect(this.onUpdated, this);
698
+ /**
699
+ * Promise resolved when the updating process finishes.
700
+ */
701
+ get updateDone() {
702
+ return this._updateDone;
521
703
  }
522
704
  /**
523
- * Once all the foreign documents were refreshed, the unused documents (and their connections)
524
- * should be terminated if their lifetime has expired.
705
+ * Test whether the document is disposed.
525
706
  */
526
- onUpdated(manager, rootDocument) {
527
- try {
528
- rootDocument.closeExpiredDocuments();
529
- }
530
- catch (e) {
531
- console.warn('Failed to close expired documents');
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
- canUpdate() {
535
- return !this.isDisposed && !this.isUpdateInProgress && !this.updateLock;
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.canUpdate(), 12, 10).then(() => {
753
+ await untilReady(() => this._canUpdate(), 12, 10).then(() => {
545
754
  try {
546
- this.updateLock = true;
755
+ this._updateLock = true;
547
756
  fn();
548
757
  }
549
758
  finally {
550
- this.updateLock = false;
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.canUpdate(), 10, 5)
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.isUpdateInProgress = true;
570
- this.updateBegan.emit(blocks);
778
+ this._isUpdateInProgress = true;
779
+ this._updateBegan.emit(blocks);
571
780
  this.virtualDocument.clear();
572
781
  for (let codeBlock of blocks) {
573
- this.blockAdded.emit({
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.updateFinished.emit(blocks);
788
+ this._updateFinished.emit(blocks);
580
789
  if (this.virtualDocument) {
581
- this.documentUpdated.emit(this.virtualDocument);
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.isUpdateInProgress = false;
800
+ this._isUpdateInProgress = false;
592
801
  }
593
802
  })
594
803
  .catch(console.error);
595
804
  });
596
- this.updateDone = update;
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