Skip to content

Commit f8a84f1

Browse files
authored
Merge pull request #715 from CodinGame/lmn/feat-allow-forcing-task-reattach-and-startup-kind
Feat allow forcing task reattach and startup kind
2 parents 2eb3a5a + df4c40b commit f8a84f1

4 files changed

Lines changed: 157 additions & 28 deletions

File tree

src/missing-services.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,6 +1648,7 @@ class TaskService implements ITaskService {
16481648
@Unsupported
16491649
extensionCallbackTaskComplete: ITaskService['extensionCallbackTaskComplete'] = unsupported
16501650
isReconnected: ITaskService['isReconnected'] = false
1651+
shouldReattach: ITaskService['shouldReattach'] = false
16511652
onDidReconnectToTasks: ITaskService['onDidReconnectToTasks'] = Event.None
16521653

16531654
getTerminalsForTasks: ITaskService['getTerminalsForTasks'] = () => undefined

src/service-override/lifecycle.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,48 @@
11
import { type IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices'
22
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'
3+
import { ILogService } from 'vs/platform/log/common/log.service'
4+
import { IStorageService } from 'vs/platform/storage/common/storage.service'
35
import { BrowserLifecycleService } from 'vs/workbench/services/lifecycle/browser/lifecycleService'
6+
import { StartupKind } from 'vs/workbench/services/lifecycle/common/lifecycle'
47
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle.service'
58
import { TimerService } from 'vs/workbench/services/timer/browser/timerService'
69
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService.service'
710

8-
export default function getServiceOverride(): IEditorOverrideServices {
11+
interface StartupKindResolver {
12+
(resolveDefault: () => StartupKind | undefined): StartupKind | undefined
13+
}
14+
export interface LifecycleServiceParams {
15+
/**
16+
* Allows to force the startup kind
17+
*/
18+
resolveStartupKind?: StartupKindResolver
19+
}
20+
21+
const DEFAULT_RESOLVE_STARTUP_KIND: StartupKindResolver = (resolveDefault) => resolveDefault()
22+
23+
class BrowserLifecycleServiceOverride extends BrowserLifecycleService {
24+
constructor(
25+
private params: LifecycleServiceParams,
26+
@ILogService logService: ILogService,
27+
@IStorageService storageService: IStorageService
28+
) {
29+
super(logService, storageService)
30+
}
31+
32+
protected override doResolveStartupKind(): StartupKind | undefined {
33+
return (this.params.resolveStartupKind ?? DEFAULT_RESOLVE_STARTUP_KIND)(() =>
34+
super.doResolveStartupKind()
35+
)
36+
}
37+
}
38+
39+
export default function getServiceOverride(
40+
options: LifecycleServiceParams = {}
41+
): IEditorOverrideServices {
942
return {
10-
[ILifecycleService.toString()]: new SyncDescriptor(BrowserLifecycleService),
43+
[ILifecycleService.toString()]: new SyncDescriptor(BrowserLifecycleServiceOverride, [options]),
1144
[ITimerService.toString()]: new SyncDescriptor(TimerService)
1245
}
1346
}
47+
48+
export { StartupKind }

src/service-override/task.ts

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,28 @@ import {
4646
import { IViewDescriptorService } from 'vs/workbench/common/views.service'
4747
import 'vs/workbench/contrib/tasks/browser/task.contribution'
4848

49+
export interface ForcedSupportedExecutions {
50+
custom?: boolean
51+
shell?: boolean
52+
process?: boolean
53+
}
54+
export interface TaskServiceOverrideParams {
55+
/**
56+
* By default, only custom tasks are supported
57+
* When using the VSCode server, the server registers all the task types as supported
58+
* But if the user has registered custom terminal backends, they may want to force support for other task types
59+
*/
60+
forcedSupportedExecutions?: ForcedSupportedExecutions
61+
62+
/**
63+
* Force task reattach, no matter the lifecycle startup kind
64+
*/
65+
shouldReattach?: boolean
66+
}
67+
4968
class CustomTaskService extends TaskService {
5069
constructor(
51-
forcedSupportedExecutions: ForcedSupportedExecutions | undefined,
70+
private params: TaskServiceOverrideParams,
5271
@IConfigurationService _configurationService: IConfigurationService,
5372
@IMarkerService _markerService: IMarkerService,
5473
@IOutputService _outputService: IOutputService,
@@ -131,38 +150,24 @@ class CustomTaskService extends TaskService {
131150
_chatAgentService
132151
)
133152

134-
if (forcedSupportedExecutions != null) {
153+
if (params.forcedSupportedExecutions != null) {
135154
this.registerSupportedExecutions(
136-
forcedSupportedExecutions.custom,
137-
forcedSupportedExecutions.shell,
138-
forcedSupportedExecutions.process
155+
params.forcedSupportedExecutions.custom,
156+
params.forcedSupportedExecutions.shell,
157+
params.forcedSupportedExecutions.process
139158
)
140159
}
141160
}
142-
}
143161

144-
export interface ForcedSupportedExecutions {
145-
custom?: boolean
146-
shell?: boolean
147-
process?: boolean
148-
}
149-
export interface TaskServiceOverrideParams {
150-
/**
151-
* By default, only custom tasks are supported
152-
* When using the VSCode server, the server registers all the task types as supported
153-
* But if the user has registered custom terminal backends, they may want to force support for other task types
154-
*/
155-
forcedSupportedExecutions?: ForcedSupportedExecutions
162+
override get shouldReattach(): boolean {
163+
return this.params.shouldReattach ?? super.shouldReattach
164+
}
156165
}
157166

158-
export default function getServiceOverride({
159-
forcedSupportedExecutions
160-
}: TaskServiceOverrideParams = {}): IEditorOverrideServices {
167+
export default function getServiceOverride(
168+
params: TaskServiceOverrideParams = {}
169+
): IEditorOverrideServices {
161170
return {
162-
[ITaskService.toString()]: new SyncDescriptor(
163-
CustomTaskService,
164-
[forcedSupportedExecutions],
165-
true
166-
)
171+
[ITaskService.toString()]: new SyncDescriptor(CustomTaskService, [params], true)
167172
}
168173
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= <loic@coderpad.io>
3+
Date: Wed, 8 Oct 2025 11:43:06 +0200
4+
Subject: [PATCH] refactor: extract shouldReattach flag
5+
6+
---
7+
.../contrib/tasks/browser/abstractTaskService.ts | 4 +++-
8+
src/vs/workbench/contrib/tasks/common/taskService.ts | 1 +
9+
.../contrib/terminal/browser/terminalService.ts | 10 ++++++----
10+
3 files changed, 10 insertions(+), 5 deletions(-)
11+
12+
diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts
13+
index 782478964be..e119365617b 100644
14+
--- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts
15+
+++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts
16+
@@ -437,8 +437,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
17+
}
18+
}
19+
20+
+ get shouldReattach(): boolean { return this._lifecycleService.startupKind === StartupKind.ReloadedWindow; }
21+
+
22+
private _attemptTaskReconnection(): void {
23+
- if (this._lifecycleService.startupKind !== StartupKind.ReloadedWindow) {
24+
+ if (!this.shouldReattach) {
25+
this._log(nls.localize('TaskService.skippingReconnection', 'Startup kind not window reload, setting connected and removing persistent tasks'), true);
26+
this._tasksReconnected = true;
27+
this._storageService.remove(AbstractTaskService.PersistentTasks_Key, StorageScope.WORKSPACE);
28+
diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts
29+
index 5cabde32dee..9891e93bf3b 100644
30+
--- a/src/vs/workbench/contrib/tasks/common/taskService.ts
31+
+++ b/src/vs/workbench/contrib/tasks/common/taskService.ts
32+
@@ -70,6 +70,7 @@ export interface ITaskService {
33+
/** Fired when task providers are registered or unregistered */
34+
onDidChangeTaskProviders: Event<void>;
35+
isReconnected: boolean;
36+
+ shouldReattach: boolean;
37+
onDidReconnectToTasks: Event<void>;
38+
supportsMultipleTaskExecutions: boolean;
39+
40+
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts
41+
index f927f4a6830..1ab41d04beb 100644
42+
--- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts
43+
+++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts
44+
@@ -45,7 +45,7 @@ import { IEditorGroupsService } from '../../../services/editor/common/editorGrou
45+
import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, AUX_WINDOW_GROUP, AUX_WINDOW_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from '../../../services/editor/common/editorService.js';
46+
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
47+
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
48+
-import { ILifecycleService, ShutdownReason, StartupKind, WillShutdownEvent } from '../../../services/lifecycle/common/lifecycle.js';
49+
+import { ILifecycleService, ShutdownReason, WillShutdownEvent } from '../../../services/lifecycle/common/lifecycle.js';
50+
import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';
51+
import { XtermTerminal } from './xterm/xtermTerminal.js';
52+
import { TerminalInstance } from './terminalInstance.js';
53+
@@ -60,6 +60,7 @@ import { isAuxiliaryWindow, mainWindow } from '../../../../base/browser/window.j
54+
import { GroupIdentifier } from '../../../common/editor.js';
55+
import { getActiveWindow } from '../../../../base/browser/dom.js';
56+
import { Registry } from '../../../../platform/registry/common/platform.js';
57+
+import { ITaskService } from '../../tasks/common/taskService.js';
58+
59+
export class TerminalService extends Disposable implements ITerminalService {
60+
declare _serviceBrand: undefined;
61+
@@ -171,7 +172,7 @@ export class TerminalService extends Disposable implements ITerminalService {
62+
@memoize get onAnyInstanceAddedCapabilityType() { return this._register(this.createOnInstanceEvent(e => e.capabilities.onDidAddCapabilityType)).event; }
63+
constructor(
64+
@IContextKeyService private _contextKeyService: IContextKeyService,
65+
- @ILifecycleService private readonly _lifecycleService: ILifecycleService,
66+
+ @ILifecycleService _lifecycleService: ILifecycleService,
67+
@ITerminalLogService private readonly _logService: ITerminalLogService,
68+
@IDialogService private _dialogService: IDialogService,
69+
@IInstantiationService private _instantiationService: IInstantiationService,
70+
@@ -191,7 +192,8 @@ export class TerminalService extends Disposable implements ITerminalService {
71+
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
72+
@ICommandService private readonly _commandService: ICommandService,
73+
@IKeybindingService private readonly _keybindingService: IKeybindingService,
74+
- @ITimerService private readonly _timerService: ITimerService
75+
+ @ITimerService private readonly _timerService: ITimerService,
76+
+ @ITaskService private readonly _taskService: ITaskService,
77+
) {
78+
super();
79+
80+
@@ -525,7 +527,7 @@ export class TerminalService extends Disposable implements ITerminalService {
81+
let lastInstance: Promise<ITerminalInstance> | undefined;
82+
for (const terminalLayout of terminalLayouts) {
83+
const attachPersistentProcess = terminalLayout.terminal!;
84+
- if (this._lifecycleService.startupKind !== StartupKind.ReloadedWindow && attachPersistentProcess.type === 'Task') {
85+
+ if (!this._taskService.shouldReattach && attachPersistentProcess.type === 'Task') {
86+
continue;
87+
}
88+
mark(`code/terminal/willRecreateTerminal/${attachPersistentProcess.id}-${attachPersistentProcess.pid}`);

0 commit comments

Comments
 (0)