From d1f0b8cfc2393c51c861c511d49437855fcc1710 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 20 Jan 2025 16:41:25 -0500 Subject: [PATCH 01/22] initial set to link pythonrc text to native repl launch. --- python_files/pythonrc.py | 2 + src/client/extensionActivation.ts | 5 +++ .../terminals/pythonStartupLinkProvider.ts | 45 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/client/terminals/pythonStartupLinkProvider.ts diff --git a/python_files/pythonrc.py b/python_files/pythonrc.py index 13f374e023b8..74dcef0f8d37 100644 --- a/python_files/pythonrc.py +++ b/python_files/pythonrc.py @@ -77,3 +77,5 @@ def __str__(self): if sys.platform != "win32" and (not is_wsl) and use_shell_integration: sys.ps1 = PS1() +# TODO: Word this better - get feedback +print("this is link to launch native repl") diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index 38f2d6a56277..dedf102a59ea 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -55,6 +55,7 @@ import { registerReplCommands, registerReplExecuteOnEnter, registerStartNativeRe import { registerTriggerForTerminalREPL } from './terminals/codeExecution/terminalReplWatcher'; import { registerPythonStartup } from './terminals/pythonStartup'; import { registerPixiFeatures } from './pythonEnvironments/common/environmentManagers/pixi'; +import { CustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider'; export async function activateComponents( // `ext` is passed to any extra activation funcs. @@ -115,6 +116,10 @@ export function activateFeatures(ext: ExtensionState, _components: Components): registerStartNativeReplCommand(ext.disposables, interpreterService); registerReplCommands(ext.disposables, interpreterService, executionHelper, commandManager); registerReplExecuteOnEnter(ext.disposables, interpreterService, commandManager); + // TODO: proably organize this into new file. + const linkProvider = new CustomTerminalLinkProvider(); + // TODO: Do not directly use windows API. Wrap it like we do with command APIs. + ext.context.subscriptions.push(window.registerTerminalLinkProvider(linkProvider)); } /// ////////////////////////// diff --git a/src/client/terminals/pythonStartupLinkProvider.ts b/src/client/terminals/pythonStartupLinkProvider.ts new file mode 100644 index 000000000000..996aff96337b --- /dev/null +++ b/src/client/terminals/pythonStartupLinkProvider.ts @@ -0,0 +1,45 @@ +/* eslint-disable class-methods-use-this */ +import * as vscode from 'vscode'; + +interface CustomTerminalLink extends vscode.TerminalLink { + command: string; +} + +export class CustomTerminalLinkProvider implements vscode.TerminalLinkProvider { + // TODO: How should I properly add this to disposables? + // Need advice, do not want to cause memory leak. + + // private disposable: Disposable; + + // constructor() { + // this.disposable = window.registerTerminalLinkProvider(this); + // } + + // dispose(): void { + // this.disposable.dispose(); + // } + + provideTerminalLinks( + context: vscode.TerminalLinkContext, + _token: vscode.CancellationToken, + ): vscode.ProviderResult { + const links: CustomTerminalLink[] = []; + const expectedNativeLink = 'this is link to launch native repl'; + + // eslint-disable-next-line no-cond-assign + if (context.line === expectedNativeLink) { + links.push({ + startIndex: 0, + length: expectedNativeLink.length, + tooltip: 'Launch Native REPL', + command: 'python.startNativeREPL', + }); + } + return links; + } + + handleTerminalLink(link: CustomTerminalLink): vscode.ProviderResult { + // TODO: probably dont use vscode.commands directly? + vscode.commands.executeCommand(link.command); + } +} From 8854bb61a3c60eb1b02424e5972cdab1477a7a73 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 20 Jan 2025 16:44:01 -0500 Subject: [PATCH 02/22] add question --- src/client/terminals/pythonStartupLinkProvider.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/terminals/pythonStartupLinkProvider.ts b/src/client/terminals/pythonStartupLinkProvider.ts index 996aff96337b..944f63c54349 100644 --- a/src/client/terminals/pythonStartupLinkProvider.ts +++ b/src/client/terminals/pythonStartupLinkProvider.ts @@ -24,6 +24,8 @@ export class CustomTerminalLinkProvider implements vscode.TerminalLinkProvider { const links: CustomTerminalLink[] = []; + // Question: What if context.line is truncated because of user zoom setting? + // Meaning what if this line is separated into two+ line in terminal? const expectedNativeLink = 'this is link to launch native repl'; // eslint-disable-next-line no-cond-assign From 78a25a48b751b3cde01373b9aeaacb222269bbd0 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 21 Jan 2025 22:48:21 -0500 Subject: [PATCH 03/22] do not use vscode.command.executeCommand directly --- src/client/terminals/pythonStartupLinkProvider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/terminals/pythonStartupLinkProvider.ts b/src/client/terminals/pythonStartupLinkProvider.ts index 944f63c54349..97f05b727832 100644 --- a/src/client/terminals/pythonStartupLinkProvider.ts +++ b/src/client/terminals/pythonStartupLinkProvider.ts @@ -1,5 +1,6 @@ /* eslint-disable class-methods-use-this */ import * as vscode from 'vscode'; +import { executeCommand } from '../common/vscodeApis/commandApis'; interface CustomTerminalLink extends vscode.TerminalLink { command: string; @@ -40,8 +41,7 @@ export class CustomTerminalLinkProvider implements vscode.TerminalLinkProvider { - // TODO: probably dont use vscode.commands directly? - vscode.commands.executeCommand(link.command); + async handleTerminalLink(link: CustomTerminalLink): Promise { + await executeCommand(link.command); } } From 590b6cb6279ad31b017ac269ceb55f957f3ba9dd Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 21 Jan 2025 22:56:19 -0500 Subject: [PATCH 04/22] avoid using windows vscode method directly, better separate customLinkProvider --- src/client/common/vscodeApis/windowApis.ts | 5 +++++ src/client/extensionActivation.ts | 7 ++----- src/client/terminals/pythonStartupLinkProvider.ts | 7 +++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/client/common/vscodeApis/windowApis.ts b/src/client/common/vscodeApis/windowApis.ts index 80825018c4a3..fc63a189f2ff 100644 --- a/src/client/common/vscodeApis/windowApis.ts +++ b/src/client/common/vscodeApis/windowApis.ts @@ -21,6 +21,7 @@ import { TerminalShellExecutionStartEvent, LogOutputChannel, OutputChannel, + TerminalLinkProvider, } from 'vscode'; import { createDeferred, Deferred } from '../utils/async'; import { Resource } from '../types'; @@ -258,3 +259,7 @@ export function createOutputChannel(name: string, languageId?: string): OutputCh export function createLogOutputChannel(name: string, options: { log: true }): LogOutputChannel { return window.createOutputChannel(name, options); } + +export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable { + return window.registerTerminalLinkProvider(provider); +} diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index dedf102a59ea..a2c77b2d8097 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -55,7 +55,7 @@ import { registerReplCommands, registerReplExecuteOnEnter, registerStartNativeRe import { registerTriggerForTerminalREPL } from './terminals/codeExecution/terminalReplWatcher'; import { registerPythonStartup } from './terminals/pythonStartup'; import { registerPixiFeatures } from './pythonEnvironments/common/environmentManagers/pixi'; -import { CustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider'; +import { CustomTerminalLinkProvider, registerCustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider'; export async function activateComponents( // `ext` is passed to any extra activation funcs. @@ -116,10 +116,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components): registerStartNativeReplCommand(ext.disposables, interpreterService); registerReplCommands(ext.disposables, interpreterService, executionHelper, commandManager); registerReplExecuteOnEnter(ext.disposables, interpreterService, commandManager); - // TODO: proably organize this into new file. - const linkProvider = new CustomTerminalLinkProvider(); - // TODO: Do not directly use windows API. Wrap it like we do with command APIs. - ext.context.subscriptions.push(window.registerTerminalLinkProvider(linkProvider)); + registerCustomTerminalLinkProvider(ext.context); // TODO: Is there way to avoid passing in ext.context? } /// ////////////////////////// diff --git a/src/client/terminals/pythonStartupLinkProvider.ts b/src/client/terminals/pythonStartupLinkProvider.ts index 97f05b727832..664e2a17a985 100644 --- a/src/client/terminals/pythonStartupLinkProvider.ts +++ b/src/client/terminals/pythonStartupLinkProvider.ts @@ -1,6 +1,8 @@ /* eslint-disable class-methods-use-this */ import * as vscode from 'vscode'; import { executeCommand } from '../common/vscodeApis/commandApis'; +import { IExtensionContext } from '../common/types'; +import { registerTerminalLinkProvider } from '../common/vscodeApis/windowApis'; interface CustomTerminalLink extends vscode.TerminalLink { command: string; @@ -45,3 +47,8 @@ export class CustomTerminalLinkProvider implements vscode.TerminalLinkProvider Date: Tue, 21 Jan 2025 23:00:44 -0500 Subject: [PATCH 05/22] compile --- src/client/extensionActivation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index a2c77b2d8097..f09e2b03fca5 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -55,7 +55,7 @@ import { registerReplCommands, registerReplExecuteOnEnter, registerStartNativeRe import { registerTriggerForTerminalREPL } from './terminals/codeExecution/terminalReplWatcher'; import { registerPythonStartup } from './terminals/pythonStartup'; import { registerPixiFeatures } from './pythonEnvironments/common/environmentManagers/pixi'; -import { CustomTerminalLinkProvider, registerCustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider'; +import { registerCustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider'; export async function activateComponents( // `ext` is passed to any extra activation funcs. From f9093d80b9e53eaaf1b8e4c8e8d5eb6ab856f236 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 13:35:43 -0500 Subject: [PATCH 06/22] Take feedback --- python_files/pythonrc.py | 3 +- src/client/extensionActivation.ts | 2 +- .../terminals/pythonStartupLinkProvider.ts | 47 ++++++++----------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/python_files/pythonrc.py b/python_files/pythonrc.py index 74dcef0f8d37..7b4c2ceeb87b 100644 --- a/python_files/pythonrc.py +++ b/python_files/pythonrc.py @@ -78,4 +78,5 @@ def __str__(self): if sys.platform != "win32" and (not is_wsl) and use_shell_integration: sys.ps1 = PS1() # TODO: Word this better - get feedback -print("this is link to launch native repl") +# TODO: add ctrl/cmd depending on OS +print("Click to launch VS Code Native REPL") diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index f09e2b03fca5..4a1acca62da5 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -116,7 +116,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components): registerStartNativeReplCommand(ext.disposables, interpreterService); registerReplCommands(ext.disposables, interpreterService, executionHelper, commandManager); registerReplExecuteOnEnter(ext.disposables, interpreterService, commandManager); - registerCustomTerminalLinkProvider(ext.context); // TODO: Is there way to avoid passing in ext.context? + registerCustomTerminalLinkProvider(ext.disposables); } /// ////////////////////////// diff --git a/src/client/terminals/pythonStartupLinkProvider.ts b/src/client/terminals/pythonStartupLinkProvider.ts index 664e2a17a985..57ec1191dd87 100644 --- a/src/client/terminals/pythonStartupLinkProvider.ts +++ b/src/client/terminals/pythonStartupLinkProvider.ts @@ -1,42 +1,36 @@ /* eslint-disable class-methods-use-this */ -import * as vscode from 'vscode'; +import { + CancellationToken, + Disposable, + ProviderResult, + TerminalLink, + TerminalLinkContext, + TerminalLinkProvider, + l10n, +} from 'vscode'; import { executeCommand } from '../common/vscodeApis/commandApis'; -import { IExtensionContext } from '../common/types'; import { registerTerminalLinkProvider } from '../common/vscodeApis/windowApis'; -interface CustomTerminalLink extends vscode.TerminalLink { +interface CustomTerminalLink extends TerminalLink { command: string; } -export class CustomTerminalLinkProvider implements vscode.TerminalLinkProvider { - // TODO: How should I properly add this to disposables? - // Need advice, do not want to cause memory leak. - - // private disposable: Disposable; - - // constructor() { - // this.disposable = window.registerTerminalLinkProvider(this); - // } - - // dispose(): void { - // this.disposable.dispose(); - // } - +export class CustomTerminalLinkProvider implements TerminalLinkProvider { provideTerminalLinks( - context: vscode.TerminalLinkContext, - _token: vscode.CancellationToken, - ): vscode.ProviderResult { + context: TerminalLinkContext, + _token: CancellationToken, + ): ProviderResult { const links: CustomTerminalLink[] = []; // Question: What if context.line is truncated because of user zoom setting? // Meaning what if this line is separated into two+ line in terminal? - const expectedNativeLink = 'this is link to launch native repl'; + const expectedNativeLink = 'VS Code Native REPL'; // eslint-disable-next-line no-cond-assign - if (context.line === expectedNativeLink) { + if (context.line.includes(expectedNativeLink)) { links.push({ - startIndex: 0, + startIndex: context.line.indexOf(expectedNativeLink), length: expectedNativeLink.length, - tooltip: 'Launch Native REPL', + tooltip: l10n.t('Launch VS Code Native REPL'), command: 'python.startNativeREPL', }); } @@ -48,7 +42,6 @@ export class CustomTerminalLinkProvider implements vscode.TerminalLinkProvider Date: Wed, 22 Jan 2025 16:58:33 -0500 Subject: [PATCH 07/22] change wording --- python_files/pythonrc.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python_files/pythonrc.py b/python_files/pythonrc.py index 7b4c2ceeb87b..dd39ab34aa17 100644 --- a/python_files/pythonrc.py +++ b/python_files/pythonrc.py @@ -8,7 +8,6 @@ use_shell_integration = sys.version_info < (3, 13) is_wsl = "microsoft-standard-WSL" in platform.release() - class REPLHooks: def __init__(self): self.global_exit = None @@ -77,6 +76,8 @@ def __str__(self): if sys.platform != "win32" and (not is_wsl) and use_shell_integration: sys.ps1 = PS1() -# TODO: Word this better - get feedback -# TODO: add ctrl/cmd depending on OS -print("Click to launch VS Code Native REPL") + +if sys.platform == "darwin": + print("Cmd click to launch VS Code Native REPL") +else: + print("Ctrl click to launch VS Code Native REPL") From 2724718cce9815c387a1d838eeb5028da6250e39 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 17:07:31 -0500 Subject: [PATCH 08/22] add python test --- python_files/tests/test_shell_integration.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index a7dfc2ff1a8f..045388cfd68c 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -2,7 +2,7 @@ import platform import sys from unittest.mock import Mock - +import pytest import pythonrc is_wsl = "microsoft-standard-WSL" in platform.release() @@ -61,3 +61,18 @@ def test_excepthook_call(): hooks.my_excepthook("mock_type", "mock_value", "mock_traceback") mock_excepthook.assert_called_once_with("mock_type", "mock_value", "mock_traceback") + +def test_print_statement_darwin(monkeypatch): + monkeypatch.setattr(sys, 'platform', 'darwin') + with monkeypatch.context() as m: + m.setattr('builtins.print', Mock()) + importlib.reload(sys.modules['pythonrc']) + print.assert_any_call("Cmd click to launch VS Code Native REPL") + + +def test_print_statement_non_darwin(monkeypatch): + monkeypatch.setattr(sys, 'platform', 'win32') + with monkeypatch.context() as m: + m.setattr('builtins.print', Mock()) + importlib.reload(sys.modules['pythonrc']) + print.assert_any_call("Ctrl click to launch VS Code Native REPL") From 6e216e2309a6cf7b14651bc03b22b3e7abb45e2f Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 17:21:28 -0500 Subject: [PATCH 09/22] add typescript test --- .../shellIntegration/pythonStartup.test.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/test/terminals/shellIntegration/pythonStartup.test.ts b/src/test/terminals/shellIntegration/pythonStartup.test.ts index 5d25c2563cf9..a5ec241403c0 100644 --- a/src/test/terminals/shellIntegration/pythonStartup.test.ts +++ b/src/test/terminals/shellIntegration/pythonStartup.test.ts @@ -3,10 +3,11 @@ import * as sinon from 'sinon'; import * as TypeMoq from 'typemoq'; -import { GlobalEnvironmentVariableCollection, Uri, WorkspaceConfiguration } from 'vscode'; +import { GlobalEnvironmentVariableCollection, Uri, WorkspaceConfiguration, Disposable } from 'vscode'; import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; import { registerPythonStartup } from '../../../client/terminals/pythonStartup'; import { IExtensionContext } from '../../../client/common/types'; +import * as pythonStartupLinkProvider from '../../../client/terminals/pythonStartupLinkProvider'; suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { let getConfigurationStub: sinon.SinonStub; @@ -20,7 +21,6 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { setup(() => { context = TypeMoq.Mock.ofType(); globalEnvironmentVariableCollection = TypeMoq.Mock.ofType(); - // Question: Why do we have to set up environmentVariableCollection and globalEnvironmentVariableCollection in this flip-flop way? // Reference: /vscode-python/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts context.setup((c) => c.environmentVariableCollection).returns(() => globalEnvironmentVariableCollection.object); @@ -122,4 +122,18 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { globalEnvironmentVariableCollection.verify((c) => c.delete('PYTHONSTARTUP'), TypeMoq.Times.once()); }); + + test('Ensure registering terminal link calls registerTerminalLinkProvider', async () => { + const registerTerminalLinkProviderStub = sinon.stub( + pythonStartupLinkProvider, + 'registerCustomTerminalLinkProvider', + ); + const disposableArray: Disposable[] = []; + pythonStartupLinkProvider.registerCustomTerminalLinkProvider(disposableArray); + + sinon.assert.calledOnce(registerTerminalLinkProviderStub); + sinon.assert.calledWith(registerTerminalLinkProviderStub, disposableArray); + + registerTerminalLinkProviderStub.restore(); + }); }); From 51c662099a42f1f4e930305990e99acec53f57d9 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 17:56:26 -0500 Subject: [PATCH 10/22] stop messing with other tests --- python_files/tests/test_shell_integration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index 045388cfd68c..5e6897e33117 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -63,16 +63,16 @@ def test_excepthook_call(): mock_excepthook.assert_called_once_with("mock_type", "mock_value", "mock_traceback") def test_print_statement_darwin(monkeypatch): - monkeypatch.setattr(sys, 'platform', 'darwin') with monkeypatch.context() as m: + m.setattr(sys, 'platform', 'darwin') m.setattr('builtins.print', Mock()) importlib.reload(sys.modules['pythonrc']) print.assert_any_call("Cmd click to launch VS Code Native REPL") def test_print_statement_non_darwin(monkeypatch): - monkeypatch.setattr(sys, 'platform', 'win32') with monkeypatch.context() as m: + m.setattr(sys, 'platform', 'win32') m.setattr('builtins.print', Mock()) importlib.reload(sys.modules['pythonrc']) print.assert_any_call("Ctrl click to launch VS Code Native REPL") From c053215f422fe1f5d3c93f347001fcc8f7b439a5 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 17:59:40 -0500 Subject: [PATCH 11/22] remove pytest --- python_files/tests/test_shell_integration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index 5e6897e33117..c34218dfebcc 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -2,7 +2,6 @@ import platform import sys from unittest.mock import Mock -import pytest import pythonrc is_wsl = "microsoft-standard-WSL" in platform.release() From ae09a31bb30b412f475c7ee322e899f5f281c177 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 18:22:11 -0500 Subject: [PATCH 12/22] attempting to fix wrongly caches monkeypatch --- python_files/tests/test_shell_integration.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index c34218dfebcc..2e10d674c28f 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -62,16 +62,17 @@ def test_excepthook_call(): mock_excepthook.assert_called_once_with("mock_type", "mock_value", "mock_traceback") def test_print_statement_darwin(monkeypatch): + importlib.reload(pythonrc) with monkeypatch.context() as m: - m.setattr(sys, 'platform', 'darwin') - m.setattr('builtins.print', Mock()) - importlib.reload(sys.modules['pythonrc']) + m.setattr(sys, "platform", "darwin") + m.setattr("builtins.print", Mock()) + importlib.reload(sys.modules["pythonrc"]) print.assert_any_call("Cmd click to launch VS Code Native REPL") - def test_print_statement_non_darwin(monkeypatch): + importlib.reload(pythonrc) with monkeypatch.context() as m: - m.setattr(sys, 'platform', 'win32') + m.setattr(sys, "platform", "win32") m.setattr('builtins.print', Mock()) - importlib.reload(sys.modules['pythonrc']) + importlib.reload(sys.modules["pythonrc"]) print.assert_any_call("Ctrl click to launch VS Code Native REPL") From 7b00e952aea2d744a62026be64904bd070cd3c87 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 18:29:46 -0500 Subject: [PATCH 13/22] fix --- python_files/tests/test_shell_integration.py | 28 +++++++++----------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index 2e10d674c28f..800e5a710ff3 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -61,18 +61,16 @@ def test_excepthook_call(): hooks.my_excepthook("mock_type", "mock_value", "mock_traceback") mock_excepthook.assert_called_once_with("mock_type", "mock_value", "mock_traceback") -def test_print_statement_darwin(monkeypatch): - importlib.reload(pythonrc) - with monkeypatch.context() as m: - m.setattr(sys, "platform", "darwin") - m.setattr("builtins.print", Mock()) - importlib.reload(sys.modules["pythonrc"]) - print.assert_any_call("Cmd click to launch VS Code Native REPL") - -def test_print_statement_non_darwin(monkeypatch): - importlib.reload(pythonrc) - with monkeypatch.context() as m: - m.setattr(sys, "platform", "win32") - m.setattr('builtins.print', Mock()) - importlib.reload(sys.modules["pythonrc"]) - print.assert_any_call("Ctrl click to launch VS Code Native REPL") +if sys.platform == "darwin": + def test_print_statement_darwin(monkeypatch): + importlib.reload(pythonrc) + with monkeypatch.context() as m: + m.setattr("builtins.print", Mock()) + print.assert_any_call("Cmd click to launch VS Code Native REPL") + +if sys.platform == "win32": + def test_print_statement_non_darwin(monkeypatch): + importlib.reload(pythonrc) + with monkeypatch.context() as m: + m.setattr('builtins.print', Mock()) + print.assert_any_call("Ctrl click to launch VS Code Native REPL") From 94cef91e9ddf9bcb83c52ffaf8a67a03eb42dfa1 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 18:30:57 -0500 Subject: [PATCH 14/22] i --- python_files/tests/test_shell_integration.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index 800e5a710ff3..8de6bfda9077 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -66,6 +66,7 @@ def test_print_statement_darwin(monkeypatch): importlib.reload(pythonrc) with monkeypatch.context() as m: m.setattr("builtins.print", Mock()) + importlib.reload(sys.modules["pythonrc"]) print.assert_any_call("Cmd click to launch VS Code Native REPL") if sys.platform == "win32": @@ -73,4 +74,5 @@ def test_print_statement_non_darwin(monkeypatch): importlib.reload(pythonrc) with monkeypatch.context() as m: m.setattr('builtins.print', Mock()) + importlib.reload(sys.modules["pythonrc"]) print.assert_any_call("Ctrl click to launch VS Code Native REPL") From b39dcd111f074734b6a3121338076273748a8c7b Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 18:36:16 -0500 Subject: [PATCH 15/22] double quote --- python_files/tests/test_shell_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index 8de6bfda9077..4c44341e94c1 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -73,6 +73,6 @@ def test_print_statement_darwin(monkeypatch): def test_print_statement_non_darwin(monkeypatch): importlib.reload(pythonrc) with monkeypatch.context() as m: - m.setattr('builtins.print', Mock()) + m.setattr("builtins.print", Mock()) importlib.reload(sys.modules["pythonrc"]) print.assert_any_call("Ctrl click to launch VS Code Native REPL") From f9e2f85effdfc2d0a7aeecfdc115e3dde48de01c Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 18:39:20 -0500 Subject: [PATCH 16/22] ruff my imports --- python_files/tests/test_shell_integration.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index 4c44341e94c1..376cb466bb50 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -2,6 +2,7 @@ import platform import sys from unittest.mock import Mock + import pythonrc is_wsl = "microsoft-standard-WSL" in platform.release() @@ -61,7 +62,9 @@ def test_excepthook_call(): hooks.my_excepthook("mock_type", "mock_value", "mock_traceback") mock_excepthook.assert_called_once_with("mock_type", "mock_value", "mock_traceback") + if sys.platform == "darwin": + def test_print_statement_darwin(monkeypatch): importlib.reload(pythonrc) with monkeypatch.context() as m: @@ -69,7 +72,9 @@ def test_print_statement_darwin(monkeypatch): importlib.reload(sys.modules["pythonrc"]) print.assert_any_call("Cmd click to launch VS Code Native REPL") + if sys.platform == "win32": + def test_print_statement_non_darwin(monkeypatch): importlib.reload(pythonrc) with monkeypatch.context() as m: From 541849b3acfd53bf41d127a47a9aa0d5f7042b16 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 18:48:48 -0500 Subject: [PATCH 17/22] reformat for the very last time --- python_files/pythonrc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python_files/pythonrc.py b/python_files/pythonrc.py index dd39ab34aa17..0f552c86d375 100644 --- a/python_files/pythonrc.py +++ b/python_files/pythonrc.py @@ -8,6 +8,7 @@ use_shell_integration = sys.version_info < (3, 13) is_wsl = "microsoft-standard-WSL" in platform.release() + class REPLHooks: def __init__(self): self.global_exit = None From ee8909fd0bb20d492e9828ca1b9fce79aefc4f44 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 19:00:49 -0500 Subject: [PATCH 18/22] address feedback, cleanup --- src/client/terminals/pythonStartupLinkProvider.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/client/terminals/pythonStartupLinkProvider.ts b/src/client/terminals/pythonStartupLinkProvider.ts index 57ec1191dd87..f5bbef2d5378 100644 --- a/src/client/terminals/pythonStartupLinkProvider.ts +++ b/src/client/terminals/pythonStartupLinkProvider.ts @@ -21,11 +21,8 @@ export class CustomTerminalLinkProvider implements TerminalLinkProvider { const links: CustomTerminalLink[] = []; - // Question: What if context.line is truncated because of user zoom setting? - // Meaning what if this line is separated into two+ line in terminal? const expectedNativeLink = 'VS Code Native REPL'; - // eslint-disable-next-line no-cond-assign if (context.line.includes(expectedNativeLink)) { links.push({ startIndex: context.line.indexOf(expectedNativeLink), From c648fdaaced92e4fa037dd743fb8ba9a4afc2fb3 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 20:43:28 -0500 Subject: [PATCH 19/22] add more test --- .../shellIntegration/pythonStartup.test.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/test/terminals/shellIntegration/pythonStartup.test.ts b/src/test/terminals/shellIntegration/pythonStartup.test.ts index a5ec241403c0..18e097614ed1 100644 --- a/src/test/terminals/shellIntegration/pythonStartup.test.ts +++ b/src/test/terminals/shellIntegration/pythonStartup.test.ts @@ -3,11 +3,23 @@ import * as sinon from 'sinon'; import * as TypeMoq from 'typemoq'; -import { GlobalEnvironmentVariableCollection, Uri, WorkspaceConfiguration, Disposable } from 'vscode'; +import { + GlobalEnvironmentVariableCollection, + Uri, + WorkspaceConfiguration, + Disposable, + CancellationToken, + TerminalLinkContext, + Terminal, + EventEmitter, + TerminalLink, +} from 'vscode'; +import { assert } from 'chai'; import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; import { registerPythonStartup } from '../../../client/terminals/pythonStartup'; import { IExtensionContext } from '../../../client/common/types'; import * as pythonStartupLinkProvider from '../../../client/terminals/pythonStartupLinkProvider'; +import { CustomTerminalLinkProvider } from '../../../client/terminals/pythonStartupLinkProvider'; suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { let getConfigurationStub: sinon.SinonStub; @@ -136,4 +148,21 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { registerTerminalLinkProviderStub.restore(); }); + + test('Verify provideTerminalLinks returns links when context.line contains expectedNativeLink', () => { + const provider = new CustomTerminalLinkProvider(); + const context: TerminalLinkContext = { + line: 'Some random string with VS Code Native REPL in it', + terminal: {} as Terminal, + }; + const token: CancellationToken = { + isCancellationRequested: false, + onCancellationRequested: new EventEmitter().event, + }; + + const links = provider.provideTerminalLinks(context, token); + + assert.isNotNull(links, 'Expected links to be not undefined'); + assert.isArray(links, 'Expected links to be an array'); + }); }); From 0a07887fb4ace9a18360c2bb332f44131a9234df Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 20:47:27 -0500 Subject: [PATCH 20/22] remove unused --- src/test/terminals/shellIntegration/pythonStartup.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/terminals/shellIntegration/pythonStartup.test.ts b/src/test/terminals/shellIntegration/pythonStartup.test.ts index 18e097614ed1..72f92fc3f55b 100644 --- a/src/test/terminals/shellIntegration/pythonStartup.test.ts +++ b/src/test/terminals/shellIntegration/pythonStartup.test.ts @@ -12,7 +12,6 @@ import { TerminalLinkContext, Terminal, EventEmitter, - TerminalLink, } from 'vscode'; import { assert } from 'chai'; import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; From c98e18da0a5c8c41a093e058bd04a536db222eca Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 23:05:26 -0500 Subject: [PATCH 21/22] more tests --- src/client/common/utils/localize.ts | 1 + src/client/terminals/pythonStartupLinkProvider.ts | 4 ++-- .../shellIntegration/pythonStartup.test.ts | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index 1e5d28d778dc..18ab501f241b 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -92,6 +92,7 @@ export namespace AttachProcess { export namespace Repl { export const disableSmartSend = l10n.t('Disable Smart Send'); + export const launchNativeRepl = l10n.t('Launch VS Code Native REPL'); } export namespace Pylance { export const remindMeLater = l10n.t('Remind me later'); diff --git a/src/client/terminals/pythonStartupLinkProvider.ts b/src/client/terminals/pythonStartupLinkProvider.ts index f5bbef2d5378..00dcbfd757aa 100644 --- a/src/client/terminals/pythonStartupLinkProvider.ts +++ b/src/client/terminals/pythonStartupLinkProvider.ts @@ -6,10 +6,10 @@ import { TerminalLink, TerminalLinkContext, TerminalLinkProvider, - l10n, } from 'vscode'; import { executeCommand } from '../common/vscodeApis/commandApis'; import { registerTerminalLinkProvider } from '../common/vscodeApis/windowApis'; +import { Repl } from '../common/utils/localize'; interface CustomTerminalLink extends TerminalLink { command: string; @@ -27,7 +27,7 @@ export class CustomTerminalLinkProvider implements TerminalLinkProvider { let getConfigurationStub: sinon.SinonStub; @@ -163,5 +164,17 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { assert.isNotNull(links, 'Expected links to be not undefined'); assert.isArray(links, 'Expected links to be an array'); + assert.isNotEmpty(links, 'Expected links to be not empty'); + + if (Array.isArray(links)) { + assert.equal(links[0].command, 'python.startNativeREPL', 'Expected command to be python.startNativeREPL'); + assert.equal( + links[0].startIndex, + context.line.indexOf('VS Code Native REPL'), + 'Expected startIndex to be 0', + ); + assert.equal(links[0].length, 'VS Code Native REPL'.length, 'Expected length to be 16'); + assert.equal(links[0].tooltip, Repl.launchNativeRepl, 'Expected tooltip to be Launch VS Code Native REPL'); + } }); }); From fb0fa8d8046c53a2039ebc191037611acd5f4197 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 22 Jan 2025 23:18:31 -0500 Subject: [PATCH 22/22] negative test case against provideTerminalLinks --- .../shellIntegration/pythonStartup.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/terminals/shellIntegration/pythonStartup.test.ts b/src/test/terminals/shellIntegration/pythonStartup.test.ts index c460870b2728..35674e188cd9 100644 --- a/src/test/terminals/shellIntegration/pythonStartup.test.ts +++ b/src/test/terminals/shellIntegration/pythonStartup.test.ts @@ -177,4 +177,21 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { assert.equal(links[0].tooltip, Repl.launchNativeRepl, 'Expected tooltip to be Launch VS Code Native REPL'); } }); + + test('Verify provideTerminalLinks returns no links when context.line does not contain expectedNativeLink', () => { + const provider = new CustomTerminalLinkProvider(); + const context: TerminalLinkContext = { + line: 'Some random string without the expected link', + terminal: {} as Terminal, + }; + const token: CancellationToken = { + isCancellationRequested: false, + onCancellationRequested: new EventEmitter().event, + }; + + const links = provider.provideTerminalLinks(context, token); + + assert.isArray(links, 'Expected links to be an array'); + assert.isEmpty(links, 'Expected links to be empty'); + }); });