diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts index 97f6da824cc..7daae831c95 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts @@ -12,7 +12,7 @@ import { Kernel, saveAllFilesAndCloseAll } from './notebook.api.test'; export type INativeInteractiveWindow = { notebookUri: vscode.Uri; inputUri: vscode.Uri; notebookEditor: vscode.NotebookEditor }; async function createInteractiveWindow(kernel: Kernel) { - const { notebookEditor } = (await vscode.commands.executeCommand( + const { notebookEditor, inputUri } = (await vscode.commands.executeCommand( 'interactive.open', // Keep focus on the owning file if there is one { viewColumn: vscode.ViewColumn.Beside, preserveFocus: false }, @@ -21,7 +21,7 @@ async function createInteractiveWindow(kernel: Kernel) { undefined )) as unknown as INativeInteractiveWindow; - return notebookEditor; + return { notebookEditor, inputUri }; } async function addCell(code: string, notebook: vscode.NotebookDocument) { @@ -61,13 +61,18 @@ async function addCellAndRun(code: string, notebook: vscode.NotebookDocument, i: await saveAllFilesAndCloseAll(); }); - test('Can open an interactive window', async () => { + test('Can open an interactive window and execute from input box', async () => { assert.ok(vscode.workspace.workspaceFolders); - const notebookEditor = await createInteractiveWindow(defaultKernel); + const { notebookEditor, inputUri } = await createInteractiveWindow(defaultKernel); assert.ok(notebookEditor); - // Try adding a cell and running it. - await addCell('print foo', notebookEditor.notebook); + const inputBox = vscode.window.visibleTextEditors.find( + (e) => e.document.uri.path === inputUri.path + ); + await inputBox!.edit((editBuilder) => { + editBuilder.insert(new vscode.Position(0, 0), 'print foo'); + }); + await vscode.commands.executeCommand('interactive.execute', notebookEditor.notebook.uri); assert.strictEqual(notebookEditor.notebook.cellCount, 1); assert.strictEqual(notebookEditor.notebook.cellAt(0).kind, vscode.NotebookCellKind.Code); @@ -75,7 +80,7 @@ async function addCellAndRun(code: string, notebook: vscode.NotebookDocument, i: test('Interactive window scrolls after execute', async () => { assert.ok(vscode.workspace.workspaceFolders); - const notebookEditor = await createInteractiveWindow(defaultKernel); + const { notebookEditor } = await createInteractiveWindow(defaultKernel); assert.ok(notebookEditor); // Run and add a bunch of cells @@ -90,13 +95,13 @@ async function addCellAndRun(code: string, notebook: vscode.NotebookDocument, i: test('Interactive window has the correct kernel', async () => { assert.ok(vscode.workspace.workspaceFolders); - const notebookEditor = await createInteractiveWindow(defaultKernel); + const { notebookEditor } = await createInteractiveWindow(defaultKernel); assert.ok(notebookEditor); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); // Create a new interactive window with a different kernel - const notebookEditor2 = await createInteractiveWindow(secondKernel); + const { notebookEditor: notebookEditor2 } = await createInteractiveWindow(secondKernel); assert.ok(notebookEditor2); // Verify the kernel is the secondary one diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index b57fdb46eb0..cdd349cae96 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -12,7 +12,7 @@ import { Schemas } from 'vs/base/common/network'; import { extname } from 'vs/base/common/resources'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { assertType } from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -462,15 +462,40 @@ registerAction2(class extends Action2 { } ], icon: icons.executeIcon, - f1: false + f1: false, + description: { + description: 'Execute the Contents of the Input Box', + args: [ + { + name: 'resource', + description: 'Interactive resource Uri', + isOptional: true + } + ] + } }); } - async run(accessor: ServicesAccessor): Promise { + async run(accessor: ServicesAccessor, context?: UriComponents): Promise { const editorService = accessor.get(IEditorService); const bulkEditService = accessor.get(IBulkEditService); const historyService = accessor.get(IInteractiveHistoryService); - const editorControl = editorService.activeEditorPane?.getControl() as { notebookEditor: NotebookEditorWidget | undefined; codeEditor: CodeEditorWidget } | undefined; + let editorControl: { notebookEditor: NotebookEditorWidget | undefined; codeEditor: CodeEditorWidget } | undefined; + if (context) { + if (context.scheme === Schemas.vscodeInteractive) { + const resourceUri = URI.revive(context); + const editors = editorService.findEditors(resourceUri).filter(id => id.editor instanceof InteractiveEditorInput && id.editor.resource?.toString() === resourceUri.toString()); + if (editors.length) { + const editorInput = editors[0].editor as InteractiveEditorInput; + const currentGroup = editors[0].groupId; + const editor = await editorService.openEditor(editorInput, currentGroup); + editorControl = editor?.getControl() as { notebookEditor: NotebookEditorWidget | undefined; codeEditor: CodeEditorWidget } | undefined; + } + } + } + else { + editorControl = editorService.activeEditorPane?.getControl() as { notebookEditor: NotebookEditorWidget | undefined; codeEditor: CodeEditorWidget } | undefined; + } if (editorControl && editorControl.notebookEditor && editorControl.codeEditor) { const notebookDocument = editorControl.notebookEditor.textModel; diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index f1155751058..167a0472f36 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -104,6 +104,7 @@ export class InteractiveEditor extends EditorPane { #notebookOptions: NotebookOptions; #editorMemento: IEditorMemento; #groupListener = this._register(new DisposableStore()); + #runbuttonToolbar: ToolBar | undefined; #onDidFocusWidget = this._register(new Emitter()); override get onDidFocus(): Event { return this.#onDidFocusWidget.event; } @@ -186,7 +187,7 @@ export class InteractiveEditor extends EditorPane { #setupRunButtonToolbar(runButtonContainer: HTMLElement) { const menu = this._register(this.#menuService.createMenu(MenuId.InteractiveInputExecute, this.#contextKeyService)); - const toolbar = this._register(new ToolBar(runButtonContainer, this.#contextMenuService, { + this.#runbuttonToolbar = this._register(new ToolBar(runButtonContainer, this.#contextMenuService, { getKeyBinding: action => this.#keybindingService.lookupKeybinding(action.id), actionViewItemProvider: action => { return createActionViewItem(this.#instantiationService, action); @@ -199,7 +200,7 @@ export class InteractiveEditor extends EditorPane { const result = { primary, secondary }; createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result); - toolbar.setActions([...primary, ...secondary]); + this.#runbuttonToolbar.setActions([...primary, ...secondary]); } #createLayoutStyles(): void { @@ -392,6 +393,9 @@ export class InteractiveEditor extends EditorPane { await super.setInput(input, options, context, token); const model = await input.resolve(); + if (this.#runbuttonToolbar) { + this.#runbuttonToolbar.context = input.resource; + } if (model === null) { throw new Error('?');