Skip to content

Commit

Permalink
basic api for cell execution and jupyter extension support
Browse files Browse the repository at this point in the history
Signed-off-by: Jonah Iden <[email protected]>
  • Loading branch information
jonah-iden committed Jun 29, 2023
1 parent 2c6ddc7 commit 4de79bf
Show file tree
Hide file tree
Showing 22 changed files with 1,024 additions and 110 deletions.
3 changes: 2 additions & 1 deletion packages/notebook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"dependencies": {
"@theia/core": "1.38.0",
"@theia/filesystem": "1.38.0",
"@theia/monaco": "1.38.0"
"@theia/monaco": "1.38.0",
"uuid": "^8.3.2"
},
"publishConfig": {
"access": "public"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ import { codicon } from '@theia/core/lib/browser';
import { NotebookModel } from '../view-model/notebook-model';
import { NotebookService } from '../service/notebook-service';
import { CellKind } from '../../common';
import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service';

export namespace NotebookCommands {
export const Add_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({
export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({
id: 'notebook.add-new-cell',
iconClass: codicon('add')
});

export const SELECT_KERNEL_COMMAND = Command.toDefaultLocalizedCommand({
id: 'notebook.selectKernel',
category: 'Notebook',
iconClass: codicon('server-environment')
});
}

@injectable()
Expand All @@ -35,13 +41,22 @@ export class NotebookActionsContribution implements CommandContribution {
@inject(NotebookService)
protected notebookService: NotebookService;

@inject(NotebookKernelQuickPickService)
protected notebookKernelQuickPickService: NotebookKernelQuickPickService;

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(NotebookCommands.Add_NEW_CELL_COMMAND, {
commands.registerCommand(NotebookCommands.ADD_NEW_CELL_COMMAND, {
execute: (notebookModel: NotebookModel, cellKind: CellKind, index?: number) => {
notebookModel.insertNewCell(index ?? notebookModel.cells.length,
[this.notebookService.createEmptyCellModel(notebookModel, cellKind)]);
}
});

commands.registerCommand(NotebookCommands.SELECT_KERNEL_COMMAND, {
execute: (notebookModel: NotebookModel) => {
this.notebookKernelQuickPickService.showQuickPick(notebookModel);
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { NotebookModel } from '../view-model/notebook-model';
import { NotebookCellModel } from '../view-model/notebook-cell-model';
import { NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NotebookContextKeys } from './notebook-context-keys';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { NotebookExecutionService } from '../service/notebook-execution-service';

export namespace NotebookCellCommands {
export const EDIT_COMMAND = Command.toDefaultLocalizedCommand({
Expand All @@ -39,6 +40,10 @@ export namespace NotebookCellCommands {
id: 'notebook.cell.split-cell',
iconClass: codicon('split-vertical'),
});
export const EXECUTE_SINGLE_CELL_COMMAND = Command.toDefaultLocalizedCommand({
id: 'notebook.cell.execute-cell',
iconClass: codicon('play'),
});
}

@injectable()
Expand All @@ -47,13 +52,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command
@inject(ContextKeyService)
protected contextKeyService: ContextKeyService;

protected runDeleteAction(notebookModel: NotebookModel, cell: NotebookCellModel): void {
notebookModel.removeCell(notebookModel.cells.indexOf(cell), 1);
}

protected requestCellEdit(notebookModel: NotebookModel, cell: NotebookCellModel): void {
cell.requestEdit();
}
@inject(NotebookExecutionService)
protected notebookExecutionService: NotebookExecutionService;

@postConstruct()
protected init(): void {
Expand All @@ -74,6 +74,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command
when: `${NOTEBOOK_CELL_TYPE} == 'markdown' && ${NOTEBOOK_CELL_MARKDOWN_EDIT_MODE}`,
order: '10'
});
menus.registerMenuAction([menuId], {
commandId: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id,
icon: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.iconClass,
when: `${NOTEBOOK_CELL_TYPE} == 'code'`,
order: '10'
});
menus.registerMenuAction([menuId], {
commandId: NotebookCellCommands.SPLIT_CELL_COMMAND.id,
icon: NotebookCellCommands.SPLIT_CELL_COMMAND.iconClass,
Expand All @@ -86,18 +92,23 @@ export class NotebookCellActionContribution implements MenuContribution, Command
});

const moreMenuPath = [menuId, 'more'];
menus.registerSubmenu(moreMenuPath, 'more', { icon: codicon('ellipsis'), role: CompoundMenuNodeRole.Submenu, order: '100' });
menus.registerSubmenu(moreMenuPath, 'more', { icon: codicon('ellipsis'), role: CompoundMenuNodeRole.Submenu, order: '999' });
menus.registerMenuAction(moreMenuPath, {
commandId: NotebookCellCommands.EDIT_COMMAND.id,
label: 'test submenu item',
});
}

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, { execute: this.requestCellEdit });
commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => cell.requestEdit() });
commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => cell.requestStopEdit() });
commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, { execute: this.runDeleteAction });
commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, {
execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => notebookModel.removeCell(notebookModel.cells.indexOf(cell), 1)
});
commands.registerCommand(NotebookCellCommands.SPLIT_CELL_COMMAND);
}

commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND, {
execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => this.notebookExecutionService.executeNotebookCells(notebookModel, [cell])
});
}
}
1 change: 1 addition & 0 deletions packages/notebook/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export * from './notebook-type-registry';
export * from './notebook-editor-widget';
export * from './service/notebook-service';
export * from './service/notebook-kernel-service';
export * from './service/notebook-execution-state-service';
export * from './service/notebook-model-resolver-service';
8 changes: 8 additions & 0 deletions packages/notebook/src/browser/notebook-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ import { createNotebookEditorWidgetContainer, NotebookEditorContainerFactory, No
import { NotebookCodeCellRenderer } from './view/notebook-code-cell-view';
import { NotebookMarkdownCellRenderer } from './view/notebook-markdown-cell-view';
import { NotebookActionsContribution } from './contributions/notebook-actions-contribution';
import { NotebookExecutionService } from './service/notebook-execution-service';
import { NotebookExecutionStateService } from './service/notebook-execution-state-service';
import { NotebookKernelService } from './service/notebook-kernel-service';
import { NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service';

export default new ContainerModule(bind => {
bindContributionProvider(bind, Symbol('notebooks'));
Expand All @@ -45,6 +49,10 @@ export default new ContainerModule(bind => {
bind(NotebookCellToolbarFactory).toSelf().inSingletonScope();

bind(NotebookService).toSelf().inSingletonScope();
bind(NotebookExecutionService).toSelf().inSingletonScope();
bind(NotebookExecutionStateService).toSelf().inSingletonScope();
bind(NotebookKernelService).toSelf().inSingletonScope();
bind(NotebookKernelQuickPickService).toSelf().inSingletonScope();

bind(NotebookCellResourceResolver).toSelf().inSingletonScope();
bind(ResourceResolver).toService(NotebookCellResourceResolver);
Expand Down
120 changes: 120 additions & 0 deletions packages/notebook/src/browser/service/notebook-execution-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// *****************************************************************************
// 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
// *****************************************************************************
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { inject, injectable } from '@theia/core/shared/inversify';
import { CellExecution, NotebookExecutionStateService } from '../service/notebook-execution-state-service';
import { CellKind, NotebookCellExecutionState } from '../../common';
import { NotebookCellModel } from '../view-model/notebook-cell-model';
import { NotebookModel } from '../view-model/notebook-model';
import { NotebookKernelService } from './notebook-kernel-service';
import { Disposable } from '@theia/core';

export interface CellExecutionParticipant {
onWillExecuteCell(executions: CellExecution[]): Promise<void>;
}

@injectable()
export class NotebookExecutionService {

@inject(NotebookExecutionStateService)
protected notebookExecutionStateService: NotebookExecutionStateService;

@inject(NotebookKernelService)
protected notebookKernelService: NotebookKernelService;

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

async executeNotebookCells(notebook: NotebookModel, cells: Iterable<NotebookCellModel>): Promise<void> {
const cellsArr = Array.from(cells)
.filter(c => c.cellKind === CellKind.Code);
if (!cellsArr.length) {
return;
}

console.debug(`NotebookExecutionService#executeNotebookCells ${JSON.stringify(cellsArr.map(c => c.handle))}`);

// create cell executions
const cellExecutions: [NotebookCellModel, CellExecution][] = [];
for (const cell of cellsArr) {
const cellExe = this.notebookExecutionStateService.getCellExecution(cell.uri);
if (!cellExe) {
cellExecutions.push([cell, this.notebookExecutionStateService.createCellExecution(notebook.uri, cell.handle)]);
}
}

const kernel = this.notebookKernelService.getSelectedOrSuggestedKernel(notebook);

if (!kernel) {
// clear all pending cell executions
cellExecutions.forEach(cellExe => cellExe[1].complete({}));
return;
}

// filter cell executions based on selected kernel
const validCellExecutions: CellExecution[] = [];
for (const [cell, cellExecution] of cellExecutions) {
if (!kernel.supportedLanguages.includes(cell.language)) {
cellExecution.complete({});
} else {
validCellExecutions.push(cellExecution);
}
}

// request execution
if (validCellExecutions.length > 0) {
await this.runExecutionParticipants(validCellExecutions);

this.notebookKernelService.selectKernelForNotebook(kernel, notebook);
await kernel.executeNotebookCellsRequest(notebook.uri, validCellExecutions.map(c => c.cellHandle));
// the connecting state can change before the kernel resolves executeNotebookCellsRequest
const unconfirmed = validCellExecutions.filter(exe => exe.state === NotebookCellExecutionState.Unconfirmed);
if (unconfirmed.length) {
console.debug(`NotebookExecutionService#executeNotebookCells completing unconfirmed executions ${JSON.stringify(unconfirmed.map(exe => exe.cellHandle))}`);
unconfirmed.forEach(exe => exe.complete({}));
}
}
}

registerExecutionParticipant(participant: CellExecutionParticipant): Disposable {
this.cellExecutionParticipants.add(participant);
return Disposable.create(() => this.cellExecutionParticipants.delete(participant));
}

private async runExecutionParticipants(executions: CellExecution[]): Promise<void> {
for (const participant of this.cellExecutionParticipants) {
await participant.onWillExecuteCell(executions);
}
return;
}

async cancelNotebookCellHandles(notebook: NotebookModel, cells: Iterable<number>): Promise<void> {
const cellsArr = Array.from(cells);
console.debug(`NotebookExecutionService#cancelNotebookCellHandles ${JSON.stringify(cellsArr)}`);
const kernel = this.notebookKernelService.getSelectedOrSuggestedKernel(notebook);
if (kernel) {
await kernel.cancelNotebookCellExecution(notebook.uri, cellsArr);

}
}

async cancelNotebookCells(notebook: NotebookModel, cells: Iterable<NotebookCellModel>): Promise<void> {
this.cancelNotebookCellHandles(notebook, Array.from(cells, cell => cell.handle));
}
}
Loading

0 comments on commit 4de79bf

Please sign in to comment.