Skip to content

Commit

Permalink
basic kernel creation and selection
Browse files Browse the repository at this point in the history
Signed-off-by: Jonah Iden <[email protected]>
  • Loading branch information
jonah-iden committed Jul 13, 2023
1 parent 205eca9 commit 4c5025b
Show file tree
Hide file tree
Showing 41 changed files with 1,897 additions and 307 deletions.
20 changes: 20 additions & 0 deletions packages/core/src/common/array-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,24 @@ export namespace ArrayUtils {
export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
return <T[]>array.filter(e => !!e);
}

/**
* groups array elements through a comparator function
* @param data array of elements to group
* @param compare comparator function: return of 0 means should group, anything above means not group
* @returns array of arrays with grouped elements
*/
export function groupBy<T>(data: ReadonlyArray<T>, compare: (a: T, b: T) => number): T[][] {
const result: T[][] = [];
let currentGroup: T[] | undefined = undefined;
for (const element of data.slice(0).sort(compare)) {
if (!currentGroup || compare(currentGroup[0], element) !== 0) {
currentGroup = [element];
result.push(currentGroup);
} else {
currentGroup.push(element);
}
}
return result;
}
}
14 changes: 12 additions & 2 deletions packages/core/src/common/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

/* eslint-disable @typescript-eslint/no-explicit-any */

import { Disposable, DisposableGroup } from './disposable';
import { Disposable, DisposableGroup, DisposableCollection } from './disposable';
import { MaybePromise } from './types';

/**
Expand Down Expand Up @@ -67,6 +67,16 @@ export namespace Event {
set maxListeners(maxListeners: number) { }
});
}

/**
* Given a collection of events, returns a single event which emits whenever any of the provided events emit.
*/
export function any<T>(...events: Event<T>[]): Event<T>;
export function any(...events: Event<any>[]): Event<void>;
export function any<T>(...events: Event<T>[]): Event<T> {
return (listener, thisArgs = undefined, disposables?: Disposable[]) =>
new DisposableCollection(...events.map(event => event(e => listener.call(thisArgs, e), undefined, disposables)));
}
}

type Callback = (...args: any[]) => any;
Expand Down Expand Up @@ -276,7 +286,7 @@ export class Emitter<T = any> {
*/
fire(event: T): any {
if (this._callbacks) {
this._callbacks.invoke(event);
return this._callbacks.invoke(event);
}
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/common/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export class URI {
return new URI(Uri.file(path));
}

public static isUri(uri: unknown): boolean {
return Uri.isUri(uri);
}

private readonly codeUri: Uri;
private _path: Path | undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ 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';
import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service';

export namespace NotebookCommands {
export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({
Expand All @@ -42,7 +42,7 @@ export class NotebookActionsContribution implements CommandContribution {
protected notebookService: NotebookService;

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

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(NotebookCommands.ADD_NEW_CELL_COMMAND, {
Expand All @@ -53,9 +53,7 @@ export class NotebookActionsContribution implements CommandContribution {
});

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

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 @@ -17,6 +17,7 @@
export * from './notebook-type-registry';
export * from './notebook-editor-widget';
export * from './service/notebook-service';
export * from './service/notebook-editor-service';
export * from './service/notebook-kernel-service';
export * from './service/notebook-execution-state-service';
export * from './service/notebook-model-resolver-service';
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class NotebookCellResource implements Resource {
}

dispose(): void {
throw new Error('Method not implemented.');
this.didChangeContentsEmitter.dispose();
}

}
Expand Down
28 changes: 25 additions & 3 deletions packages/notebook/src/browser/notebook-editor-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import * as React from '@theia/core/shared/react';
import { CommandRegistry, URI } from '@theia/core';
import { ReactWidget, Navigatable, SaveableSource, Saveable } from '@theia/core/lib/browser';
import { ReactWidget, Navigatable, SaveableSource, Saveable, Message } from '@theia/core/lib/browser';
import { ReactNode } from '@theia/core/shared/react';
import { CellKind } from '../common';
import { Cellrenderer as CellRenderer, NotebookCellListView } from './view/notebook-cell-list-view';
Expand All @@ -25,6 +25,8 @@ import { NotebookMarkdownCellRenderer } from './view/notebook-markdown-cell-view
import { NotebookModel } from './view-model/notebook-model';
import { NotebookCellToolbarFactory } from './view/notebook-cell-toolbar-factory';
import { inject, injectable, interfaces } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol';
import { NotebookEditorWidgetService } from './service/notebook-editor-service';

export const NotebookEditorContainerFactory = Symbol('NotebookModelFactory');

Expand All @@ -44,7 +46,7 @@ export interface NotebookEditorProps {
readonly notebookType: string,
notebookData: NotebookModel
}

export const NOTEBOOK_EDITOR_ID_PREFIX = 'notebook:';
@injectable()
export class NotebookEditorWidget extends ReactWidget implements Navigatable, SaveableSource {
static readonly ID = 'notebook';
Expand All @@ -57,19 +59,29 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
@inject(CommandRegistry)
protected commandRegistry: CommandRegistry;

@inject(NotebookEditorWidgetService)
protected notebookEditorService: NotebookEditorWidgetService;

private readonly onDidChangeModelEmitter = new Emitter<void>();
readonly onDidChangeModel = this.onDidChangeModelEmitter.event;

private readonly renderers = new Map<CellKind, CellRenderer>();

get notebookType(): string {
return this.props.notebookType;
}

get model(): NotebookModel {
return this.props.notebookData;
}

constructor(
@inject(NotebookCodeCellRenderer) codeCellRenderer: NotebookCodeCellRenderer,
@inject(NotebookMarkdownCellRenderer) markdownCellRenderer: NotebookMarkdownCellRenderer,
@inject(NotebookEditorProps) private readonly props: NotebookEditorProps) {
super();
this.saveable = this.props.notebookData;
this.id = 'notebook:' + this.props.uri.toString();
this.id = NOTEBOOK_EDITOR_ID_PREFIX + this.props.uri.toString();

this.title.closable = true;
this.update();
Expand All @@ -93,4 +105,14 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
commandRegistry={this.commandRegistry}/>
</div>;
}

protected override onAfterAttach(msg: Message): void {
super.onAfterAttach(msg);
this.notebookEditorService.addNotebookEditor(this);
}

protected override onAfterDetach(msg: Message): void {
super.onAfterDetach(msg);
this.notebookEditorService.removeNotebookEditor(this);
}
}
8 changes: 6 additions & 2 deletions packages/notebook/src/browser/notebook-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ 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 { NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service';
import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service';
import { NotebookKernelHistoryService } from './service/notebookKernelHistoryService';
import { NotebookEditorWidgetService } from './service/notebook-editor-service';

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

bind(NotebookService).toSelf().inSingletonScope();
bind(NotebookEditorWidgetService).toSelf().inSingletonScope();
bind(NotebookExecutionService).toSelf().inSingletonScope();
bind(NotebookExecutionStateService).toSelf().inSingletonScope();
bind(NotebookKernelService).toSelf().inSingletonScope();
bind(NotebookKernelQuickPickService).toSelf().inSingletonScope();
bind(NotebookKernelHistoryService).toSelf().inSingletonScope();
bind(NotebookKernelQuickPickService).to(KernelPickerMRUStrategy).inSingletonScope();

bind(NotebookCellResourceResolver).toSelf().inSingletonScope();
bind(ResourceResolver).toService(NotebookCellResourceResolver);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@

import { inject, injectable } from '@theia/core/shared/inversify';
import { ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service';
import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol';
import { NotebookCellModel } from '../view-model/notebook-cell-model';
import { NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE } from '../contributions/notebook-context-keys';
import { DisposableCollection } from '@theia/core';
import { Disposable, DisposableCollection } from '@theia/core';
import { CellKind } from '../../common';

@injectable()
Expand Down
85 changes: 85 additions & 0 deletions packages/notebook/src/browser/service/notebook-editor-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// *****************************************************************************
// 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 { Disposable, DisposableCollection, Emitter } from '@theia/core';
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import { ApplicationShell } from '@theia/core/lib/browser';
import { NotebookEditorWidget, NOTEBOOK_EDITOR_ID_PREFIX } from '../notebook-editor-widget';

@injectable()
export class NotebookEditorWidgetService implements Disposable {

@inject(ApplicationShell)
protected applicationShell: ApplicationShell;

private readonly notebookEditors = new Map<string, NotebookEditorWidget>();

private readonly onNotebookEditorAddEmitter = new Emitter<NotebookEditorWidget>();
private readonly onNotebookEditorsRemoveEmitter = new Emitter<NotebookEditorWidget>();
readonly onDidAddNotebookEditor = this.onNotebookEditorAddEmitter.event;
readonly onDidRemoveNotebookEditor = this.onNotebookEditorsRemoveEmitter.event;

private readonly onFocusedEditorChangedEmitter = new Emitter<NotebookEditorWidget>();
readonly onFocusedEditorChanged = this.onFocusedEditorChangedEmitter.event;

private readonly listeners = new DisposableCollection();

currentfocusedEditor?: NotebookEditorWidget = undefined;

@postConstruct()
protected init(): void {
this.listeners.push(this.applicationShell.onDidChangeActiveWidget(event => {
if (event.newValue?.id.startsWith(NOTEBOOK_EDITOR_ID_PREFIX) && event.newValue !== this.currentfocusedEditor) {
this.currentfocusedEditor = event.newValue as NotebookEditorWidget;
this.onFocusedEditorChangedEmitter.fire(this.currentfocusedEditor);
}
}));
}

dispose(): void {
this.onNotebookEditorAddEmitter.dispose();
this.onNotebookEditorsRemoveEmitter.dispose();
this.listeners.dispose();
}

// --- editor management

addNotebookEditor(editor: NotebookEditorWidget): void {
this.notebookEditors.set(editor.id, editor);
this.onNotebookEditorAddEmitter.fire(editor);
}

removeNotebookEditor(editor: NotebookEditorWidget): void {
if (this.notebookEditors.has(editor.id)) {
this.notebookEditors.delete(editor.id);
this.onNotebookEditorsRemoveEmitter.fire(editor);
}
}

getNotebookEditor(editorId: string): NotebookEditorWidget | undefined {
return this.notebookEditors.get(editorId);
}

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ 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 } from './notebook-kernel-service';
import { Disposable } from '@theia/core';
import { NotebookKernelService, NotebookKernel } from './notebook-kernel-service';
import { CommandService, Disposable } from '@theia/core';
import { NotebookKernelQuickPickService, NotebookKernelQuickPickServiceImpl } from './notebook-kernel-quick-pick-service';
import { NotebookKernelHistoryService } from './notebookKernelHistoryService';
import { NotebookCommands } from '../contributions/notebook-actions-contribution';

export interface CellExecutionParticipant {
onWillExecuteCell(executions: CellExecution[]): Promise<void>;
Expand All @@ -39,6 +42,15 @@ export class NotebookExecutionService {
@inject(NotebookKernelService)
protected notebookKernelService: NotebookKernelService;

@inject(NotebookKernelHistoryService)
protected notebookKernelHistoryService: NotebookKernelHistoryService;

@inject(CommandService)
protected commandService: CommandService;

@inject(NotebookKernelQuickPickService)
protected notebookKernelQuickPickService: NotebookKernelQuickPickServiceImpl;

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

async executeNotebookCells(notebook: NotebookModel, cells: Iterable<NotebookCellModel>): Promise<void> {
Expand All @@ -59,7 +71,7 @@ export class NotebookExecutionService {
}
}

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

if (!kernel) {
// clear all pending cell executions
Expand Down Expand Up @@ -117,4 +129,16 @@ export class NotebookExecutionService {
async cancelNotebookCells(notebook: NotebookModel, cells: Iterable<NotebookCellModel>): Promise<void> {
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 @@ -18,9 +18,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { URI } from '@theia/core';
import { Disposable, Emitter, URI } from '@theia/core';
import { inject, injectable } from '@theia/core/shared/inversify';
import { Disposable, Emitter } from '@theia/core/shared/vscode-languageserver-protocol';
import { NotebookService } from './notebook-service';
import {
CellEditType, CellExecuteOutputEdit, CellExecuteOutputItemEdit, CellExecutionUpdateType,
Expand Down
Loading

0 comments on commit 4c5025b

Please sign in to comment.