Skip to content

Commit ba56b21

Browse files
authored
Resolve REPL regression on indentation, disable PyREPL only when shell integration is enabled (#25296)
Resolves: #25295 #25240 #25242
1 parent 9774130 commit ba56b21

File tree

8 files changed

+28
-15
lines changed

8 files changed

+28
-15
lines changed

package.nls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"python.tensorBoard.logDirectory.description": "Set this setting to your preferred TensorBoard log directory to skip log directory prompt when starting TensorBoard.",
7474
"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`.",
7575
"python.tensorBoard.logDirectory.deprecationMessage": "Tensorboard support has been moved to the extension Tensorboard extension. Instead use the setting `tensorBoard.logDirectory`.",
76-
"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.",
76+
"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.",
7777
"python.terminal.activateEnvInCurrentTerminal.description": "Activate Python Environment in the current Terminal on load of the Extension.",
7878
"python.terminal.activateEnvironment.description": "Activate Python Environment in all Terminals created.",
7979
"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.",

python_files/pythonrc.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import readline
66

77
original_ps1 = ">>> "
8-
use_shell_integration = sys.version_info < (3, 13)
98
is_wsl = "microsoft-standard-WSL" in platform.release()
109

1110

@@ -75,7 +74,7 @@ def __str__(self):
7574
return result
7675

7776

78-
if sys.platform != "win32" and (not is_wsl) and use_shell_integration:
77+
if sys.platform != "win32" and (not is_wsl):
7978
sys.ps1 = PS1()
8079

8180
if sys.platform == "darwin":

src/client/common/configSettings.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,9 @@ export class PythonSettings implements IPythonSettings {
377377
launchArgs: [],
378378
activateEnvironment: true,
379379
activateEnvInCurrentTerminal: false,
380-
enableShellIntegration: false,
380+
shellIntegration: {
381+
enabled: false,
382+
},
381383
};
382384

383385
this.REPL = pythonSettings.get<IREPLSettings>('REPL')!;

src/client/common/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ export interface ITerminalSettings {
188188
readonly launchArgs: string[];
189189
readonly activateEnvironment: boolean;
190190
readonly activateEnvInCurrentTerminal: boolean;
191-
readonly enableShellIntegration: boolean;
191+
readonly shellIntegration: {
192+
enabled: boolean;
193+
};
192194
}
193195

194196
export interface IREPLSettings {

src/client/extensionActivation.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import { DebuggerTypeName } from './debugger/constants';
5353
import { StopWatch } from './common/utils/stopWatch';
5454
import { registerReplCommands, registerReplExecuteOnEnter, registerStartNativeReplCommand } from './repl/replCommands';
5555
import { registerTriggerForTerminalREPL } from './terminals/codeExecution/terminalReplWatcher';
56-
import { registerBasicRepl, registerPythonStartup } from './terminals/pythonStartup';
56+
import { registerPythonStartup } from './terminals/pythonStartup';
5757
import { registerPixiFeatures } from './pythonEnvironments/common/environmentManagers/pixi';
5858
import { registerCustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider';
5959
import { registerEnvExtFeatures } from './envExt/api.internal';
@@ -184,7 +184,6 @@ async function activateLegacy(ext: ExtensionState, startupStopWatch: StopWatch):
184184
serviceManager.get<ITerminalAutoActivation>(ITerminalAutoActivation).register();
185185

186186
await registerPythonStartup(ext.context);
187-
await registerBasicRepl(ext.context);
188187

189188
serviceManager.get<ICodeExecutionManager>(ICodeExecutionManager).registerCommands();
190189

src/client/terminals/codeExecution/helper.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,12 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
9999
const endLineVal = activeEditor?.selection?.end.line ?? 0;
100100
const emptyHighlightVal = activeEditor?.selection?.isEmpty ?? true;
101101
let smartSendSettingsEnabledVal = true;
102+
let shellIntegrationEnabled = false;
102103
const configuration = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
103104
if (configuration) {
104105
const pythonSettings = configuration.getSettings(this.activeResourceService.getActiveResource());
105106
smartSendSettingsEnabledVal = pythonSettings.REPL.enableREPLSmartSend;
107+
shellIntegrationEnabled = pythonSettings.terminal.shellIntegration.enabled;
106108
}
107109

108110
const input = JSON.stringify({
@@ -125,6 +127,16 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
125127
await this.moveToNextBlock(lineOffset, activeEditor);
126128
}
127129

130+
// For new _pyrepl for Python3.13+ && !shellIntegration, we need to send code via bracketed paste mode.
131+
if (object.attach_bracket_paste && !shellIntegrationEnabled && _replType === ReplType.terminal) {
132+
let trimmedNormalized = object.normalized.replace(/\n$/, '');
133+
if (trimmedNormalized.endsWith(':\n')) {
134+
// In case where statement is unfinished via :, truncate so auto-indentation lands nicely.
135+
trimmedNormalized = trimmedNormalized.replace(/\n$/, '');
136+
}
137+
return `\u001b[200~${trimmedNormalized}\u001b[201~`;
138+
}
139+
128140
return parse(object.normalized);
129141
} catch (ex) {
130142
traceError(ex, 'Python: Failed to normalize code for execution in terminal');

src/client/terminals/pythonStartup.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ async function applyPythonStartupSetting(context: ExtensionContext): Promise<voi
2121
const sourcePath = path.join(EXTENSION_ROOT_DIR, 'python_files', 'pythonrc.py');
2222
await copy(Uri.file(sourcePath), destPath, { overwrite: true });
2323
context.environmentVariableCollection.replace('PYTHONSTARTUP', destPath.fsPath);
24+
// When shell integration is enabled, we disable PyREPL from cpython.
25+
context.environmentVariableCollection.replace('PYTHON_BASIC_REPL', '1');
2426
} else {
2527
context.environmentVariableCollection.delete('PYTHONSTARTUP');
28+
context.environmentVariableCollection.delete('PYTHON_BASIC_REPL');
2629
}
2730
}
2831

@@ -36,8 +39,3 @@ export async function registerPythonStartup(context: ExtensionContext): Promise<
3639
}),
3740
);
3841
}
39-
40-
export async function registerBasicRepl(context: ExtensionContext): Promise<void> {
41-
// TODO: Configurable by setting
42-
context.environmentVariableCollection.replace('PYTHON_BASIC_REPL', '1');
43-
}

src/test/terminals/shellIntegration/pythonStartup.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from 'vscode';
1717
import { assert } from 'chai';
1818
import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis';
19-
import { registerBasicRepl, registerPythonStartup } from '../../../client/terminals/pythonStartup';
19+
import { registerPythonStartup } from '../../../client/terminals/pythonStartup';
2020
import { IExtensionContext } from '../../../client/common/types';
2121
import * as pythonStartupLinkProvider from '../../../client/terminals/pythonStartupLinkProvider';
2222
import { CustomTerminalLinkProvider } from '../../../client/terminals/pythonStartupLinkProvider';
@@ -135,8 +135,9 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => {
135135
globalEnvironmentVariableCollection.verify((c) => c.delete('PYTHONSTARTUP'), TypeMoq.Times.once());
136136
});
137137

138-
test('PYTHON_BASIC_REPL is set when registerBasicRepl is called', async () => {
139-
await registerBasicRepl(context.object);
138+
test('PYTHON_BASIC_REPL is set when shell integration is enabled', async () => {
139+
pythonConfig.setup((p) => p.get('terminal.shellIntegration.enabled')).returns(() => true);
140+
await registerPythonStartup(context.object);
140141
globalEnvironmentVariableCollection.verify(
141142
(c) => c.replace('PYTHON_BASIC_REPL', '1', TypeMoq.It.isAny()),
142143
TypeMoq.Times.once(),

0 commit comments

Comments
 (0)