Skip to content

Commit 7cea36b

Browse files
authored
1 parent dc50906 commit 7cea36b

File tree

2 files changed

+144
-5
lines changed

2 files changed

+144
-5
lines changed

src/features/terminal/terminalManager.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,11 +289,13 @@ export class TerminalManagerImpl implements TerminalManager {
289289
});
290290
}
291291

292-
// Uncomment the code line below after the issue is resolved:
293-
// https://github.com/microsoft/vscode-python-environments/issues/172
294-
// const name = options.name ?? `Python: ${environment.displayName}`;
292+
// Follow Python extension's terminal naming convention:
293+
// - Default: 'Python'
294+
// - Dedicated: 'Python: {filename}' (set by getDedicatedTerminal)
295+
const name = options.name ?? 'Python';
295296
const newTerminal = createTerminal({
296297
...options,
298+
name,
297299
env: envVars,
298300
});
299301

@@ -342,7 +344,10 @@ export class TerminalManagerImpl implements TerminalManager {
342344
const uriDir = uriStat.isDirectory() ? terminalKey.fsPath : path.dirname(terminalKey.fsPath);
343345
const cwd = config.get<boolean>('terminal.executeInFileDir', false) ? uriDir : projectDir;
344346

345-
const newTerminal = await this.create(environment, { cwd });
347+
// Follow Python extension's naming: 'Python: {filename}' for dedicated terminals
348+
const fileName = path.basename(terminalKey.fsPath).replace('.py', '');
349+
const name = `Python: ${fileName}`;
350+
const newTerminal = await this.create(environment, { cwd, name });
346351
this.dedicatedTerminals.set(key, newTerminal);
347352

348353
const disable = onDidCloseTerminal((terminal) => {

src/test/features/terminal/terminalManager.unit.test.ts

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
// Licensed under the MIT License.
33

44
import * as assert from 'assert';
5+
import * as fsapi from 'fs-extra';
6+
import * as os from 'os';
7+
import * as path from 'path';
58
import * as sinon from 'sinon';
6-
import { Disposable, Event, EventEmitter, Progress, Terminal, TerminalOptions, Uri } from 'vscode';
9+
import { Disposable, Event, EventEmitter, Progress, Terminal, TerminalOptions, Uri, WorkspaceConfiguration } from 'vscode';
710
import { PythonEnvironment } from '../../../api';
811
import * as windowApis from '../../../common/window.apis';
912
import * as workspaceApis from '../../../common/workspace.apis';
@@ -185,3 +188,134 @@ suite('TerminalManager - create()', () => {
185188
assert.strictEqual(terminalActivation.activateCalls, 0, 'No activate() when disableActivation is true');
186189
});
187190
});
191+
192+
suite('TerminalManager - terminal naming', () => {
193+
let terminalActivation: TestTerminalActivation;
194+
let mockGetAutoActivationType: sinon.SinonStub;
195+
let terminalManager: TerminalManagerImpl;
196+
let mockTerminal: Partial<Terminal> & { show: sinon.SinonStub };
197+
let createTerminalStub: sinon.SinonStub;
198+
199+
const createMockEnvironment = (): PythonEnvironment => ({
200+
envId: { id: 'test-env-id', managerId: 'test-manager' },
201+
name: 'Test Environment',
202+
displayName: 'Test Environment',
203+
shortDisplayName: 'TestEnv',
204+
displayPath: '/path/to/env',
205+
version: '3.9.0',
206+
environmentPath: Uri.file('/path/to/python'),
207+
sysPrefix: '/path/to/env',
208+
execInfo: {
209+
run: { executable: '/path/to/python' },
210+
activation: [{ executable: '/path/to/activate' }],
211+
},
212+
});
213+
214+
setup(() => {
215+
terminalActivation = new TestTerminalActivation();
216+
217+
mockTerminal = {
218+
name: 'Test Terminal',
219+
creationOptions: {} as TerminalOptions,
220+
shellIntegration: undefined,
221+
show: sinon.stub(),
222+
sendText: sinon.stub(),
223+
};
224+
225+
mockGetAutoActivationType = sinon.stub(terminalUtils, 'getAutoActivationType');
226+
sinon.stub(terminalUtils, 'waitForShellIntegration').resolves(false);
227+
sinon.stub(activationUtils, 'isActivatableEnvironment').returns(true);
228+
sinon.stub(shellDetector, 'identifyTerminalShell').returns('bash');
229+
230+
createTerminalStub = sinon.stub(windowApis, 'createTerminal').returns(mockTerminal as Terminal);
231+
sinon.stub(windowApis, 'onDidOpenTerminal').returns(new Disposable(() => {}));
232+
sinon.stub(windowApis, 'onDidCloseTerminal').returns(new Disposable(() => {}));
233+
sinon.stub(windowApis, 'onDidChangeWindowState').returns(new Disposable(() => {}));
234+
sinon.stub(windowApis, 'terminals').returns([]);
235+
sinon.stub(windowApis, 'withProgress').callsFake(async (_options, task) => {
236+
const mockProgress: Progress<{ message?: string; increment?: number }> = { report: () => {} };
237+
const mockCancellationToken = {
238+
isCancellationRequested: false,
239+
onCancellationRequested: () => new Disposable(() => {}),
240+
};
241+
return task(mockProgress, mockCancellationToken as never);
242+
});
243+
244+
sinon.stub(workspaceApis, 'onDidChangeConfiguration').returns(new Disposable(() => {}));
245+
});
246+
247+
teardown(() => {
248+
sinon.restore();
249+
terminalActivation.dispose();
250+
});
251+
252+
function createTerminalManager(): TerminalManagerImpl {
253+
const emptyEnvProviders: ShellEnvsProvider[] = [];
254+
const emptyScriptProviders: ShellStartupScriptProvider[] = [];
255+
return new TerminalManagerImpl(terminalActivation, emptyEnvProviders, emptyScriptProviders);
256+
}
257+
258+
test('getDedicatedTerminal sets Python file name as terminal name', async () => {
259+
mockGetAutoActivationType.returns(terminalUtils.ACT_TYPE_OFF);
260+
terminalManager = createTerminalManager();
261+
const env = createMockEnvironment();
262+
263+
const optionsList: TerminalOptions[] = [];
264+
createTerminalStub.callsFake((options) => {
265+
optionsList.push(options);
266+
return mockTerminal as Terminal;
267+
});
268+
269+
const tempRoot = await fsapi.mkdtemp(path.join(os.tmpdir(), 'py-envs-'));
270+
const projectPath = path.join(tempRoot, 'project');
271+
const filePath = path.join(projectPath, 'main.py');
272+
await fsapi.ensureDir(projectPath);
273+
await fsapi.writeFile(filePath, 'print("hello")');
274+
const projectUri = Uri.file(projectPath);
275+
const fileUri = Uri.file(filePath);
276+
277+
const config = { get: sinon.stub().returns(false) } as unknown as WorkspaceConfiguration;
278+
sinon.stub(workspaceApis, 'getConfiguration').returns(config);
279+
280+
try {
281+
await terminalManager.getDedicatedTerminal(fileUri, projectUri, env);
282+
283+
assert.strictEqual(
284+
optionsList[0]?.name,
285+
'Python: main',
286+
'Dedicated terminal should use the file name in the title',
287+
);
288+
} finally {
289+
await fsapi.remove(tempRoot);
290+
}
291+
});
292+
293+
test('getProjectTerminal sets Python as terminal name', async () => {
294+
mockGetAutoActivationType.returns(terminalUtils.ACT_TYPE_OFF);
295+
terminalManager = createTerminalManager();
296+
const env = createMockEnvironment();
297+
298+
const optionsList: TerminalOptions[] = [];
299+
createTerminalStub.callsFake((options) => {
300+
optionsList.push(options);
301+
return mockTerminal as Terminal;
302+
});
303+
304+
const tempRoot = await fsapi.mkdtemp(path.join(os.tmpdir(), 'py-envs-'));
305+
const projectPath = path.join(tempRoot, 'project');
306+
await fsapi.ensureDir(projectPath);
307+
const projectUri = Uri.file(projectPath);
308+
309+
try {
310+
await terminalManager.getProjectTerminal(projectUri, env);
311+
312+
assert.strictEqual(
313+
optionsList[0]?.name,
314+
'Python',
315+
'Project terminal should use the Python title',
316+
);
317+
} finally {
318+
await fsapi.remove(tempRoot);
319+
}
320+
});
321+
});

0 commit comments

Comments
 (0)