Skip to content

Commit

Permalink
General notebook API improvements (#13012)
Browse files Browse the repository at this point in the history
Signed-off-by: Jonah Iden <jonah.iden@typefox.io>
  • Loading branch information
jonah-iden authored Dec 14, 2023
1 parent ad2e106 commit 0910b02
Show file tree
Hide file tree
Showing 27 changed files with 435 additions and 520 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ApplicationShell, codicon, CommonCommands } from '@theia/core/lib/brows
import { NotebookModel } from '../view-model/notebook-model';
import { NotebookService } from '../service/notebook-service';
import { CellEditType, CellKind } from '../../common';
import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service';
import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service';
import { NotebookExecutionService } from '../service/notebook-execution-service';
import { NotebookEditorWidget } from '../notebook-editor-widget';

Expand Down Expand Up @@ -66,7 +66,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
protected notebookService: NotebookService;

@inject(NotebookKernelQuickPickService)
protected notebookKernelQuickPickService: KernelPickerMRUStrategy;
protected notebookKernelQuickPickService: NotebookKernelQuickPickService;

@inject(NotebookExecutionService)
protected notebookExecutionService: NotebookExecutionService;
Expand Down
1 change: 1 addition & 0 deletions packages/notebook/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export * from './service/notebook-execution-state-service';
export * from './service/notebook-model-resolver-service';
export * from './service/notebook-renderer-messaging-service';
export * from './renderers/cell-output-webview';
export * from './notebook-types';
4 changes: 2 additions & 2 deletions packages/notebook/src/browser/notebook-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { NotebookActionsContribution } from './contributions/notebook-actions-co
import { NotebookExecutionService } from './service/notebook-execution-service';
import { NotebookExecutionStateService } from './service/notebook-execution-state-service';
import { NotebookKernelService } from './service/notebook-kernel-service';
import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service';
import { NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service';
import { NotebookKernelHistoryService } from './service/notebook-kernel-history-service';
import { NotebookEditorWidgetService } from './service/notebook-editor-widget-service';
import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service';
Expand Down Expand Up @@ -65,7 +65,7 @@ export default new ContainerModule(bind => {
bind(NotebookKernelService).toSelf().inSingletonScope();
bind(NotebookRendererMessagingService).toSelf().inSingletonScope();
bind(NotebookKernelHistoryService).toSelf().inSingletonScope();
bind(NotebookKernelQuickPickService).to(KernelPickerMRUStrategy).inSingletonScope();
bind(NotebookKernelQuickPickService).toSelf().inSingletonScope();

bind(NotebookCellResourceResolver).toSelf().inSingletonScope();
bind(ResourceResolver).toService(NotebookCellResourceResolver);
Expand Down
172 changes: 172 additions & 0 deletions packages/notebook/src/browser/notebook-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// *****************************************************************************
// Copyright (C) 2023 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import {
CellData, CellEditType, CellMetadataEdit, CellOutput, CellOutputItem, CellRange, NotebookCellContentChangeEvent,
NotebookCellInternalMetadata,
NotebookCellsChangeInternalMetadataEvent,
NotebookCellsChangeLanguageEvent,
NotebookCellsChangeMetadataEvent,
NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookDocumentMetadata
} from '../common';
import { NotebookCell } from './view-model/notebook-cell-model';

export interface NotebookTextModelChangedEvent {
readonly rawEvents: NotebookContentChangedEvent[];
// readonly versionId: number;
readonly synchronous?: boolean;
readonly endSelectionState?: SelectionState;
};

export type NotebookContentChangedEvent = (NotebookCellsInitializeEvent<NotebookCell> | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent |
NotebookCellsModelChangedEvent<NotebookCell> | NotebookCellsModelMoveEvent<NotebookCell> | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent |
NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent |
NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent); // & { transient: boolean };

export interface NotebookCellsInitializeEvent<T> {
readonly kind: NotebookCellsChangeType.Initialize;
readonly changes: NotebookCellTextModelSplice<T>[];
}

export interface NotebookDocumentChangeMetadataEvent {
readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata;
readonly metadata: NotebookDocumentMetadata;
}

export interface NotebookCellsModelChangedEvent<T> {
readonly kind: NotebookCellsChangeType.ModelChange;
readonly changes: NotebookCellTextModelSplice<T>[];
}

export interface NotebookModelWillAddRemoveEvent {
readonly rawEvent: NotebookCellsModelChangedEvent<CellData>;
};

export interface NotebookCellsModelMoveEvent<T> {
readonly kind: NotebookCellsChangeType.Move;
readonly index: number;
readonly length: number;
readonly newIdx: number;
readonly cells: T[];
}

export interface NotebookOutputChangedEvent {
readonly kind: NotebookCellsChangeType.Output;
readonly index: number;
readonly outputs: CellOutput[];
readonly append: boolean;
}

export interface NotebookOutputItemChangedEvent {
readonly kind: NotebookCellsChangeType.OutputItem;
readonly index: number;
readonly outputId: string;
readonly outputItems: CellOutputItem[];
readonly append: boolean;
}

export interface NotebookDocumentUnknownChangeEvent {
readonly kind: NotebookCellsChangeType.Unknown;
}

export enum SelectionStateType {
Handle = 0,
Index = 1
}

export interface SelectionHandleState {
kind: SelectionStateType.Handle;
primary: number | null;
selections: number[];
}

export interface SelectionIndexState {
kind: SelectionStateType.Index;
focus: CellRange;
selections: CellRange[];
}

export type SelectionState = SelectionHandleState | SelectionIndexState;

export interface NotebookModelWillAddRemoveEvent {
readonly newCellIds?: number[];
readonly rawEvent: NotebookCellsModelChangedEvent<CellData>;
};

export interface CellOutputEdit {
editType: CellEditType.Output;
index: number;
outputs: CellOutput[];
append?: boolean;
}

export interface CellOutputEditByHandle {
editType: CellEditType.Output;
handle: number;
outputs: CellOutput[];
append?: boolean;
}

export interface CellOutputItemEdit {
editType: CellEditType.OutputItems;
items: CellOutputItem[];
outputId: string;
append?: boolean;
}

export interface CellLanguageEdit {
editType: CellEditType.CellLanguage;
index: number;
language: string;
}

export interface DocumentMetadataEdit {
editType: CellEditType.DocumentMetadata;
metadata: NotebookDocumentMetadata;
}

export interface CellMoveEdit {
editType: CellEditType.Move;
index: number;
length: number;
newIdx: number;
}

export interface CellReplaceEdit {
editType: CellEditType.Replace;
index: number;
count: number;
cells: CellData[];
}

export type ImmediateCellEditOperation = CellOutputEditByHandle | CellOutputItemEdit | CellPartialInternalMetadataEditByHandle; // add more later on
export type CellEditOperation = ImmediateCellEditOperation | CellReplaceEdit | CellOutputEdit |
CellMetadataEdit | CellLanguageEdit | DocumentMetadataEdit | CellMoveEdit; // add more later on

export type NullablePartialNotebookCellInternalMetadata = {
[Key in keyof Partial<NotebookCellInternalMetadata>]: NotebookCellInternalMetadata[Key] | null
};
export interface CellPartialInternalMetadataEditByHandle {
editType: CellEditType.PartialInternalMetadata;
handle: number;
internalMetadata: NullablePartialNotebookCellInternalMetadata;
}

export interface NotebookCellOutputsSplice {
start: number;
deleteCount: number;
newOutputs: CellOutput[];
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,59 @@
// *****************************************************************************

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

@injectable()
export class NotebookCellContextManager implements Disposable {
export class NotebookCellContextManager implements NotebookCellContextManager, Disposable {
@inject(ContextKeyService) protected contextKeyService: ContextKeyService;

@inject(NotebookExecutionStateService)
protected readonly executionStateService: NotebookExecutionStateService;

protected readonly toDispose = new DisposableCollection();

protected currentStore: ScopedValueStore;
protected currentContext: HTMLLIElement;

protected readonly onDidChangeContextEmitter = new Emitter<void>();
protected readonly onDidChangeContextEmitter = new Emitter<ContextKeyChangeEvent>();
readonly onDidChangeContext = this.onDidChangeContextEmitter.event;

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

this.currentContext = newHtmlContext;
const currentStore = this.contextKeyService.createScoped(newHtmlContext);
this.toDispose.push(currentStore);
this.currentStore = this.contextKeyService.createScoped(newHtmlContext);

currentStore.setContext(NOTEBOOK_CELL_TYPE, cell.cellKind === CellKind.Code ? 'code' : 'markdown');
this.currentStore.setContext(NOTEBOOK_CELL_TYPE, cell.cellKind === CellKind.Code ? 'code' : 'markdown');

this.toDispose.push(this.contextKeyService.onDidChange(e => {
this.onDidChangeContextEmitter.fire(e);
}));

this.toDispose.push(cell.onDidRequestCellEditChange(cellEdit => {
currentStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit);
this.onDidChangeContextEmitter.fire();
this.currentStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit);
this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE) });
}));
this.toDispose.push(this.executionStateService.onDidChangeExecution(e => {
if (e.affectsCell(cell.uri)) {
currentStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed);
currentStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle');
this.onDidChangeContextEmitter.fire();
this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed);
this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle');
this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_EXECUTING) || keys.has(NOTEBOOK_CELL_EXECUTION_STATE) });
}
}));
this.onDidChangeContextEmitter.fire();
this.onDidChangeContextEmitter.fire({ affects: keys => true });
}
}

dispose(): void {
this.toDispose.dispose();
this.currentStore?.dispose();
this.onDidChangeContextEmitter.dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ export class NotebookEditorWidgetService implements Disposable {
return this.notebookEditors.get(editorId);
}

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ import { CellExecution, NotebookExecutionStateService } from '../service/noteboo
import { CellKind, NotebookCellExecutionState } from '../../common';
import { NotebookCellModel } from '../view-model/notebook-cell-model';
import { NotebookModel } from '../view-model/notebook-model';
import { NotebookKernelService, NotebookKernel } from './notebook-kernel-service';
import { NotebookKernelService } from './notebook-kernel-service';
import { CommandService, Disposable } from '@theia/core';
import { NotebookKernelQuickPickService, NotebookKernelQuickPickServiceImpl } from './notebook-kernel-quick-pick-service';
import { NotebookKernelQuickPickService } from './notebook-kernel-quick-pick-service';
import { NotebookKernelHistoryService } from './notebook-kernel-history-service';
import { NotebookCommands } from '../contributions/notebook-actions-contribution';

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

@inject(NotebookKernelQuickPickService)
protected notebookKernelQuickPickService: NotebookKernelQuickPickServiceImpl;
protected notebookKernelQuickPickService: NotebookKernelQuickPickService;

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

Expand All @@ -69,7 +68,7 @@ export class NotebookExecutionService {
}
}

const kernel = await this.resolveKernel(notebook);
const kernel = await this.notebookKernelHistoryService.resolveSelectedKernel(notebook);

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

async resolveKernel(notebook: NotebookModel): Promise<NotebookKernel | undefined> {
const alreadySelected = this.notebookKernelHistoryService.getKernels(notebook);

if (alreadySelected.selected) {
return alreadySelected.selected;
}

await this.commandService.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, notebook);
const { selected } = this.notebookKernelHistoryService.getKernels(notebook);
return selected;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { NotebookService } from './notebook-service';
import {
CellEditType, CellExecuteOutputEdit, CellExecuteOutputItemEdit, CellExecutionUpdateType,
CellUri, CellPartialInternalMetadataEditByHandle, NotebookCellExecutionState, CellEditOperation, NotebookCellInternalMetadata
CellUri, NotebookCellExecutionState, NotebookCellInternalMetadata
} from '../../common';
import { CellPartialInternalMetadataEditByHandle, CellEditOperation } from '../notebook-types';
import { NotebookModel } from '../view-model/notebook-model';
import { v4 } from 'uuid';

Expand All @@ -43,10 +44,6 @@ export interface CellExecutionStateUpdate {
isPaused?: boolean;
}

export interface ICellExecutionComplete {
runEndTime?: number;
lastRunSuccess?: boolean;
}
export enum NotebookExecutionType {
cell,
notebook
Expand Down
Loading

0 comments on commit 0910b02

Please sign in to comment.