@theia/notebook 1.47.1 → 1.48.2

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 (112) hide show
  1. package/lib/browser/contributions/cell-operations.d.ts +8 -0
  2. package/lib/browser/contributions/cell-operations.d.ts.map +1 -0
  3. package/lib/browser/contributions/cell-operations.js +37 -0
  4. package/lib/browser/contributions/cell-operations.js.map +1 -0
  5. package/lib/browser/contributions/notebook-actions-contribution.d.ts +10 -2
  6. package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
  7. package/lib/browser/contributions/notebook-actions-contribution.js +61 -7
  8. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  9. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +16 -1
  10. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
  11. package/lib/browser/contributions/notebook-cell-actions-contribution.js +154 -17
  12. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  13. package/lib/browser/notebook-editor-widget.d.ts +8 -2
  14. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  15. package/lib/browser/notebook-editor-widget.js +38 -13
  16. package/lib/browser/notebook-editor-widget.js.map +1 -1
  17. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  18. package/lib/browser/notebook-frontend-module.js +6 -9
  19. package/lib/browser/notebook-frontend-module.js.map +1 -1
  20. package/lib/browser/notebook-types.d.ts +10 -2
  21. package/lib/browser/notebook-types.d.ts.map +1 -1
  22. package/lib/browser/notebook-types.js.map +1 -1
  23. package/lib/browser/service/notebook-context-manager.d.ts +24 -0
  24. package/lib/browser/service/notebook-context-manager.d.ts.map +1 -0
  25. package/lib/browser/service/notebook-context-manager.js +112 -0
  26. package/lib/browser/service/notebook-context-manager.js.map +1 -0
  27. package/lib/browser/service/notebook-editor-widget-service.d.ts +3 -0
  28. package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -1
  29. package/lib/browser/service/notebook-editor-widget-service.js +21 -11
  30. package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
  31. package/lib/browser/service/notebook-kernel-service.d.ts +1 -0
  32. package/lib/browser/service/notebook-kernel-service.d.ts.map +1 -1
  33. package/lib/browser/service/notebook-kernel-service.js +6 -3
  34. package/lib/browser/service/notebook-kernel-service.js.map +1 -1
  35. package/lib/browser/service/notebook-monaco-text-model-service.d.ts +16 -0
  36. package/lib/browser/service/notebook-monaco-text-model-service.d.ts.map +1 -0
  37. package/lib/browser/service/notebook-monaco-text-model-service.js +49 -0
  38. package/lib/browser/service/notebook-monaco-text-model-service.js.map +1 -0
  39. package/lib/browser/service/notebook-service.d.ts +10 -2
  40. package/lib/browser/service/notebook-service.d.ts.map +1 -1
  41. package/lib/browser/service/notebook-service.js +19 -6
  42. package/lib/browser/service/notebook-service.js.map +1 -1
  43. package/lib/browser/view/notebook-cell-editor.d.ts +7 -0
  44. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  45. package/lib/browser/view/notebook-cell-editor.js +43 -3
  46. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  47. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  48. package/lib/browser/view/notebook-cell-list-view.js +6 -3
  49. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  50. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts +2 -0
  51. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts.map +1 -1
  52. package/lib/browser/view/notebook-cell-toolbar-factory.js +12 -6
  53. package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
  54. package/lib/browser/view/notebook-cell-toolbar.js +1 -1
  55. package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
  56. package/lib/browser/view/notebook-code-cell-view.d.ts +11 -0
  57. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  58. package/lib/browser/view/notebook-code-cell-view.js +52 -4
  59. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  60. package/lib/browser/view/notebook-main-toolbar.d.ts +6 -1
  61. package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
  62. package/lib/browser/view/notebook-main-toolbar.js +23 -5
  63. package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
  64. package/lib/browser/view/notebook-markdown-cell-view.d.ts +2 -0
  65. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  66. package/lib/browser/view/notebook-markdown-cell-view.js +9 -4
  67. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  68. package/lib/browser/view/notebook-viewport-service.d.ts +17 -0
  69. package/lib/browser/view/notebook-viewport-service.d.ts.map +1 -0
  70. package/lib/browser/view/notebook-viewport-service.js +60 -0
  71. package/lib/browser/view/notebook-viewport-service.js.map +1 -0
  72. package/lib/browser/view-model/notebook-cell-model.d.ts +9 -15
  73. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  74. package/lib/browser/view-model/notebook-cell-model.js +22 -23
  75. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  76. package/lib/browser/view-model/notebook-model.d.ts +7 -4
  77. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  78. package/lib/browser/view-model/notebook-model.js +50 -6
  79. package/lib/browser/view-model/notebook-model.js.map +1 -1
  80. package/lib/common/notebook-common.d.ts +7 -0
  81. package/lib/common/notebook-common.d.ts.map +1 -1
  82. package/lib/common/notebook-common.js +12 -1
  83. package/lib/common/notebook-common.js.map +1 -1
  84. package/package.json +8 -7
  85. package/src/browser/contributions/cell-operations.ts +38 -0
  86. package/src/browser/contributions/notebook-actions-contribution.ts +66 -6
  87. package/src/browser/contributions/notebook-cell-actions-contribution.ts +194 -22
  88. package/src/browser/notebook-editor-widget.tsx +43 -17
  89. package/src/browser/notebook-frontend-module.ts +8 -11
  90. package/src/browser/notebook-types.ts +13 -1
  91. package/src/browser/service/notebook-context-manager.ts +127 -0
  92. package/src/browser/service/notebook-editor-widget-service.ts +20 -10
  93. package/src/browser/service/notebook-kernel-service.ts +6 -2
  94. package/src/browser/service/notebook-monaco-text-model-service.ts +48 -0
  95. package/src/browser/service/notebook-service.ts +26 -5
  96. package/src/browser/style/index.css +25 -8
  97. package/src/browser/view/notebook-cell-editor.tsx +52 -10
  98. package/src/browser/view/notebook-cell-list-view.tsx +8 -4
  99. package/src/browser/view/notebook-cell-toolbar-factory.tsx +9 -4
  100. package/src/browser/view/notebook-cell-toolbar.tsx +1 -1
  101. package/src/browser/view/notebook-code-cell-view.tsx +64 -8
  102. package/src/browser/view/notebook-main-toolbar.tsx +26 -5
  103. package/src/browser/view/notebook-markdown-cell-view.tsx +9 -4
  104. package/src/browser/view/notebook-viewport-service.ts +61 -0
  105. package/src/browser/view-model/notebook-cell-model.ts +26 -33
  106. package/src/browser/view-model/notebook-model.ts +70 -7
  107. package/src/common/notebook-common.ts +13 -0
  108. package/lib/browser/service/notebook-cell-context-manager.d.ts +0 -16
  109. package/lib/browser/service/notebook-cell-context-manager.d.ts.map +0 -1
  110. package/lib/browser/service/notebook-cell-context-manager.js +0 -74
  111. package/lib/browser/service/notebook-cell-context-manager.js.map +0 -1
  112. package/src/browser/service/notebook-cell-context-manager.ts +0 -72
@@ -0,0 +1,127 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { inject, injectable } from '@theia/core/shared/inversify';
18
+ import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service';
19
+ import { DisposableCollection, Emitter } from '@theia/core';
20
+ import { NotebookKernelService } from './notebook-kernel-service';
21
+ import {
22
+ NOTEBOOK_CELL_EDITABLE,
23
+ NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE,
24
+ NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
25
+ NOTEBOOK_CELL_TYPE, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED,
26
+ NOTEBOOK_VIEW_TYPE
27
+ } from '../contributions/notebook-context-keys';
28
+ import { NotebookEditorWidget } from '../notebook-editor-widget';
29
+ import { NotebookCellModel } from '../view-model/notebook-cell-model';
30
+ import { CellKind } from '../../common';
31
+ import { NotebookExecutionStateService } from './notebook-execution-state-service';
32
+
33
+ @injectable()
34
+ export class NotebookContextManager {
35
+ @inject(ContextKeyService) protected contextKeyService: ContextKeyService;
36
+
37
+ @inject(NotebookKernelService)
38
+ protected readonly notebookKernelService: NotebookKernelService;
39
+
40
+ @inject(NotebookExecutionStateService)
41
+ protected readonly executionStateService: NotebookExecutionStateService;
42
+
43
+ protected readonly toDispose = new DisposableCollection();
44
+
45
+ protected readonly onDidChangeContextEmitter = new Emitter<ContextKeyChangeEvent>();
46
+ readonly onDidChangeContext = this.onDidChangeContextEmitter.event;
47
+
48
+ protected _context?: HTMLElement;
49
+
50
+ scopedStore: ScopedValueStore;
51
+
52
+ get context(): HTMLElement | undefined {
53
+ return this._context;
54
+ }
55
+
56
+ init(widget: NotebookEditorWidget): void {
57
+ this._context = widget.node;
58
+ this.scopedStore = this.contextKeyService.createScoped(widget.node);
59
+
60
+ this.toDispose.dispose();
61
+
62
+ this.scopedStore.setContext(NOTEBOOK_VIEW_TYPE, widget?.notebookType);
63
+
64
+ // Kernel related keys
65
+ const kernel = widget?.model ? this.notebookKernelService.getSelectedNotebookKernel(widget.model) : undefined;
66
+ this.scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!kernel);
67
+ this.scopedStore.setContext(NOTEBOOK_KERNEL, kernel?.id);
68
+ this.toDispose.push(this.notebookKernelService.onDidChangeSelectedKernel(e => {
69
+ if (e.notebook.toString() === widget?.getResourceUri()?.toString()) {
70
+ this.scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!e.newKernel);
71
+ this.scopedStore.setContext(NOTEBOOK_KERNEL, e.newKernel);
72
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL]));
73
+ }
74
+ }));
75
+
76
+ // Cell Selection realted keys
77
+ this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell);
78
+ widget.model?.onDidChangeSelectedCell(e => {
79
+ this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!e);
80
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_FOCUSED]));
81
+ });
82
+
83
+ widget.model?.onDidChangeSelectedCell(e => this.selectedCellChanged(e));
84
+
85
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_VIEW_TYPE, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL]));
86
+ }
87
+
88
+ protected cellDisposables = new DisposableCollection();
89
+
90
+ selectedCellChanged(cell: NotebookCellModel | undefined): void {
91
+ this.cellDisposables.dispose();
92
+
93
+ this.scopedStore.setContext(NOTEBOOK_CELL_TYPE, cell ? cell.cellKind === CellKind.Code ? 'code' : 'markdown' : undefined);
94
+
95
+ if (cell) {
96
+ this.scopedStore.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cell.editing);
97
+ this.scopedStore.setContext(NOTEBOOK_CELL_EDITABLE, cell.cellKind === CellKind.Markup && !cell.editing);
98
+ this.cellDisposables.push(cell.onDidRequestCellEditChange(cellEdit => {
99
+ this.scopedStore.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit);
100
+ this.scopedStore.setContext(NOTEBOOK_CELL_EDITABLE, cell.cellKind === CellKind.Markup && !cellEdit);
101
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_MARKDOWN_EDIT_MODE]));
102
+ }));
103
+ this.cellDisposables.push(this.executionStateService.onDidChangeExecution(e => {
104
+ if (cell && e.affectsCell(cell.uri)) {
105
+ this.scopedStore.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed);
106
+ this.scopedStore.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle');
107
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE]));
108
+ }
109
+ }));
110
+ }
111
+
112
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_TYPE]));
113
+
114
+ }
115
+
116
+ onDidEditorTextFocus(focus: boolean): void {
117
+ this.scopedStore.setContext('inputFocus', focus);
118
+ }
119
+
120
+ createContextKeyChangedEvent(affectedKeys: string[]): ContextKeyChangeEvent {
121
+ return { affects: keys => affectedKeys.some(key => keys.has(key)) };
122
+ }
123
+
124
+ dispose(): void {
125
+ this.toDispose.dispose();
126
+ }
127
+ }
@@ -23,6 +23,8 @@ import { Emitter } from '@theia/core';
23
23
  import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
24
24
  import { ApplicationShell } from '@theia/core/lib/browser';
25
25
  import { NotebookEditorWidget } from '../notebook-editor-widget';
26
+ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
27
+ import { NOTEBOOK_EDITOR_FOCUSED } from '../contributions/notebook-context-keys';
26
28
 
27
29
  @injectable()
28
30
  export class NotebookEditorWidgetService {
@@ -30,6 +32,9 @@ export class NotebookEditorWidgetService {
30
32
  @inject(ApplicationShell)
31
33
  protected applicationShell: ApplicationShell;
32
34
 
35
+ @inject(ContextKeyService)
36
+ protected contextKeyService: ContextKeyService;
37
+
33
38
  private readonly notebookEditors = new Map<string, NotebookEditorWidget>();
34
39
 
35
40
  private readonly onNotebookEditorAddEmitter = new Emitter<NotebookEditorWidget>();
@@ -45,16 +50,7 @@ export class NotebookEditorWidgetService {
45
50
  @postConstruct()
46
51
  protected init(): void {
47
52
  this.applicationShell.onDidChangeActiveWidget(event => {
48
- if (event.newValue instanceof NotebookEditorWidget) {
49
- if (event.newValue !== this.focusedEditor) {
50
- this.focusedEditor = event.newValue;
51
- this.onDidChangeFocusedEditorEmitter.fire(this.focusedEditor);
52
- }
53
- } else if (event.newValue) {
54
- // Only unfocus editor if a new widget has been focused
55
- this.focusedEditor = undefined;
56
- this.onDidChangeFocusedEditorEmitter.fire(undefined);
57
- }
53
+ this.notebookEditorFocusChanged(event.newValue as NotebookEditorWidget, event.newValue instanceof NotebookEditorWidget);
58
54
  });
59
55
  }
60
56
 
@@ -85,4 +81,18 @@ export class NotebookEditorWidgetService {
85
81
  return Array.from(this.notebookEditors.values());
86
82
  }
87
83
 
84
+ notebookEditorFocusChanged(editor: NotebookEditorWidget, focus: boolean): void {
85
+ if (focus) {
86
+ if (editor !== this.focusedEditor) {
87
+ this.focusedEditor = editor;
88
+ this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, true);
89
+ this.onDidChangeFocusedEditorEmitter.fire(this.focusedEditor);
90
+ }
91
+ } else if (this.focusedEditor) {
92
+ this.focusedEditor = undefined;
93
+ this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, false);
94
+ this.onDidChangeFocusedEditorEmitter.fire(undefined);
95
+ }
96
+ }
97
+
88
98
  }
@@ -239,14 +239,18 @@ export class NotebookKernelService {
239
239
  const all = kernels.map(obj => obj.kernel);
240
240
 
241
241
  // bound kernel
242
- const selectedId = this.notebookBindings[`${notebook.viewType}/${notebook.uri}`];
243
- const selected = selectedId ? this.kernels.get(selectedId)?.kernel : undefined;
242
+ const selected = this.getSelectedNotebookKernel(notebook);
244
243
  const suggestions = kernels.filter(item => item.instanceAffinity > 1).map(item => item.kernel); // TODO implement notebookAffinity
245
244
  const hidden = kernels.filter(item => item.instanceAffinity < 0).map(item => item.kernel);
246
245
  return { all, selected, suggestions, hidden };
247
246
 
248
247
  }
249
248
 
249
+ getSelectedNotebookKernel(notebook: NotebookTextModelLike): NotebookKernel | undefined {
250
+ const selectedId = this.notebookBindings[`${notebook.viewType}/${notebook.uri}`];
251
+ return selectedId ? this.kernels.get(selectedId)?.kernel : undefined;
252
+ }
253
+
250
254
  selectKernelForNotebook(kernel: NotebookKernel | undefined, notebook: NotebookTextModelLike): void {
251
255
  const key = `${notebook.viewType}/${notebook.uri}`;
252
256
  const oldKernel = this.notebookBindings[key];
@@ -0,0 +1,48 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { ReferenceCollection, URI, Reference, Event } from '@theia/core';
18
+ import { inject, injectable } from '@theia/core/shared/inversify';
19
+ import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
20
+ import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
21
+ import { NotebookModel } from '../view-model/notebook-model';
22
+
23
+ /**
24
+ * special service for creating monaco textmodels for notebook cells.
25
+ * Its for optimization purposes since there is alot of overhead otherwise with calling the backend to create a document for each cell and other smaller things.
26
+ */
27
+ @injectable()
28
+ export class NotebookMonacoTextModelService {
29
+
30
+ @inject(MonacoTextModelService)
31
+ protected readonly monacoTextModelService: MonacoTextModelService;
32
+
33
+ protected readonly cellmodels = new ReferenceCollection<string, MonacoEditorModel>(
34
+ uri => this.monacoTextModelService.createUnmangedModel(new URI(uri))
35
+ );
36
+
37
+ getOrCreateNotebookCellModelReference(uri: URI): Promise<Reference<MonacoEditorModel>> {
38
+ return this.cellmodels.acquire(uri.toString());
39
+ }
40
+
41
+ async createTextModelsForNotebook(notebook: NotebookModel): Promise<void> {
42
+ await Promise.all(notebook.cells.map(cell => this.getOrCreateNotebookCellModelReference(cell.uri)));
43
+ }
44
+
45
+ get onDidCreateNotebookCellModel(): Event<MonacoEditorModel> {
46
+ return this.cellmodels.onDidCreate;
47
+ }
48
+ }
@@ -20,9 +20,10 @@ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
20
20
  import { NotebookData, TransientOptions } from '../../common';
21
21
  import { NotebookModel, NotebookModelFactory, NotebookModelProps } from '../view-model/notebook-model';
22
22
  import { FileService } from '@theia/filesystem/lib/browser/file-service';
23
- import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
24
23
  import { NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from '../view-model/notebook-cell-model';
25
24
  import { Deferred } from '@theia/core/lib/common/promise-util';
25
+ import { NotebookMonacoTextModelService } from './notebook-monaco-text-model-service';
26
+ import { CellEditOperation } from '../notebook-types';
26
27
 
27
28
  export const NotebookProvider = Symbol('notebook provider');
28
29
 
@@ -37,21 +38,28 @@ export interface NotebookSerializer {
37
38
  fromNotebook(data: NotebookData): Promise<BinaryBuffer>;
38
39
  }
39
40
 
41
+ export interface NotebookWorkspaceEdit {
42
+ edits: {
43
+ resource: URI;
44
+ edit: CellEditOperation
45
+ }[]
46
+ }
47
+
40
48
  @injectable()
41
49
  export class NotebookService implements Disposable {
42
50
 
43
51
  @inject(FileService)
44
52
  protected fileService: FileService;
45
53
 
46
- @inject(MonacoTextModelService)
47
- protected modelService: MonacoTextModelService;
48
-
49
54
  @inject(NotebookModelFactory)
50
55
  protected notebookModelFactory: (props: NotebookModelProps) => NotebookModel;
51
56
 
52
57
  @inject(NotebookCellModelFactory)
53
58
  protected notebookCellModelFactory: (props: NotebookCellModelProps) => NotebookCellModel;
54
59
 
60
+ @inject(NotebookMonacoTextModelService)
61
+ protected textModelService: NotebookMonacoTextModelService;
62
+
55
63
  protected willUseNotebookSerializerEmitter = new Emitter<string>();
56
64
  readonly onWillUseNotebookSerializer = this.willUseNotebookSerializerEmitter.event;
57
65
 
@@ -111,7 +119,7 @@ export class NotebookService implements Disposable {
111
119
  this.notebookModels.set(resource.uri.toString(), model);
112
120
  // Resolve cell text models right after creating the notebook model
113
121
  // This ensures that all text models are available in the plugin host
114
- await Promise.all(model.cells.map(e => e.resolveTextModel()));
122
+ this.textModelService.createTextModelsForNotebook(model);
115
123
  this.didAddNotebookDocumentEmitter.fire(model);
116
124
  return model;
117
125
  }
@@ -178,4 +186,17 @@ export class NotebookService implements Disposable {
178
186
  listNotebookDocuments(): NotebookModel[] {
179
187
  return [...this.notebookModels.values()];
180
188
  }
189
+
190
+ applyWorkspaceEdit(workspaceEdit: NotebookWorkspaceEdit): boolean {
191
+ try {
192
+ workspaceEdit.edits.forEach(edit => {
193
+ const notebook = this.getNotebookEditorModel(edit.resource);
194
+ notebook?.applyEdits([edit.edit], true);
195
+ });
196
+ return true;
197
+ } catch (e) {
198
+ console.error(e);
199
+ return false;
200
+ }
201
+ }
181
202
  }
@@ -129,11 +129,27 @@
129
129
  background-color: var(--theia-editor-background);
130
130
  }
131
131
 
132
- .theia-notebook-cell-sidebar {
132
+ .theia-notebook-cell-sidebar-toolbar {
133
133
  display: flex;
134
134
  flex-direction: column;
135
135
  padding: 2px;
136
136
  background-color: var(--theia-editor-background);
137
+ flex-grow: 1;
138
+ }
139
+
140
+ .theia-notebook-cell-sidebar {
141
+ display: flex;
142
+ flex-direction: column;
143
+ }
144
+
145
+ .theia-notebook-code-cell-execution-order {
146
+ display: block;
147
+ font-family: var(--monaco-monospace-font);
148
+ font-size: 10px;
149
+ opacity: 0.7;
150
+ text-align: center;
151
+ white-space: pre;
152
+ padding: 5px 0;
137
153
  }
138
154
 
139
155
  .theia-notebook-cell-toolbar-item {
@@ -159,22 +175,23 @@
159
175
  flex-direction: row;
160
176
  }
161
177
 
162
- .theia-notebook-cell-sidebar {
178
+ .theia-notebook-main-container {
163
179
  display: flex;
164
180
  flex-direction: column;
181
+ height: 100%;
182
+ overflow: hidden;
165
183
  }
166
184
 
167
- .theia-notebook-main-container {
185
+ .theia-notebook-viewport {
168
186
  display: flex;
169
- flex-direction: column;
170
- height: 100%;
171
187
  overflow: hidden;
188
+ height: 100%;
172
189
  }
173
190
 
174
191
  .theia-notebook-scroll-container {
175
- flex: 1;
176
- overflow: hidden;
177
- position: relative;
192
+ flex: 1;
193
+ overflow: hidden;
194
+ position: relative;
178
195
  }
179
196
 
180
197
  .theia-notebook-main-toolbar {
@@ -18,17 +18,24 @@ import * as React from '@theia/core/shared/react';
18
18
  import { NotebookModel } from '../view-model/notebook-model';
19
19
  import { NotebookCellModel } from '../view-model/notebook-cell-model';
20
20
  import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
21
- import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor';
21
+ import { MonacoEditor, MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor';
22
22
  import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
23
- import { DisposableCollection } from '@theia/core';
23
+ import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey';
24
+ import { NotebookContextManager } from '../service/notebook-context-manager';
25
+ import { DisposableCollection, OS } from '@theia/core';
26
+ import { NotebookViewportService } from './notebook-viewport-service';
27
+ import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo';
24
28
 
25
29
  interface CellEditorProps {
26
30
  notebookModel: NotebookModel,
27
31
  cell: NotebookCellModel,
28
- monacoServices: MonacoEditorServices
32
+ monacoServices: MonacoEditorServices,
33
+ notebookContextManager: NotebookContextManager;
34
+ notebookViewportService?: NotebookViewportService,
35
+ fontInfo?: BareFontInfo;
29
36
  }
30
37
 
31
- const DEFAULT_EDITOR_OPTIONS = {
38
+ const DEFAULT_EDITOR_OPTIONS: MonacoEditor.IOptions = {
32
39
  ...MonacoEditorProvider.inlineOptions,
33
40
  minHeight: -1,
34
41
  maxHeight: -1,
@@ -46,7 +53,27 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
46
53
 
47
54
  override componentDidMount(): void {
48
55
  this.disposeEditor();
49
- this.initEditor();
56
+ this.toDispose.push(this.props.cell.onWillFocusCellEditor(() => {
57
+ this.editor?.getControl().focus();
58
+ }));
59
+ this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(() => {
60
+ if (this.props.notebookModel.selectedCell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
61
+ if (document.activeElement && 'blur' in document.activeElement) {
62
+ (document.activeElement as HTMLElement).blur();
63
+ }
64
+ }
65
+ }));
66
+ if (!this.props.notebookViewportService || (this.container && this.props.notebookViewportService.isElementInViewport(this.container))) {
67
+ this.initEditor();
68
+ } else {
69
+ const disposable = this.props.notebookViewportService?.onDidChangeViewport(() => {
70
+ if (!this.editor && this.container && this.props.notebookViewportService!.isElementInViewport(this.container)) {
71
+ this.initEditor();
72
+ disposable.dispose();
73
+ }
74
+ });
75
+ this.toDispose.push(disposable);
76
+ }
50
77
  }
51
78
 
52
79
  override componentWillUnmount(): void {
@@ -62,13 +89,15 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
62
89
  const { cell, notebookModel, monacoServices } = this.props;
63
90
  if (this.container) {
64
91
  const editorNode = this.container;
92
+ editorNode.style.height = '';
65
93
  const editorModel = await cell.resolveTextModel();
66
94
  const uri = cell.uri;
67
95
  this.editor = new SimpleMonacoEditor(uri,
68
96
  editorModel,
69
97
  editorNode,
70
98
  monacoServices,
71
- DEFAULT_EDITOR_OPTIONS);
99
+ DEFAULT_EDITOR_OPTIONS,
100
+ [[IContextKeyService, this.props.notebookContextManager.scopedStore]]);
72
101
  this.toDispose.push(this.editor);
73
102
  this.editor.setLanguage(cell.language);
74
103
  this.toDispose.push(this.editor.getControl().onDidContentSizeChange(() => {
@@ -78,6 +107,15 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
78
107
  this.toDispose.push(this.editor.onDocumentContentChanged(e => {
79
108
  notebookModel.cellDirtyChanged(cell, true);
80
109
  }));
110
+ this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => {
111
+ this.props.notebookContextManager.onDidEditorTextFocus(true);
112
+ }));
113
+ this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => {
114
+ this.props.notebookContextManager.onDidEditorTextFocus(false);
115
+ }));
116
+ if (cell.editing && notebookModel.selectedCell === cell) {
117
+ this.editor.getControl().focus();
118
+ }
81
119
  }
82
120
  }
83
121
 
@@ -89,11 +127,15 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
89
127
  this.editor?.refresh();
90
128
  };
91
129
 
130
+ protected estimateHeight(): string {
131
+ const lineHeight = this.props.fontInfo?.lineHeight ?? 20;
132
+ return this.props.cell.text.split(OS.backend.EOL).length * lineHeight + 10 + 7 + 'px';
133
+ }
134
+
92
135
  override render(): React.ReactNode {
93
136
  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
- }
137
+ ref={container => this.setContainer(container)} style={{ height: this.editor ? undefined : this.estimateHeight() }}>
138
+ </div >;
139
+ }
98
140
 
99
141
  }
@@ -50,9 +50,13 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
50
50
  if (e.newCellIds && e.newCellIds.length > 0) {
51
51
  this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]) });
52
52
  } else {
53
- this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell)});
53
+ this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell) });
54
54
  }
55
55
  }));
56
+
57
+ this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(cell => {
58
+ this.setState({ ...this.state, selectedCell: cell });
59
+ }));
56
60
  }
57
61
 
58
62
  override componentWillUnmount(): void {
@@ -71,15 +75,15 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
71
75
  onDragOver={e => this.onDragOver(e, cell, 'top')} />
72
76
  {this.shouldRenderDragOverIndicator(cell, 'top') && <CellDropIndicator />}
73
77
  <li className={'theia-notebook-cell' + (this.state.selectedCell === cell ? ' focused' : '') + (this.isEnabled() ? ' draggable' : '')}
74
- onClick={() => {
75
- this.setState({ selectedCell: cell });
78
+ onClick={e => {
79
+ this.setState({ ...this.state, selectedCell: cell });
76
80
  this.props.notebookModel.setSelectedCell(cell);
77
81
  }}
78
82
  onDragStart={e => this.onDragStart(e, index)}
79
83
  onDragOver={e => this.onDragOver(e, cell)}
80
84
  onDrop={e => this.onDrop(e, index)}
81
85
  draggable={true}
82
- ref={(node: HTMLLIElement) => cell.refChanged(node)}>
86
+ ref={ref => cell === this.state.selectedCell && ref?.scrollIntoView({ block: 'nearest' })}>
83
87
  <div className={'theia-notebook-cell-marker' + (this.state.selectedCell === cell ? ' theia-notebook-cell-marker-selected' : '')}></div>
84
88
  <div className='theia-notebook-cell-content'>
85
89
  {this.renderCellContent(cell, index)}
@@ -23,6 +23,7 @@ import { ContextMenuRenderer } from '@theia/core/lib/browser';
23
23
  import { NotebookModel } from '../view-model/notebook-model';
24
24
  import { NotebookCellModel } from '../view-model/notebook-cell-model';
25
25
  import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-model';
26
+ import { NotebookContextManager } from '../service/notebook-context-manager';
26
27
 
27
28
  export interface NotebookCellToolbarItem {
28
29
  id: string;
@@ -48,21 +49,24 @@ export class NotebookCellToolbarFactory {
48
49
  @inject(CommandRegistry)
49
50
  protected readonly commandRegistry: CommandRegistry;
50
51
 
52
+ @inject(NotebookContextManager)
53
+ protected readonly notebookContextManager: NotebookContextManager;
54
+
51
55
  renderCellToolbar(menuPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode {
52
56
  return <NotebookCellToolbar getMenuItems={() => this.getMenuItems(menuPath, notebookModel, cell)}
53
- onContextKeysChanged={cell.notebookCellContextManager.onDidChangeContext} />;
57
+ onContextKeysChanged={this.notebookContextManager.onDidChangeContext} />;
54
58
  }
55
59
 
56
60
  renderSidebar(menuPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): React.ReactNode {
57
61
  return <NotebookCellSidebar getMenuItems={() => this.getMenuItems(menuPath, notebookModel, cell, output)}
58
- onContextKeysChanged={cell.notebookCellContextManager.onDidChangeContext} />;
62
+ onContextKeysChanged={this.notebookContextManager.onDidChangeContext} />;
59
63
  }
60
64
 
61
65
  private getMenuItems(menuItemPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): NotebookCellToolbarItem[] {
62
66
  const inlineItems: NotebookCellToolbarItem[] = [];
63
67
 
64
68
  for (const menuNode of this.menuRegistry.getMenu(menuItemPath).children) {
65
- if (!menuNode.when || this.contextKeyService.match(menuNode.when, cell.context ?? undefined)) {
69
+ if (!menuNode.when || this.contextKeyService.match(menuNode.when, this.notebookContextManager.context)) {
66
70
  if (menuNode.role === CompoundMenuNodeRole.Flat) {
67
71
  inlineItems.push(...menuNode.children?.map(child => this.createToolbarItem(child, notebookModel, cell, output)) ?? []);
68
72
  } else {
@@ -85,7 +89,8 @@ export class NotebookCellToolbarFactory {
85
89
  anchor: e.nativeEvent,
86
90
  menuPath,
87
91
  includeAnchorArg: false,
88
- args: [notebookModel, cell, output]
92
+ args: [cell],
93
+ context: this.notebookContextManager.context
89
94
  }) :
90
95
  () => this.commandRegistry.executeCommand(menuNode.command!, notebookModel, cell, output),
91
96
  isVisible: () => menuPath ? true : Boolean(this.commandRegistry.getVisibleHandler(menuNode.command!, notebookModel, cell, output)),
@@ -65,7 +65,7 @@ export class NotebookCellToolbar extends NotebookCellActionBar {
65
65
  export class NotebookCellSidebar extends NotebookCellActionBar {
66
66
 
67
67
  override render(): React.ReactNode {
68
- return <div className='theia-notebook-cell-sidebar'>
68
+ return <div className='theia-notebook-cell-sidebar-toolbar'>
69
69
  {this.state.inlineItems.filter(e => e.isVisible()).map(item => this.renderItem(item))}
70
70
  </div>;
71
71
  }