Skip to content

Commit 0910b02

Browse files
authored
General notebook API improvements (#13012)
Signed-off-by: Jonah Iden <[email protected]>
1 parent ad2e106 commit 0910b02

27 files changed

+435
-520
lines changed

packages/notebook/src/browser/contributions/notebook-actions-contribution.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { ApplicationShell, codicon, CommonCommands } from '@theia/core/lib/brows
2020
import { NotebookModel } from '../view-model/notebook-model';
2121
import { NotebookService } from '../service/notebook-service';
2222
import { CellEditType, CellKind } from '../../common';
23-
import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service';
23+
import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service';
2424
import { NotebookExecutionService } from '../service/notebook-execution-service';
2525
import { NotebookEditorWidget } from '../notebook-editor-widget';
2626

@@ -66,7 +66,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
6666
protected notebookService: NotebookService;
6767

6868
@inject(NotebookKernelQuickPickService)
69-
protected notebookKernelQuickPickService: KernelPickerMRUStrategy;
69+
protected notebookKernelQuickPickService: NotebookKernelQuickPickService;
7070

7171
@inject(NotebookExecutionService)
7272
protected notebookExecutionService: NotebookExecutionService;

packages/notebook/src/browser/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ export * from './service/notebook-execution-state-service';
2424
export * from './service/notebook-model-resolver-service';
2525
export * from './service/notebook-renderer-messaging-service';
2626
export * from './renderers/cell-output-webview';
27+
export * from './notebook-types';

packages/notebook/src/browser/notebook-frontend-module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { NotebookActionsContribution } from './contributions/notebook-actions-co
3737
import { NotebookExecutionService } from './service/notebook-execution-service';
3838
import { NotebookExecutionStateService } from './service/notebook-execution-state-service';
3939
import { NotebookKernelService } from './service/notebook-kernel-service';
40-
import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service';
40+
import { NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service';
4141
import { NotebookKernelHistoryService } from './service/notebook-kernel-history-service';
4242
import { NotebookEditorWidgetService } from './service/notebook-editor-widget-service';
4343
import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service';
@@ -65,7 +65,7 @@ export default new ContainerModule(bind => {
6565
bind(NotebookKernelService).toSelf().inSingletonScope();
6666
bind(NotebookRendererMessagingService).toSelf().inSingletonScope();
6767
bind(NotebookKernelHistoryService).toSelf().inSingletonScope();
68-
bind(NotebookKernelQuickPickService).to(KernelPickerMRUStrategy).inSingletonScope();
68+
bind(NotebookKernelQuickPickService).toSelf().inSingletonScope();
6969

7070
bind(NotebookCellResourceResolver).toSelf().inSingletonScope();
7171
bind(ResourceResolver).toService(NotebookCellResourceResolver);
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// *****************************************************************************
2+
// Copyright (C) 2023 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 {
18+
CellData, CellEditType, CellMetadataEdit, CellOutput, CellOutputItem, CellRange, NotebookCellContentChangeEvent,
19+
NotebookCellInternalMetadata,
20+
NotebookCellsChangeInternalMetadataEvent,
21+
NotebookCellsChangeLanguageEvent,
22+
NotebookCellsChangeMetadataEvent,
23+
NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookDocumentMetadata
24+
} from '../common';
25+
import { NotebookCell } from './view-model/notebook-cell-model';
26+
27+
export interface NotebookTextModelChangedEvent {
28+
readonly rawEvents: NotebookContentChangedEvent[];
29+
// readonly versionId: number;
30+
readonly synchronous?: boolean;
31+
readonly endSelectionState?: SelectionState;
32+
};
33+
34+
export type NotebookContentChangedEvent = (NotebookCellsInitializeEvent<NotebookCell> | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent |
35+
NotebookCellsModelChangedEvent<NotebookCell> | NotebookCellsModelMoveEvent<NotebookCell> | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent |
36+
NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent |
37+
NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent); // & { transient: boolean };
38+
39+
export interface NotebookCellsInitializeEvent<T> {
40+
readonly kind: NotebookCellsChangeType.Initialize;
41+
readonly changes: NotebookCellTextModelSplice<T>[];
42+
}
43+
44+
export interface NotebookDocumentChangeMetadataEvent {
45+
readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata;
46+
readonly metadata: NotebookDocumentMetadata;
47+
}
48+
49+
export interface NotebookCellsModelChangedEvent<T> {
50+
readonly kind: NotebookCellsChangeType.ModelChange;
51+
readonly changes: NotebookCellTextModelSplice<T>[];
52+
}
53+
54+
export interface NotebookModelWillAddRemoveEvent {
55+
readonly rawEvent: NotebookCellsModelChangedEvent<CellData>;
56+
};
57+
58+
export interface NotebookCellsModelMoveEvent<T> {
59+
readonly kind: NotebookCellsChangeType.Move;
60+
readonly index: number;
61+
readonly length: number;
62+
readonly newIdx: number;
63+
readonly cells: T[];
64+
}
65+
66+
export interface NotebookOutputChangedEvent {
67+
readonly kind: NotebookCellsChangeType.Output;
68+
readonly index: number;
69+
readonly outputs: CellOutput[];
70+
readonly append: boolean;
71+
}
72+
73+
export interface NotebookOutputItemChangedEvent {
74+
readonly kind: NotebookCellsChangeType.OutputItem;
75+
readonly index: number;
76+
readonly outputId: string;
77+
readonly outputItems: CellOutputItem[];
78+
readonly append: boolean;
79+
}
80+
81+
export interface NotebookDocumentUnknownChangeEvent {
82+
readonly kind: NotebookCellsChangeType.Unknown;
83+
}
84+
85+
export enum SelectionStateType {
86+
Handle = 0,
87+
Index = 1
88+
}
89+
90+
export interface SelectionHandleState {
91+
kind: SelectionStateType.Handle;
92+
primary: number | null;
93+
selections: number[];
94+
}
95+
96+
export interface SelectionIndexState {
97+
kind: SelectionStateType.Index;
98+
focus: CellRange;
99+
selections: CellRange[];
100+
}
101+
102+
export type SelectionState = SelectionHandleState | SelectionIndexState;
103+
104+
export interface NotebookModelWillAddRemoveEvent {
105+
readonly newCellIds?: number[];
106+
readonly rawEvent: NotebookCellsModelChangedEvent<CellData>;
107+
};
108+
109+
export interface CellOutputEdit {
110+
editType: CellEditType.Output;
111+
index: number;
112+
outputs: CellOutput[];
113+
append?: boolean;
114+
}
115+
116+
export interface CellOutputEditByHandle {
117+
editType: CellEditType.Output;
118+
handle: number;
119+
outputs: CellOutput[];
120+
append?: boolean;
121+
}
122+
123+
export interface CellOutputItemEdit {
124+
editType: CellEditType.OutputItems;
125+
items: CellOutputItem[];
126+
outputId: string;
127+
append?: boolean;
128+
}
129+
130+
export interface CellLanguageEdit {
131+
editType: CellEditType.CellLanguage;
132+
index: number;
133+
language: string;
134+
}
135+
136+
export interface DocumentMetadataEdit {
137+
editType: CellEditType.DocumentMetadata;
138+
metadata: NotebookDocumentMetadata;
139+
}
140+
141+
export interface CellMoveEdit {
142+
editType: CellEditType.Move;
143+
index: number;
144+
length: number;
145+
newIdx: number;
146+
}
147+
148+
export interface CellReplaceEdit {
149+
editType: CellEditType.Replace;
150+
index: number;
151+
count: number;
152+
cells: CellData[];
153+
}
154+
155+
export type ImmediateCellEditOperation = CellOutputEditByHandle | CellOutputItemEdit | CellPartialInternalMetadataEditByHandle; // add more later on
156+
export type CellEditOperation = ImmediateCellEditOperation | CellReplaceEdit | CellOutputEdit |
157+
CellMetadataEdit | CellLanguageEdit | DocumentMetadataEdit | CellMoveEdit; // add more later on
158+
159+
export type NullablePartialNotebookCellInternalMetadata = {
160+
[Key in keyof Partial<NotebookCellInternalMetadata>]: NotebookCellInternalMetadata[Key] | null
161+
};
162+
export interface CellPartialInternalMetadataEditByHandle {
163+
editType: CellEditType.PartialInternalMetadata;
164+
handle: number;
165+
internalMetadata: NullablePartialNotebookCellInternalMetadata;
166+
}
167+
168+
export interface NotebookCellOutputsSplice {
169+
start: number;
170+
deleteCount: number;
171+
newOutputs: CellOutput[];
172+
};

packages/notebook/src/browser/service/notebook-cell-context-manager.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,54 +15,59 @@
1515
// *****************************************************************************
1616

1717
import { inject, injectable } from '@theia/core/shared/inversify';
18-
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
18+
import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service';
1919
import { NotebookCellModel } from '../view-model/notebook-cell-model';
2020
import { NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE } from '../contributions/notebook-context-keys';
2121
import { Disposable, DisposableCollection, Emitter } from '@theia/core';
2222
import { CellKind } from '../../common';
2323
import { NotebookExecutionStateService } from '../service/notebook-execution-state-service';
2424

2525
@injectable()
26-
export class NotebookCellContextManager implements Disposable {
26+
export class NotebookCellContextManager implements NotebookCellContextManager, Disposable {
2727
@inject(ContextKeyService) protected contextKeyService: ContextKeyService;
2828

2929
@inject(NotebookExecutionStateService)
3030
protected readonly executionStateService: NotebookExecutionStateService;
3131

3232
protected readonly toDispose = new DisposableCollection();
3333

34+
protected currentStore: ScopedValueStore;
3435
protected currentContext: HTMLLIElement;
3536

36-
protected readonly onDidChangeContextEmitter = new Emitter<void>();
37+
protected readonly onDidChangeContextEmitter = new Emitter<ContextKeyChangeEvent>();
3738
readonly onDidChangeContext = this.onDidChangeContextEmitter.event;
3839

3940
updateCellContext(cell: NotebookCellModel, newHtmlContext: HTMLLIElement): void {
4041
if (newHtmlContext !== this.currentContext) {
4142
this.toDispose.dispose();
4243

4344
this.currentContext = newHtmlContext;
44-
const currentStore = this.contextKeyService.createScoped(newHtmlContext);
45-
this.toDispose.push(currentStore);
45+
this.currentStore = this.contextKeyService.createScoped(newHtmlContext);
4646

47-
currentStore.setContext(NOTEBOOK_CELL_TYPE, cell.cellKind === CellKind.Code ? 'code' : 'markdown');
47+
this.currentStore.setContext(NOTEBOOK_CELL_TYPE, cell.cellKind === CellKind.Code ? 'code' : 'markdown');
48+
49+
this.toDispose.push(this.contextKeyService.onDidChange(e => {
50+
this.onDidChangeContextEmitter.fire(e);
51+
}));
4852

4953
this.toDispose.push(cell.onDidRequestCellEditChange(cellEdit => {
50-
currentStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit);
51-
this.onDidChangeContextEmitter.fire();
54+
this.currentStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit);
55+
this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE) });
5256
}));
5357
this.toDispose.push(this.executionStateService.onDidChangeExecution(e => {
5458
if (e.affectsCell(cell.uri)) {
55-
currentStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed);
56-
currentStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle');
57-
this.onDidChangeContextEmitter.fire();
59+
this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed);
60+
this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle');
61+
this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_EXECUTING) || keys.has(NOTEBOOK_CELL_EXECUTION_STATE) });
5862
}
5963
}));
60-
this.onDidChangeContextEmitter.fire();
64+
this.onDidChangeContextEmitter.fire({ affects: keys => true });
6165
}
6266
}
6367

6468
dispose(): void {
6569
this.toDispose.dispose();
70+
this.currentStore?.dispose();
6671
this.onDidChangeContextEmitter.dispose();
6772
}
6873
}

packages/notebook/src/browser/service/notebook-editor-widget-service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ export class NotebookEditorWidgetService implements Disposable {
8686
return this.notebookEditors.get(editorId);
8787
}
8888

89-
listNotebookEditors(): readonly NotebookEditorWidget[] {
90-
return [...this.notebookEditors].map(e => e[1]);
89+
getNotebookEditors(): readonly NotebookEditorWidget[] {
90+
return Array.from(this.notebookEditors.values());
9191
}
9292

9393
}

packages/notebook/src/browser/service/notebook-execution-service.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ import { CellExecution, NotebookExecutionStateService } from '../service/noteboo
2323
import { CellKind, NotebookCellExecutionState } from '../../common';
2424
import { NotebookCellModel } from '../view-model/notebook-cell-model';
2525
import { NotebookModel } from '../view-model/notebook-model';
26-
import { NotebookKernelService, NotebookKernel } from './notebook-kernel-service';
26+
import { NotebookKernelService } from './notebook-kernel-service';
2727
import { CommandService, Disposable } from '@theia/core';
28-
import { NotebookKernelQuickPickService, NotebookKernelQuickPickServiceImpl } from './notebook-kernel-quick-pick-service';
28+
import { NotebookKernelQuickPickService } from './notebook-kernel-quick-pick-service';
2929
import { NotebookKernelHistoryService } from './notebook-kernel-history-service';
30-
import { NotebookCommands } from '../contributions/notebook-actions-contribution';
3130

3231
export interface CellExecutionParticipant {
3332
onWillExecuteCell(executions: CellExecution[]): Promise<void>;
@@ -49,7 +48,7 @@ export class NotebookExecutionService {
4948
protected commandService: CommandService;
5049

5150
@inject(NotebookKernelQuickPickService)
52-
protected notebookKernelQuickPickService: NotebookKernelQuickPickServiceImpl;
51+
protected notebookKernelQuickPickService: NotebookKernelQuickPickService;
5352

5453
private readonly cellExecutionParticipants = new Set<CellExecutionParticipant>();
5554

@@ -69,7 +68,7 @@ export class NotebookExecutionService {
6968
}
7069
}
7170

72-
const kernel = await this.resolveKernel(notebook);
71+
const kernel = await this.notebookKernelHistoryService.resolveSelectedKernel(notebook);
7372

7473
if (!kernel) {
7574
// clear all pending cell executions
@@ -125,15 +124,4 @@ export class NotebookExecutionService {
125124
this.cancelNotebookCellHandles(notebook, Array.from(cells, cell => cell.handle));
126125
}
127126

128-
async resolveKernel(notebook: NotebookModel): Promise<NotebookKernel | undefined> {
129-
const alreadySelected = this.notebookKernelHistoryService.getKernels(notebook);
130-
131-
if (alreadySelected.selected) {
132-
return alreadySelected.selected;
133-
}
134-
135-
await this.commandService.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, notebook);
136-
const { selected } = this.notebookKernelHistoryService.getKernels(notebook);
137-
return selected;
138-
}
139127
}

packages/notebook/src/browser/service/notebook-execution-state-service.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import { inject, injectable } from '@theia/core/shared/inversify';
2323
import { NotebookService } from './notebook-service';
2424
import {
2525
CellEditType, CellExecuteOutputEdit, CellExecuteOutputItemEdit, CellExecutionUpdateType,
26-
CellUri, CellPartialInternalMetadataEditByHandle, NotebookCellExecutionState, CellEditOperation, NotebookCellInternalMetadata
26+
CellUri, NotebookCellExecutionState, NotebookCellInternalMetadata
2727
} from '../../common';
28+
import { CellPartialInternalMetadataEditByHandle, CellEditOperation } from '../notebook-types';
2829
import { NotebookModel } from '../view-model/notebook-model';
2930
import { v4 } from 'uuid';
3031

@@ -43,10 +44,6 @@ export interface CellExecutionStateUpdate {
4344
isPaused?: boolean;
4445
}
4546

46-
export interface ICellExecutionComplete {
47-
runEndTime?: number;
48-
lastRunSuccess?: boolean;
49-
}
5047
export enum NotebookExecutionType {
5148
cell,
5249
notebook

0 commit comments

Comments
 (0)