From a6b3a6fe2f030a1401e184fb32e0e9a560f18ad2 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 19 Aug 2024 10:29:16 -0700 Subject: [PATCH 01/11] use new API --- package.json | 5 +++-- src/client/repl/replCommandHandler.ts | 28 +++++++++++---------------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 085eaf694718..01e0eeaab95e 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "terminalDataWriteEvent", "terminalExecuteCommandEvent", "contribIssueReporter", - "codeActionAI" + "codeActionAI", + "notebookReplDocument" ], "author": { "name": "Microsoft Corporation" @@ -1155,7 +1156,7 @@ { "command": "python.execInREPL", "key": "shift+enter", - "when": "!accessibilityModeEnabled && config.python.REPL.sendToNativeREPL && activeEditor != 'workbench.editor.interactive'&& editorLangId == python && editorTextFocus && !jupyter.ownsSelection && !notebookEditorFocused" + "when": "!accessibilityModeEnabled && config.python.REPL.sendToNativeREPL && activeEditor != 'workbench.editor.interactive' && editorLangId == python && editorTextFocus && !jupyter.ownsSelection && !notebookEditorFocused" }, { "command": "python.execInREPLEnter", diff --git a/src/client/repl/replCommandHandler.ts b/src/client/repl/replCommandHandler.ts index 599692a4300e..7dee72debda4 100644 --- a/src/client/repl/replCommandHandler.ts +++ b/src/client/repl/replCommandHandler.ts @@ -23,29 +23,23 @@ export async function openInteractiveREPL( notebookController: NotebookController, notebookDocument: NotebookDocument | undefined, ): Promise { - let notebookEditor: NotebookEditor | undefined; + let viewColumn = ViewColumn.Beside; // Case where NotebookDocument (REPL document already exists in the tab) if (notebookDocument) { const existingReplViewColumn = getExistingReplViewColumn(notebookDocument); - const replViewColumn = existingReplViewColumn ?? ViewColumn.Beside; - notebookEditor = await window.showNotebookDocument(notebookDocument!, { viewColumn: replViewColumn }); + viewColumn = existingReplViewColumn ?? viewColumn; } else if (!notebookDocument) { - // Case where NotebookDocument doesnt exist, open new REPL tab - const interactiveWindowObject = (await commands.executeCommand( - 'interactive.open', - { - preserveFocus: true, - viewColumn: ViewColumn.Beside, - }, - undefined, - notebookController.id, - 'Python REPL', - )) as { notebookEditor: NotebookEditor }; - notebookEditor = interactiveWindowObject.notebookEditor; - notebookDocument = interactiveWindowObject.notebookEditor.notebook; + // Case where NotebookDocument doesnt exist, create a blank one. + notebookDocument = await workspace.openNotebookDocument('jupyter-notebook'); } - return notebookEditor!; + const editor = window.showNotebookDocument(notebookDocument!, { viewColumn, asRepl: true }); + await commands.executeCommand('notebook.selectKernel', { + editor, + id: notebookController.id, + }); + + return editor; } /** From 93ed35d1dda50c9c9cc00fe0f1ecfce93a94d0d0 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 19 Aug 2024 10:54:00 -0700 Subject: [PATCH 02/11] change controller type --- src/client/repl/replController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/repl/replController.ts b/src/client/repl/replController.ts index 7c1f8fd0c6b2..08c2a27066a1 100644 --- a/src/client/repl/replController.ts +++ b/src/client/repl/replController.ts @@ -9,7 +9,7 @@ export function createReplController( const server = createPythonServer([interpreterPath], cwd); disposables.push(server); - const controller = vscode.notebooks.createNotebookController('pythonREPL', 'interactive', 'Python REPL'); + const controller = vscode.notebooks.createNotebookController('pythonREPL', 'jupyter-notebook', 'Python REPL'); controller.supportedLanguages = ['python']; controller.supportsExecutionOrder = true; From 4079cd1f5ff119a0e06f0db346f98e0a7b839f41 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 30 Aug 2024 10:59:14 -0700 Subject: [PATCH 03/11] bump version, use new context key --- package.json | 8 ++++---- src/client/repl/replCommandHandler.ts | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 01e0eeaab95e..c1b7f226b026 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "theme": "dark" }, "engines": { - "vscode": "^1.94.0-20240913" + "vscode": "^1.93.0-20240415" }, "enableTelemetry": false, "keywords": [ @@ -1151,17 +1151,17 @@ { "command": "python.execSelectionInTerminal", "key": "shift+enter", - "when": "editorTextFocus && editorLangId == python && !findInputFocussed && !replaceInputFocussed && !jupyter.ownsSelection && !notebookEditorFocused && activeEditor != 'workbench.editor.interactive'" + "when": "editorTextFocus && editorLangId == python && !findInputFocussed && !replaceInputFocussed && !jupyter.ownsSelection && !notebookEditorFocused && !isCompositeNotebook" }, { "command": "python.execInREPL", "key": "shift+enter", - "when": "!accessibilityModeEnabled && config.python.REPL.sendToNativeREPL && activeEditor != 'workbench.editor.interactive' && editorLangId == python && editorTextFocus && !jupyter.ownsSelection && !notebookEditorFocused" + "when": "!accessibilityModeEnabled && config.python.REPL.sendToNativeREPL && editorLangId == python && editorTextFocus && !jupyter.ownsSelection && !notebookEditorFocused && !isCompositeNotebook" }, { "command": "python.execInREPLEnter", "key": "enter", - "when": "!config.interactiveWindow.executeWithShiftEnter && activeEditor == 'workbench.editor.interactive'" + "when": "!config.interactiveWindow.executeWithShiftEnter && isCompositeNotebook" }, { "command": "python.refreshTensorBoard", diff --git a/src/client/repl/replCommandHandler.ts b/src/client/repl/replCommandHandler.ts index 7dee72debda4..878a4cce336d 100644 --- a/src/client/repl/replCommandHandler.ts +++ b/src/client/repl/replCommandHandler.ts @@ -12,6 +12,7 @@ import { workspace, } from 'vscode'; import { getExistingReplViewColumn } from './replUtils'; +import { PVSC_EXTENSION_ID } from '../common/constants'; /** * Function that opens/show REPL using IW UI. @@ -33,10 +34,11 @@ export async function openInteractiveREPL( // Case where NotebookDocument doesnt exist, create a blank one. notebookDocument = await workspace.openNotebookDocument('jupyter-notebook'); } - const editor = window.showNotebookDocument(notebookDocument!, { viewColumn, asRepl: true }); + const editor = window.showNotebookDocument(notebookDocument!, { viewColumn, asRepl: true, label: 'Python REPL' }); await commands.executeCommand('notebook.selectKernel', { editor, id: notebookController.id, + extension: PVSC_EXTENSION_ID, }); return editor; From e9c6d907f32a5ef26049a379413d00e7700f72ba Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 30 Aug 2024 11:49:25 -0700 Subject: [PATCH 04/11] manually add dts --- .../vscode.proposed.notebookReplDocument.d.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 types/vscode.proposed.notebookReplDocument.d.ts diff --git a/types/vscode.proposed.notebookReplDocument.d.ts b/types/vscode.proposed.notebookReplDocument.d.ts new file mode 100644 index 000000000000..eefdb42cfc61 --- /dev/null +++ b/types/vscode.proposed.notebookReplDocument.d.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export interface NotebookDocumentShowOptions { + /** + * The notebook should be opened in a REPL editor, + * where the last cell of the notebook is an input box and the rest are read-only. + */ + readonly asRepl?: boolean; + + /** + * The label to be used for the editor tab. + */ + readonly label?: string; + } +} From 5057747b547e4357ceab12a023bac9b84092fa48 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 30 Aug 2024 11:51:19 -0700 Subject: [PATCH 05/11] update lock --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 87af2d83a205..2efac8b36c7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,7 +116,7 @@ "yargs": "^15.3.1" }, "engines": { - "vscode": "^1.94.0-20240913" + "vscode": "^1.93.0-20240415" } }, "node_modules/@aashutoshrathi/word-wrap": { From 84d23cc4ce966c7ecda4cbf90a7403c33d9366e8 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 30 Aug 2024 13:36:05 -0700 Subject: [PATCH 06/11] add cell at the correct index --- src/client/repl/replCommandHandler.ts | 4 ++-- src/client/repl/replCommands.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/repl/replCommandHandler.ts b/src/client/repl/replCommandHandler.ts index 878a4cce336d..412fe1adccb8 100644 --- a/src/client/repl/replCommandHandler.ts +++ b/src/client/repl/replCommandHandler.ts @@ -74,7 +74,7 @@ export async function executeNotebookCell(notebookDocument: NotebookDocument, co await addCellToNotebook(notebookDocument, code); // Execute the cell commands.executeCommand('notebook.cell.execute', { - ranges: [{ start: cellCount, end: cellCount + 1 }], + ranges: [{ start: cellCount - 1, end: cellCount }], document: notebookDocument.uri, }); } @@ -89,7 +89,7 @@ async function addCellToNotebook(notebookDocument: NotebookDocument, code: strin const notebookCellData = new NotebookCellData(NotebookCellKind.Code, code as string, 'python'); const { cellCount } = notebookDocument!; // Add new cell to interactive window document - const notebookEdit = NotebookEdit.insertCells(cellCount, [notebookCellData]); + const notebookEdit = NotebookEdit.insertCells(cellCount - 1, [notebookCellData]); const workspaceEdit = new WorkspaceEdit(); workspaceEdit.set(notebookDocument!.uri, [notebookEdit]); await workspace.applyEdit(workspaceEdit); diff --git a/src/client/repl/replCommands.ts b/src/client/repl/replCommands.ts index 120ddf13effc..e67783837340 100644 --- a/src/client/repl/replCommands.ts +++ b/src/client/repl/replCommands.ts @@ -111,13 +111,13 @@ export async function registerReplExecuteOnEnter( if (editor) { // Execute right away when complete code and Not multi-line if (completeCode && !isMultiLineText(editor)) { - await commands.executeCommand('interactive.execute'); + await commands.executeCommand('repl.execute'); } else { insertNewLineToREPLInput(editor); // Handle case when user enters on blank line, just trigger interactive.execute if (editor && editor.document.lineAt(editor.selection.active.line).text === '') { - await commands.executeCommand('interactive.execute'); + await commands.executeCommand('repl.execute'); } } } From 5271067c04ced0a0f735792059df20fbe9d6ba49 Mon Sep 17 00:00:00 2001 From: amunger Date: Tue, 17 Sep 2024 18:01:53 -0700 Subject: [PATCH 07/11] account for updates --- src/client/repl/nativeRepl.ts | 2 +- src/client/repl/replCommandHandler.ts | 18 ++++++++-------- .../vscode.proposed.notebookReplDocument.d.ts | 21 +++++++++++++++---- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 8b233f765468..a0345387953d 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -150,7 +150,7 @@ export class NativeRepl implements Disposable { this.replController.updateNotebookAffinity(this.notebookDocument, NotebookControllerAffinity.Default); await selectNotebookKernel(notebookEditor, this.replController.id, PVSC_EXTENSION_ID); if (code) { - await executeNotebookCell(this.notebookDocument, code); + await executeNotebookCell(notebookEditor, code); } } } diff --git a/src/client/repl/replCommandHandler.ts b/src/client/repl/replCommandHandler.ts index 412fe1adccb8..b8fe579647a1 100644 --- a/src/client/repl/replCommandHandler.ts +++ b/src/client/repl/replCommandHandler.ts @@ -34,7 +34,7 @@ export async function openInteractiveREPL( // Case where NotebookDocument doesnt exist, create a blank one. notebookDocument = await workspace.openNotebookDocument('jupyter-notebook'); } - const editor = window.showNotebookDocument(notebookDocument!, { viewColumn, asRepl: true, label: 'Python REPL' }); + const editor = window.showNotebookDocument(notebookDocument!, { viewColumn, asRepl: 'Python REPL' }); await commands.executeCommand('notebook.selectKernel', { editor, id: notebookController.id, @@ -69,13 +69,14 @@ export async function selectNotebookKernel( * @param code * @return Promise */ -export async function executeNotebookCell(notebookDocument: NotebookDocument, code: string): Promise { - const { cellCount } = notebookDocument; - await addCellToNotebook(notebookDocument, code); +export async function executeNotebookCell(notebookEditor: NotebookEditor, code: string): Promise { + const { notebook, replOptions } = notebookEditor; + const cellIndex = replOptions?.appendIndex ?? notebook.cellCount; + await addCellToNotebook(notebook, cellIndex, code); // Execute the cell commands.executeCommand('notebook.cell.execute', { - ranges: [{ start: cellCount - 1, end: cellCount }], - document: notebookDocument.uri, + ranges: [{ start: cellIndex, end: cellIndex + 1 }], + document: notebook.uri, }); } @@ -85,11 +86,10 @@ export async function executeNotebookCell(notebookDocument: NotebookDocument, co * @param code * */ -async function addCellToNotebook(notebookDocument: NotebookDocument, code: string): Promise { +async function addCellToNotebook(notebookDocument: NotebookDocument, index: number, code: string): Promise { const notebookCellData = new NotebookCellData(NotebookCellKind.Code, code as string, 'python'); - const { cellCount } = notebookDocument!; // Add new cell to interactive window document - const notebookEdit = NotebookEdit.insertCells(cellCount - 1, [notebookCellData]); + const notebookEdit = NotebookEdit.insertCells(index, [notebookCellData]); const workspaceEdit = new WorkspaceEdit(); workspaceEdit.set(notebookDocument!.uri, [notebookEdit]); await workspace.applyEdit(workspaceEdit); diff --git a/types/vscode.proposed.notebookReplDocument.d.ts b/types/vscode.proposed.notebookReplDocument.d.ts index eefdb42cfc61..d78450e944a8 100644 --- a/types/vscode.proposed.notebookReplDocument.d.ts +++ b/types/vscode.proposed.notebookReplDocument.d.ts @@ -8,13 +8,26 @@ declare module 'vscode' { export interface NotebookDocumentShowOptions { /** * The notebook should be opened in a REPL editor, - * where the last cell of the notebook is an input box and the rest are read-only. + * where the last cell of the notebook is an input box and the other cells are the read-only history. + * When the value is a string, it will be used as the label for the editor tab. */ - readonly asRepl?: boolean; + readonly asRepl?: boolean | string | { + /** + * The label to be used for the editor tab. + */ + readonly label: string; + }; + } + export interface NotebookEditor { /** - * The label to be used for the editor tab. + * Information about the REPL editor if the notebook was opened as a repl. */ - readonly label?: string; + replOptions?: { + /** + * The index where new cells should be appended. + */ + appendIndex: number; + }; } } From 81e0e4a280842bc0a77c465a73d9d10c975b8564 Mon Sep 17 00:00:00 2001 From: amunger Date: Thu, 19 Sep 2024 13:43:28 -0700 Subject: [PATCH 08/11] keep IW and REPL execute separate --- package.json | 7 ++- src/client/common/application/commands.ts | 1 + src/client/common/constants.ts | 1 + src/client/repl/replCommands.ts | 56 ++++++++++++++--------- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index c1b7f226b026..0bebb1033b16 100644 --- a/package.json +++ b/package.json @@ -1161,7 +1161,12 @@ { "command": "python.execInREPLEnter", "key": "enter", - "when": "!config.interactiveWindow.executeWithShiftEnter && isCompositeNotebook" + "when": "!config.interactiveWindow.executeWithShiftEnter && activeEditor == 'workbench.editor.repl'" + }, + { + "command": "python.execInInteractiveWindowEnter", + "key": "enter", + "when": "!config.interactiveWindow.executeWithShiftEnter && activeEditor == 'workbench.editor.interactive'" }, { "command": "python.refreshTensorBoard", diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index 4580a91a78d1..aaa53de87410 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -99,6 +99,7 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu [Commands.Start_Native_REPL]: [undefined | Uri]; [Commands.Exec_In_REPL]: [undefined | Uri]; [Commands.Exec_In_REPL_Enter]: [undefined | Uri]; + [Commands.Exec_In_IW_Enter]: [undefined | Uri]; [Commands.Exec_In_Terminal]: [undefined, Uri]; [Commands.Exec_In_Terminal_Icon]: [undefined, Uri]; [Commands.Debug_In_Terminal]: [Uri]; diff --git a/src/client/common/constants.ts b/src/client/common/constants.ts index 23e9c131b25c..4c12102f7677 100644 --- a/src/client/common/constants.ts +++ b/src/client/common/constants.ts @@ -49,6 +49,7 @@ export namespace Commands { export const Exec_In_REPL = 'python.execInREPL'; export const Exec_Selection_In_Django_Shell = 'python.execSelectionInDjangoShell'; export const Exec_In_REPL_Enter = 'python.execInREPLEnter'; + export const Exec_In_IW_Enter = 'python.execInInteractiveWindowEnter'; export const Exec_Selection_In_Terminal = 'python.execSelectionInTerminal'; export const GetSelectedInterpreterPath = 'python.interpreterPath'; export const InstallJupyter = 'python.installJupyter'; diff --git a/src/client/repl/replCommands.ts b/src/client/repl/replCommands.ts index e67783837340..82b4aae4e5ee 100644 --- a/src/client/repl/replCommands.ts +++ b/src/client/repl/replCommands.ts @@ -98,29 +98,43 @@ export async function registerReplExecuteOnEnter( ): Promise { disposables.push( commandManager.registerCommand(Commands.Exec_In_REPL_Enter, async (uri: Uri) => { - const interpreter = await interpreterService.getActiveInterpreter(uri); - if (!interpreter) { - commands.executeCommand(Commands.TriggerEnvironmentSelection, uri).then(noop, noop); - return; - } + await onInputEnter(uri, 'repl.execute', interpreterService, disposables); + }), + ); + disposables.push( + commandManager.registerCommand(Commands.Exec_In_IW_Enter, async (uri: Uri) => { + await onInputEnter(uri, 'interactive.execute', interpreterService, disposables); + }), + ); +} - const nativeRepl = await getNativeRepl(interpreter, disposables); - const completeCode = await nativeRepl?.checkUserInputCompleteCode(window.activeTextEditor); - const editor = window.activeTextEditor; +async function onInputEnter( + uri: Uri, + commandName: string, + interpreterService: IInterpreterService, + disposables: Disposable[], +): Promise { + const interpreter = await interpreterService.getActiveInterpreter(uri); + if (!interpreter) { + commands.executeCommand(Commands.TriggerEnvironmentSelection, uri).then(noop, noop); + return; + } - if (editor) { - // Execute right away when complete code and Not multi-line - if (completeCode && !isMultiLineText(editor)) { - await commands.executeCommand('repl.execute'); - } else { - insertNewLineToREPLInput(editor); + const nativeRepl = await getNativeRepl(interpreter, disposables); + const completeCode = await nativeRepl?.checkUserInputCompleteCode(window.activeTextEditor); + const editor = window.activeTextEditor; - // Handle case when user enters on blank line, just trigger interactive.execute - if (editor && editor.document.lineAt(editor.selection.active.line).text === '') { - await commands.executeCommand('repl.execute'); - } - } + if (editor) { + // Execute right away when complete code and Not multi-line + if (completeCode && !isMultiLineText(editor)) { + await commands.executeCommand(commandName); + } else { + insertNewLineToREPLInput(editor); + + // Handle case when user enters on blank line, just trigger interactive.execute + if (editor && editor.document.lineAt(editor.selection.active.line).text === '') { + await commands.executeCommand(commandName); } - }), - ); + } + } } From 5d3a50c6d9328014c226d48621909fa21ef91bee Mon Sep 17 00:00:00 2001 From: amunger Date: Thu, 19 Sep 2024 13:47:38 -0700 Subject: [PATCH 09/11] revert lock --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 2efac8b36c7c..87af2d83a205 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,7 +116,7 @@ "yargs": "^15.3.1" }, "engines": { - "vscode": "^1.93.0-20240415" + "vscode": "^1.94.0-20240913" } }, "node_modules/@aashutoshrathi/word-wrap": { From 205c2cc2bc2f0e2fa326e176a850e0ae778d9887 Mon Sep 17 00:00:00 2001 From: amunger Date: Thu, 19 Sep 2024 13:51:37 -0700 Subject: [PATCH 10/11] undo engine change --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31f2fa6cb5ad..88e551ee0f24 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "theme": "dark" }, "engines": { - "vscode": "^1.93.0-20240415" + "vscode": "^1.94.0-20240913" }, "enableTelemetry": false, "keywords": [ From ffc0d32fbb045624352e5e696a60cbaa6eeea01a Mon Sep 17 00:00:00 2001 From: amunger Date: Fri, 20 Sep 2024 11:41:27 -0700 Subject: [PATCH 11/11] bump engine past latest API changes --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87af2d83a205..f2034d927997 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,7 +116,7 @@ "yargs": "^15.3.1" }, "engines": { - "vscode": "^1.94.0-20240913" + "vscode": "^1.94.0-20240918" } }, "node_modules/@aashutoshrathi/word-wrap": { diff --git a/package.json b/package.json index 88e551ee0f24..12432f34191d 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "theme": "dark" }, "engines": { - "vscode": "^1.94.0-20240913" + "vscode": "^1.94.0-20240918" }, "enableTelemetry": false, "keywords": [