@theia/notebook 1.49.1 → 1.50.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.
- package/lib/browser/contributions/cell-operations.js +1 -1
- package/lib/browser/contributions/cell-operations.js.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.d.ts +3 -0
- package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.js +54 -34
- package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +2 -2
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js +42 -42
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-color-contribution.js +2 -2
- package/lib/browser/contributions/notebook-color-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-context-keys.d.ts +4 -1
- package/lib/browser/contributions/notebook-context-keys.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-context-keys.js +8 -2
- package/lib/browser/contributions/notebook-context-keys.js.map +1 -1
- package/lib/browser/contributions/notebook-label-provider-contribution.d.ts +3 -0
- package/lib/browser/contributions/notebook-label-provider-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-label-provider-contribution.js +33 -13
- package/lib/browser/contributions/notebook-label-provider-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-outline-contribution.d.ts +0 -2
- package/lib/browser/contributions/notebook-outline-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-outline-contribution.js +21 -17
- package/lib/browser/contributions/notebook-outline-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-output-action-contribution.js +7 -7
- package/lib/browser/contributions/notebook-output-action-contribution.js.map +1 -1
- package/lib/browser/index.js +11 -11
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/notebook-cell-resource-resolver.js +14 -14
- package/lib/browser/notebook-cell-resource-resolver.js.map +1 -1
- package/lib/browser/notebook-editor-widget-factory.d.ts +3 -2
- package/lib/browser/notebook-editor-widget-factory.d.ts.map +1 -1
- package/lib/browser/notebook-editor-widget-factory.js +10 -10
- package/lib/browser/notebook-editor-widget-factory.js.map +1 -1
- package/lib/browser/notebook-editor-widget.d.ts +3 -0
- package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
- package/lib/browser/notebook-editor-widget.js +32 -26
- 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 +1 -0
- package/lib/browser/notebook-frontend-module.js.map +1 -1
- package/lib/browser/notebook-open-handler.js +2 -2
- package/lib/browser/notebook-open-handler.js.map +1 -1
- package/lib/browser/notebook-output-utils.js.map +1 -1
- package/lib/browser/notebook-renderer-registry.js +2 -2
- package/lib/browser/notebook-renderer-registry.js.map +1 -1
- package/lib/browser/notebook-type-registry.js +6 -6
- package/lib/browser/notebook-type-registry.js.map +1 -1
- package/lib/browser/notebook-types.d.ts +6 -6
- package/lib/browser/notebook-types.d.ts.map +1 -1
- package/lib/browser/notebook-types.js +1 -1
- package/lib/browser/notebook-types.js.map +1 -1
- package/lib/browser/renderers/cell-output-webview.d.ts +1 -1
- package/lib/browser/renderers/cell-output-webview.d.ts.map +1 -1
- package/lib/browser/service/notebook-clipboard-service.js +4 -4
- package/lib/browser/service/notebook-clipboard-service.js.map +1 -1
- package/lib/browser/service/notebook-context-manager.d.ts.map +1 -1
- package/lib/browser/service/notebook-context-manager.js +14 -10
- package/lib/browser/service/notebook-context-manager.js.map +1 -1
- package/lib/browser/service/notebook-editor-widget-service.js +10 -10
- package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
- package/lib/browser/service/notebook-execution-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-execution-service.js +22 -12
- package/lib/browser/service/notebook-execution-service.js.map +1 -1
- package/lib/browser/service/notebook-execution-state-service.d.ts +1 -1
- package/lib/browser/service/notebook-execution-state-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-execution-state-service.js +22 -22
- package/lib/browser/service/notebook-execution-state-service.js.map +1 -1
- package/lib/browser/service/notebook-kernel-history-service.js +12 -12
- package/lib/browser/service/notebook-kernel-history-service.js.map +1 -1
- package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts +7 -7
- package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-kernel-quick-pick-service.js +12 -12
- package/lib/browser/service/notebook-kernel-quick-pick-service.js.map +1 -1
- package/lib/browser/service/notebook-kernel-service.js +10 -10
- package/lib/browser/service/notebook-kernel-service.js.map +1 -1
- package/lib/browser/service/notebook-model-resolver-service.d.ts +1 -1
- package/lib/browser/service/notebook-model-resolver-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-model-resolver-service.js +19 -25
- package/lib/browser/service/notebook-model-resolver-service.js.map +1 -1
- package/lib/browser/service/notebook-monaco-text-model-service.js +6 -6
- package/lib/browser/service/notebook-monaco-text-model-service.js.map +1 -1
- package/lib/browser/service/notebook-renderer-messaging-service.js +4 -4
- package/lib/browser/service/notebook-renderer-messaging-service.js.map +1 -1
- package/lib/browser/service/notebook-service.d.ts +1 -1
- package/lib/browser/service/notebook-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-service.js +31 -25
- package/lib/browser/service/notebook-service.js.map +1 -1
- package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-editor.js +27 -8
- package/lib/browser/view/notebook-cell-editor.js.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.d.ts +4 -1
- package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.js +30 -9
- package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar-factory.js +14 -14
- package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
- package/lib/browser/view/notebook-code-cell-view.d.ts +1 -0
- package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-code-cell-view.js +26 -20
- package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
- package/lib/browser/view/notebook-main-toolbar.d.ts +18 -2
- package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
- package/lib/browser/view/notebook-main-toolbar.js +75 -17
- package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
- package/lib/browser/view/notebook-markdown-cell-view.d.ts +1 -0
- package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-markdown-cell-view.js +14 -8
- package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
- package/lib/browser/view/notebook-viewport-service.js +2 -2
- package/lib/browser/view/notebook-viewport-service.js.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.d.ts +5 -4
- package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.js +19 -20
- package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
- package/lib/browser/view-model/notebook-cell-output-model.js +7 -7
- package/lib/browser/view-model/notebook-cell-output-model.js.map +1 -1
- package/lib/browser/view-model/notebook-model.d.ts +16 -6
- package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-model.js +73 -66
- package/lib/browser/view-model/notebook-model.js.map +1 -1
- package/lib/common/index.js +2 -2
- package/lib/common/index.js.map +1 -1
- package/lib/common/notebook-common.d.ts +4 -4
- package/lib/common/notebook-common.d.ts.map +1 -1
- package/lib/common/notebook-common.js +8 -8
- package/lib/common/notebook-common.js.map +1 -1
- package/package.json +8 -8
- package/src/browser/contributions/notebook-actions-contribution.ts +34 -12
- package/src/browser/contributions/notebook-cell-actions-contribution.ts +20 -19
- package/src/browser/contributions/notebook-context-keys.ts +7 -0
- package/src/browser/contributions/notebook-label-provider-contribution.ts +30 -8
- package/src/browser/contributions/notebook-outline-contribution.ts +11 -9
- package/src/browser/notebook-editor-widget-factory.ts +2 -3
- package/src/browser/notebook-editor-widget.tsx +8 -0
- package/src/browser/notebook-frontend-module.ts +2 -1
- package/src/browser/service/notebook-context-manager.ts +6 -1
- package/src/browser/service/notebook-execution-service.ts +12 -0
- package/src/browser/service/notebook-model-resolver-service.ts +12 -18
- package/src/browser/service/notebook-monaco-text-model-service.ts +2 -2
- package/src/browser/service/notebook-service.ts +20 -17
- package/src/browser/style/index.css +10 -1
- package/src/browser/view/notebook-cell-editor.tsx +27 -6
- package/src/browser/view/notebook-cell-list-view.tsx +35 -9
- package/src/browser/view/notebook-code-cell-view.tsx +7 -0
- package/src/browser/view/notebook-main-toolbar.tsx +82 -7
- package/src/browser/view/notebook-markdown-cell-view.tsx +6 -0
- package/src/browser/view-model/notebook-cell-model.ts +8 -6
- package/src/browser/view-model/notebook-model.ts +60 -49
- package/src/common/notebook-common.ts +1 -1
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
.theia-notebook-cell-divider {
|
|
174
|
-
height:
|
|
174
|
+
height: 25px;
|
|
175
175
|
width: 100%;
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -225,6 +225,7 @@
|
|
|
225
225
|
|
|
226
226
|
.theia-notebook-main-toolbar-item-text {
|
|
227
227
|
padding: 0 4px;
|
|
228
|
+
white-space: nowrap;
|
|
228
229
|
}
|
|
229
230
|
|
|
230
231
|
.theia-notebook-toolbar-separator {
|
|
@@ -280,3 +281,11 @@
|
|
|
280
281
|
line-height: 22px;
|
|
281
282
|
opacity: 0.7;
|
|
282
283
|
}
|
|
284
|
+
|
|
285
|
+
.theia-notebook-drag-ghost-image {
|
|
286
|
+
position: absolute;
|
|
287
|
+
top: -99999px;
|
|
288
|
+
left: -99999px;
|
|
289
|
+
height: 500px;
|
|
290
|
+
width: 500px;
|
|
291
|
+
}
|
|
@@ -25,6 +25,7 @@ import { NotebookContextManager } from '../service/notebook-context-manager';
|
|
|
25
25
|
import { DisposableCollection, OS } from '@theia/core';
|
|
26
26
|
import { NotebookViewportService } from './notebook-viewport-service';
|
|
27
27
|
import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo';
|
|
28
|
+
import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE } from '../contributions/notebook-context-keys';
|
|
28
29
|
|
|
29
30
|
interface CellEditorProps {
|
|
30
31
|
notebookModel: NotebookModel,
|
|
@@ -54,8 +55,19 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
54
55
|
|
|
55
56
|
override componentDidMount(): void {
|
|
56
57
|
this.disposeEditor();
|
|
57
|
-
this.toDispose.push(this.props.cell.onWillFocusCellEditor(
|
|
58
|
+
this.toDispose.push(this.props.cell.onWillFocusCellEditor(focusRequest => {
|
|
58
59
|
this.editor?.getControl().focus();
|
|
60
|
+
const lineCount = this.editor?.getControl().getModel()?.getLineCount();
|
|
61
|
+
if (focusRequest && lineCount) {
|
|
62
|
+
this.editor?.getControl().setPosition(focusRequest === 'lastLine' ?
|
|
63
|
+
{ lineNumber: lineCount, column: 1 } :
|
|
64
|
+
{ lineNumber: focusRequest, column: 1 },
|
|
65
|
+
'keyboard');
|
|
66
|
+
}
|
|
67
|
+
const currentLine = this.editor?.getControl().getPosition()?.lineNumber;
|
|
68
|
+
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, currentLine === 1);
|
|
69
|
+
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount);
|
|
70
|
+
|
|
59
71
|
}));
|
|
60
72
|
|
|
61
73
|
this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => {
|
|
@@ -66,11 +78,9 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
66
78
|
this.editor?.setLanguage(language);
|
|
67
79
|
}));
|
|
68
80
|
|
|
69
|
-
this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
(document.activeElement as HTMLElement).blur();
|
|
73
|
-
}
|
|
81
|
+
this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => {
|
|
82
|
+
if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
|
|
83
|
+
this.props.notebookContextManager.context?.focus();
|
|
74
84
|
}
|
|
75
85
|
}));
|
|
76
86
|
if (!this.props.notebookViewportService || (this.container && this.props.notebookViewportService.isElementInViewport(this.container))) {
|
|
@@ -119,10 +129,21 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
119
129
|
}));
|
|
120
130
|
this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => {
|
|
121
131
|
this.props.notebookContextManager.onDidEditorTextFocus(true);
|
|
132
|
+
this.props.notebookModel.setSelectedCell(cell, false);
|
|
122
133
|
}));
|
|
123
134
|
this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => {
|
|
124
135
|
this.props.notebookContextManager.onDidEditorTextFocus(false);
|
|
125
136
|
}));
|
|
137
|
+
this.toDispose.push(this.editor.getControl().onDidChangeCursorPosition(e => {
|
|
138
|
+
if (e.secondaryPositions.length === 0) {
|
|
139
|
+
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, e.position.lineNumber === 1);
|
|
140
|
+
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE,
|
|
141
|
+
e.position.lineNumber === this.editor!.getControl().getModel()!.getLineCount());
|
|
142
|
+
} else {
|
|
143
|
+
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, false);
|
|
144
|
+
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, false);
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
126
147
|
if (cell.editing && notebookModel.selectedCell === cell) {
|
|
127
148
|
this.editor.getControl().focus();
|
|
128
149
|
}
|
|
@@ -25,6 +25,7 @@ import { NotebookCellActionContribution } from '../contributions/notebook-cell-a
|
|
|
25
25
|
|
|
26
26
|
export interface CellRenderer {
|
|
27
27
|
render(notebookData: NotebookModel, cell: NotebookCellModel, index: number): React.ReactNode
|
|
28
|
+
renderDragImage(cell: NotebookCellModel): HTMLElement
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
interface CellListProps {
|
|
@@ -36,6 +37,7 @@ interface CellListProps {
|
|
|
36
37
|
|
|
37
38
|
interface NotebookCellListState {
|
|
38
39
|
selectedCell?: NotebookCellModel;
|
|
40
|
+
scrollIntoView: boolean;
|
|
39
41
|
dragOverIndicator: { cell: NotebookCellModel, position: 'top' | 'bottom' } | undefined;
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -43,19 +45,33 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
43
45
|
|
|
44
46
|
protected toDispose = new DisposableCollection();
|
|
45
47
|
|
|
48
|
+
protected dragGhost: HTMLElement | undefined;
|
|
49
|
+
|
|
46
50
|
constructor(props: CellListProps) {
|
|
47
51
|
super(props);
|
|
48
|
-
this.state = { selectedCell: props.notebookModel.selectedCell, dragOverIndicator: undefined };
|
|
52
|
+
this.state = { selectedCell: props.notebookModel.selectedCell, dragOverIndicator: undefined, scrollIntoView: true };
|
|
49
53
|
this.toDispose.push(props.notebookModel.onDidAddOrRemoveCell(e => {
|
|
50
54
|
if (e.newCellIds && e.newCellIds.length > 0) {
|
|
51
|
-
this.setState({
|
|
55
|
+
this.setState({
|
|
56
|
+
...this.state,
|
|
57
|
+
selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]),
|
|
58
|
+
scrollIntoView: true
|
|
59
|
+
});
|
|
52
60
|
} else {
|
|
53
|
-
this.setState({
|
|
61
|
+
this.setState({
|
|
62
|
+
...this.state,
|
|
63
|
+
selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell),
|
|
64
|
+
scrollIntoView: false
|
|
65
|
+
});
|
|
54
66
|
}
|
|
55
67
|
}));
|
|
56
68
|
|
|
57
|
-
this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(
|
|
58
|
-
this.setState({
|
|
69
|
+
this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(e => {
|
|
70
|
+
this.setState({
|
|
71
|
+
...this.state,
|
|
72
|
+
selectedCell: e.cell,
|
|
73
|
+
scrollIntoView: e.scrollIntoView
|
|
74
|
+
});
|
|
59
75
|
}));
|
|
60
76
|
}
|
|
61
77
|
|
|
@@ -77,13 +93,13 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
77
93
|
<li className={'theia-notebook-cell' + (this.state.selectedCell === cell ? ' focused' : '') + (this.isEnabled() ? ' draggable' : '')}
|
|
78
94
|
onClick={e => {
|
|
79
95
|
this.setState({ ...this.state, selectedCell: cell });
|
|
80
|
-
this.props.notebookModel.setSelectedCell(cell);
|
|
96
|
+
this.props.notebookModel.setSelectedCell(cell, false);
|
|
81
97
|
}}
|
|
82
|
-
onDragStart={e => this.onDragStart(e, index)}
|
|
98
|
+
onDragStart={e => this.onDragStart(e, index, cell)}
|
|
83
99
|
onDragOver={e => this.onDragOver(e, cell)}
|
|
84
100
|
onDrop={e => this.onDrop(e, index)}
|
|
85
101
|
draggable={true}
|
|
86
|
-
ref={ref => cell === this.state.selectedCell && ref?.scrollIntoView({ block: 'nearest' })}>
|
|
102
|
+
ref={ref => cell === this.state.selectedCell && this.state.scrollIntoView && ref?.scrollIntoView({ block: 'nearest' })}>
|
|
87
103
|
<div className={'theia-notebook-cell-marker' + (this.state.selectedCell === cell ? ' theia-notebook-cell-marker-selected' : '')}></div>
|
|
88
104
|
<div className='theia-notebook-cell-content'>
|
|
89
105
|
{this.renderCellContent(cell, index)}
|
|
@@ -114,12 +130,22 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
114
130
|
return renderer.render(this.props.notebookModel, cell, index);
|
|
115
131
|
}
|
|
116
132
|
|
|
117
|
-
protected onDragStart(event: React.DragEvent<HTMLLIElement>, index: number): void {
|
|
133
|
+
protected onDragStart(event: React.DragEvent<HTMLLIElement>, index: number, cell: NotebookCellModel): void {
|
|
118
134
|
event.stopPropagation();
|
|
119
135
|
if (!this.isEnabled()) {
|
|
120
136
|
event.preventDefault();
|
|
121
137
|
return;
|
|
122
138
|
}
|
|
139
|
+
|
|
140
|
+
if (this.dragGhost) {
|
|
141
|
+
this.dragGhost.remove();
|
|
142
|
+
}
|
|
143
|
+
this.dragGhost = document.createElement('div');
|
|
144
|
+
this.dragGhost.classList.add('theia-notebook-drag-ghost-image');
|
|
145
|
+
this.dragGhost.appendChild(this.props.renderers.get(cell.cellKind)?.renderDragImage(cell) ?? document.createElement('div'));
|
|
146
|
+
document.body.appendChild(this.dragGhost);
|
|
147
|
+
event.dataTransfer.setDragImage(this.dragGhost, -10, 0);
|
|
148
|
+
|
|
123
149
|
event.dataTransfer.setData('text/theia-notebook-cell-index', index.toString());
|
|
124
150
|
event.dataTransfer.setData('text/plain', this.props.notebookModel.cells[index].source);
|
|
125
151
|
}
|
|
@@ -99,6 +99,13 @@ export class NotebookCodeCellRenderer implements CellRenderer {
|
|
|
99
99
|
</div >;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
renderDragImage(cell: NotebookCellModel): HTMLElement {
|
|
103
|
+
const dragImage = document.createElement('div');
|
|
104
|
+
dragImage.className = 'theia-notebook-drag-image';
|
|
105
|
+
dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/codeText', 'Code cell selected');
|
|
106
|
+
return dragImage;
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
protected getOrCreateMonacoFontInfo(): BareFontInfo {
|
|
103
110
|
if (!this.fontInfo) {
|
|
104
111
|
this.fontInfo = this.createFontInfo();
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
import { ArrayUtils, CommandRegistry, CompoundMenuNodeRole, DisposableCollection, MenuModelRegistry, MenuNode, nls } from '@theia/core';
|
|
17
17
|
import * as React from '@theia/core/shared/react';
|
|
18
|
-
import { codicon } from '@theia/core/lib/browser';
|
|
18
|
+
import { codicon, ContextMenuRenderer } from '@theia/core/lib/browser';
|
|
19
19
|
import { NotebookCommands, NotebookMenus } from '../contributions/notebook-actions-contribution';
|
|
20
20
|
import { NotebookModel } from '../view-model/notebook-model';
|
|
21
21
|
import { NotebookKernelService } from '../service/notebook-kernel-service';
|
|
@@ -32,6 +32,7 @@ export interface NotebookMainToolbarProps {
|
|
|
32
32
|
contextKeyService: ContextKeyService;
|
|
33
33
|
editorNode: HTMLElement;
|
|
34
34
|
notebookContextManager: NotebookContextManager;
|
|
35
|
+
contextMenuRenderer: ContextMenuRenderer;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
@injectable()
|
|
@@ -41,6 +42,7 @@ export class NotebookMainToolbarRenderer {
|
|
|
41
42
|
@inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry;
|
|
42
43
|
@inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService;
|
|
43
44
|
@inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager;
|
|
45
|
+
@inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
|
|
44
46
|
|
|
45
47
|
render(notebookModel: NotebookModel, editorNode: HTMLElement): React.ReactNode {
|
|
46
48
|
return <NotebookMainToolbar notebookModel={notebookModel}
|
|
@@ -49,11 +51,20 @@ export class NotebookMainToolbarRenderer {
|
|
|
49
51
|
commandRegistry={this.commandRegistry}
|
|
50
52
|
contextKeyService={this.contextKeyService}
|
|
51
53
|
editorNode={editorNode}
|
|
52
|
-
notebookContextManager={this.notebookContextManager}
|
|
54
|
+
notebookContextManager={this.notebookContextManager}
|
|
55
|
+
contextMenuRenderer={this.contextMenuRenderer} />;
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
interface NotebookMainToolbarState {
|
|
60
|
+
selectedKernelLabel?: string;
|
|
61
|
+
numberOfHiddenItems: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProps, NotebookMainToolbarState> {
|
|
65
|
+
|
|
66
|
+
// The minimum area between items and kernel select before hiding items in a context menu
|
|
67
|
+
static readonly MIN_FREE_AREA = 10;
|
|
57
68
|
|
|
58
69
|
protected toDispose = new DisposableCollection();
|
|
59
70
|
|
|
@@ -61,10 +72,18 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
61
72
|
NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_CELL_ADD_GROUP[NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_CELL_ADD_GROUP.length - 1],
|
|
62
73
|
NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_EXECUTION_GROUP[NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_EXECUTION_GROUP.length - 1]];
|
|
63
74
|
|
|
75
|
+
protected gapElement: HTMLDivElement | undefined;
|
|
76
|
+
protected lastGapElementWidth: number = 0;
|
|
77
|
+
|
|
78
|
+
protected resizeObserver: ResizeObserver = new ResizeObserver(() => this.calculateItemsToHide());
|
|
79
|
+
|
|
64
80
|
constructor(props: NotebookMainToolbarProps) {
|
|
65
81
|
super(props);
|
|
66
82
|
|
|
67
|
-
this.state = {
|
|
83
|
+
this.state = {
|
|
84
|
+
selectedKernelLabel: props.notebookKernelService.getSelectedOrSuggestedKernel(props.notebookModel)?.label,
|
|
85
|
+
numberOfHiddenItems: 0,
|
|
86
|
+
};
|
|
68
87
|
this.toDispose.push(props.notebookKernelService.onDidChangeSelectedKernel(event => {
|
|
69
88
|
if (props.notebookModel.uri.isEqual(event.notebook)) {
|
|
70
89
|
this.setState({ selectedKernelLabel: props.notebookKernelService.getKernel(event.newKernel ?? '')?.label });
|
|
@@ -97,10 +116,49 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
97
116
|
this.toDispose.dispose();
|
|
98
117
|
}
|
|
99
118
|
|
|
119
|
+
override componentDidUpdate(): void {
|
|
120
|
+
this.calculateItemsToHide();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
override componentDidMount(): void {
|
|
124
|
+
this.calculateItemsToHide();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
protected calculateItemsToHide(): void {
|
|
128
|
+
const numberOfMenuItems = this.getMenuItems().length;
|
|
129
|
+
if (this.gapElement && this.gapElement.getBoundingClientRect().width < NotebookMainToolbar.MIN_FREE_AREA && this.state.numberOfHiddenItems < numberOfMenuItems) {
|
|
130
|
+
this.setState({ ...this.state, numberOfHiddenItems: this.state.numberOfHiddenItems + 1 });
|
|
131
|
+
this.lastGapElementWidth = this.gapElement.getBoundingClientRect().width;
|
|
132
|
+
} else if (this.gapElement && this.gapElement.getBoundingClientRect().width > this.lastGapElementWidth && this.state.numberOfHiddenItems > 0) {
|
|
133
|
+
this.setState({ ...this.state, numberOfHiddenItems: 0 });
|
|
134
|
+
this.lastGapElementWidth = this.gapElement.getBoundingClientRect().width;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
protected renderContextMenu(event: MouseEvent, menuItems: readonly MenuNode[]): void {
|
|
139
|
+
const hiddenItems = menuItems.slice(menuItems.length - this.calculateNumberOfHiddenItems(menuItems));
|
|
140
|
+
const contextMenu = this.props.menuRegistry.getMenu([NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU]);
|
|
141
|
+
|
|
142
|
+
contextMenu.children.map(item => item.id).forEach(id => contextMenu.removeNode(id));
|
|
143
|
+
hiddenItems.forEach(item => contextMenu.addNode(item));
|
|
144
|
+
|
|
145
|
+
this.props.contextMenuRenderer.render({
|
|
146
|
+
anchor: event,
|
|
147
|
+
menuPath: [NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU],
|
|
148
|
+
context: this.props.editorNode,
|
|
149
|
+
args: [this.props.notebookModel.uri]
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
100
153
|
override render(): React.ReactNode {
|
|
154
|
+
const menuItems = this.getMenuItems();
|
|
101
155
|
return <div className='theia-notebook-main-toolbar'>
|
|
102
|
-
{this.
|
|
103
|
-
|
|
156
|
+
{menuItems.slice(0, menuItems.length - this.calculateNumberOfHiddenItems(menuItems)).map(item => this.renderMenuItem(item))}
|
|
157
|
+
{
|
|
158
|
+
this.state.numberOfHiddenItems > 0 &&
|
|
159
|
+
<span className={`${codicon('ellipsis')} action-label theia-notebook-main-toolbar-item`} onClick={e => this.renderContextMenu(e.nativeEvent, menuItems)} />
|
|
160
|
+
}
|
|
161
|
+
<div ref={element => this.gapElementChanged(element)} style={{ flexGrow: 1 }}></div>
|
|
104
162
|
<div className='theia-notebook-main-toolbar-item action-label'
|
|
105
163
|
onClick={() => this.props.commandRegistry.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, this.props.notebookModel)}>
|
|
106
164
|
<span className={codicon('server-environment')} />
|
|
@@ -108,7 +166,18 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
108
166
|
{this.state.selectedKernelLabel ?? nls.localizeByDefault('Select Kernel')}
|
|
109
167
|
</span>
|
|
110
168
|
</div>
|
|
111
|
-
</div>;
|
|
169
|
+
</div >;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected gapElementChanged(element: HTMLDivElement | null): void {
|
|
173
|
+
if (this.gapElement) {
|
|
174
|
+
this.resizeObserver.unobserve(this.gapElement);
|
|
175
|
+
}
|
|
176
|
+
this.gapElement = element ?? undefined;
|
|
177
|
+
if (this.gapElement) {
|
|
178
|
+
this.lastGapElementWidth = this.gapElement.getBoundingClientRect().width;
|
|
179
|
+
this.resizeObserver.observe(this.gapElement);
|
|
180
|
+
}
|
|
112
181
|
}
|
|
113
182
|
|
|
114
183
|
protected renderMenuItem(item: MenuNode, submenu?: string): React.ReactNode {
|
|
@@ -157,4 +226,10 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
157
226
|
menus.filter(item => item.children && item.children.length > 0)
|
|
158
227
|
.forEach(item => this.getAllContextKeys(item.children!, keySet));
|
|
159
228
|
}
|
|
229
|
+
|
|
230
|
+
protected calculateNumberOfHiddenItems(allMenuItems: readonly MenuNode[]): number {
|
|
231
|
+
return this.state.numberOfHiddenItems >= allMenuItems.length ?
|
|
232
|
+
allMenuItems.length :
|
|
233
|
+
this.state.numberOfHiddenItems % allMenuItems.length;
|
|
234
|
+
}
|
|
160
235
|
}
|
|
@@ -42,6 +42,12 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
|
|
|
42
42
|
cell={cell} notebookModel={notebookModel} notebookContextManager={this.notebookContextManager} />;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
renderDragImage(cell: NotebookCellModel): HTMLElement {
|
|
46
|
+
const dragImage = document.createElement('div');
|
|
47
|
+
dragImage.className = 'theia-notebook-drag-image';
|
|
48
|
+
dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/markdownText', 'Mardown cell selected');
|
|
49
|
+
return dragImage;
|
|
50
|
+
}
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
interface MarkdownCellProps {
|
|
@@ -36,6 +36,8 @@ import { LanguageService } from '@theia/core/lib/browser/language-service';
|
|
|
36
36
|
export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
|
|
37
37
|
export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel;
|
|
38
38
|
|
|
39
|
+
export type CellEditorFocusRequest = number | 'lastLine' | undefined;
|
|
40
|
+
|
|
39
41
|
export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps): interfaces.Container {
|
|
40
42
|
const child = parent.createChild();
|
|
41
43
|
|
|
@@ -104,7 +106,7 @@ export class NotebookCellModel implements NotebookCell, Disposable {
|
|
|
104
106
|
protected readonly onDidRequestCellEditChangeEmitter = new Emitter<boolean>();
|
|
105
107
|
readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event;
|
|
106
108
|
|
|
107
|
-
protected readonly onWillFocusCellEditorEmitter = new Emitter<
|
|
109
|
+
protected readonly onWillFocusCellEditorEmitter = new Emitter<CellEditorFocusRequest>();
|
|
108
110
|
readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event;
|
|
109
111
|
|
|
110
112
|
protected readonly onWillBlurCellEditorEmitter = new Emitter<void>();
|
|
@@ -262,7 +264,6 @@ export class NotebookCellModel implements NotebookCell, Disposable {
|
|
|
262
264
|
this.onDidChangeMetadataEmitter.dispose();
|
|
263
265
|
this.onDidChangeInternalMetadataEmitter.dispose();
|
|
264
266
|
this.onDidChangeLanguageEmitter.dispose();
|
|
265
|
-
this.textModel?.dispose();
|
|
266
267
|
this.toDispose.dispose();
|
|
267
268
|
}
|
|
268
269
|
|
|
@@ -278,9 +279,9 @@ export class NotebookCellModel implements NotebookCell, Disposable {
|
|
|
278
279
|
this.onDidRequestCellEditChangeEmitter.fire(false);
|
|
279
280
|
}
|
|
280
281
|
|
|
281
|
-
requestFocusEditor(): void {
|
|
282
|
+
requestFocusEditor(focusRequest?: CellEditorFocusRequest): void {
|
|
282
283
|
this.requestEdit();
|
|
283
|
-
this.onWillFocusCellEditorEmitter.fire();
|
|
284
|
+
this.onWillFocusCellEditorEmitter.fire(focusRequest);
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
requestBlurEditor(): void {
|
|
@@ -354,9 +355,10 @@ export class NotebookCellModel implements NotebookCell, Disposable {
|
|
|
354
355
|
|
|
355
356
|
const ref = await this.textModelService.getOrCreateNotebookCellModelReference(this.uri);
|
|
356
357
|
this.textModel = ref.object;
|
|
357
|
-
this.
|
|
358
|
+
this.toDispose.push(ref);
|
|
359
|
+
this.toDispose.push(this.textModel.onDidChangeContent(e => {
|
|
358
360
|
this.props.source = e.model.getText();
|
|
359
|
-
});
|
|
361
|
+
}));
|
|
360
362
|
return ref.object;
|
|
361
363
|
}
|
|
362
364
|
|
|
@@ -33,6 +33,7 @@ import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-mod
|
|
|
33
33
|
import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
|
|
34
34
|
import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service';
|
|
35
35
|
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
|
|
36
|
+
import type { NotebookModelResolverService } from '../service/notebook-model-resolver-service';
|
|
36
37
|
|
|
37
38
|
export const NotebookModelFactory = Symbol('NotebookModelFactory');
|
|
38
39
|
|
|
@@ -45,6 +46,8 @@ export function createNotebookModelContainer(parent: interfaces.Container, props
|
|
|
45
46
|
return child;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
export const NotebookModelResolverServiceProxy = Symbol('NotebookModelResolverServiceProxy');
|
|
50
|
+
|
|
48
51
|
const NotebookModelProps = Symbol('NotebookModelProps');
|
|
49
52
|
export interface NotebookModelProps {
|
|
50
53
|
data: NotebookData;
|
|
@@ -53,6 +56,11 @@ export interface NotebookModelProps {
|
|
|
53
56
|
serializer: NotebookSerializer;
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
export interface SelectedCellChangeEvent {
|
|
60
|
+
cell: NotebookCellModel | undefined;
|
|
61
|
+
scrollIntoView: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
@injectable()
|
|
57
65
|
export class NotebookModel implements Saveable, Disposable {
|
|
58
66
|
|
|
@@ -68,7 +76,10 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
68
76
|
protected readonly onDidChangeContentEmitter = new QueueableEmitter<NotebookContentChangedEvent>();
|
|
69
77
|
readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
|
|
70
78
|
|
|
71
|
-
protected readonly
|
|
79
|
+
protected readonly onContentChangedEmitter = new Emitter<void>();
|
|
80
|
+
readonly onContentChanged = this.onContentChangedEmitter.event;
|
|
81
|
+
|
|
82
|
+
protected readonly onDidChangeSelectedCellEmitter = new Emitter<SelectedCellChangeEvent>();
|
|
72
83
|
readonly onDidChangeSelectedCell = this.onDidChangeSelectedCellEmitter.event;
|
|
73
84
|
|
|
74
85
|
protected readonly onDidDisposeEmitter = new Emitter<void>();
|
|
@@ -89,15 +100,20 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
89
100
|
|
|
90
101
|
@inject(NotebookCellModelFactory)
|
|
91
102
|
protected cellModelFactory: NotebookCellModelFactory;
|
|
92
|
-
|
|
103
|
+
|
|
104
|
+
@inject(NotebookModelResolverServiceProxy)
|
|
105
|
+
protected modelResolverService: NotebookModelResolverService;
|
|
93
106
|
|
|
94
107
|
protected nextHandle: number = 0;
|
|
95
108
|
|
|
96
109
|
protected _dirty = false;
|
|
97
110
|
|
|
98
111
|
set dirty(dirty: boolean) {
|
|
112
|
+
const oldState = this._dirty;
|
|
99
113
|
this._dirty = dirty;
|
|
100
|
-
|
|
114
|
+
if (oldState !== dirty) {
|
|
115
|
+
this.onDirtyChangedEmitter.fire();
|
|
116
|
+
}
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
get dirty(): boolean {
|
|
@@ -156,28 +172,20 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
156
172
|
this.onDidDisposeEmitter.fire();
|
|
157
173
|
}
|
|
158
174
|
|
|
159
|
-
async save(options
|
|
175
|
+
async save(options?: SaveOptions): Promise<void> {
|
|
160
176
|
this.dirtyCells = [];
|
|
161
177
|
this.dirty = false;
|
|
162
178
|
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
metadata: this.metadata
|
|
166
|
-
});
|
|
179
|
+
const data = this.getData();
|
|
180
|
+
const serializedNotebook = await this.props.serializer.fromNotebook(data);
|
|
167
181
|
this.fileService.writeFile(this.uri, serializedNotebook);
|
|
168
182
|
|
|
169
183
|
this.onDidSaveNotebookEmitter.fire();
|
|
170
184
|
}
|
|
171
185
|
|
|
172
186
|
createSnapshot(): Saveable.Snapshot {
|
|
173
|
-
const model = this;
|
|
174
187
|
return {
|
|
175
|
-
read()
|
|
176
|
-
return JSON.stringify({
|
|
177
|
-
cells: model.cells.map(cell => cell.getData()),
|
|
178
|
-
metadata: model.metadata
|
|
179
|
-
});
|
|
180
|
-
}
|
|
188
|
+
read: () => JSON.stringify(this.getData())
|
|
181
189
|
};
|
|
182
190
|
}
|
|
183
191
|
|
|
@@ -186,28 +194,20 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
186
194
|
if (!rawData) {
|
|
187
195
|
throw new Error('could not read notebook snapshot');
|
|
188
196
|
}
|
|
189
|
-
const data = JSON.parse(rawData);
|
|
190
|
-
|
|
191
|
-
const handle = this.nextHandle++;
|
|
192
|
-
return this.cellModelFactory({
|
|
193
|
-
uri: CellUri.generate(this.uri, handle),
|
|
194
|
-
handle: handle,
|
|
195
|
-
source: cell.source,
|
|
196
|
-
language: cell.language,
|
|
197
|
-
cellKind: cell.cellKind,
|
|
198
|
-
outputs: cell.outputs,
|
|
199
|
-
metadata: cell.metadata,
|
|
200
|
-
internalMetadata: cell.internalMetadata,
|
|
201
|
-
collapseState: cell.collapseState
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
this.addCellOutputListeners(cells);
|
|
205
|
-
|
|
206
|
-
this.metadata = data.metadata;
|
|
207
|
-
|
|
197
|
+
const data = JSON.parse(rawData) as NotebookData;
|
|
198
|
+
this.setData(data);
|
|
208
199
|
}
|
|
209
200
|
|
|
210
201
|
async revert(options?: Saveable.RevertOptions): Promise<void> {
|
|
202
|
+
if (!options?.soft) {
|
|
203
|
+
// Load the data from the file again
|
|
204
|
+
try {
|
|
205
|
+
const data = await this.modelResolverService.resolveExistingNotebookData(this.props.resource, this.props.viewType);
|
|
206
|
+
this.setData(data, false);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
console.error('Failed to revert notebook', err);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
211
|
this.dirty = false;
|
|
212
212
|
}
|
|
213
213
|
|
|
@@ -222,11 +222,23 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
222
222
|
this.dirtyCells.splice(this.dirtyCells.indexOf(cell), 1);
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
225
|
+
this.dirty = this.dirtyCells.length > 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
setData(data: NotebookData, markDirty = true): void {
|
|
229
|
+
// Replace all cells in the model
|
|
230
|
+
this.dirtyCells = [];
|
|
231
|
+
this.replaceCells(0, this.cells.length, data.cells, false);
|
|
232
|
+
this.metadata = data.metadata;
|
|
233
|
+
this.dirty = markDirty;
|
|
234
|
+
this.onDidChangeContentEmitter.fire();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
getData(): NotebookData {
|
|
238
|
+
return {
|
|
239
|
+
cells: this.cells.map(cell => cell.getData()),
|
|
240
|
+
metadata: this.metadata
|
|
241
|
+
};
|
|
230
242
|
}
|
|
231
243
|
|
|
232
244
|
undo(): void {
|
|
@@ -239,10 +251,10 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
239
251
|
this.undoRedoService.redo(this.uri);
|
|
240
252
|
}
|
|
241
253
|
|
|
242
|
-
setSelectedCell(cell: NotebookCellModel): void {
|
|
254
|
+
setSelectedCell(cell: NotebookCellModel, scrollIntoView?: boolean): void {
|
|
243
255
|
if (this.selectedCell !== cell) {
|
|
244
256
|
this.selectedCell = cell;
|
|
245
|
-
this.onDidChangeSelectedCellEmitter.fire(cell);
|
|
257
|
+
this.onDidChangeSelectedCellEmitter.fire({ cell, scrollIntoView: scrollIntoView ?? true });
|
|
246
258
|
}
|
|
247
259
|
}
|
|
248
260
|
|
|
@@ -271,16 +283,19 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
271
283
|
end: edit.editType === CellEditType.Replace ? edit.index + edit.count : cellIndex,
|
|
272
284
|
originalIndex: index
|
|
273
285
|
};
|
|
274
|
-
})
|
|
286
|
+
});
|
|
275
287
|
|
|
276
288
|
for (const { edit, cellIndex } of editsWithDetails) {
|
|
277
289
|
const cell = this.cells[cellIndex];
|
|
278
290
|
if (cell) {
|
|
279
291
|
this.cellDirtyChanged(cell, true);
|
|
280
292
|
}
|
|
293
|
+
|
|
294
|
+
let scrollIntoView = true;
|
|
281
295
|
switch (edit.editType) {
|
|
282
296
|
case CellEditType.Replace:
|
|
283
297
|
this.replaceCells(edit.index, edit.count, edit.cells, computeUndoRedo);
|
|
298
|
+
scrollIntoView = edit.cells.length > 0;
|
|
284
299
|
break;
|
|
285
300
|
case CellEditType.Output: {
|
|
286
301
|
if (edit.append) {
|
|
@@ -323,15 +338,15 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
323
338
|
|
|
324
339
|
// if selected cell is affected update it because it can potentially have been replaced
|
|
325
340
|
if (cell === this.selectedCell) {
|
|
326
|
-
this.setSelectedCell(this.cells[cellIndex]);
|
|
341
|
+
this.setSelectedCell(this.cells[Math.min(cellIndex, this.cells.length - 1)], scrollIntoView);
|
|
327
342
|
}
|
|
328
343
|
}
|
|
329
344
|
|
|
330
345
|
this.onDidChangeContentEmitter.fire();
|
|
331
|
-
|
|
346
|
+
this.onContentChangedEmitter.fire();
|
|
332
347
|
}
|
|
333
348
|
|
|
334
|
-
protected
|
|
349
|
+
protected replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): void {
|
|
335
350
|
const cells = newCells.map(cell => {
|
|
336
351
|
const handle = this.nextHandle++;
|
|
337
352
|
return this.cellModelFactory({
|
|
@@ -362,10 +377,6 @@ export class NotebookModel implements Saveable, Disposable {
|
|
|
362
377
|
async () => this.replaceCells(start, deleteCount, newCells, false));
|
|
363
378
|
}
|
|
364
379
|
|
|
365
|
-
// Ensure that all text model have been created
|
|
366
|
-
// Otherwise we run into a race condition once we fire `onDidChangeContent`
|
|
367
|
-
await Promise.all(cells.map(cell => cell.resolveTextModel()));
|
|
368
|
-
|
|
369
380
|
this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) });
|
|
370
381
|
this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ModelChange, changes });
|
|
371
382
|
if (cells.length > 0) {
|
|
@@ -273,7 +273,7 @@ export namespace CellUri {
|
|
|
273
273
|
const s = handle.toString(_radix);
|
|
274
274
|
const p = s.length < _lengths.length ? _lengths[s.length - 1] : 'z';
|
|
275
275
|
|
|
276
|
-
const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')}
|
|
276
|
+
const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')}`;
|
|
277
277
|
return notebook.withScheme(cellUriScheme).withFragment(fragment);
|
|
278
278
|
}
|
|
279
279
|
|