Skip to content

Commit f414250

Browse files
djpltwesm
authored andcommitted
Make the cursor focus switch automatically to the terminal after laun… (microsoft/vscode-python#17884)
* Make the cursor focus switch automatically to the terminal after launching a python process with configurability (microsoft/vscode-python#14851) * Add focus on terminal to code execution manager
1 parent 7cce815 commit f414250

File tree

9 files changed

+73
-19
lines changed

9 files changed

+73
-19
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make cursor focus switch automatically to the terminal after launching a python process with configuration option. (Thanks [djplt](https://github.com/djplt))

extensions/positron-python/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,12 @@
945945
"scope": "resource",
946946
"type": "boolean"
947947
},
948+
"python.terminal.focusAfterLaunch": {
949+
"default": false,
950+
"description": "When launching a python process, whether to focus on the terminal.",
951+
"scope": "resource",
952+
"type": "boolean"
953+
},
948954
"python.terminal.launchArgs": {
949955
"default": [],
950956
"description": "Python launch arguments to use when executing a file in the terminal.",

extensions/positron-python/src/client/common/application/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface ICommandNameWithoutArgumentTypeMapping {
2727
['workbench.action.debug.stop']: [];
2828
['workbench.action.reloadWindow']: [];
2929
['workbench.action.closeActiveEditor']: [];
30+
['workbench.action.terminal.focus']: [];
3031
['editor.action.formatDocument']: [];
3132
['editor.action.rename']: [];
3233
[Commands.ViewOutput]: [];

extensions/positron-python/src/client/common/configSettings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ export class PythonSettings implements IPythonSettings {
465465
? this.terminal
466466
: {
467467
executeInFileDir: true,
468+
focusAfterLaunch: false,
468469
launchArgs: [],
469470
activateEnvironment: true,
470471
activateEnvInCurrentTerminal: false,

extensions/positron-python/src/client/common/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ export interface IFormattingSettings {
276276

277277
export interface ITerminalSettings {
278278
readonly executeInFileDir: boolean;
279+
readonly focusAfterLaunch: boolean;
279280
readonly launchArgs: string[];
280281
readonly activateEnvironment: boolean;
281282
readonly activateEnvInCurrentTerminal: boolean;

extensions/positron-python/src/client/debugger/extension/adapter/activator.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
// Licensed under the MIT License.
33

44
'use strict';
5-
5+
import { Uri } from 'vscode';
66
import { inject, injectable } from 'inversify';
77
import { IExtensionSingleActivationService } from '../../../activation/types';
88
import { IDebugService } from '../../../common/application/types';
9-
import { IDisposableRegistry } from '../../../common/types';
9+
import { IConfigurationService, IDisposableRegistry } from '../../../common/types';
10+
import { ICommandManager } from '../../../common/application/types';
1011
import { DebuggerTypeName } from '../../constants';
1112
import { IAttachProcessProviderFactory } from '../attachQuickPick/types';
1213
import { IDebugAdapterDescriptorFactory, IDebugSessionLoggingFactory, IOutdatedDebuggerPromptFactory } from '../types';
@@ -16,6 +17,8 @@ export class DebugAdapterActivator implements IExtensionSingleActivationService
1617
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
1718
constructor(
1819
@inject(IDebugService) private readonly debugService: IDebugService,
20+
@inject(IConfigurationService) private readonly configSettings: IConfigurationService,
21+
@inject(ICommandManager) private commandManager: ICommandManager,
1922
@inject(IDebugAdapterDescriptorFactory) private descriptorFactory: IDebugAdapterDescriptorFactory,
2023
@inject(IDebugSessionLoggingFactory) private debugSessionLoggingFactory: IDebugSessionLoggingFactory,
2124
@inject(IOutdatedDebuggerPromptFactory) private debuggerPromptFactory: IOutdatedDebuggerPromptFactory,
@@ -32,8 +35,19 @@ export class DebugAdapterActivator implements IExtensionSingleActivationService
3235
this.disposables.push(
3336
this.debugService.registerDebugAdapterTrackerFactory(DebuggerTypeName, this.debuggerPromptFactory),
3437
);
38+
3539
this.disposables.push(
3640
this.debugService.registerDebugAdapterDescriptorFactory(DebuggerTypeName, this.descriptorFactory),
3741
);
42+
this.disposables.push(
43+
this.debugService.onDidStartDebugSession((debugSession) => {
44+
if (this.shouldTerminalFocusOnStart(debugSession.workspaceFolder?.uri))
45+
this.commandManager.executeCommand('workbench.action.terminal.focus');
46+
}),
47+
);
48+
}
49+
50+
private shouldTerminalFocusOnStart(uri: Uri | undefined): boolean {
51+
return this.configSettings.getSettings(uri)?.terminal.focusAfterLaunch;
3852
}
3953
}

extensions/positron-python/src/client/terminals/codeExecution/codeExecutionManager.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ICommandManager, IDocumentManager } from '../../common/application/type
1010
import { Commands } from '../../common/constants';
1111
import '../../common/extensions';
1212
import { IFileSystem } from '../../common/platform/types';
13-
import { IDisposableRegistry, Resource } from '../../common/types';
13+
import { IDisposableRegistry, IConfigurationService, Resource } from '../../common/types';
1414
import { noop } from '../../common/utils/misc';
1515
import { IServiceContainer } from '../../ioc/types';
1616
import { traceError } from '../../logging';
@@ -26,6 +26,7 @@ export class CodeExecutionManager implements ICodeExecutionManager {
2626
@inject(IDocumentManager) private documentManager: IDocumentManager,
2727
@inject(IDisposableRegistry) private disposableRegistry: Disposable[],
2828
@inject(IFileSystem) private fileSystem: IFileSystem,
29+
@inject(IConfigurationService) private readonly configSettings: IConfigurationService,
2930
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
3031
) {}
3132

@@ -38,22 +39,32 @@ export class CodeExecutionManager implements ICodeExecutionManager {
3839
this.disposableRegistry.push(
3940
this.commandManager.registerCommand(cmd as any, async (file: Resource) => {
4041
const trigger = cmd === Commands.Exec_In_Terminal ? 'command' : 'icon';
41-
await this.executeFileInTerminal(file, trigger).catch((ex) =>
42-
traceError('Failed to execute file in terminal', ex),
43-
);
42+
await this.executeFileInTerminal(file, trigger)
43+
.then(() => {
44+
if (this.shouldTerminalFocusOnStart(file))
45+
this.commandManager.executeCommand('workbench.action.terminal.focus');
46+
})
47+
.catch((ex) => traceError('Failed to execute file in terminal', ex));
4448
}),
4549
);
4650
});
4751
this.disposableRegistry.push(
48-
this.commandManager.registerCommand(
49-
Commands.Exec_Selection_In_Terminal,
50-
this.executeSelectionInTerminal.bind(this),
51-
),
52+
this.commandManager.registerCommand(Commands.Exec_Selection_In_Terminal as any, async (file: Resource) => {
53+
await this.executeSelectionInTerminal().then(() => {
54+
if (this.shouldTerminalFocusOnStart(file))
55+
this.commandManager.executeCommand('workbench.action.terminal.focus');
56+
});
57+
}),
5258
);
5359
this.disposableRegistry.push(
5460
this.commandManager.registerCommand(
55-
Commands.Exec_Selection_In_Django_Shell,
56-
this.executeSelectionInDjangoShell.bind(this),
61+
Commands.Exec_Selection_In_Django_Shell as any,
62+
async (file: Resource) => {
63+
await this.executeSelectionInDjangoShell().then(() => {
64+
if (this.shouldTerminalFocusOnStart(file))
65+
this.commandManager.executeCommand('workbench.action.terminal.focus');
66+
});
67+
},
5768
),
5869
);
5970
}
@@ -115,4 +126,8 @@ export class CodeExecutionManager implements ICodeExecutionManager {
115126

116127
await executionService.execute(normalizedCode, activeEditor!.document.uri);
117128
}
129+
130+
private shouldTerminalFocusOnStart(uri: Uri | undefined): boolean {
131+
return this.configSettings.getSettings(uri)?.terminal.focusAfterLaunch;
132+
}
118133
}

extensions/positron-python/src/test/debugger/extension/adapter/activator.unit.test.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
import * as assert from 'assert';
77
import { anything, instance, mock, verify, when } from 'ts-mockito';
88
import { IExtensionSingleActivationService } from '../../../../client/activation/types';
9+
import { CommandManager } from '../../../../client/common/application/commandManager';
910
import { DebugService } from '../../../../client/common/application/debugService';
10-
import { IDebugService } from '../../../../client/common/application/types';
11+
import { ICommandManager, IDebugService } from '../../../../client/common/application/types';
1112
import { ConfigurationService } from '../../../../client/common/configuration/service';
12-
import { IDisposableRegistry, IPythonSettings } from '../../../../client/common/types';
13+
import { IConfigurationService, IDisposableRegistry, IPythonSettings } from '../../../../client/common/types';
1314
import { DebugAdapterActivator } from '../../../../client/debugger/extension/adapter/activator';
1415
import { DebugAdapterDescriptorFactory } from '../../../../client/debugger/extension/adapter/factory';
1516
import { DebugSessionLoggingFactory } from '../../../../client/debugger/extension/adapter/logging';
@@ -27,27 +28,36 @@ import { noop } from '../../../core';
2728
suite('Debugging - Adapter Factory and logger Registration', () => {
2829
let activator: IExtensionSingleActivationService;
2930
let debugService: IDebugService;
31+
let commandManager: ICommandManager;
3032
let descriptorFactory: IDebugAdapterDescriptorFactory;
3133
let loggingFactory: IDebugSessionLoggingFactory;
3234
let debuggerPromptFactory: IOutdatedDebuggerPromptFactory;
3335
let disposableRegistry: IDisposableRegistry;
3436
let attachFactory: IAttachProcessProviderFactory;
37+
let configService: IConfigurationService;
3538

3639
setup(() => {
37-
const configurationService = mock(ConfigurationService);
40+
attachFactory = mock(AttachProcessProviderFactory);
41+
42+
debugService = mock(DebugService);
43+
when(debugService.onDidStartDebugSession).thenReturn(() => noop as any);
44+
45+
commandManager = mock(CommandManager);
3846

39-
when(configurationService.getSettings(undefined)).thenReturn(({
47+
configService = mock(ConfigurationService);
48+
when(configService.getSettings(undefined)).thenReturn(({
4049
experiments: { enabled: true },
4150
} as any) as IPythonSettings);
42-
attachFactory = mock(AttachProcessProviderFactory);
4351

44-
debugService = mock(DebugService);
4552
descriptorFactory = mock(DebugAdapterDescriptorFactory);
4653
loggingFactory = mock(DebugSessionLoggingFactory);
4754
debuggerPromptFactory = mock(OutdatedDebuggerPromptFactory);
4855
disposableRegistry = [];
56+
4957
activator = new DebugAdapterActivator(
5058
instance(debugService),
59+
instance(configService),
60+
instance(commandManager),
5161
instance(descriptorFactory),
5262
instance(loggingFactory),
5363
instance(debuggerPromptFactory),
@@ -72,9 +82,10 @@ suite('Debugging - Adapter Factory and logger Registration', () => {
7282
const disposable = { dispose: noop };
7383
when(debugService.registerDebugAdapterTrackerFactory(anything(), anything())).thenReturn(disposable);
7484
when(debugService.registerDebugAdapterDescriptorFactory(anything(), anything())).thenReturn(disposable);
85+
when(debugService.onDidStartDebugSession).thenReturn(() => disposable);
7586

7687
await activator.activate();
7788

78-
assert.deepEqual(disposableRegistry, [disposable, disposable, disposable]);
89+
assert.deepEqual(disposableRegistry, [disposable, disposable, disposable, disposable]);
7990
});
8091
});

extensions/positron-python/src/test/terminals/codeExecution/codeExecutionManager.unit.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { IFileSystem } from '../../../client/common/platform/types';
1010
import { IServiceContainer } from '../../../client/ioc/types';
1111
import { CodeExecutionManager } from '../../../client/terminals/codeExecution/codeExecutionManager';
1212
import { ICodeExecutionHelper, ICodeExecutionManager, ICodeExecutionService } from '../../../client/terminals/types';
13+
import { IConfigurationService } from '../../../client/common/types';
1314

1415
suite('Terminal - Code Execution Manager', () => {
1516
let executionManager: ICodeExecutionManager;
@@ -18,6 +19,7 @@ suite('Terminal - Code Execution Manager', () => {
1819
let disposables: Disposable[] = [];
1920
let serviceContainer: TypeMoq.IMock<IServiceContainer>;
2021
let documentManager: TypeMoq.IMock<IDocumentManager>;
22+
let configService: TypeMoq.IMock<IConfigurationService>;
2123
let fileSystem: TypeMoq.IMock<IFileSystem>;
2224
setup(() => {
2325
fileSystem = TypeMoq.Mock.ofType<IFileSystem>();
@@ -33,11 +35,13 @@ suite('Terminal - Code Execution Manager', () => {
3335
documentManager = TypeMoq.Mock.ofType<IDocumentManager>();
3436
commandManager = TypeMoq.Mock.ofType<ICommandManager>(undefined, TypeMoq.MockBehavior.Strict);
3537
serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
38+
configService = TypeMoq.Mock.ofType<IConfigurationService>();
3639
executionManager = new CodeExecutionManager(
3740
commandManager.object,
3841
documentManager.object,
3942
disposables,
4043
fileSystem.object,
44+
configService.object,
4145
serviceContainer.object,
4246
);
4347
});

0 commit comments

Comments
 (0)