Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow invoking kernel picker for inactive notebook given NotebookEditor in notebook.selectKernel command args #132879

Merged
merged 3 commits into from
Sep 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extensionStoragePaths));
const extHostNotebookDocuments = rpcProtocol.set(ExtHostContext.ExtHostNotebookDocuments, new ExtHostNotebookDocuments(extHostLogService, extHostNotebook));
const extHostNotebookEditors = rpcProtocol.set(ExtHostContext.ExtHostNotebookEditors, new ExtHostNotebookEditors(extHostLogService, rpcProtocol, extHostNotebook));
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook, extHostLogService));
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook, extHostCommands, extHostLogService));
const extHostNotebookRenderers = rpcProtocol.set(ExtHostContext.ExtHostNotebookRenderers, new ExtHostNotebookRenderers(rpcProtocol, extHostNotebook));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/api/common/extHostInteractive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class ExtHostInteractive implements ExtHostInteractiveShape {
private _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
private _commands: ExtHostCommands
) {
const apiCommand = new ApiCommand(
const openApiCommand = new ApiCommand(
'interactive.open',
'_interactive.open',
'Open interactive window and return notebook editor and input URI',
Expand All @@ -35,7 +35,7 @@ export class ExtHostInteractive implements ExtHostInteractiveShape {
return { notebookUri: URI.revive(v.notebookUri), inputUri: URI.revive(v.inputUri) };
})
);
this._commands.registerApiCommand(apiCommand);
this._commands.registerApiCommand(openApiCommand);
}

$willAddInteractiveDocument(uri: UriComponents, eol: string, modeId: string, notebookUri: UriComponents) {
Expand Down
32 changes: 32 additions & 0 deletions src/vs/workbench/api/common/extHostNotebookKernels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto } from 'vs/workbench/api/common/extHost.protocol';
import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
import { ExtHostCell, ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument';
Expand All @@ -32,6 +33,11 @@ interface IKernelData {
associatedNotebooks: ResourceMap<boolean>;
}

type ExtHostSelectKernelArgs = ControllerInfo | { notebookEditor: vscode.NotebookEditor } | ControllerInfo & { notebookEditor: vscode.NotebookEditor } | undefined;
export type SelectKernelReturnArgs = ControllerInfo | { notebookEditorId: string } | ControllerInfo & { notebookEditorId: string } | undefined;
type ControllerInfo = { id: string, extension: string };


export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {

private readonly _proxy: MainThreadNotebookKernelsShape;
Expand All @@ -44,9 +50,35 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
mainContext: IMainContext,
private readonly _initData: IExtHostInitDataService,
private readonly _extHostNotebook: ExtHostNotebookController,
private _commands: ExtHostCommands,
@ILogService private readonly _logService: ILogService,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebookKernels);

// todo@rebornix @joyceerhl: move to APICommands once stablized.
const selectKernelApiCommand = new ApiCommand(
'notebook.selectKernel',
'_notebook.selectKernel',
'Trigger kernel picker for specified notebook editor widget',
[
new ApiCommandArgument<ExtHostSelectKernelArgs, SelectKernelReturnArgs>('options', 'Select kernel options', v => true, (v: ExtHostSelectKernelArgs) => {
if (v && 'notebookEditor' in v && 'id' in v) {
const notebookEditorId = this._extHostNotebook.getIdByEditor(v.notebookEditor);
return {
id: v.id, extension: v.extension, notebookEditorId
};
} else if (v && 'notebookEditor' in v) {
const notebookEditorId = this._extHostNotebook.getIdByEditor(v.notebookEditor);
if (notebookEditorId === undefined) {
throw new Error(`Cannot invoke 'notebook.selectKernel' for unrecognized notebook editor ${v.notebookEditor.document.uri.toString()}`);
}
return { notebookEditorId };
}
return v;
})
],
ApiCommandResult.Void);
this._commands.registerApiCommand(selectKernelApiCommand);
}

createNotebookController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler?: (cells: vscode.NotebookCell[], notebook: vscode.NotebookDocument, controller: vscode.NotebookController) => void | Thenable<void>, preloads?: vscode.NotebookRendererScript[]): vscode.NotebookController {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IQuickInputButton, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { Registry } from 'vs/platform/registry/common/platform';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import type { SelectKernelReturnArgs } from 'vs/workbench/api/common/extHostNotebookKernels';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSION_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { NOTEBOOK_ACTIONS_CATEGORY, SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
Expand All @@ -33,10 +34,10 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
registerAction2(class extends Action2 {
constructor() {
super({
id: SELECT_KERNEL_ID,
id: '_notebook.selectKernel',
category: NOTEBOOK_ACTIONS_CATEGORY,
title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' },
precondition: NOTEBOOK_IS_ACTIVE_EDITOR,
// precondition: NOTEBOOK_IS_ACTIVE_EDITOR,
icon: selectKernelIcon,
f1: true,
menu: [{
Expand Down Expand Up @@ -77,6 +78,9 @@ registerAction2(class extends Action2 {
},
'extension': {
'type': 'string'
},
'notebookEditorId': {
'type': 'string'
}
}
}
Expand All @@ -86,35 +90,53 @@ registerAction2(class extends Action2 {
});
}

async run(accessor: ServicesAccessor, context?: { id: string, extension: string, ui?: boolean, notebookEditor?: NotebookEditorWidget }): Promise<boolean> {
async run(accessor: ServicesAccessor, context?: SelectKernelReturnArgs | { ui?: boolean, notebookEditor?: NotebookEditorWidget }): Promise<boolean> {
const notebookKernelService = accessor.get(INotebookKernelService);
const editorService = accessor.get(IEditorService);
const quickInputService = accessor.get(IQuickInputService);
const labelService = accessor.get(ILabelService);
const logService = accessor.get(ILogService);
const viewletService = accessor.get(IViewletService);

const editor = context?.notebookEditor ?? getNotebookEditorFromEditorPane(editorService.activeEditorPane);
let editor;
if (context !== undefined) {
if ('notebookEditorId' in context) {
const editorId = context.notebookEditorId;
const matchingEditor = editorService.visibleEditorPanes.find((editorPane) => {
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
return notebookEditor?.getId() === editorId;
});
editor = getNotebookEditorFromEditorPane(matchingEditor);
} else if ('notebookEditor' in context) {
editor = context?.notebookEditor;
} else {
editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane);
}
}

if (!editor || !editor.hasModel()) {
return false;
}
let controllerId = context && 'id' in context ? context.id : undefined;
let extensionId = context && 'extension' in context ? context.extension : undefined;

if (context && (typeof context.id !== 'string' || typeof context.extension !== 'string')) {
if (controllerId && (typeof controllerId !== 'string' || typeof extensionId !== 'string')) {
// validate context: id & extension MUST be strings
context = undefined;
controllerId = undefined;
extensionId = undefined;
}

const notebook = editor.textModel;
const { selected, all } = notebookKernelService.getMatchingKernel(notebook);

if (selected && context && selected.id === context.id && ExtensionIdentifier.equals(selected.extension, context.extension)) {
if (selected && controllerId && selected.id === controllerId && ExtensionIdentifier.equals(selected.extension, extensionId)) {
// current kernel is wanted kernel -> done
return true;
}

let newKernel: INotebookKernel | undefined;
if (context) {
const wantedId = `${context.extension}/${context.id}`;
if (controllerId) {
const wantedId = `${extensionId}/${controllerId}`;
for (let candidate of all) {
if (candidate.id === wantedId) {
newKernel = candidate;
Expand Down Expand Up @@ -323,7 +345,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
tooltip: isSuggested ? nls.localize('tooltop', "{0} (suggestion)", tooltip) : tooltip,
command: SELECT_KERNEL_ID,
},
'notebook.selectKernel',
'_notebook.selectKernel',
StatusbarAlignment.RIGHT,
10
));
Expand All @@ -341,7 +363,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
command: SELECT_KERNEL_ID,
backgroundColor: { id: 'statusBarItem.prominentBackground' }
},
'notebook.selectKernel',
'_notebook.selectKernel',
StatusbarAlignment.RIGHT,
10
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { MarshalledId } from 'vs/base/common/marshalling';

// Kernel Command
export const SELECT_KERNEL_ID = 'notebook.selectKernel';
export const SELECT_KERNEL_ID = '_notebook.selectKernel';
export const NOTEBOOK_ACTIONS_CATEGORY = { value: localize('notebookActions.category', "Notebook"), original: 'Notebook' };

export const CELL_TITLE_CELL_GROUP_ID = 'inline/cell';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ suite('NotebookKernel', function () {
let extHostDocuments: ExtHostDocuments;
let extHostNotebooks: ExtHostNotebookController;
let extHostNotebookDocuments: ExtHostNotebookDocuments;
let extHostCommands: ExtHostCommands;

const notebookUri = URI.parse('test:///notebook.file');
const kernelData = new Map<number, INotebookKernelDto2>();
Expand Down Expand Up @@ -84,7 +85,8 @@ suite('NotebookKernel', function () {
return URI.from({ scheme: 'test', path: generateUuid() });
}
};
extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, extHostDocuments, extHostStoragePaths);
extHostCommands = new ExtHostCommands(rpcProtocol, new NullLogService());
extHostNotebooks = new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extHostStoragePaths);

extHostNotebookDocuments = new ExtHostNotebookDocuments(new NullLogService(), extHostNotebooks);

Expand Down Expand Up @@ -130,6 +132,7 @@ suite('NotebookKernel', function () {
rpcProtocol,
new class extends mock<IExtHostInitDataService>() { },
extHostNotebooks,
extHostCommands,
new NullLogService()
);
});
Expand Down