@theia/notebook 1.48.3 → 1.49.1

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 (124) hide show
  1. package/lib/browser/contributions/notebook-actions-contribution.d.ts +7 -1
  2. package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
  3. package/lib/browser/contributions/notebook-actions-contribution.js +81 -7
  4. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  5. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +5 -0
  6. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
  7. package/lib/browser/contributions/notebook-cell-actions-contribution.js +86 -9
  8. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  9. package/lib/browser/contributions/notebook-label-provider-contribution.d.ts +16 -0
  10. package/lib/browser/contributions/notebook-label-provider-contribution.d.ts.map +1 -0
  11. package/lib/browser/contributions/notebook-label-provider-contribution.js +65 -0
  12. package/lib/browser/contributions/notebook-label-provider-contribution.js.map +1 -0
  13. package/lib/browser/contributions/notebook-outline-contribution.d.ts +30 -0
  14. package/lib/browser/contributions/notebook-outline-contribution.d.ts.map +1 -0
  15. package/lib/browser/contributions/notebook-outline-contribution.js +109 -0
  16. package/lib/browser/contributions/notebook-outline-contribution.js.map +1 -0
  17. package/lib/browser/contributions/notebook-output-action-contribution.d.ts +16 -0
  18. package/lib/browser/contributions/notebook-output-action-contribution.d.ts.map +1 -0
  19. package/lib/browser/contributions/notebook-output-action-contribution.js +85 -0
  20. package/lib/browser/contributions/notebook-output-action-contribution.js.map +1 -0
  21. package/lib/browser/contributions/notebook-preferences.d.ts +4 -0
  22. package/lib/browser/contributions/notebook-preferences.d.ts.map +1 -0
  23. package/lib/browser/contributions/notebook-preferences.js +31 -0
  24. package/lib/browser/contributions/notebook-preferences.js.map +1 -0
  25. package/lib/browser/notebook-cell-resource-resolver.d.ts +4 -0
  26. package/lib/browser/notebook-cell-resource-resolver.d.ts.map +1 -1
  27. package/lib/browser/notebook-cell-resource-resolver.js +35 -2
  28. package/lib/browser/notebook-cell-resource-resolver.js.map +1 -1
  29. package/lib/browser/notebook-editor-widget-factory.d.ts +1 -0
  30. package/lib/browser/notebook-editor-widget-factory.d.ts.map +1 -1
  31. package/lib/browser/notebook-editor-widget-factory.js +17 -3
  32. package/lib/browser/notebook-editor-widget-factory.js.map +1 -1
  33. package/lib/browser/notebook-editor-widget.d.ts +5 -1
  34. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  35. package/lib/browser/notebook-editor-widget.js +20 -2
  36. package/lib/browser/notebook-editor-widget.js.map +1 -1
  37. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  38. package/lib/browser/notebook-frontend-module.js +15 -2
  39. package/lib/browser/notebook-frontend-module.js.map +1 -1
  40. package/lib/browser/notebook-open-handler.d.ts +14 -9
  41. package/lib/browser/notebook-open-handler.d.ts.map +1 -1
  42. package/lib/browser/notebook-open-handler.js +38 -16
  43. package/lib/browser/notebook-open-handler.js.map +1 -1
  44. package/lib/browser/notebook-type-registry.d.ts +5 -1
  45. package/lib/browser/notebook-type-registry.d.ts.map +1 -1
  46. package/lib/browser/notebook-type-registry.js +27 -7
  47. package/lib/browser/notebook-type-registry.js.map +1 -1
  48. package/lib/browser/notebook-types.d.ts +2 -0
  49. package/lib/browser/notebook-types.d.ts.map +1 -1
  50. package/lib/browser/notebook-types.js.map +1 -1
  51. package/lib/browser/service/notebook-clipboard-service.d.ts +10 -0
  52. package/lib/browser/service/notebook-clipboard-service.d.ts.map +1 -0
  53. package/lib/browser/service/notebook-clipboard-service.js +42 -0
  54. package/lib/browser/service/notebook-clipboard-service.js.map +1 -0
  55. package/lib/browser/service/notebook-context-manager.d.ts +4 -1
  56. package/lib/browser/service/notebook-context-manager.d.ts.map +1 -1
  57. package/lib/browser/service/notebook-context-manager.js +33 -12
  58. package/lib/browser/service/notebook-context-manager.js.map +1 -1
  59. package/lib/browser/service/notebook-service.d.ts.map +1 -1
  60. package/lib/browser/service/notebook-service.js +4 -0
  61. package/lib/browser/service/notebook-service.js.map +1 -1
  62. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  63. package/lib/browser/view/notebook-cell-editor.js +11 -2
  64. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  65. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  66. package/lib/browser/view/notebook-cell-list-view.js +4 -2
  67. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  68. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts +6 -4
  69. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts.map +1 -1
  70. package/lib/browser/view/notebook-cell-toolbar-factory.js +21 -18
  71. package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
  72. package/lib/browser/view/notebook-cell-toolbar.d.ts.map +1 -1
  73. package/lib/browser/view/notebook-cell-toolbar.js +3 -2
  74. package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
  75. package/lib/browser/view/notebook-code-cell-view.d.ts +5 -1
  76. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  77. package/lib/browser/view/notebook-code-cell-view.js +45 -17
  78. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  79. package/lib/browser/view/notebook-main-toolbar.d.ts +5 -3
  80. package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
  81. package/lib/browser/view/notebook-main-toolbar.js +17 -9
  82. package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
  83. package/lib/browser/view/notebook-markdown-cell-view.js +11 -8
  84. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  85. package/lib/browser/view-model/notebook-cell-model.d.ts +20 -0
  86. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  87. package/lib/browser/view-model/notebook-cell-model.js +61 -2
  88. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  89. package/lib/browser/view-model/notebook-model.d.ts +4 -2
  90. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  91. package/lib/browser/view-model/notebook-model.js +25 -14
  92. package/lib/browser/view-model/notebook-model.js.map +1 -1
  93. package/lib/common/notebook-common.d.ts +7 -1
  94. package/lib/common/notebook-common.d.ts.map +1 -1
  95. package/lib/common/notebook-common.js +28 -4
  96. package/lib/common/notebook-common.js.map +1 -1
  97. package/package.json +9 -7
  98. package/src/browser/contributions/notebook-actions-contribution.ts +90 -9
  99. package/src/browser/contributions/notebook-cell-actions-contribution.ts +88 -11
  100. package/src/browser/contributions/notebook-label-provider-contribution.ts +63 -0
  101. package/src/browser/contributions/notebook-outline-contribution.ts +112 -0
  102. package/src/browser/contributions/notebook-output-action-contribution.ts +82 -0
  103. package/src/browser/contributions/notebook-preferences.ts +31 -0
  104. package/src/browser/notebook-cell-resource-resolver.ts +39 -1
  105. package/src/browser/notebook-editor-widget-factory.ts +18 -5
  106. package/src/browser/notebook-editor-widget.tsx +20 -2
  107. package/src/browser/notebook-frontend-module.ts +20 -4
  108. package/src/browser/notebook-open-handler.ts +48 -20
  109. package/src/browser/notebook-type-registry.ts +26 -6
  110. package/src/browser/notebook-types.ts +2 -0
  111. package/src/browser/service/notebook-clipboard-service.ts +43 -0
  112. package/src/browser/service/notebook-context-manager.ts +36 -10
  113. package/src/browser/service/notebook-service.ts +4 -0
  114. package/src/browser/style/index.css +19 -4
  115. package/src/browser/view/notebook-cell-editor.tsx +12 -2
  116. package/src/browser/view/notebook-cell-list-view.tsx +5 -2
  117. package/src/browser/view/notebook-cell-toolbar-factory.tsx +17 -15
  118. package/src/browser/view/notebook-cell-toolbar.tsx +3 -2
  119. package/src/browser/view/notebook-code-cell-view.tsx +51 -18
  120. package/src/browser/view/notebook-main-toolbar.tsx +20 -11
  121. package/src/browser/view/notebook-markdown-cell-view.tsx +12 -7
  122. package/src/browser/view-model/notebook-cell-model.ts +70 -2
  123. package/src/browser/view-model/notebook-model.ts +29 -16
  124. package/src/common/notebook-common.ts +29 -4
@@ -61,18 +61,23 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, n
61
61
  return () => listener.dispose();
62
62
  }, [editMode]);
63
63
 
64
- let markdownContent = React.useMemo(() => markdownRenderer.render(new MarkdownStringImpl(cell.source)).element.innerHTML, [cell, editMode]);
65
- if (markdownContent.length === 0) {
66
- markdownContent = `<i class="theia-notebook-empty-markdown">${nls.localizeByDefault('Empty markdown cell, double-click or press enter to edit.')}</i>`;
64
+ let markdownContent: HTMLElement = React.useMemo(() => {
65
+ const markdownString = new MarkdownStringImpl(cell.source, { supportHtml: true, isTrusted: true });
66
+ return markdownRenderer.render(markdownString).element;
67
+ }, [cell, editMode]);
68
+
69
+ if (!markdownContent.hasChildNodes()) {
70
+ const italic = document.createElement('i');
71
+ italic.className = 'theia-notebook-empty-markdown';
72
+ italic.innerText = nls.localizeByDefault('Empty markdown cell, double-click or press enter to edit.');
73
+ italic.style.pointerEvents = 'none';
74
+ markdownContent = italic;
67
75
  }
68
76
 
69
77
  return editMode ?
70
78
  <CellEditor cell={cell} notebookModel={notebookModel} monacoServices={monacoServices} notebookContextManager={notebookContextManager} /> :
71
79
  <div className='theia-notebook-markdown-content'
72
80
  onDoubleClick={() => cell.requestEdit()}
73
- // This sets the non React HTML node from the markdown renderers output as a child node to this react component
74
- // This is currently sadly the best way we have to combine React (Virtual Nodes) and normal dom nodes
75
- // the HTML is already sanitized by the markdown renderer, so we don't need to sanitize it again
76
- dangerouslySetInnerHTML={{ __html: markdownContent }} // eslint-disable-line react/no-danger
81
+ ref={node => node?.replaceChildren(markdownContent)}
77
82
  />;
78
83
  }
@@ -21,6 +21,7 @@
21
21
  import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core';
22
22
  import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
23
23
  import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
24
+ import { type MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
24
25
  import {
25
26
  CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata,
26
27
  NotebookCellMetadata, CellOutput, CellData, CellOutputItem
@@ -28,6 +29,9 @@ import {
28
29
  import { NotebookCellOutputsSplice } from '../notebook-types';
29
30
  import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service';
30
31
  import { NotebookCellOutputModel } from './notebook-cell-output-model';
32
+ import { PreferenceService } from '@theia/core/lib/browser';
33
+ import { NOTEBOOK_LINE_NUMBERS } from '../contributions/notebook-preferences';
34
+ import { LanguageService } from '@theia/core/lib/browser/language-service';
31
35
 
32
36
  export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
33
37
  export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel;
@@ -103,12 +107,27 @@ export class NotebookCellModel implements NotebookCell, Disposable {
103
107
  protected readonly onWillFocusCellEditorEmitter = new Emitter<void>();
104
108
  readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event;
105
109
 
110
+ protected readonly onWillBlurCellEditorEmitter = new Emitter<void>();
111
+ readonly onWillBlurCellEditor = this.onWillBlurCellEditorEmitter.event;
112
+
113
+ protected readonly onDidChangeEditorOptionsEmitter = new Emitter<MonacoEditor.IOptions>();
114
+ readonly onDidChangeEditorOptions: Event<MonacoEditor.IOptions> = this.onDidChangeEditorOptionsEmitter.event;
115
+
116
+ protected readonly outputVisibilityChangeEmitter = new Emitter<boolean>();
117
+ readonly onDidChangeOutputVisibility: Event<boolean> = this.outputVisibilityChangeEmitter.event;
118
+
106
119
  @inject(NotebookCellModelProps)
107
120
  protected readonly props: NotebookCellModelProps;
108
121
 
109
122
  @inject(NotebookMonacoTextModelService)
110
123
  protected readonly textModelService: NotebookMonacoTextModelService;
111
124
 
125
+ @inject(LanguageService)
126
+ protected readonly languageService: LanguageService;
127
+
128
+ @inject(PreferenceService)
129
+ protected readonly preferenceService: PreferenceService;
130
+
112
131
  get outputs(): NotebookCellOutputModel[] {
113
132
  return this._outputs;
114
133
  }
@@ -167,16 +186,19 @@ export class NotebookCellModel implements NotebookCell, Disposable {
167
186
  return;
168
187
  }
169
188
 
170
- this.props.language = newLanguage;
171
189
  if (this.textModel) {
172
190
  this.textModel.setLanguageId(newLanguage);
173
191
  }
174
192
 
175
- this.language = newLanguage;
193
+ this.props.language = newLanguage;
176
194
  this.onDidChangeLanguageEmitter.fire(newLanguage);
177
195
  this.onDidChangeContentEmitter.fire('language');
178
196
  }
179
197
 
198
+ get languageName(): string {
199
+ return this.languageService.getLanguage(this.language)?.name ?? this.language;
200
+ }
201
+
180
202
  get uri(): URI {
181
203
  return this.props.uri;
182
204
  }
@@ -192,11 +214,45 @@ export class NotebookCellModel implements NotebookCell, Disposable {
192
214
  return this._editing;
193
215
  }
194
216
 
217
+ protected _editorOptions: MonacoEditor.IOptions = {};
218
+ get editorOptions(): Readonly<MonacoEditor.IOptions> {
219
+ return this._editorOptions;
220
+ }
221
+
222
+ set editorOptions(options: MonacoEditor.IOptions) {
223
+ this._editorOptions = options;
224
+ this.onDidChangeEditorOptionsEmitter.fire(options);
225
+ }
226
+
227
+ protected _outputVisible: boolean = true;
228
+ get outputVisible(): boolean {
229
+ return this._outputVisible;
230
+ }
231
+
232
+ set outputVisible(visible: boolean) {
233
+ if (this._outputVisible !== visible) {
234
+ this._outputVisible = visible;
235
+ this.outputVisibilityChangeEmitter.fire(visible);
236
+ }
237
+ }
238
+
195
239
  @postConstruct()
196
240
  protected init(): void {
197
241
  this._outputs = this.props.outputs.map(op => new NotebookCellOutputModel(op));
198
242
  this._metadata = this.props.metadata ?? {};
199
243
  this._internalMetadata = this.props.internalMetadata ?? {};
244
+
245
+ this.editorOptions = {
246
+ lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS)
247
+ };
248
+ this.toDispose.push(this.preferenceService.onPreferenceChanged(e => {
249
+ if (e.preferenceName === NOTEBOOK_LINE_NUMBERS) {
250
+ this.editorOptions = {
251
+ ...this.editorOptions,
252
+ lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS)
253
+ };
254
+ }
255
+ }));
200
256
  }
201
257
 
202
258
  dispose(): void {
@@ -227,6 +283,11 @@ export class NotebookCellModel implements NotebookCell, Disposable {
227
283
  this.onWillFocusCellEditorEmitter.fire();
228
284
  }
229
285
 
286
+ requestBlurEditor(): void {
287
+ this.requestStopEdit();
288
+ this.onWillBlurCellEditorEmitter.fire();
289
+ }
290
+
230
291
  spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void {
231
292
  if (splice.deleteCount > 0 && splice.newOutputs.length > 0) {
232
293
  const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length);
@@ -298,6 +359,13 @@ export class NotebookCellModel implements NotebookCell, Disposable {
298
359
  });
299
360
  return ref.object;
300
361
  }
362
+
363
+ restartOutputRenderer(outputId: string): void {
364
+ const output = this.outputs.find(out => out.outputId === outputId);
365
+ if (output) {
366
+ this.onDidChangeOutputItemsEmitter.fire(output);
367
+ }
368
+ }
301
369
  }
302
370
 
303
371
  function computeRunStartTimeAdjustment(oldMetadata: NotebookCellInternalMetadata, newMetadata: NotebookCellInternalMetadata): number | undefined {
@@ -1,5 +1,5 @@
1
1
  // *****************************************************************************
2
- // Copyright (C) 20023 Typefox and others.
2
+ // Copyright (C) 2023 Typefox and others.
3
3
  //
4
4
  // This program and the accompanying materials are made available under the
5
5
  // terms of the Eclipse Public License v. 2.0 which is available at
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { Disposable, Emitter, Event, Resource, URI } from '@theia/core';
17
+ import { Disposable, Emitter, Event, QueueableEmitter, Resource, URI } from '@theia/core';
18
18
  import { Saveable, SaveOptions } from '@theia/core/lib/browser';
19
19
  import {
20
20
  CellData, CellEditType, CellUri, NotebookCellInternalMetadata,
@@ -65,12 +65,15 @@ export class NotebookModel implements Saveable, Disposable {
65
65
  protected readonly onDidAddOrRemoveCellEmitter = new Emitter<NotebookModelWillAddRemoveEvent>();
66
66
  readonly onDidAddOrRemoveCell = this.onDidAddOrRemoveCellEmitter.event;
67
67
 
68
- protected readonly onDidChangeContentEmitter = new Emitter<NotebookContentChangedEvent[]>();
68
+ protected readonly onDidChangeContentEmitter = new QueueableEmitter<NotebookContentChangedEvent>();
69
69
  readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
70
70
 
71
71
  protected readonly onDidChangeSelectedCellEmitter = new Emitter<NotebookCellModel | undefined>();
72
72
  readonly onDidChangeSelectedCell = this.onDidChangeSelectedCellEmitter.event;
73
73
 
74
+ protected readonly onDidDisposeEmitter = new Emitter<void>();
75
+ readonly onDidDispose = this.onDidDisposeEmitter.event;
76
+
74
77
  get onDidChangeReadOnly(): Event<boolean | MarkdownString> {
75
78
  return this.props.resource.onDidChangeReadOnly ?? Event.None;
76
79
  }
@@ -138,7 +141,7 @@ export class NotebookModel implements Saveable, Disposable {
138
141
 
139
142
  this.addCellOutputListeners(this.cells);
140
143
 
141
- this.metadata = this.metadata;
144
+ this.metadata = this.props.data.metadata;
142
145
 
143
146
  this.nextHandle = this.cells.length;
144
147
  }
@@ -148,7 +151,9 @@ export class NotebookModel implements Saveable, Disposable {
148
151
  this.onDidSaveNotebookEmitter.dispose();
149
152
  this.onDidAddOrRemoveCellEmitter.dispose();
150
153
  this.onDidChangeContentEmitter.dispose();
154
+ this.onDidChangeSelectedCellEmitter.dispose();
151
155
  this.cells.forEach(cell => cell.dispose());
156
+ this.onDidDisposeEmitter.fire();
152
157
  }
153
158
 
154
159
  async save(options: SaveOptions): Promise<void> {
@@ -235,8 +240,10 @@ export class NotebookModel implements Saveable, Disposable {
235
240
  }
236
241
 
237
242
  setSelectedCell(cell: NotebookCellModel): void {
238
- this.selectedCell = cell;
239
- this.onDidChangeSelectedCellEmitter.fire(cell);
243
+ if (this.selectedCell !== cell) {
244
+ this.selectedCell = cell;
245
+ this.onDidChangeSelectedCellEmitter.fire(cell);
246
+ }
240
247
  }
241
248
 
242
249
  private addCellOutputListeners(cells: NotebookCellModel[]): void {
@@ -281,13 +288,18 @@ export class NotebookModel implements Saveable, Disposable {
281
288
  } else {
282
289
  // could definitely be more efficient. See vscode __spliceNotebookCellOutputs2
283
290
  // For now, just replace the whole existing output with the new output
284
- cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: edit.outputs });
291
+ cell.spliceNotebookCellOutputs({ start: 0, deleteCount: edit.deleteCount ?? cell.outputs.length, newOutputs: edit.outputs });
285
292
  }
286
-
293
+ this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.Output, index: cellIndex, outputs: cell.outputs, append: edit.append ?? false });
287
294
  break;
288
295
  }
289
296
  case CellEditType.OutputItems:
290
297
  cell.changeOutputItems(edit.outputId, !!edit.append, edit.items);
298
+ this.onDidChangeContentEmitter.queue({
299
+ kind: NotebookCellsChangeType.OutputItem, index: cellIndex, outputItems: edit.items,
300
+ outputId: edit.outputId, append: edit.append ?? false
301
+ });
302
+
291
303
  break;
292
304
  case CellEditType.Metadata:
293
305
  this.changeCellMetadata(this.cells[cellIndex], edit.metadata, computeUndoRedo);
@@ -308,12 +320,15 @@ export class NotebookModel implements Saveable, Disposable {
308
320
  this.moveCellToIndex(cellIndex, edit.length, edit.newIdx, computeUndoRedo);
309
321
  break;
310
322
  }
323
+
311
324
  // if selected cell is affected update it because it can potentially have been replaced
312
325
  if (cell === this.selectedCell) {
313
326
  this.setSelectedCell(this.cells[cellIndex]);
314
327
  }
315
328
  }
316
329
 
330
+ this.onDidChangeContentEmitter.fire();
331
+
317
332
  }
318
333
 
319
334
  protected async replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): Promise<void> {
@@ -352,7 +367,7 @@ export class NotebookModel implements Saveable, Disposable {
352
367
  await Promise.all(cells.map(cell => cell.resolveTextModel()));
353
368
 
354
369
  this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) });
355
- this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ModelChange, changes }]);
370
+ this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ModelChange, changes });
356
371
  if (cells.length > 0) {
357
372
  this.setSelectedCell(cells[cells.length - 1]);
358
373
  cells[cells.length - 1].requestEdit();
@@ -370,9 +385,7 @@ export class NotebookModel implements Saveable, Disposable {
370
385
  }
371
386
 
372
387
  cell.internalMetadata = newInternalMetadata;
373
- this.onDidChangeContentEmitter.fire([
374
- { kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata }
375
- ]);
388
+ this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata });
376
389
  }
377
390
 
378
391
  protected updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean): void {
@@ -385,7 +398,7 @@ export class NotebookModel implements Saveable, Disposable {
385
398
  }
386
399
 
387
400
  this.metadata = metadata;
388
- this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }]);
401
+ this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata });
389
402
  }
390
403
 
391
404
  protected changeCellMetadataPartial(cell: NotebookCellModel, metadata: NullablePartialNotebookCellMetadata, computeUndoRedo: boolean): void {
@@ -417,7 +430,7 @@ export class NotebookModel implements Saveable, Disposable {
417
430
  }
418
431
 
419
432
  cell.metadata = metadata;
420
- this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellMetadata, index: this.cells.indexOf(cell), metadata: cell.metadata }]);
433
+ this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeCellMetadata, index: this.cells.indexOf(cell), metadata: cell.metadata });
421
434
  }
422
435
 
423
436
  protected changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void {
@@ -427,7 +440,7 @@ export class NotebookModel implements Saveable, Disposable {
427
440
 
428
441
  cell.language = languageId;
429
442
 
430
- this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }]);
443
+ this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId });
431
444
  }
432
445
 
433
446
  protected moveCellToIndex(fromIndex: number, length: number, toIndex: number, computeUndoRedo: boolean): boolean {
@@ -440,7 +453,7 @@ export class NotebookModel implements Saveable, Disposable {
440
453
 
441
454
  const cells = this.cells.splice(fromIndex, length);
442
455
  this.cells.splice(toIndex, 0, ...cells);
443
- this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells }]);
456
+ this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells });
444
457
 
445
458
  return true;
446
459
  }
@@ -261,7 +261,8 @@ export function isTextStreamMime(mimeType: string): boolean {
261
261
 
262
262
  export namespace CellUri {
263
263
 
264
- export const scheme = 'vscode-notebook-cell';
264
+ export const cellUriScheme = 'vscode-notebook-cell';
265
+ export const outputUriScheme = 'vscode-notebook-cell-output';
265
266
 
266
267
  const _lengths = ['W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f'];
267
268
  const _padRegexp = new RegExp(`^[${_lengths.join('')}]+`);
@@ -273,11 +274,11 @@ export namespace CellUri {
273
274
  const p = s.length < _lengths.length ? _lengths[s.length - 1] : 'z';
274
275
 
275
276
  const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')} `;
276
- return notebook.withScheme(scheme).withFragment(fragment);
277
+ return notebook.withScheme(cellUriScheme).withFragment(fragment);
277
278
  }
278
279
 
279
280
  export function parse(cell: URI): { notebook: URI; handle: number } | undefined {
280
- if (cell.scheme !== scheme) {
281
+ if (cell.scheme !== cellUriScheme) {
281
282
  return undefined;
282
283
  }
283
284
 
@@ -298,6 +299,30 @@ export namespace CellUri {
298
299
  };
299
300
  }
300
301
 
302
+ export function generateCellOutputUri(notebook: URI, outputId?: string): URI {
303
+ return notebook
304
+ .withScheme(outputUriScheme)
305
+ .withQuery(`op${outputId ?? ''},${notebook.scheme !== 'file' ? notebook.scheme : ''}`);
306
+ };
307
+
308
+ export function parseCellOutputUri(uri: URI): { notebook: URI; outputId?: string } | undefined {
309
+ if (uri.scheme !== outputUriScheme) {
310
+ return;
311
+ }
312
+
313
+ const match = /^op([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?\,(.*)$/i.exec(uri.query);
314
+ if (!match) {
315
+ return undefined;
316
+ }
317
+
318
+ const outputId = match[1] || undefined;
319
+ const scheme = match[2];
320
+ return {
321
+ outputId,
322
+ notebook: uri.withScheme(scheme || 'file').withoutQuery()
323
+ };
324
+ }
325
+
301
326
  export function generateCellPropertyUri(notebook: URI, handle: number, cellScheme: string): URI {
302
327
  return CellUri.generate(notebook, handle).withScheme(cellScheme);
303
328
  }
@@ -307,6 +332,6 @@ export namespace CellUri {
307
332
  return undefined;
308
333
  }
309
334
 
310
- return CellUri.parse(uri.withScheme(scheme));
335
+ return CellUri.parse(uri.withScheme(cellUriScheme));
311
336
  }
312
337
  }