From eb1d1b15990a40f73fe83c317863f0ac9dba55b4 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Thu, 5 Feb 2026 08:44:57 -0800 Subject: [PATCH 1/2] Match Python extension behavior --- src/features/terminal/terminalManager.ts | 13 +- .../terminal/terminalManager.unit.test.ts | 134 ++++++++++++++++++ 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/src/features/terminal/terminalManager.ts b/src/features/terminal/terminalManager.ts index 6b9a6686..9da1ecd5 100644 --- a/src/features/terminal/terminalManager.ts +++ b/src/features/terminal/terminalManager.ts @@ -289,11 +289,13 @@ export class TerminalManagerImpl implements TerminalManager { }); } - // Uncomment the code line below after the issue is resolved: - // https://github.com/microsoft/vscode-python-environments/issues/172 - // const name = options.name ?? `Python: ${environment.displayName}`; + // Follow Python extension's terminal naming convention: + // - Default: 'Python' + // - Dedicated: 'Python: {filename}' (set by getDedicatedTerminal) + const name = options.name ?? 'Python'; const newTerminal = createTerminal({ ...options, + name, env: envVars, }); @@ -342,7 +344,10 @@ export class TerminalManagerImpl implements TerminalManager { const uriDir = uriStat.isDirectory() ? terminalKey.fsPath : path.dirname(terminalKey.fsPath); const cwd = config.get('terminal.executeInFileDir', false) ? uriDir : projectDir; - const newTerminal = await this.create(environment, { cwd }); + // Follow Python extension's naming: 'Python: {filename}' for dedicated terminals + const fileName = path.basename(terminalKey.fsPath).replace('.py', ''); + const name = `Python: ${fileName}`; + const newTerminal = await this.create(environment, { cwd, name }); this.dedicatedTerminals.set(key, newTerminal); const disable = onDidCloseTerminal((terminal) => { diff --git a/src/test/features/terminal/terminalManager.unit.test.ts b/src/test/features/terminal/terminalManager.unit.test.ts index f053765f..60acf03a 100644 --- a/src/test/features/terminal/terminalManager.unit.test.ts +++ b/src/test/features/terminal/terminalManager.unit.test.ts @@ -2,6 +2,9 @@ // Licensed under the MIT License. import * as assert from 'assert'; +import * as fsapi from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; import * as sinon from 'sinon'; import { Disposable, Event, EventEmitter, Progress, Terminal, TerminalOptions, Uri } from 'vscode'; import { PythonEnvironment } from '../../../api'; @@ -185,3 +188,134 @@ suite('TerminalManager - create()', () => { assert.strictEqual(terminalActivation.activateCalls, 0, 'No activate() when disableActivation is true'); }); }); + +suite('TerminalManager - terminal naming', () => { + let terminalActivation: TestTerminalActivation; + let mockGetAutoActivationType: sinon.SinonStub; + let terminalManager: TerminalManagerImpl; + let mockTerminal: Partial & { show: sinon.SinonStub }; + let createTerminalStub: sinon.SinonStub; + + const createMockEnvironment = (): PythonEnvironment => ({ + envId: { id: 'test-env-id', managerId: 'test-manager' }, + name: 'Test Environment', + displayName: 'Test Environment', + shortDisplayName: 'TestEnv', + displayPath: '/path/to/env', + version: '3.9.0', + environmentPath: Uri.file('/path/to/python'), + sysPrefix: '/path/to/env', + execInfo: { + run: { executable: '/path/to/python' }, + activation: [{ executable: '/path/to/activate' }], + }, + }); + + setup(() => { + terminalActivation = new TestTerminalActivation(); + + mockTerminal = { + name: 'Test Terminal', + creationOptions: {} as TerminalOptions, + shellIntegration: undefined, + show: sinon.stub(), + sendText: sinon.stub(), + }; + + mockGetAutoActivationType = sinon.stub(terminalUtils, 'getAutoActivationType'); + sinon.stub(terminalUtils, 'waitForShellIntegration').resolves(false); + sinon.stub(activationUtils, 'isActivatableEnvironment').returns(true); + sinon.stub(shellDetector, 'identifyTerminalShell').returns('bash'); + + createTerminalStub = sinon.stub(windowApis, 'createTerminal').returns(mockTerminal as Terminal); + sinon.stub(windowApis, 'onDidOpenTerminal').returns(new Disposable(() => {})); + sinon.stub(windowApis, 'onDidCloseTerminal').returns(new Disposable(() => {})); + sinon.stub(windowApis, 'onDidChangeWindowState').returns(new Disposable(() => {})); + sinon.stub(windowApis, 'terminals').returns([]); + sinon.stub(windowApis, 'withProgress').callsFake(async (_options, task) => { + const mockProgress: Progress<{ message?: string; increment?: number }> = { report: () => {} }; + const mockCancellationToken = { + isCancellationRequested: false, + onCancellationRequested: () => new Disposable(() => {}), + }; + return task(mockProgress, mockCancellationToken as never); + }); + + sinon.stub(workspaceApis, 'onDidChangeConfiguration').returns(new Disposable(() => {})); + }); + + teardown(() => { + sinon.restore(); + terminalActivation.dispose(); + }); + + function createTerminalManager(): TerminalManagerImpl { + const emptyEnvProviders: ShellEnvsProvider[] = []; + const emptyScriptProviders: ShellStartupScriptProvider[] = []; + return new TerminalManagerImpl(terminalActivation, emptyEnvProviders, emptyScriptProviders); + } + + test('getDedicatedTerminal sets Python file name as terminal name', async () => { + mockGetAutoActivationType.returns(terminalUtils.ACT_TYPE_OFF); + terminalManager = createTerminalManager(); + const env = createMockEnvironment(); + + const optionsList: TerminalOptions[] = []; + createTerminalStub.callsFake((options) => { + optionsList.push(options); + return mockTerminal as Terminal; + }); + + const tempRoot = await fsapi.mkdtemp(path.join(os.tmpdir(), 'py-envs-')); + const projectPath = path.join(tempRoot, 'project'); + const filePath = path.join(projectPath, 'main.py'); + await fsapi.ensureDir(projectPath); + await fsapi.writeFile(filePath, 'print("hello")'); + const projectUri = Uri.file(projectPath); + const fileUri = Uri.file(filePath); + + const config = { get: sinon.stub().returns(false) }; + sinon.stub(workspaceApis, 'getConfiguration').returns(config as any); + + try { + await terminalManager.getDedicatedTerminal(fileUri, projectUri, env); + + assert.strictEqual( + optionsList[0]?.name, + 'Python: main', + 'Dedicated terminal should use the file name in the title', + ); + } finally { + await fsapi.remove(tempRoot); + } + }); + + test('getProjectTerminal sets Python as terminal name', async () => { + mockGetAutoActivationType.returns(terminalUtils.ACT_TYPE_OFF); + terminalManager = createTerminalManager(); + const env = createMockEnvironment(); + + const optionsList: TerminalOptions[] = []; + createTerminalStub.callsFake((options) => { + optionsList.push(options); + return mockTerminal as Terminal; + }); + + const tempRoot = await fsapi.mkdtemp(path.join(os.tmpdir(), 'py-envs-')); + const projectPath = path.join(tempRoot, 'project'); + await fsapi.ensureDir(projectPath); + const projectUri = Uri.file(projectPath); + + try { + await terminalManager.getProjectTerminal(projectUri, env); + + assert.strictEqual( + optionsList[0]?.name, + 'Python', + 'Project terminal should use the Python title', + ); + } finally { + await fsapi.remove(tempRoot); + } + }); +}); From aa11ecae46fb576f2e9962e6ef4184a461e43cff Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Thu, 5 Feb 2026 08:49:04 -0800 Subject: [PATCH 2/2] lint --- src/test/features/terminal/terminalManager.unit.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/features/terminal/terminalManager.unit.test.ts b/src/test/features/terminal/terminalManager.unit.test.ts index 60acf03a..3e1862d2 100644 --- a/src/test/features/terminal/terminalManager.unit.test.ts +++ b/src/test/features/terminal/terminalManager.unit.test.ts @@ -6,7 +6,7 @@ import * as fsapi from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; import * as sinon from 'sinon'; -import { Disposable, Event, EventEmitter, Progress, Terminal, TerminalOptions, Uri } from 'vscode'; +import { Disposable, Event, EventEmitter, Progress, Terminal, TerminalOptions, Uri, WorkspaceConfiguration } from 'vscode'; import { PythonEnvironment } from '../../../api'; import * as windowApis from '../../../common/window.apis'; import * as workspaceApis from '../../../common/workspace.apis'; @@ -274,8 +274,8 @@ suite('TerminalManager - terminal naming', () => { const projectUri = Uri.file(projectPath); const fileUri = Uri.file(filePath); - const config = { get: sinon.stub().returns(false) }; - sinon.stub(workspaceApis, 'getConfiguration').returns(config as any); + const config = { get: sinon.stub().returns(false) } as unknown as WorkspaceConfiguration; + sinon.stub(workspaceApis, 'getConfiguration').returns(config); try { await terminalManager.getDedicatedTerminal(fileUri, projectUri, env);