From df26dbd3e0c6d0d9da43b5115df90930cc6d98b7 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 5 Aug 2024 14:05:33 -0700 Subject: [PATCH 01/14] Add cmd palette option to Run Native REPL --- package.json | 11 +++++++++ package.nls.json | 1 + src/client/common/application/commands.ts | 1 + src/client/common/constants.ts | 1 + src/client/extensionActivation.ts | 3 ++- src/client/repl/nativeRepl.ts | 6 +++-- src/client/repl/replCommandHandler.ts | 2 +- src/client/repl/replCommands.ts | 29 +++++++++++++++++++++++ 8 files changed, 50 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 0b2def5937f6..724d34629a72 100644 --- a/package.json +++ b/package.json @@ -346,6 +346,11 @@ "command": "python.startREPL", "title": "%python.command.python.startREPL.title%" }, + { + "category": "Python", + "command": "python.startNativeREPL", + "title": "%python.command.python.startNativeREPL.title%" + }, { "category": "Python", "command": "python.viewLanguageServerOutput", @@ -1331,6 +1336,12 @@ "title": "%python.command.python.startREPL.title%", "when": "!virtualWorkspace && shellExecutionSupported" }, + { + "category": "Python", + "command": "python.startNativeREPL", + "title": "%python.command.python.startNativeREPL.title%", + "when": "!virtualWorkspace && shellExecutionSupported" + }, { "category": "Python", "command": "python.viewLanguageServerOutput", diff --git a/package.nls.json b/package.nls.json index dcf8a2ddf5f9..5752a0deb9b8 100644 --- a/package.nls.json +++ b/package.nls.json @@ -1,5 +1,6 @@ { "python.command.python.startREPL.title": "Start Terminal REPL", + "python.command.python.startNativeREPL.title": "Start Native Python REPL", "python.command.python.createEnvironment.title": "Create Environment...", "python.command.python.createNewFile.title": "New Python File", "python.command.python.createTerminal.title": "Create Terminal", diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index 388bcf8052fa..4580a91a78d1 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -96,6 +96,7 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu ['workbench.action.openIssueReporter']: [{ extensionId: string; issueBody: string }]; [Commands.GetSelectedInterpreterPath]: [{ workspaceFolder: string } | string[]]; [Commands.TriggerEnvironmentSelection]: [undefined | Uri]; + [Commands.Start_Native_REPL]: [undefined | Uri]; [Commands.Exec_In_REPL]: [undefined | Uri]; [Commands.Exec_In_REPL_Enter]: [undefined | Uri]; [Commands.Exec_In_Terminal]: [undefined, Uri]; diff --git a/src/client/common/constants.ts b/src/client/common/constants.ts index d5b82f68ae97..23e9c131b25c 100644 --- a/src/client/common/constants.ts +++ b/src/client/common/constants.ts @@ -62,6 +62,7 @@ export namespace Commands { export const Set_Interpreter = 'python.setInterpreter'; export const Set_ShebangInterpreter = 'python.setShebangInterpreter'; export const Start_REPL = 'python.startREPL'; + export const Start_Native_REPL = 'python.startNativeREPL'; export const Tests_Configure = 'python.configureTests'; export const TriggerEnvironmentSelection = 'python.triggerEnvSelection'; export const ViewOutput = 'python.viewOutput'; diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index 6f2a4565299f..61eef2fbf7e2 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -52,7 +52,7 @@ import { initializePersistentStateForTriggers } from './common/persistentState'; import { logAndNotifyOnLegacySettings } from './logging/settingLogs'; import { DebuggerTypeName } from './debugger/constants'; import { StopWatch } from './common/utils/stopWatch'; -import { registerReplCommands, registerReplExecuteOnEnter } from './repl/replCommands'; +import { registerReplCommands, registerReplExecuteOnEnter, registerStartNativeReplCommand } from './repl/replCommands'; export async function activateComponents( // `ext` is passed to any extra activation funcs. @@ -108,6 +108,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components): ); const executionHelper = ext.legacyIOC.serviceContainer.get(ICodeExecutionHelper); const commandManager = ext.legacyIOC.serviceContainer.get(ICommandManager); + registerStartNativeReplCommand(ext.disposables, interpreterService, commandManager); registerReplCommands(ext.disposables, interpreterService, executionHelper, commandManager); registerReplExecuteOnEnter(ext.disposables, interpreterService, commandManager); } diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index e6a596f4434a..24ad2c23a2f2 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -84,14 +84,16 @@ export class NativeRepl implements Disposable { * Function that opens interactive repl, selects kernel, and send/execute code to the native repl. * @param code */ - public async sendToNativeRepl(code: string): Promise { + public async sendToNativeRepl(code?: string): Promise { const notebookEditor = await openInteractiveREPL(this.replController, this.notebookDocument); this.notebookDocument = notebookEditor.notebook; if (this.notebookDocument) { this.replController.updateNotebookAffinity(this.notebookDocument, NotebookControllerAffinity.Default); await selectNotebookKernel(notebookEditor, this.replController.id, PVSC_EXTENSION_ID); - await executeNotebookCell(this.notebookDocument, code); + if (code) { + await executeNotebookCell(this.notebookDocument, code); + } } } } diff --git a/src/client/repl/replCommandHandler.ts b/src/client/repl/replCommandHandler.ts index 599692a4300e..9cba24e032a4 100644 --- a/src/client/repl/replCommandHandler.ts +++ b/src/client/repl/replCommandHandler.ts @@ -24,7 +24,7 @@ export async function openInteractiveREPL( notebookDocument: NotebookDocument | undefined, ): Promise { let notebookEditor: NotebookEditor | undefined; - + // TODO: Need to set proper working directory when user attempts to open native REPL // Case where NotebookDocument (REPL document already exists in the tab) if (notebookDocument) { const existingReplViewColumn = getExistingReplViewColumn(notebookDocument); diff --git a/src/client/repl/replCommands.ts b/src/client/repl/replCommands.ts index c3f167ff51cc..8341983d9fe1 100644 --- a/src/client/repl/replCommands.ts +++ b/src/client/repl/replCommands.ts @@ -15,6 +15,35 @@ import { isMultiLineText, } from './replUtils'; +/** + * Register Start Native REPL command in the command palette + * + * @param disposables + * @param interpreterService + * @param commandManager + * @returns Promise + */ +export async function registerStartNativeReplCommand( + disposables: Disposable[], + interpreterService: IInterpreterService, + commandManager: ICommandManager, +): Promise { + disposables.push( + commandManager.registerCommand(Commands.Start_Native_REPL, async (uri: Uri) => { + const interpreter = await getActiveInterpreter(uri, interpreterService); + if (interpreter) { + if (interpreter) { + const nativeRepl = getNativeRepl(interpreter, disposables); + const activeEditor = window.activeTextEditor; + if (activeEditor) { + await nativeRepl.sendToNativeRepl(); + } + } + } + }), + ); +} + /** * Registers REPL command for shift+enter if sendToNativeREPL setting is enabled. * @param disposables From 9dba9b6c5f356f38b728cc3d7e0370a67ebfef44 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 5 Aug 2024 14:20:08 -0700 Subject: [PATCH 02/14] fix bug --- src/client/repl/replCommands.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/client/repl/replCommands.ts b/src/client/repl/replCommands.ts index 8341983d9fe1..08086ce08658 100644 --- a/src/client/repl/replCommands.ts +++ b/src/client/repl/replCommands.ts @@ -34,10 +34,7 @@ export async function registerStartNativeReplCommand( if (interpreter) { if (interpreter) { const nativeRepl = getNativeRepl(interpreter, disposables); - const activeEditor = window.activeTextEditor; - if (activeEditor) { - await nativeRepl.sendToNativeRepl(); - } + await nativeRepl.sendToNativeRepl(); } } }), From 29d44104697c81ec76592555d58d0ff94905842a Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 6 Aug 2024 13:10:21 -0700 Subject: [PATCH 03/14] provide option for multi-workspace case --- src/client/repl/nativeRepl.ts | 73 ++++++++++++++++++++++++++----- src/client/repl/pythonServer.ts | 6 ++- src/client/repl/replCommands.ts | 6 +-- src/client/repl/replController.ts | 3 +- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 24ad2c23a2f2..5aece205bde1 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -1,32 +1,51 @@ // Native Repl class that holds instance of pythonServer and replController -import { NotebookController, NotebookControllerAffinity, NotebookDocument, TextEditor, workspace } from 'vscode'; +import { + NotebookController, + NotebookControllerAffinity, + NotebookDocument, + QuickPickItem, + TextEditor, + workspace, + WorkspaceFolder, +} from 'vscode'; import { Disposable } from 'vscode-jsonrpc'; import { PVSC_EXTENSION_ID } from '../common/constants'; +import { showQuickPickWithBack } from '../common/vscodeApis/windowApis'; +import { getWorkspaceFolders } from '../common/vscodeApis/workspaceApis'; import { PythonEnvironment } from '../pythonEnvironments/info'; import { createPythonServer, PythonServer } from './pythonServer'; import { executeNotebookCell, openInteractiveREPL, selectNotebookKernel } from './replCommandHandler'; import { createReplController } from './replController'; export class NativeRepl implements Disposable { - private pythonServer: PythonServer; + // Adding ! since it will get initialized in create method, not the constructor. + private pythonServer!: PythonServer; - private interpreter: PythonEnvironment; + private cwd: string | undefined; + + private interpreter!: PythonEnvironment; private disposables: Disposable[] = []; - private replController: NotebookController; + private replController!: NotebookController; private notebookDocument: NotebookDocument | undefined; // TODO: In the future, could also have attribute of URI for file specific REPL. - constructor(interpreter: PythonEnvironment) { - this.interpreter = interpreter; + private constructor() { + this.watchNotebookClosed(); + } - this.pythonServer = createPythonServer([interpreter.path as string]); - this.replController = this.setReplController(); + // Static async factory method to handle asynchronous initialization + public static async create(interpreter: PythonEnvironment): Promise { + const instance = new NativeRepl(); + instance.interpreter = interpreter; + await instance.setReplDirectory(); + instance.pythonServer = createPythonServer([interpreter.path as string], instance.cwd); + instance.replController = instance.setReplController(); - this.watchNotebookClosed(); + return instance; } dispose(): void { @@ -47,13 +66,43 @@ export class NativeRepl implements Disposable { ); } + /** + * Function that set up desired directory for REPL. + * If there is multiple workspaces, prompt the user to choose + * which directory we should set in context of native REPL. + */ + private async setReplDirectory(): Promise { + // TODO: Figure out uri via workspaceFolder as uri parameter always + // seem to be undefined from parameter when trying to access from replCommands.ts + const workspaces: readonly WorkspaceFolder[] | undefined = getWorkspaceFolders(); + + if (workspaces) { + // eslint-disable-next-line no-shadow + const workspacesQuickPickItems: QuickPickItem[] = workspaces.map((workspace) => ({ + label: workspace.name, + description: workspace.uri.fsPath, + })); + if (workspacesQuickPickItems.length === 1) { + this.cwd = workspacesQuickPickItems[0].description; + } else { + // Show choices of workspaces for user to choose from. + const selection = (await showQuickPickWithBack(workspacesQuickPickItems, { + placeHolder: 'Select a desired workspace to set for REPL', + matchOnDescription: true, + ignoreFocusOut: true, + })) as QuickPickItem; + this.cwd = selection?.description; + } + } + } + /** * Function that check if NotebookController for REPL exists, and returns it in Singleton manner. * @returns NotebookController */ public setReplController(): NotebookController { if (!this.replController) { - return createReplController(this.interpreter.path, this.disposables); + return createReplController(this.interpreter!.path, this.disposables, this.cwd); } return this.replController; } @@ -105,9 +154,9 @@ let nativeRepl: NativeRepl | undefined; // In multi REPL scenario, hashmap of UR * @param interpreter * @returns Native REPL instance */ -export function getNativeRepl(interpreter: PythonEnvironment, disposables: Disposable[]): NativeRepl { +export async function getNativeRepl(interpreter: PythonEnvironment, disposables: Disposable[]): Promise { if (!nativeRepl) { - nativeRepl = new NativeRepl(interpreter); + nativeRepl = await NativeRepl.create(interpreter); disposables.push(nativeRepl); } return nativeRepl; diff --git a/src/client/repl/pythonServer.ts b/src/client/repl/pythonServer.ts index ca45ea900baf..fbcb1104dc69 100644 --- a/src/client/repl/pythonServer.ts +++ b/src/client/repl/pythonServer.ts @@ -89,12 +89,14 @@ class PythonServerImpl implements Disposable { } } -export function createPythonServer(interpreter: string[]): PythonServer { +export function createPythonServer(interpreter: string[], cwd?: string): PythonServer { if (serverInstance) { return serverInstance; } - const pythonServer = ch.spawn(interpreter[0], [...interpreter.slice(1), SERVER_PATH]); + const pythonServer = ch.spawn(interpreter[0], [...interpreter.slice(1), SERVER_PATH], { + cwd, // Launch with correct workspace directory + }); pythonServer.stderr.on('data', (data) => { traceError(data.toString()); diff --git a/src/client/repl/replCommands.ts b/src/client/repl/replCommands.ts index 08086ce08658..989f068fb593 100644 --- a/src/client/repl/replCommands.ts +++ b/src/client/repl/replCommands.ts @@ -33,7 +33,7 @@ export async function registerStartNativeReplCommand( const interpreter = await getActiveInterpreter(uri, interpreterService); if (interpreter) { if (interpreter) { - const nativeRepl = getNativeRepl(interpreter, disposables); + const nativeRepl = await getNativeRepl(interpreter, disposables); await nativeRepl.sendToNativeRepl(); } } @@ -65,7 +65,7 @@ export async function registerReplCommands( const interpreter = await getActiveInterpreter(uri, interpreterService); if (interpreter) { - const nativeRepl = getNativeRepl(interpreter, disposables); + const nativeRepl = await getNativeRepl(interpreter, disposables); const activeEditor = window.activeTextEditor; if (activeEditor) { const code = await getSelectedTextToExecute(activeEditor); @@ -102,7 +102,7 @@ export async function registerReplExecuteOnEnter( return; } - const nativeRepl = getNativeRepl(interpreter, disposables); + const nativeRepl = await getNativeRepl(interpreter, disposables); const completeCode = await nativeRepl?.checkUserInputCompleteCode(window.activeTextEditor); const editor = window.activeTextEditor; diff --git a/src/client/repl/replController.ts b/src/client/repl/replController.ts index 4760edc98036..7c1f8fd0c6b2 100644 --- a/src/client/repl/replController.ts +++ b/src/client/repl/replController.ts @@ -4,8 +4,9 @@ import { createPythonServer } from './pythonServer'; export function createReplController( interpreterPath: string, disposables: vscode.Disposable[], + cwd?: string, ): vscode.NotebookController { - const server = createPythonServer([interpreterPath]); + const server = createPythonServer([interpreterPath], cwd); disposables.push(server); const controller = vscode.notebooks.createNotebookController('pythonREPL', 'interactive', 'Python REPL'); From b9fb4f4e2b2bb4cea6e1b3ed39dee4653dd6a009 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 6 Aug 2024 13:17:19 -0700 Subject: [PATCH 04/14] add TODO, remove TODO --- src/client/repl/nativeRepl.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 5aece205bde1..74fe8ac53b97 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -72,7 +72,7 @@ export class NativeRepl implements Disposable { * which directory we should set in context of native REPL. */ private async setReplDirectory(): Promise { - // TODO: Figure out uri via workspaceFolder as uri parameter always + // Figure out uri via workspaceFolder as uri parameter always // seem to be undefined from parameter when trying to access from replCommands.ts const workspaces: readonly WorkspaceFolder[] | undefined = getWorkspaceFolders(); @@ -82,6 +82,9 @@ export class NativeRepl implements Disposable { label: workspace.name, description: workspace.uri.fsPath, })); + + // TODO: Handle case where there is no workspace. (Empty workspace scenario) + if (workspacesQuickPickItems.length === 1) { this.cwd = workspacesQuickPickItems[0].description; } else { From 39a8e2a3a65596fcea819bf98a1cc6e50d2ddcff Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 6 Aug 2024 13:23:26 -0700 Subject: [PATCH 05/14] add case to handle no workspace scenario --- src/client/repl/nativeRepl.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 74fe8ac53b97..94f1cbf4543b 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -83,9 +83,10 @@ export class NativeRepl implements Disposable { description: workspace.uri.fsPath, })); - // TODO: Handle case where there is no workspace. (Empty workspace scenario) - - if (workspacesQuickPickItems.length === 1) { + // TODO: Handle case where there is no workspace. + if (workspacesQuickPickItems.length === 0) { + this.cwd = process.cwd(); // Yields '/' on no workspace scenario. + } else if (workspacesQuickPickItems.length === 1) { this.cwd = workspacesQuickPickItems[0].description; } else { // Show choices of workspaces for user to choose from. From 0617a9ead775b7ab3b263e9dc7c966ca0d28228e Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 6 Aug 2024 13:26:49 -0700 Subject: [PATCH 06/14] Remove TODO since done --- src/client/repl/replCommandHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/repl/replCommandHandler.ts b/src/client/repl/replCommandHandler.ts index 9cba24e032a4..599692a4300e 100644 --- a/src/client/repl/replCommandHandler.ts +++ b/src/client/repl/replCommandHandler.ts @@ -24,7 +24,7 @@ export async function openInteractiveREPL( notebookDocument: NotebookDocument | undefined, ): Promise { let notebookEditor: NotebookEditor | undefined; - // TODO: Need to set proper working directory when user attempts to open native REPL + // Case where NotebookDocument (REPL document already exists in the tab) if (notebookDocument) { const existingReplViewColumn = getExistingReplViewColumn(notebookDocument); From 86dd286941b84e24db8e7bee2663dead9febea67 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 6 Aug 2024 13:59:07 -0700 Subject: [PATCH 07/14] get rid of back button for quick selection --- src/client/repl/nativeRepl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 94f1cbf4543b..24e41625090b 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -11,7 +11,7 @@ import { } from 'vscode'; import { Disposable } from 'vscode-jsonrpc'; import { PVSC_EXTENSION_ID } from '../common/constants'; -import { showQuickPickWithBack } from '../common/vscodeApis/windowApis'; +import { showQuickPick } from '../common/vscodeApis/windowApis'; import { getWorkspaceFolders } from '../common/vscodeApis/workspaceApis'; import { PythonEnvironment } from '../pythonEnvironments/info'; import { createPythonServer, PythonServer } from './pythonServer'; @@ -90,7 +90,7 @@ export class NativeRepl implements Disposable { this.cwd = workspacesQuickPickItems[0].description; } else { // Show choices of workspaces for user to choose from. - const selection = (await showQuickPickWithBack(workspacesQuickPickItems, { + const selection = (await showQuickPick(workspacesQuickPickItems, { placeHolder: 'Select a desired workspace to set for REPL', matchOnDescription: true, ignoreFocusOut: true, From a996a73dd3455e5e02d6b7f0f07d8fceac1c410e Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 6 Aug 2024 14:00:30 -0700 Subject: [PATCH 08/14] align cwd option wording with exiting terminal prompt --- src/client/repl/nativeRepl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 24e41625090b..8d411697574d 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -91,7 +91,7 @@ export class NativeRepl implements Disposable { } else { // Show choices of workspaces for user to choose from. const selection = (await showQuickPick(workspacesQuickPickItems, { - placeHolder: 'Select a desired workspace to set for REPL', + placeHolder: 'Select current working directory for new REPL', matchOnDescription: true, ignoreFocusOut: true, })) as QuickPickItem; From 71ee8bf05f04664c7d673df0e98e8c12afad92f6 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 6 Aug 2024 23:13:10 -0700 Subject: [PATCH 09/14] switch to using registerCommand from vscodeApis/commandApis --- src/client/repl/replCommands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/repl/replCommands.ts b/src/client/repl/replCommands.ts index 989f068fb593..5570fa8384f4 100644 --- a/src/client/repl/replCommands.ts +++ b/src/client/repl/replCommands.ts @@ -14,6 +14,7 @@ import { insertNewLineToREPLInput, isMultiLineText, } from './replUtils'; +import { registerCommand } from '../common/vscodeApis/commandApis'; /** * Register Start Native REPL command in the command palette @@ -26,10 +27,9 @@ import { export async function registerStartNativeReplCommand( disposables: Disposable[], interpreterService: IInterpreterService, - commandManager: ICommandManager, ): Promise { disposables.push( - commandManager.registerCommand(Commands.Start_Native_REPL, async (uri: Uri) => { + registerCommand(Commands.Start_Native_REPL, async (uri: Uri) => { const interpreter = await getActiveInterpreter(uri, interpreterService); if (interpreter) { if (interpreter) { From f7d371d1323a9bb7cec206b7aacb53564b885781 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 7 Aug 2024 00:16:56 -0700 Subject: [PATCH 10/14] start adding test for changed nativeRepl.ts --- src/client/extensionActivation.ts | 2 +- src/test/repl/nativeRepl.test.ts | 70 +++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/test/repl/nativeRepl.test.ts diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index 61eef2fbf7e2..77ed2edf6716 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -108,7 +108,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components): ); const executionHelper = ext.legacyIOC.serviceContainer.get(ICodeExecutionHelper); const commandManager = ext.legacyIOC.serviceContainer.get(ICommandManager); - registerStartNativeReplCommand(ext.disposables, interpreterService, commandManager); + registerStartNativeReplCommand(ext.disposables, interpreterService); registerReplCommands(ext.disposables, interpreterService, executionHelper, commandManager); registerReplExecuteOnEnter(ext.disposables, interpreterService, commandManager); } diff --git a/src/test/repl/nativeRepl.test.ts b/src/test/repl/nativeRepl.test.ts new file mode 100644 index 000000000000..0fc55abe1a64 --- /dev/null +++ b/src/test/repl/nativeRepl.test.ts @@ -0,0 +1,70 @@ +/* eslint-disable no-unused-expressions */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import * as TypeMoq from 'typemoq'; +import * as sinon from 'sinon'; +import { Disposable } from 'vscode'; +import { expect } from 'chai'; + +import { IInterpreterService } from '../../client/interpreter/contracts'; +import { PythonEnvironment } from '../../client/pythonEnvironments/info'; +import { getNativeRepl, NativeRepl } from '../../client/repl/nativeRepl'; + +suite('REPL - Native REPL', () => { + let interpreterService: TypeMoq.IMock; + + let disposable: TypeMoq.IMock; + let disposableArray: Disposable[] = []; + + let setReplDirectoryStub: sinon.SinonStub; + let setReplControllerSpy: sinon.SinonSpy; + + setup(() => { + interpreterService = TypeMoq.Mock.ofType(); + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); + disposable = TypeMoq.Mock.ofType(); + disposableArray = [disposable.object]; + + setReplDirectoryStub = sinon.stub(NativeRepl.prototype as any, 'setReplDirectory').resolves(); // Stubbing private method + // Use a spy instead of a stub for setReplController + setReplControllerSpy = sinon.spy(NativeRepl.prototype, 'setReplController'); + }); + + teardown(() => { + disposableArray.forEach((d) => { + if (d) { + d.dispose(); + } + }); + + disposableArray = []; + sinon.restore(); + }); + + test('getNativeRepl should call create constructor', async () => { + const createMethodStub = sinon.stub(NativeRepl, 'create'); + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); + const interpreter = await interpreterService.object.getActiveInterpreter(); + await getNativeRepl(interpreter as PythonEnvironment, disposableArray); + + expect(createMethodStub.calledOnce).to.be.true; + }); + + test('create should call setReplDirectory, setReplController', async () => { + const interpreter = await interpreterService.object.getActiveInterpreter(); + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); + + await NativeRepl.create(interpreter as PythonEnvironment); + + expect(setReplDirectoryStub.calledOnce).to.be.true; + expect(setReplControllerSpy.calledOnce).to.be.true; + + setReplDirectoryStub.restore(); + setReplControllerSpy.restore(); + }); +}); From 4e7c87b56293fde2171b5ea81ceaf8f2e079103c Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 7 Aug 2024 00:20:20 -0700 Subject: [PATCH 11/14] wording to python.startTerminalREPL.title --- package.json | 4 ++-- package.nls.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 724d34629a72..a43bb33944e2 100644 --- a/package.json +++ b/package.json @@ -344,7 +344,7 @@ { "category": "Python", "command": "python.startREPL", - "title": "%python.command.python.startREPL.title%" + "title": "%python.command.python.startTerminalREPL.title%" }, { "category": "Python", @@ -1333,7 +1333,7 @@ { "category": "Python", "command": "python.startREPL", - "title": "%python.command.python.startREPL.title%", + "title": "%python.command.python.startTerminalREPL.title%", "when": "!virtualWorkspace && shellExecutionSupported" }, { diff --git a/package.nls.json b/package.nls.json index 5752a0deb9b8..5a5029231b17 100644 --- a/package.nls.json +++ b/package.nls.json @@ -1,5 +1,5 @@ { - "python.command.python.startREPL.title": "Start Terminal REPL", + "python.command.python.startTerminalREPL.title": "Start Terminal REPL", "python.command.python.startNativeREPL.title": "Start Native Python REPL", "python.command.python.createEnvironment.title": "Create Environment...", "python.command.python.createNewFile.title": "New Python File", From da00faee39ba861e847609d0e20494a03267c793 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 7 Aug 2024 00:21:50 -0700 Subject: [PATCH 12/14] stop bothering me package-lock.json --- package-lock.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index bde67795d468..73b3b4431bb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@iarna/toml": "^2.2.5", "@vscode/extension-telemetry": "^0.8.4", "arch": "^2.1.0", - "diff-match-patch": "^1.0.5", "fs-extra": "^10.0.1", "glob": "^7.2.0", "hash.js": "^1.1.7", @@ -4637,11 +4636,6 @@ "node": ">=0.3.1" } }, - "node_modules/diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" - }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -17608,11 +17602,6 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, - "diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" - }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", From fffdf0b49f996371de758c96291dade477d0583e Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 7 Aug 2024 00:32:50 -0700 Subject: [PATCH 13/14] TODO was done --- src/client/repl/nativeRepl.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 8d411697574d..7a0fbd03ecde 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -83,7 +83,6 @@ export class NativeRepl implements Disposable { description: workspace.uri.fsPath, })); - // TODO: Handle case where there is no workspace. if (workspacesQuickPickItems.length === 0) { this.cwd = process.cwd(); // Yields '/' on no workspace scenario. } else if (workspacesQuickPickItems.length === 1) { From e7c6b5ad76cdaaab91751d0c755fe5fba46d15b6 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 7 Aug 2024 11:17:48 -0700 Subject: [PATCH 14/14] better names --- src/client/repl/nativeRepl.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client/repl/nativeRepl.ts b/src/client/repl/nativeRepl.ts index 7a0fbd03ecde..e28d21228666 100644 --- a/src/client/repl/nativeRepl.ts +++ b/src/client/repl/nativeRepl.ts @@ -39,13 +39,13 @@ export class NativeRepl implements Disposable { // Static async factory method to handle asynchronous initialization public static async create(interpreter: PythonEnvironment): Promise { - const instance = new NativeRepl(); - instance.interpreter = interpreter; - await instance.setReplDirectory(); - instance.pythonServer = createPythonServer([interpreter.path as string], instance.cwd); - instance.replController = instance.setReplController(); + const nativeRepl = new NativeRepl(); + nativeRepl.interpreter = interpreter; + await nativeRepl.setReplDirectory(); + nativeRepl.pythonServer = createPythonServer([interpreter.path as string], nativeRepl.cwd); + nativeRepl.replController = nativeRepl.setReplController(); - return instance; + return nativeRepl; } dispose(): void {