@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.
- package/lib/browser/contributions/cell-operations.d.ts +8 -0
- package/lib/browser/contributions/cell-operations.d.ts.map +1 -0
- package/lib/browser/contributions/cell-operations.js +37 -0
- package/lib/browser/contributions/cell-operations.js.map +1 -0
- package/lib/browser/contributions/notebook-actions-contribution.d.ts +10 -2
- package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.js +61 -7
- package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +16 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js +154 -17
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- package/lib/browser/notebook-editor-widget.d.ts +8 -2
- package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
- package/lib/browser/notebook-editor-widget.js +38 -13
- package/lib/browser/notebook-editor-widget.js.map +1 -1
- package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
- package/lib/browser/notebook-frontend-module.js +6 -9
- package/lib/browser/notebook-frontend-module.js.map +1 -1
- package/lib/browser/notebook-types.d.ts +10 -2
- package/lib/browser/notebook-types.d.ts.map +1 -1
- package/lib/browser/notebook-types.js.map +1 -1
- package/lib/browser/service/notebook-context-manager.d.ts +24 -0
- package/lib/browser/service/notebook-context-manager.d.ts.map +1 -0
- package/lib/browser/service/notebook-context-manager.js +112 -0
- package/lib/browser/service/notebook-context-manager.js.map +1 -0
- package/lib/browser/service/notebook-editor-widget-service.d.ts +3 -0
- package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-editor-widget-service.js +21 -11
- package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
- package/lib/browser/service/notebook-kernel-service.d.ts +1 -0
- package/lib/browser/service/notebook-kernel-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-kernel-service.js +6 -3
- package/lib/browser/service/notebook-kernel-service.js.map +1 -1
- package/lib/browser/service/notebook-monaco-text-model-service.d.ts +16 -0
- package/lib/browser/service/notebook-monaco-text-model-service.d.ts.map +1 -0
- package/lib/browser/service/notebook-monaco-text-model-service.js +49 -0
- package/lib/browser/service/notebook-monaco-text-model-service.js.map +1 -0
- package/lib/browser/service/notebook-service.d.ts +10 -2
- package/lib/browser/service/notebook-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-service.js +19 -6
- package/lib/browser/service/notebook-service.js.map +1 -1
- package/lib/browser/view/notebook-cell-editor.d.ts +7 -0
- package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-editor.js +43 -3
- package/lib/browser/view/notebook-cell-editor.js.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.js +6 -3
- package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar-factory.d.ts +2 -0
- package/lib/browser/view/notebook-cell-toolbar-factory.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar-factory.js +12 -6
- package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar.js +1 -1
- package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
- package/lib/browser/view/notebook-code-cell-view.d.ts +11 -0
- package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-code-cell-view.js +52 -4
- package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
- package/lib/browser/view/notebook-main-toolbar.d.ts +6 -1
- package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
- package/lib/browser/view/notebook-main-toolbar.js +23 -5
- package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
- package/lib/browser/view/notebook-markdown-cell-view.d.ts +2 -0
- package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-markdown-cell-view.js +9 -4
- package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
- package/lib/browser/view/notebook-viewport-service.d.ts +17 -0
- package/lib/browser/view/notebook-viewport-service.d.ts.map +1 -0
- package/lib/browser/view/notebook-viewport-service.js +60 -0
- package/lib/browser/view/notebook-viewport-service.js.map +1 -0
- package/lib/browser/view-model/notebook-cell-model.d.ts +9 -15
- package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.js +22 -23
- package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
- package/lib/browser/view-model/notebook-model.d.ts +7 -4
- package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-model.js +50 -6
- package/lib/browser/view-model/notebook-model.js.map +1 -1
- package/lib/common/notebook-common.d.ts +7 -0
- package/lib/common/notebook-common.d.ts.map +1 -1
- package/lib/common/notebook-common.js +12 -1
- package/lib/common/notebook-common.js.map +1 -1
- package/package.json +8 -7
- package/src/browser/contributions/cell-operations.ts +38 -0
- package/src/browser/contributions/notebook-actions-contribution.ts +66 -6
- package/src/browser/contributions/notebook-cell-actions-contribution.ts +194 -22
- package/src/browser/notebook-editor-widget.tsx +43 -17
- package/src/browser/notebook-frontend-module.ts +8 -11
- package/src/browser/notebook-types.ts +13 -1
- package/src/browser/service/notebook-context-manager.ts +127 -0
- package/src/browser/service/notebook-editor-widget-service.ts +20 -10
- package/src/browser/service/notebook-kernel-service.ts +6 -2
- package/src/browser/service/notebook-monaco-text-model-service.ts +48 -0
- package/src/browser/service/notebook-service.ts +26 -5
- package/src/browser/style/index.css +25 -8
- package/src/browser/view/notebook-cell-editor.tsx +52 -10
- package/src/browser/view/notebook-cell-list-view.tsx +8 -4
- package/src/browser/view/notebook-cell-toolbar-factory.tsx +9 -4
- package/src/browser/view/notebook-cell-toolbar.tsx +1 -1
- package/src/browser/view/notebook-code-cell-view.tsx +64 -8
- package/src/browser/view/notebook-main-toolbar.tsx +26 -5
- package/src/browser/view/notebook-markdown-cell-view.tsx +9 -4
- package/src/browser/view/notebook-viewport-service.ts +61 -0
- package/src/browser/view-model/notebook-cell-model.ts +26 -33
- package/src/browser/view-model/notebook-model.ts +70 -7
- package/src/common/notebook-common.ts +13 -0
- package/lib/browser/service/notebook-cell-context-manager.d.ts +0 -16
- package/lib/browser/service/notebook-cell-context-manager.d.ts.map +0 -1
- package/lib/browser/service/notebook-cell-context-manager.js +0 -74
- package/lib/browser/service/notebook-cell-context-manager.js.map +0 -1
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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 {
|
|
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.
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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={
|
|
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={
|
|
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={
|
|
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,
|
|
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: [
|
|
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
|
}
|