Skip to content

Commit dc0150c

Browse files
committed
debug: make auto attach apply state transition in new workspaces
Previous there was a case: 1. Auto attach was enabled in user settings and workspace A was open 2. Switch to workspace B, and then turn auto attach off in user settings 3. Switching back to workspace A, environment variables were not cleared Now, the last state is stored in the workspace settings so that we can tear down the previous state if necessary.
1 parent b0c6e84 commit dc0150c

2 files changed

Lines changed: 58 additions & 23 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Extension",
9+
"type": "extensionHost",
10+
"request": "launch",
11+
"skipFiles": ["<node_internals>/**"],
12+
"args": [
13+
"--extensionDevelopmentPath=${workspaceFolder}",
14+
],
15+
"outFiles": [
16+
"${workspaceFolder}/out/**/*.js",
17+
],
18+
}
19+
]
20+
}

extensions/debug-auto-launch/src/extension.ts

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const JS_DEBUG_USEPREVIEWAA = 'usePreviewAutoAttach';
1717
const JS_DEBUG_IPC_KEY = 'jsDebugIpcState';
1818
const NODE_DEBUG_SETTINGS = 'debug.node';
1919
const AUTO_ATTACH_SETTING = 'autoAttach';
20+
const LAST_STATE_STORAGE_KEY = 'lastState';
2021

2122
type AUTO_ATTACH_VALUES = 'disabled' | 'on' | 'off';
2223

@@ -28,10 +29,14 @@ const enum State {
2829
}
2930

3031
// on activation this feature is always disabled...
31-
let currentState = Promise.resolve({ state: State.Disabled, transitionData: null as unknown });
32+
let currentState: Promise<{ context: vscode.ExtensionContext, state: State; transitionData: unknown }>;
3233
let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item
3334

3435
export function activate(context: vscode.ExtensionContext): void {
36+
const previousState = context.workspaceState.get<State>(LAST_STATE_STORAGE_KEY, State.Disabled);
37+
currentState = Promise.resolve(transitions[previousState].onActivate?.(context, readCurrentState()))
38+
.then(() => ({ context, state: State.Disabled, transitionData: null }));
39+
3540
context.subscriptions.push(vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting));
3641

3742
// settings that can result in the "state" being changed--on/off/disable or useV3 toggles
@@ -43,17 +48,17 @@ export function activate(context: vscode.ExtensionContext): void {
4348
context.subscriptions.push(
4449
vscode.workspace.onDidChangeConfiguration((e) => {
4550
if (effectualConfigurationSettings.some(setting => e.affectsConfiguration(setting))) {
46-
updateAutoAttach(context);
51+
updateAutoAttach();
4752
}
4853
})
4954
);
5055

51-
updateAutoAttach(context);
56+
updateAutoAttach();
5257
}
5358

5459
export async function deactivate(): Promise<void> {
55-
const { state, transitionData } = await currentState;
56-
await transitions[state].exit?.(transitionData);
60+
const { context, state, transitionData } = await currentState;
61+
await transitions[state].exit?.(context, transitionData);
5762
}
5863

5964
function toggleAutoAttachSetting() {
@@ -136,30 +141,33 @@ interface CachedIpcState {
136141
}
137142

138143
interface StateTransition<StateData> {
139-
exit?(stateData: StateData): Promise<void> | void;
144+
onActivate?(context: vscode.ExtensionContext, currentState: State): Promise<void>;
145+
exit?(context: vscode.ExtensionContext, stateData: StateData): Promise<void> | void;
140146
enter?(context: vscode.ExtensionContext): Promise<StateData> | StateData;
141147
}
142148

149+
const makeTransition = <T>(tsn: StateTransition<T>) => tsn; // helper to apply generic type
150+
143151
/**
144152
* Map of logic that happens when auto attach states are entered and exited.
145153
* All state transitions are queued and run in order; promises are awaited.
146154
*/
147155
const transitions: { [S in State]: StateTransition<unknown> } = {
148-
[State.Disabled]: {
156+
[State.Disabled]: makeTransition({
149157
async enter(context) {
150158
statusItem?.hide();
151159
await clearJsDebugAttachState(context);
152160
},
153-
},
161+
}),
154162

155-
[State.Off]: {
163+
[State.Off]: makeTransition({
156164
enter(context) {
157165
const statusItem = ensureStatusBarExists(context);
158166
statusItem.text = OFF_TEXT;
159167
},
160-
},
168+
}),
161169

162-
[State.OnWithNodeDebug]: {
170+
[State.OnWithNodeDebug]: makeTransition({
163171
async enter(context) {
164172
const statusItem = ensureStatusBarExists(context);
165173
const vscode_pid = process.env['VSCODE_PID'];
@@ -171,16 +179,16 @@ const transitions: { [S in State]: StateTransition<unknown> } = {
171179
async exit() {
172180
await vscode.commands.executeCommand('extension.node-debug.stopAutoAttach');
173181
},
174-
},
182+
}),
175183

176-
[State.OnWithJsDebug]: {
184+
[State.OnWithJsDebug]: makeTransition<Server | null>({
177185
async enter(context) {
178186
const ipcAddress = await getIpcAddress(context);
179187
if (!ipcAddress) {
180-
return { context };
188+
return null;
181189
}
182190

183-
const server = await new Promise((resolve, reject) => {
191+
const server = await new Promise<Server>((resolve, reject) => {
184192
const s = createServer((socket) => {
185193
let data: Buffer[] = [];
186194
socket.on('data', (chunk) => data.push(chunk));
@@ -201,10 +209,10 @@ const transitions: { [S in State]: StateTransition<unknown> } = {
201209

202210
const statusItem = ensureStatusBarExists(context);
203211
statusItem.text = ON_TEXT;
204-
return { server, context };
212+
return server || null;
205213
},
206214

207-
async exit({ server, context }: { server?: Server, context: vscode.ExtensionContext }) {
215+
async exit(context, server) {
208216
// we don't need to clear the environment variables--the bootloader will
209217
// no-op if the debug server is closed. This prevents having to reload
210218
// terminals if users want to turn it back on.
@@ -217,24 +225,31 @@ const transitions: { [S in State]: StateTransition<unknown> } = {
217225
await clearJsDebugAttachState(context);
218226
}
219227
},
220-
},
228+
229+
async onActivate(context, currentState) {
230+
if (currentState === State.OnWithNodeDebug || currentState === State.Disabled) {
231+
await clearJsDebugAttachState(context);
232+
}
233+
}
234+
}),
221235
};
222236

223237
/**
224238
* Updates the auto attach feature based on the user or workspace setting
225239
*/
226-
function updateAutoAttach(context: vscode.ExtensionContext) {
240+
function updateAutoAttach() {
227241
const newState = readCurrentState();
228242

229-
currentState = currentState.then(async ({ state: oldState, transitionData }) => {
243+
currentState = currentState.then(async ({ context, state: oldState, transitionData }) => {
230244
if (newState === oldState) {
231-
return { state: oldState, transitionData };
245+
return { context, state: oldState, transitionData };
232246
}
233247

234-
await transitions[oldState].exit?.(transitionData);
248+
await transitions[oldState].exit?.(context, transitionData);
235249
const newData = await transitions[newState].enter?.(context);
250+
await context.workspaceState.update(LAST_STATE_STORAGE_KEY, newState);
236251

237-
return { state: newState, transitionData: newData };
252+
return { context, state: newState, transitionData: newData };
238253
});
239254
}
240255

0 commit comments

Comments
 (0)