@theia/notebook 1.44.0 → 1.45.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 (100) hide show
  1. package/lib/browser/contributions/notebook-actions-contribution.d.ts +2 -2
  2. package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
  3. package/lib/browser/contributions/notebook-actions-contribution.js +1 -1
  4. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  5. package/lib/browser/index.d.ts +1 -0
  6. package/lib/browser/index.d.ts.map +1 -1
  7. package/lib/browser/index.js +1 -0
  8. package/lib/browser/index.js.map +1 -1
  9. package/lib/browser/notebook-editor-widget.d.ts +4 -1
  10. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  11. package/lib/browser/notebook-editor-widget.js +9 -3
  12. package/lib/browser/notebook-editor-widget.js.map +1 -1
  13. package/lib/browser/notebook-frontend-module.js +1 -1
  14. package/lib/browser/notebook-frontend-module.js.map +1 -1
  15. package/lib/browser/notebook-output-utils.d.ts +13 -0
  16. package/lib/browser/notebook-output-utils.d.ts.map +1 -0
  17. package/lib/browser/notebook-output-utils.js +112 -0
  18. package/lib/browser/notebook-output-utils.js.map +1 -0
  19. package/lib/browser/notebook-types.d.ts +120 -0
  20. package/lib/browser/notebook-types.d.ts.map +1 -0
  21. package/lib/browser/notebook-types.js +28 -0
  22. package/lib/browser/notebook-types.js.map +1 -0
  23. package/lib/browser/service/notebook-cell-context-manager.d.ts +5 -4
  24. package/lib/browser/service/notebook-cell-context-manager.d.ts.map +1 -1
  25. package/lib/browser/service/notebook-cell-context-manager.js +15 -10
  26. package/lib/browser/service/notebook-cell-context-manager.js.map +1 -1
  27. package/lib/browser/service/notebook-editor-widget-service.d.ts +2 -5
  28. package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -1
  29. package/lib/browser/service/notebook-editor-widget-service.js +12 -15
  30. package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
  31. package/lib/browser/service/notebook-execution-service.d.ts +3 -4
  32. package/lib/browser/service/notebook-execution-service.d.ts.map +1 -1
  33. package/lib/browser/service/notebook-execution-service.js +2 -12
  34. package/lib/browser/service/notebook-execution-service.js.map +1 -1
  35. package/lib/browser/service/notebook-execution-state-service.d.ts +2 -5
  36. package/lib/browser/service/notebook-execution-state-service.d.ts.map +1 -1
  37. package/lib/browser/service/notebook-execution-state-service.js.map +1 -1
  38. package/lib/browser/service/notebook-kernel-history-service.d.ts +4 -1
  39. package/lib/browser/service/notebook-kernel-history-service.d.ts.map +1 -1
  40. package/lib/browser/service/notebook-kernel-history-service.js +15 -0
  41. package/lib/browser/service/notebook-kernel-history-service.js.map +1 -1
  42. package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts +5 -11
  43. package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts.map +1 -1
  44. package/lib/browser/service/notebook-kernel-quick-pick-service.js +33 -48
  45. package/lib/browser/service/notebook-kernel-quick-pick-service.js.map +1 -1
  46. package/lib/browser/view/notebook-cell-editor.d.ts +1 -1
  47. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  48. package/lib/browser/view/notebook-cell-editor.js +5 -5
  49. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  50. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  51. package/lib/browser/view/notebook-cell-list-view.js +9 -3
  52. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  53. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts +1 -0
  54. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts.map +1 -1
  55. package/lib/browser/view/notebook-cell-toolbar-factory.js +2 -1
  56. package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
  57. package/lib/browser/view/notebook-cell-toolbar.d.ts +5 -4
  58. package/lib/browser/view/notebook-cell-toolbar.d.ts.map +1 -1
  59. package/lib/browser/view/notebook-cell-toolbar.js +6 -4
  60. package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
  61. package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
  62. package/lib/browser/view/notebook-main-toolbar.js +8 -0
  63. package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
  64. package/lib/browser/view-model/notebook-cell-model.d.ts +22 -2
  65. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  66. package/lib/browser/view-model/notebook-cell-model.js +5 -0
  67. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  68. package/lib/browser/view-model/notebook-cell-output-model.d.ts.map +1 -1
  69. package/lib/browser/view-model/notebook-cell-output-model.js +4 -3
  70. package/lib/browser/view-model/notebook-cell-output-model.js.map +1 -1
  71. package/lib/browser/view-model/notebook-model.d.ts +9 -7
  72. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  73. package/lib/browser/view-model/notebook-model.js +42 -36
  74. package/lib/browser/view-model/notebook-model.js.map +1 -1
  75. package/lib/common/notebook-common.d.ts +14 -146
  76. package/lib/common/notebook-common.d.ts.map +1 -1
  77. package/lib/common/notebook-common.js +13 -9
  78. package/lib/common/notebook-common.js.map +1 -1
  79. package/package.json +7 -7
  80. package/src/browser/contributions/notebook-actions-contribution.ts +2 -2
  81. package/src/browser/index.ts +1 -0
  82. package/src/browser/notebook-editor-widget.tsx +11 -5
  83. package/src/browser/notebook-frontend-module.ts +2 -2
  84. package/src/browser/notebook-output-utils.ts +119 -0
  85. package/src/browser/notebook-types.ts +172 -0
  86. package/src/browser/service/notebook-cell-context-manager.ts +17 -12
  87. package/src/browser/service/notebook-editor-widget-service.ts +14 -19
  88. package/src/browser/service/notebook-execution-service.ts +4 -16
  89. package/src/browser/service/notebook-execution-state-service.ts +2 -5
  90. package/src/browser/service/notebook-kernel-history-service.ts +18 -1
  91. package/src/browser/service/notebook-kernel-quick-pick-service.ts +29 -49
  92. package/src/browser/view/notebook-cell-editor.tsx +7 -5
  93. package/src/browser/view/notebook-cell-list-view.tsx +8 -3
  94. package/src/browser/view/notebook-cell-toolbar-factory.tsx +3 -1
  95. package/src/browser/view/notebook-cell-toolbar.tsx +8 -5
  96. package/src/browser/view/notebook-main-toolbar.tsx +9 -0
  97. package/src/browser/view-model/notebook-cell-model.ts +30 -3
  98. package/src/browser/view-model/notebook-cell-output-model.ts +6 -5
  99. package/src/browser/view-model/notebook-model.ts +52 -45
  100. package/src/common/notebook-common.ts +22 -181
@@ -18,19 +18,18 @@
18
18
  * Copyright (c) Microsoft Corporation. All rights reserved.
19
19
  * Licensed under the MIT License. See License.txt in the project root for license information.
20
20
  *--------------------------------------------------------------------------------------------*/
21
- import { ArrayUtils, Command, CommandService, DisposableCollection, Event, nls, QuickInputButton, QuickInputService, QuickPickInput, QuickPickItem, URI, } from '@theia/core';
21
+ import { ArrayUtils, CommandService, DisposableCollection, Event, nls, QuickInputButton, QuickInputService, QuickPickInput, QuickPickItem, URI, } from '@theia/core';
22
22
  import { inject, injectable } from '@theia/core/shared/inversify';
23
23
  import { NotebookKernelService, NotebookKernel, NotebookKernelMatchResult, SourceCommand } from './notebook-kernel-service';
24
24
  import { NotebookModel } from '../view-model/notebook-model';
25
25
  import { NotebookEditorWidget } from '../notebook-editor-widget';
26
26
  import { codicon, OpenerService } from '@theia/core/lib/browser';
27
27
  import { NotebookKernelHistoryService } from './notebook-kernel-history-service';
28
+ import { NotebookCommand, NotebookModelResource } from '../../common';
28
29
  import debounce = require('@theia/core/shared/lodash.debounce');
29
30
 
30
31
  export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter';
31
32
 
32
- export const NotebookKernelQuickPickService = Symbol('NotebookKernelQuickPickService');
33
-
34
33
  type KernelPick = QuickPickItem & { kernel: NotebookKernel };
35
34
  function isKernelPick(item: QuickPickInput<QuickPickItem>): item is KernelPick {
36
35
  return 'kernel' in item;
@@ -45,7 +44,7 @@ function isSourcePick(item: QuickPickInput<QuickPickItem>): item is SourcePick {
45
44
  }
46
45
  type InstallExtensionPick = QuickPickItem & { extensionIds: string[] };
47
46
 
48
- type KernelSourceQuickPickItem = QuickPickItem & { command: Command; documentation?: string };
47
+ type KernelSourceQuickPickItem = QuickPickItem & { command: NotebookCommand; documentation?: string };
49
48
  function isKernelSourceQuickPickItem(item: QuickPickItem): item is KernelSourceQuickPickItem {
50
49
  return 'command' in item;
51
50
  }
@@ -82,7 +81,7 @@ function toKernelQuickPick(kernel: NotebookKernel, selected: NotebookKernel | un
82
81
  }
83
82
 
84
83
  @injectable()
85
- export abstract class NotebookKernelQuickPickServiceImpl {
84
+ export class NotebookKernelQuickPickService {
86
85
 
87
86
  @inject(NotebookKernelService)
88
87
  protected readonly notebookKernelService: NotebookKernelService;
@@ -91,6 +90,12 @@ export abstract class NotebookKernelQuickPickServiceImpl {
91
90
  @inject(CommandService)
92
91
  protected readonly commandService: CommandService;
93
92
 
93
+ @inject(OpenerService)
94
+ protected openerService: OpenerService;
95
+
96
+ @inject(NotebookKernelHistoryService)
97
+ protected notebookKernelHistoryService: NotebookKernelHistoryService;
98
+
94
99
  async showQuickPick(editor: NotebookModel, wantedId?: string, skipAutoRun?: boolean): Promise<boolean> {
95
100
  const notebook = editor;
96
101
  const matchResult = this.getMatchingResult(notebook);
@@ -200,40 +205,6 @@ export abstract class NotebookKernelQuickPickServiceImpl {
200
205
  return false;
201
206
  }
202
207
 
203
- protected getMatchingResult(notebook: NotebookModel): NotebookKernelMatchResult {
204
- return this.notebookKernelService.getMatchingKernel(notebook);
205
- }
206
-
207
- protected abstract getKernelPickerQuickPickItems(matchResult: NotebookKernelMatchResult): QuickPickInput<KernelQuickPickItem>[];
208
-
209
- protected async handleQuickPick(editor: NotebookModel, pick: KernelQuickPickItem, quickPickItems: KernelQuickPickItem[]): Promise<boolean> {
210
- if (isKernelPick(pick)) {
211
- const newKernel = pick.kernel;
212
- this.selectKernel(editor, newKernel);
213
- return true;
214
- }
215
-
216
- if (isSourcePick(pick)) {
217
- // selected explicitly, it should trigger the execution?
218
- pick.action.run(this.commandService);
219
- }
220
-
221
- return true;
222
- }
223
-
224
- protected selectKernel(notebook: NotebookModel, kernel: NotebookKernel): void {
225
- this.notebookKernelService.selectKernelForNotebook(kernel, notebook);
226
- }
227
- }
228
- @injectable()
229
- export class KernelPickerMRUStrategy extends NotebookKernelQuickPickServiceImpl {
230
-
231
- @inject(OpenerService)
232
- protected openerService: OpenerService;
233
-
234
- @inject(NotebookKernelHistoryService)
235
- protected notebookKernelHistoryService: NotebookKernelHistoryService;
236
-
237
208
  protected getKernelPickerQuickPickItems(matchResult: NotebookKernelMatchResult): QuickPickInput<KernelQuickPickItem>[] {
238
209
  const quickPickItems: QuickPickInput<KernelQuickPickItem>[] = [];
239
210
 
@@ -266,17 +237,17 @@ export class KernelPickerMRUStrategy extends NotebookKernelQuickPickServiceImpl
266
237
  return quickPickItems;
267
238
  }
268
239
 
269
- protected override selectKernel(notebook: NotebookModel, kernel: NotebookKernel): void {
240
+ protected selectKernel(notebook: NotebookModel, kernel: NotebookKernel): void {
270
241
  const currentInfo = this.notebookKernelService.getMatchingKernel(notebook);
271
242
  if (currentInfo.selected) {
272
243
  // there is already a selected kernel
273
244
  this.notebookKernelHistoryService.addMostRecentKernel(currentInfo.selected);
274
245
  }
275
- super.selectKernel(notebook, kernel);
246
+ this.notebookKernelService.selectKernelForNotebook(kernel, notebook);
276
247
  this.notebookKernelHistoryService.addMostRecentKernel(kernel);
277
248
  }
278
249
 
279
- protected override getMatchingResult(notebook: NotebookModel): NotebookKernelMatchResult {
250
+ protected getMatchingResult(notebook: NotebookModel): NotebookKernelMatchResult {
280
251
  const { selected, all } = this.notebookKernelHistoryService.getKernels(notebook);
281
252
  const matchingResult = this.notebookKernelService.getMatchingKernel(notebook);
282
253
  return {
@@ -287,12 +258,23 @@ export class KernelPickerMRUStrategy extends NotebookKernelQuickPickServiceImpl
287
258
  };
288
259
  }
289
260
 
290
- protected override async handleQuickPick(editor: NotebookModel, pick: KernelQuickPickItem, items: KernelQuickPickItem[]): Promise<boolean> {
261
+ protected async handleQuickPick(editor: NotebookModel, pick: KernelQuickPickItem, items: KernelQuickPickItem[]): Promise<boolean> {
291
262
  if (pick.id === 'selectAnother') {
292
263
  return this.displaySelectAnotherQuickPick(editor, items.length === 1 && items[0] === pick);
293
264
  }
294
265
 
295
- return super.handleQuickPick(editor, pick, items);
266
+ if (isKernelPick(pick)) {
267
+ const newKernel = pick.kernel;
268
+ this.selectKernel(editor, newKernel);
269
+ return true;
270
+ }
271
+
272
+ if (isSourcePick(pick)) {
273
+ // selected explicitly, it should trigger the execution?
274
+ pick.action.run(this.commandService);
275
+ }
276
+
277
+ return true;
296
278
  }
297
279
 
298
280
  private async displaySelectAnotherQuickPick(editor: NotebookModel, kernelListEmpty: boolean): Promise<boolean> {
@@ -488,10 +470,8 @@ export class KernelPickerMRUStrategy extends NotebookKernelQuickPickServiceImpl
488
470
  quickPick.show();
489
471
  }
490
472
 
491
- private async executeCommand<T>(notebook: NotebookModel, command: string | Command): Promise<T | undefined | void> {
492
- const id = typeof command === 'string' ? command : command.id;
493
-
494
- return this.commandService.executeCommand(id, { uri: notebook.uri });
495
-
473
+ private async executeCommand<T>(notebook: NotebookModel, command: NotebookCommand): Promise<T | undefined | void> {
474
+ const args = (command.arguments || []).concat([NotebookModelResource.create(notebook.uri)]);
475
+ return this.commandService.executeCommand(command.id, ...args);
496
476
  }
497
477
  }
@@ -77,13 +77,12 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
77
77
  }));
78
78
  this.toDispose.push(this.editor.onDocumentContentChanged(e => {
79
79
  notebookModel.cellDirtyChanged(cell, true);
80
- cell.source = e.document.getText();
81
80
  }));
82
81
  }
83
82
  }
84
83
 
85
- protected assignRef = (component: HTMLDivElement) => {
86
- this.container = component;
84
+ protected setContainer(component: HTMLDivElement | null): void {
85
+ this.container = component ?? undefined;
87
86
  };
88
87
 
89
88
  protected handleResize = () => {
@@ -91,7 +90,10 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
91
90
  };
92
91
 
93
92
  override render(): React.ReactNode {
94
- return <div className='theia-notebook-cell-editor' onResize={this.handleResize} id={this.props.cell.uri.toString()} ref={this.assignRef}></div>;
95
- }
93
+ return <div className='theia-notebook-cell-editor' onResize={this.handleResize} id={this.props.cell.uri.toString()}
94
+ ref={container => this.setContainer(container)}>
95
+
96
+ </div>;
97
+ }
96
98
 
97
99
  }
@@ -47,7 +47,11 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
47
47
  super(props);
48
48
  this.state = { selectedCell: undefined, dragOverIndicator: undefined };
49
49
  this.toDispose.push(props.notebookModel.onDidAddOrRemoveCell(e => {
50
- this.setState({ selectedCell: undefined });
50
+ if (e.newCellIds && e.newCellIds.length > 0) {
51
+ this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]) });
52
+ } else {
53
+ this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell)});
54
+ }
51
55
  }));
52
56
  }
53
57
 
@@ -101,7 +105,8 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
101
105
 
102
106
  protected onDragStart(event: React.DragEvent<HTMLLIElement>, index: number): void {
103
107
  event.stopPropagation();
104
- event.dataTransfer.setData('text/notebook-cell-index', index.toString());
108
+ event.dataTransfer.setData('text/theia-notebook-cell-index', index.toString());
109
+ event.dataTransfer.setData('text/plain', this.props.notebookModel.cells[index].source);
105
110
  }
106
111
 
107
112
  protected onDragOver(event: React.DragEvent<HTMLLIElement>, cell: NotebookCellModel, position?: 'top' | 'bottom'): void {
@@ -112,7 +117,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
112
117
  }
113
118
 
114
119
  protected onDrop(event: React.DragEvent<HTMLLIElement>, dropElementIndex: number): void {
115
- const index = parseInt(event.dataTransfer.getData('text/notebook-cell-index'));
120
+ const index = parseInt(event.dataTransfer.getData('text/theia-notebook-cell-index'));
116
121
  const isTargetBelow = index < dropElementIndex;
117
122
  let newIdx = this.state.dragOverIndicator?.position === 'top' ? dropElementIndex : dropElementIndex + 1;
118
123
  newIdx = isTargetBelow ? newIdx - 1 : newIdx;
@@ -29,6 +29,7 @@ export interface NotebookCellToolbarItem {
29
29
  icon?: string;
30
30
  label?: string;
31
31
  onClick: (e: React.MouseEvent) => void;
32
+ contextKeys?: Set<string>
32
33
  }
33
34
 
34
35
  @injectable()
@@ -85,7 +86,8 @@ export class NotebookCellToolbarFactory {
85
86
  includeAnchorArg: false,
86
87
  args: [notebookModel, cell, output]
87
88
  }) :
88
- () => this.commandRegistry.executeCommand(menuNode.command!, notebookModel, cell, output)
89
+ () => this.commandRegistry.executeCommand(menuNode.command!, notebookModel, cell, output),
90
+ contextKeys: menuNode.when ? this.contextKeyService.parseKeys(menuNode.when) : undefined
89
91
  };
90
92
  }
91
93
  }
@@ -17,24 +17,27 @@ import * as React from '@theia/core/shared/react';
17
17
  import { ACTION_ITEM } from '@theia/core/lib/browser';
18
18
  import { NotebookCellToolbarItem } from './notebook-cell-toolbar-factory';
19
19
  import { DisposableCollection, Event } from '@theia/core';
20
+ import { ContextKeyChangeEvent } from '@theia/core/lib/browser/context-key-service';
20
21
 
21
22
  export interface NotebookCellToolbarProps {
22
23
  getMenuItems: () => NotebookCellToolbarItem[];
23
- onContextKeysChanged: Event<void>;
24
+ onContextKeysChanged: Event<ContextKeyChangeEvent>;
24
25
  }
25
26
 
26
27
  interface NotebookCellToolbarState {
27
28
  inlineItems: NotebookCellToolbarItem[];
28
29
  }
29
30
 
30
- abstract class NotebookCellActionItems extends React.Component<NotebookCellToolbarProps, NotebookCellToolbarState> {
31
+ abstract class NotebookCellActionBar extends React.Component<NotebookCellToolbarProps, NotebookCellToolbarState> {
31
32
 
32
33
  protected toDispose = new DisposableCollection();
33
34
 
34
35
  constructor(props: NotebookCellToolbarProps) {
35
36
  super(props);
36
37
  this.toDispose.push(props.onContextKeysChanged(e => {
37
- this.setState({ inlineItems: this.props.getMenuItems() });
38
+ if (this.props.getMenuItems().some(item => item.contextKeys ? e.affects(item.contextKeys) : false)) {
39
+ this.setState({ inlineItems: this.props.getMenuItems() });
40
+ }
38
41
  }));
39
42
  this.state = { inlineItems: this.props.getMenuItems() };
40
43
  }
@@ -49,7 +52,7 @@ abstract class NotebookCellActionItems extends React.Component<NotebookCellToolb
49
52
 
50
53
  }
51
54
 
52
- export class NotebookCellToolbar extends NotebookCellActionItems {
55
+ export class NotebookCellToolbar extends NotebookCellActionBar {
53
56
 
54
57
  override render(): React.ReactNode {
55
58
  return <div className='theia-notebook-cell-toolbar'>
@@ -59,7 +62,7 @@ export class NotebookCellToolbar extends NotebookCellActionItems {
59
62
 
60
63
  }
61
64
 
62
- export class NotebookCellSidebar extends NotebookCellActionItems {
65
+ export class NotebookCellSidebar extends NotebookCellActionBar {
63
66
 
64
67
  override render(): React.ReactNode {
65
68
  return <div className='theia-notebook-cell-sidebar'>
@@ -66,6 +66,15 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
66
66
  this.setState({ selectedKernelLabel: props.notebookKernelService.getSelectedOrSuggestedKernel(props.notebookModel)?.label });
67
67
  }
68
68
  }));
69
+
70
+ // TODO maybe we need a mechanism to check for changes in the menu to update this toolbar
71
+ const contextKeys = new Set<string>();
72
+ this.getMenuItems().filter(item => item.when).forEach(item => props.contextKeyService.parseKeys(item.when!)?.forEach(key => contextKeys.add(key)));
73
+ props.contextKeyService.onDidChange(e => {
74
+ if (e.affects(contextKeys)) {
75
+ this.forceUpdate();
76
+ }
77
+ });
69
78
  }
70
79
 
71
80
  override componentWillUnmount(): void {
@@ -20,12 +20,14 @@
20
20
 
21
21
  import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core';
22
22
  import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
23
+ import { ContextKeyChangeEvent } from '@theia/core/lib/browser/context-key-service';
23
24
  import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
24
25
  import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
25
26
  import {
26
- CellInternalMetadataChangedEvent, CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata,
27
- NotebookCellMetadata, NotebookCellOutputsSplice, CellOutput, CellData, NotebookCell, CellOutputItem
27
+ CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata,
28
+ NotebookCellMetadata, CellOutput, CellData, CellOutputItem
28
29
  } from '../../common';
30
+ import { NotebookCellOutputsSplice } from '../notebook-types';
29
31
  import { NotebookCellOutputModel } from './notebook-cell-output-model';
30
32
 
31
33
  export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
@@ -47,7 +49,28 @@ const NotebookCellContextManager = Symbol('NotebookCellContextManager');
47
49
  interface NotebookCellContextManager {
48
50
  updateCellContext(cell: NotebookCellModel, context: HTMLElement): void;
49
51
  dispose(): void;
50
- onDidChangeContext: Event<void>;
52
+ onDidChangeContext: Event<ContextKeyChangeEvent>;
53
+ }
54
+
55
+ export interface CellInternalMetadataChangedEvent {
56
+ readonly lastRunSuccessChanged?: boolean;
57
+ }
58
+
59
+ export interface NotebookCell {
60
+ readonly uri: URI;
61
+ handle: number;
62
+ language: string;
63
+ cellKind: CellKind;
64
+ outputs: CellOutput[];
65
+ metadata: NotebookCellMetadata;
66
+ internalMetadata: NotebookCellInternalMetadata;
67
+ text: string;
68
+ onDidChangeOutputs?: Event<NotebookCellOutputsSplice>;
69
+ onDidChangeOutputItems?: Event<CellOutput>;
70
+ onDidChangeLanguage: Event<string>;
71
+ onDidChangeMetadata: Event<void>;
72
+ onDidChangeInternalMetadata: Event<CellInternalMetadataChangedEvent>;
73
+
51
74
  }
52
75
 
53
76
  const NotebookCellModelProps = Symbol('NotebookModelProps');
@@ -144,6 +167,7 @@ export class NotebookCellModel implements NotebookCell, Disposable {
144
167
  }
145
168
  set source(source: string) {
146
169
  this.props.source = source;
170
+ this.textModel?.textEditorModel.setValue(source);
147
171
  }
148
172
  get language(): string {
149
173
  return this.props.language;
@@ -274,6 +298,9 @@ export class NotebookCellModel implements NotebookCell, Disposable {
274
298
 
275
299
  const ref = await this.textModelService.createModelReference(this.uri);
276
300
  this.textModel = ref.object;
301
+ this.textModel.onDidChangeContent(e => {
302
+ this.props.source = e.model.getText();
303
+ });
277
304
  return ref.object;
278
305
  }
279
306
  }
@@ -15,8 +15,8 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { Disposable, Emitter } from '@theia/core';
18
- import { BinaryBuffer } from '@theia/core/lib/common/buffer';
19
18
  import { CellOutput, CellOutputItem, isTextStreamMime } from '../../common';
19
+ import { compressOutputItemStreams } from '../notebook-output-utils';
20
20
 
21
21
  export class NotebookCellOutputModel implements Disposable {
22
22
 
@@ -73,10 +73,10 @@ export class NotebookCellOutputModel implements Disposable {
73
73
  if (this.outputs.length > 1 && this.outputs.every(item => isTextStreamMime(item.mime))) {
74
74
  // Look for the mimes in the items, and keep track of their order.
75
75
  // Merge the streams into one output item, per mime type.
76
- const mimeOutputs = new Map<string, BinaryBuffer[]>();
76
+ const mimeOutputs = new Map<string, Uint8Array[]>();
77
77
  const mimeTypes: string[] = [];
78
78
  this.outputs.forEach(item => {
79
- let items: BinaryBuffer[];
79
+ let items: Uint8Array[];
80
80
  if (mimeOutputs.has(item.mime)) {
81
81
  items = mimeOutputs.get(item.mime)!;
82
82
  } else {
@@ -84,13 +84,14 @@ export class NotebookCellOutputModel implements Disposable {
84
84
  mimeOutputs.set(item.mime, items);
85
85
  mimeTypes.push(item.mime);
86
86
  }
87
- items.push(item.data);
87
+ items.push(item.data.buffer);
88
88
  });
89
89
  this.outputs.length = 0;
90
90
  mimeTypes.forEach(mime => {
91
+ const compressionResult = compressOutputItemStreams(mimeOutputs.get(mime)!);
91
92
  this.outputs.push({
92
93
  mime,
93
- data: BinaryBuffer.concat(mimeOutputs.get(mime)!)
94
+ data: compressionResult.data
94
95
  });
95
96
  });
96
97
  }
@@ -17,18 +17,16 @@
17
17
  import { Disposable, Emitter, URI } from '@theia/core';
18
18
  import { Saveable, SaveOptions } from '@theia/core/lib/browser';
19
19
  import {
20
- CellData,
21
- CellEditOperation, CellEditType, CellUri, NotebookCellInternalMetadata,
20
+ CellData, CellEditType, CellUri, NotebookCellInternalMetadata,
22
21
  NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookData,
23
- NotebookDocumentMetadata, NotebookModelWillAddRemoveEvent,
24
- NotebookTextModelChangedEvent, NullablePartialNotebookCellInternalMetadata
22
+ NotebookDocumentMetadata,
25
23
  } from '../../common';
24
+ import { NotebookContentChangedEvent, NotebookModelWillAddRemoveEvent, CellEditOperation, NullablePartialNotebookCellInternalMetadata } from '../notebook-types';
26
25
  import { NotebookSerializer } from '../service/notebook-service';
27
26
  import { FileService } from '@theia/filesystem/lib/browser/file-service';
28
27
  import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-model';
29
28
  import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
30
29
  import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
31
- import { NotebookKernel } from '../service/notebook-kernel-service';
32
30
  import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service';
33
31
 
34
32
  export const NotebookModelFactory = Symbol('NotebookModelFactory');
@@ -62,7 +60,7 @@ export class NotebookModel implements Saveable, Disposable {
62
60
  protected readonly onDidAddOrRemoveCellEmitter = new Emitter<NotebookModelWillAddRemoveEvent>();
63
61
  readonly onDidAddOrRemoveCell = this.onDidAddOrRemoveCellEmitter.event;
64
62
 
65
- protected readonly onDidChangeContentEmitter = new Emitter<NotebookTextModelChangedEvent>();
63
+ protected readonly onDidChangeContentEmitter = new Emitter<NotebookContentChangedEvent[]>();
66
64
  readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
67
65
 
68
66
  @inject(FileService)
@@ -81,11 +79,19 @@ export class NotebookModel implements Saveable, Disposable {
81
79
  protected cellModelFactory: NotebookCellModelFactory;
82
80
  readonly autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange';
83
81
 
84
- nextHandle: number = 0;
82
+ protected nextHandle: number = 0;
85
83
 
86
- kernel?: NotebookKernel;
84
+ protected _dirty: boolean = false;
85
+
86
+ set dirty(dirty: boolean) {
87
+ this._dirty = dirty;
88
+ this.onDirtyChangedEmitter.fire();
89
+ }
90
+
91
+ get dirty(): boolean {
92
+ return this._dirty;
93
+ }
87
94
 
88
- dirty: boolean;
89
95
  selectedCell?: NotebookCellModel;
90
96
  protected dirtyCells: NotebookCellModel[] = [];
91
97
 
@@ -121,18 +127,6 @@ export class NotebookModel implements Saveable, Disposable {
121
127
 
122
128
  this.metadata = this.metadata;
123
129
 
124
- this.modelService.onDidCreate(editorModel => {
125
- const modelUri = new URI(editorModel.uri);
126
- if (modelUri.scheme === CellUri.scheme) {
127
- const cellUri = CellUri.parse(modelUri);
128
- if (cellUri && cellUri.notebook.isEqual(this.uri)) {
129
- const cell = this.cells.find(c => c.handle === cellUri.handle);
130
- if (cell) {
131
- cell.textModel = editorModel;
132
- }
133
- }
134
- }
135
- });
136
130
  this.nextHandle = this.cells.length;
137
131
  }
138
132
 
@@ -147,7 +141,6 @@ export class NotebookModel implements Saveable, Disposable {
147
141
  async save(options: SaveOptions): Promise<void> {
148
142
  this.dirtyCells = [];
149
143
  this.dirty = false;
150
- this.onDirtyChangedEmitter.fire();
151
144
 
152
145
  const serializedNotebook = await this.props.serializer.fromNotebook({
153
146
  cells: this.cells.map(cell => cell.getData()),
@@ -170,9 +163,34 @@ export class NotebookModel implements Saveable, Disposable {
170
163
  };
171
164
  }
172
165
 
166
+ async applySnapshot(snapshot: Saveable.Snapshot): Promise<void> {
167
+ const rawData = 'read' in snapshot ? snapshot.read() : snapshot.value;
168
+ if (!rawData) {
169
+ throw new Error('could not read notebook snapshot');
170
+ }
171
+ const data = JSON.parse(rawData);
172
+ const cells = data.cells.map((cell: CellData, index: number) => {
173
+ const handle = this.nextHandle++;
174
+ return this.cellModelFactory({
175
+ uri: CellUri.generate(this.uri, handle),
176
+ handle: handle,
177
+ source: cell.source,
178
+ language: cell.language,
179
+ cellKind: cell.cellKind,
180
+ outputs: cell.outputs,
181
+ metadata: cell.metadata,
182
+ internalMetadata: cell.internalMetadata,
183
+ collapseState: cell.collapseState
184
+ });
185
+ });
186
+ this.addCellOutputListeners(cells);
187
+
188
+ this.metadata = data.metadata;
189
+
190
+ }
191
+
173
192
  async revert(options?: Saveable.RevertOptions): Promise<void> {
174
193
  this.dirty = false;
175
- this.onDirtyChangedEmitter.fire();
176
194
  }
177
195
 
178
196
  isDirty(): boolean {
@@ -186,8 +204,8 @@ export class NotebookModel implements Saveable, Disposable {
186
204
  this.dirtyCells.splice(this.dirtyCells.indexOf(cell), 1);
187
205
  }
188
206
 
189
- const oldDirtyState = this.dirty;
190
- this.dirty = this.dirtyCells.length > 0;
207
+ const oldDirtyState = this._dirty;
208
+ this._dirty = this.dirtyCells.length > 0;
191
209
  if (this.dirty !== oldDirtyState) {
192
210
  this.onDirtyChangedEmitter.fire();
193
211
  }
@@ -211,7 +229,6 @@ export class NotebookModel implements Saveable, Disposable {
211
229
  for (const cell of cells) {
212
230
  cell.onDidChangeOutputs(() => {
213
231
  this.dirty = true;
214
- this.onDirtyChangedEmitter.fire();
215
232
  });
216
233
  }
217
234
  }
@@ -294,7 +311,7 @@ export class NotebookModel implements Saveable, Disposable {
294
311
  });
295
312
  this.addCellOutputListeners(cells);
296
313
 
297
- const changes: NotebookCellTextModelSplice<NotebookCellModel>[] = [[start, deleteCount, cells]];
314
+ const changes: NotebookCellTextModelSplice<NotebookCellModel>[] = [{ start, deleteCount, newItems: cells }];
298
315
 
299
316
  const deletedCells = this.cells.splice(start, deleteCount, ...cells);
300
317
 
@@ -308,8 +325,8 @@ export class NotebookModel implements Saveable, Disposable {
308
325
  async () => this.replaceCells(start, deleteCount, newCells, false));
309
326
  }
310
327
 
311
- this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes } });
312
- this.onDidChangeContentEmitter.fire({ rawEvents: [{ kind: NotebookCellsChangeType.ModelChange, changes }] });
328
+ this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) });
329
+ this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ModelChange, changes }]);
313
330
  }
314
331
 
315
332
  private changeCellInternalMetadataPartial(cell: NotebookCellModel, internalMetadata: NullablePartialNotebookCellInternalMetadata): void {
@@ -323,11 +340,9 @@ export class NotebookModel implements Saveable, Disposable {
323
340
  }
324
341
 
325
342
  cell.internalMetadata = newInternalMetadata;
326
- this.onDidChangeContentEmitter.fire({
327
- rawEvents: [
328
- { kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata }
329
- ]
330
- });
343
+ this.onDidChangeContentEmitter.fire([
344
+ { kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata }
345
+ ]);
331
346
  }
332
347
 
333
348
  private updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean): void {
@@ -340,10 +355,7 @@ export class NotebookModel implements Saveable, Disposable {
340
355
  }
341
356
 
342
357
  this.metadata = metadata;
343
- this.onDidChangeContentEmitter.fire({
344
- rawEvents: [{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }],
345
- synchronous: true,
346
- });
358
+ this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }]);
347
359
  }
348
360
 
349
361
  private changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void {
@@ -353,10 +365,7 @@ export class NotebookModel implements Saveable, Disposable {
353
365
 
354
366
  cell.language = languageId;
355
367
 
356
- this.onDidChangeContentEmitter.fire({
357
- rawEvents: [{ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }],
358
- synchronous: true,
359
- });
368
+ this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }]);
360
369
  }
361
370
 
362
371
  private moveCellToIndex(fromIndex: number, length: number, toIndex: number, computeUndoRedo: boolean): boolean {
@@ -369,9 +378,7 @@ export class NotebookModel implements Saveable, Disposable {
369
378
 
370
379
  const cells = this.cells.splice(fromIndex, length);
371
380
  this.cells.splice(toIndex, 0, ...cells);
372
- this.onDidChangeContentEmitter.fire({
373
- rawEvents: [{ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells }],
374
- });
381
+ this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells }]);
375
382
 
376
383
  return true;
377
384
  }