diff --git a/package.nls.json b/package.nls.json index 37a9ce435f2f..57f2ed95b2c0 100644 --- a/package.nls.json +++ b/package.nls.json @@ -73,7 +73,7 @@ "python.tensorBoard.logDirectory.description": "Set this setting to your preferred TensorBoard log directory to skip log directory prompt when starting TensorBoard.", "python.tensorBoard.logDirectory.markdownDeprecationMessage": "Tensorboard support has been moved to the extension [Tensorboard extension](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.tensorboard). Instead use the setting `tensorBoard.logDirectory`.", "python.tensorBoard.logDirectory.deprecationMessage": "Tensorboard support has been moved to the extension Tensorboard extension. Instead use the setting `tensorBoard.logDirectory`.", - "python.terminal.shellIntegration.enabled.description": "Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) for the terminals running python. Shell integration enhances the terminal experience by enabling command decorations, run recent command, improving accessibility among other things.", + "python.terminal.shellIntegration.enabled.description": "Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) for the terminals running python. Shell integration enhances the terminal experience by enabling command decorations, run recent command, improving accessibility among other things. Note: PyREPL (available in Python 3.13+) is automatically disabled when shell integration is enabled to avoid cursor indentation issues.", "python.terminal.activateEnvInCurrentTerminal.description": "Activate Python Environment in the current Terminal on load of the Extension.", "python.terminal.activateEnvironment.description": "Activate Python Environment in all Terminals created.", "python.terminal.executeInFileDir.description": "When executing a file in the terminal, whether to use execute in the file's directory, instead of the current open folder.", diff --git a/python_files/pythonrc.py b/python_files/pythonrc.py index afd32520cf01..005b06bcdd15 100644 --- a/python_files/pythonrc.py +++ b/python_files/pythonrc.py @@ -5,7 +5,6 @@ import readline original_ps1 = ">>> " -use_shell_integration = sys.version_info < (3, 13) is_wsl = "microsoft-standard-WSL" in platform.release() @@ -75,7 +74,7 @@ def __str__(self): return result -if sys.platform != "win32" and (not is_wsl) and use_shell_integration: +if sys.platform != "win32" and (not is_wsl): sys.ps1 = PS1() if sys.platform == "darwin": diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index 634e0106fe7b..91c06d9331fd 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -377,7 +377,9 @@ export class PythonSettings implements IPythonSettings { launchArgs: [], activateEnvironment: true, activateEnvInCurrentTerminal: false, - enableShellIntegration: false, + shellIntegration: { + enabled: false, + }, }; this.REPL = pythonSettings.get('REPL')!; diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 2cb393d89bdf..c30ad704b6c1 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -188,7 +188,9 @@ export interface ITerminalSettings { readonly launchArgs: string[]; readonly activateEnvironment: boolean; readonly activateEnvInCurrentTerminal: boolean; - readonly enableShellIntegration: boolean; + readonly shellIntegration: { + enabled: boolean; + }; } export interface IREPLSettings { diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index 8fae9d5131ff..8330d5010f7a 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -53,7 +53,7 @@ import { DebuggerTypeName } from './debugger/constants'; import { StopWatch } from './common/utils/stopWatch'; import { registerReplCommands, registerReplExecuteOnEnter, registerStartNativeReplCommand } from './repl/replCommands'; import { registerTriggerForTerminalREPL } from './terminals/codeExecution/terminalReplWatcher'; -import { registerBasicRepl, registerPythonStartup } from './terminals/pythonStartup'; +import { registerPythonStartup } from './terminals/pythonStartup'; import { registerPixiFeatures } from './pythonEnvironments/common/environmentManagers/pixi'; import { registerCustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider'; import { registerEnvExtFeatures } from './envExt/api.internal'; @@ -184,7 +184,6 @@ async function activateLegacy(ext: ExtensionState, startupStopWatch: StopWatch): serviceManager.get(ITerminalAutoActivation).register(); await registerPythonStartup(ext.context); - await registerBasicRepl(ext.context); serviceManager.get(ICodeExecutionManager).registerCommands(); diff --git a/src/client/terminals/codeExecution/helper.ts b/src/client/terminals/codeExecution/helper.ts index 26ebe35aae43..4efad5ee174e 100644 --- a/src/client/terminals/codeExecution/helper.ts +++ b/src/client/terminals/codeExecution/helper.ts @@ -99,10 +99,12 @@ export class CodeExecutionHelper implements ICodeExecutionHelper { const endLineVal = activeEditor?.selection?.end.line ?? 0; const emptyHighlightVal = activeEditor?.selection?.isEmpty ?? true; let smartSendSettingsEnabledVal = true; + let shellIntegrationEnabled = false; const configuration = this.serviceContainer.get(IConfigurationService); if (configuration) { const pythonSettings = configuration.getSettings(this.activeResourceService.getActiveResource()); smartSendSettingsEnabledVal = pythonSettings.REPL.enableREPLSmartSend; + shellIntegrationEnabled = pythonSettings.terminal.shellIntegration.enabled; } const input = JSON.stringify({ @@ -125,6 +127,16 @@ export class CodeExecutionHelper implements ICodeExecutionHelper { await this.moveToNextBlock(lineOffset, activeEditor); } + // For new _pyrepl for Python3.13+ && !shellIntegration, we need to send code via bracketed paste mode. + if (object.attach_bracket_paste && !shellIntegrationEnabled && _replType === ReplType.terminal) { + let trimmedNormalized = object.normalized.replace(/\n$/, ''); + if (trimmedNormalized.endsWith(':\n')) { + // In case where statement is unfinished via :, truncate so auto-indentation lands nicely. + trimmedNormalized = trimmedNormalized.replace(/\n$/, ''); + } + return `\u001b[200~${trimmedNormalized}\u001b[201~`; + } + return parse(object.normalized); } catch (ex) { traceError(ex, 'Python: Failed to normalize code for execution in terminal'); diff --git a/src/client/terminals/pythonStartup.ts b/src/client/terminals/pythonStartup.ts index 1a2576dce772..f0c3bf89c3b4 100644 --- a/src/client/terminals/pythonStartup.ts +++ b/src/client/terminals/pythonStartup.ts @@ -21,8 +21,11 @@ async function applyPythonStartupSetting(context: ExtensionContext): Promise { - // TODO: Configurable by setting - context.environmentVariableCollection.replace('PYTHON_BASIC_REPL', '1'); -} diff --git a/src/test/terminals/shellIntegration/pythonStartup.test.ts b/src/test/terminals/shellIntegration/pythonStartup.test.ts index 3c755adf0d9b..833a4f29e972 100644 --- a/src/test/terminals/shellIntegration/pythonStartup.test.ts +++ b/src/test/terminals/shellIntegration/pythonStartup.test.ts @@ -16,7 +16,7 @@ import { } from 'vscode'; import { assert } from 'chai'; import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; -import { registerBasicRepl, registerPythonStartup } from '../../../client/terminals/pythonStartup'; +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'; @@ -135,8 +135,9 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { globalEnvironmentVariableCollection.verify((c) => c.delete('PYTHONSTARTUP'), TypeMoq.Times.once()); }); - test('PYTHON_BASIC_REPL is set when registerBasicRepl is called', async () => { - await registerBasicRepl(context.object); + test('PYTHON_BASIC_REPL is set when shell integration is enabled', async () => { + pythonConfig.setup((p) => p.get('terminal.shellIntegration.enabled')).returns(() => true); + await registerPythonStartup(context.object); globalEnvironmentVariableCollection.verify( (c) => c.replace('PYTHON_BASIC_REPL', '1', TypeMoq.It.isAny()), TypeMoq.Times.once(),