debug: use only js-debug auto attach, collapse settings
This PR removes the hook in node-debug's auto attach, and uses only js-debug auto attach. As referenced in the linked issues, this involves removing `debug.javascript.usePreviewAutoAttach` and collapsing `debug.node.autoAttach` into `debug.javascript.autoAttachFilter`. The latter option gains a new state: `disabled`. Since there's no runtime cost to having auto attach around, there is now no distinct off versus disabled state. The status bar item and the `Debug: Toggle Auto Attach` command now open a quickpick, which looks like this: ![](https://memes.peet.io/img/20-09-9d2b6c0a-8b3f-4481-b2df-0753c54ee02b.png) The current setting value is selected in the quickpick. If there is a workspace setting for auto attach, the quickpick toggle the setting there by default. Otherwise (as in the image) it will target the user settings. The targeting is more explicit and defaults to the user instead of the workspace, which should help reduce confusion (#97087). Selecting the "scope change" item will reopen the quickpick in that location. Aside from the extra options for the `disabled` state in js-debug's contributions, there's no changes required to it or its interaction with debug-auto-launch. Side note: I really wanted a separator between the states and the scope change item, but this is not possible from an extension #74967. Fixes https://github.com/microsoft/vscode/issues/105883 Fixes https://github.com/microsoft/vscode-js-debug/issues/732 (the rest of it) Fixes https://github.com/microsoft/vscode/issues/105963 Fixes https://github.com/microsoft/vscode/issues/97087
This commit is contained in:
parent
122fc9a1b8
commit
cda3fbe7f8
|
@ -17,33 +17,6 @@
|
||||||
"watch": "gulp watch-extension:debug-auto-launch"
|
"watch": "gulp watch-extension:debug-auto-launch"
|
||||||
},
|
},
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"configuration": {
|
|
||||||
"title": "Node debug",
|
|
||||||
"properties": {
|
|
||||||
"debug.node.autoAttach": {
|
|
||||||
"scope": "window",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"disabled",
|
|
||||||
"on",
|
|
||||||
"off"
|
|
||||||
],
|
|
||||||
"enumDescriptions": [
|
|
||||||
"%debug.node.autoAttach.disabled.description%",
|
|
||||||
"%debug.node.autoAttach.on.description%",
|
|
||||||
"%debug.node.autoAttach.off.description%"
|
|
||||||
],
|
|
||||||
"description": "%debug.node.autoAttach.description%",
|
|
||||||
"default": "disabled"
|
|
||||||
},
|
|
||||||
"debug.javascript.usePreviewAutoAttach": {
|
|
||||||
"scope": "window",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true,
|
|
||||||
"description": "%debug.javascript.usePreviewAutoAttach%"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "extension.node-debug.toggleAutoAttach",
|
"command": "extension.node-debug.toggleAutoAttach",
|
||||||
|
@ -57,5 +30,11 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^12.11.7"
|
"@types/node": "^12.11.7"
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"singleQuote": true,
|
||||||
|
"arrowParens": "avoid"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"displayName": "Node Debug Auto-attach",
|
"displayName": "Node Debug Auto-attach",
|
||||||
"description": "Helper for auto-attach feature when node-debug extensions are not active.",
|
"description": "Helper for auto-attach feature when node-debug extensions are not active.",
|
||||||
|
|
||||||
"debug.node.autoAttach.description": "Automatically attach node debugger when node.js was launched in debug mode from integrated terminal.",
|
|
||||||
"debug.javascript.usePreviewAutoAttach": "Whether to use the preview debugger's version of auto attach.",
|
|
||||||
"debug.node.autoAttach.disabled.description": "Auto attach is disabled and not shown in status bar.",
|
|
||||||
"debug.node.autoAttach.on.description": "Auto attach is active.",
|
|
||||||
"debug.node.autoAttach.off.description": "Auto attach is inactive.",
|
|
||||||
|
|
||||||
"toggle.auto.attach": "Toggle Auto Attach"
|
"toggle.auto.attach": "Toggle Auto Attach"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,121 +8,152 @@ import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const ON_TEXT = localize('status.text.auto.attach.on', 'Auto Attach: On');
|
const TEXT_ALWAYS = localize('status.text.auto.attach.always', 'Auto Attach: Always');
|
||||||
const OFF_TEXT = localize('status.text.auto.attach.off', 'Auto Attach: Off');
|
const TEXT_SMART = localize('status.text.auto.attach.smart', 'Auto Attach: Smart');
|
||||||
|
const TEXT_WITH_FLAG = localize('status.text.auto.attach.withFlag', 'Auto Attach: With Flag');
|
||||||
|
const TEXT_STATE_DESCRIPTION = {
|
||||||
|
[State.Disabled]: localize(
|
||||||
|
'debug.javascript.autoAttach.disabled.description',
|
||||||
|
'Auto attach is disabled and not shown in status bar',
|
||||||
|
),
|
||||||
|
[State.Always]: localize(
|
||||||
|
'debug.javascript.autoAttach.always.description',
|
||||||
|
'Auto attach to every Node.js process launched in the terminal',
|
||||||
|
),
|
||||||
|
[State.Smart]: localize(
|
||||||
|
'debug.javascript.autoAttach.smart.description',
|
||||||
|
"Auto attach when running scripts that aren't in a node_modules folder",
|
||||||
|
),
|
||||||
|
[State.OnlyWithFlag]: localize(
|
||||||
|
'debug.javascript.autoAttach.onlyWithFlag.description',
|
||||||
|
'Only auto attach when the `--inspect` flag is given',
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
const TOGGLE_COMMAND = 'extension.node-debug.toggleAutoAttach';
|
const TOGGLE_COMMAND = 'extension.node-debug.toggleAutoAttach';
|
||||||
const JS_DEBUG_SETTINGS = 'debug.javascript';
|
const STORAGE_IPC = 'jsDebugIpcState';
|
||||||
const JS_DEBUG_USEPREVIEWAA = 'usePreviewAutoAttach';
|
|
||||||
const JS_DEBUG_IPC_KEY = 'jsDebugIpcState';
|
|
||||||
const JS_DEBUG_REFRESH_SETTINGS = ['autoAttachSmartPattern', 'autoAttachFilter']; // settings that, when changed, should cause us to refresh js-debug vars
|
|
||||||
const NODE_DEBUG_SETTINGS = 'debug.node';
|
|
||||||
const AUTO_ATTACH_SETTING = 'autoAttach';
|
|
||||||
const LAST_STATE_STORAGE_KEY = 'lastState';
|
|
||||||
|
|
||||||
|
const SETTING_SECTION = 'debug.javascript';
|
||||||
|
const SETTING_STATE = 'autoAttachFilter';
|
||||||
|
|
||||||
type AUTO_ATTACH_VALUES = 'disabled' | 'on' | 'off';
|
/**
|
||||||
|
* settings that, when changed, should cause us to refresh the state vars
|
||||||
|
*/
|
||||||
|
const SETTINGS_CAUSE_REFRESH = new Set(
|
||||||
|
['autoAttachSmartPattern', SETTING_STATE].map(s => `${SETTING_SECTION}.${s}`),
|
||||||
|
);
|
||||||
|
|
||||||
const enum State {
|
const enum State {
|
||||||
Disabled,
|
Disabled = 'disabled',
|
||||||
Off,
|
OnlyWithFlag = 'onlyWithFlag',
|
||||||
OnWithJsDebug,
|
Smart = 'smart',
|
||||||
OnWithNodeDebug,
|
Always = 'always',
|
||||||
}
|
}
|
||||||
|
|
||||||
// on activation this feature is always disabled...
|
let currentState: Promise<{ context: vscode.ExtensionContext; state: State | null }>;
|
||||||
let currentState: Promise<{ context: vscode.ExtensionContext, state: State; transitionData: unknown }>;
|
|
||||||
let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item
|
let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item
|
||||||
|
let server: Promise<Server | undefined> | undefined; // auto attach server
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext): void {
|
export function activate(context: vscode.ExtensionContext): void {
|
||||||
const previousState = context.workspaceState.get<State>(LAST_STATE_STORAGE_KEY, State.Disabled);
|
currentState = Promise.resolve({ context, state: null });
|
||||||
currentState = Promise.resolve(transitions[previousState].onActivate?.(context, readCurrentState()))
|
|
||||||
.then(() => ({ context, state: State.Disabled, transitionData: null }));
|
|
||||||
|
|
||||||
context.subscriptions.push(vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting));
|
|
||||||
|
|
||||||
// settings that can result in the "state" being changed--on/off/disable or useV3 toggles
|
|
||||||
const effectualConfigurationSettings = [
|
|
||||||
`${NODE_DEBUG_SETTINGS}.${AUTO_ATTACH_SETTING}`,
|
|
||||||
`${JS_DEBUG_SETTINGS}.${JS_DEBUG_USEPREVIEWAA}`,
|
|
||||||
];
|
|
||||||
|
|
||||||
const refreshConfigurationSettings = JS_DEBUG_REFRESH_SETTINGS.map(s => `${JS_DEBUG_SETTINGS}.${s}`);
|
|
||||||
|
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
vscode.workspace.onDidChangeConfiguration((e) => {
|
vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting),
|
||||||
if (effectualConfigurationSettings.some(setting => e.affectsConfiguration(setting))) {
|
|
||||||
updateAutoAttach();
|
|
||||||
} else if (refreshConfigurationSettings.some(setting => e.affectsConfiguration(setting))) {
|
|
||||||
currentState = currentState.then(async s => {
|
|
||||||
if (s.state !== State.OnWithJsDebug) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
await transitions[State.OnWithJsDebug].exit?.(context, s.transitionData);
|
|
||||||
await clearJsDebugAttachState(context);
|
|
||||||
const transitionData = await transitions[State.OnWithJsDebug].enter?.(context);
|
|
||||||
return { context, state: State.OnWithJsDebug, transitionData };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
updateAutoAttach();
|
context.subscriptions.push(
|
||||||
|
vscode.workspace.onDidChangeConfiguration(e => {
|
||||||
|
// Whenever a setting is changed, disable auto attach, and re-enable
|
||||||
|
// it (if necessary) to refresh variables.
|
||||||
|
if (
|
||||||
|
e.affectsConfiguration(`${SETTING_SECTION}.${SETTING_STATE}`) ||
|
||||||
|
[...SETTINGS_CAUSE_REFRESH].some(setting => e.affectsConfiguration(setting))
|
||||||
|
) {
|
||||||
|
updateAutoAttach(State.Disabled);
|
||||||
|
updateAutoAttach(readCurrentState());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
updateAutoAttach(readCurrentState());
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deactivate(): Promise<void> {
|
export async function deactivate(): Promise<void> {
|
||||||
const { context, state, transitionData } = await currentState;
|
await destroyAttachServer();
|
||||||
await transitions[state].exit?.(context, transitionData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleAutoAttachSetting() {
|
type StatePickItem =
|
||||||
const conf = vscode.workspace.getConfiguration(NODE_DEBUG_SETTINGS);
|
| (vscode.QuickPickItem & { state: State })
|
||||||
if (conf) {
|
| (vscode.QuickPickItem & { scope: vscode.ConfigurationTarget })
|
||||||
let value = <AUTO_ATTACH_VALUES>conf.get(AUTO_ATTACH_SETTING);
|
| (vscode.QuickPickItem & { type: 'separator' });
|
||||||
if (value === 'on') {
|
|
||||||
value = 'off';
|
|
||||||
} else {
|
|
||||||
value = 'on';
|
|
||||||
}
|
|
||||||
|
|
||||||
const info = conf.inspect(AUTO_ATTACH_SETTING);
|
function getDefaultScope(info: ReturnType<vscode.WorkspaceConfiguration['inspect']>) {
|
||||||
let target: vscode.ConfigurationTarget = vscode.ConfigurationTarget.Global;
|
if (!info) {
|
||||||
if (info) {
|
return vscode.ConfigurationTarget.Global;
|
||||||
if (info.workspaceFolderValue) {
|
} else if (info.workspaceFolderValue) {
|
||||||
target = vscode.ConfigurationTarget.WorkspaceFolder;
|
return vscode.ConfigurationTarget.WorkspaceFolder;
|
||||||
} else if (info.workspaceValue) {
|
} else if (info.workspaceValue) {
|
||||||
target = vscode.ConfigurationTarget.Workspace;
|
return vscode.ConfigurationTarget.Workspace;
|
||||||
} else if (info.globalValue) {
|
} else if (info.globalValue) {
|
||||||
target = vscode.ConfigurationTarget.Global;
|
return vscode.ConfigurationTarget.Global;
|
||||||
} else if (info.defaultValue) {
|
|
||||||
// setting not yet used: store setting in workspace
|
|
||||||
if (vscode.workspace.workspaceFolders) {
|
|
||||||
target = vscode.ConfigurationTarget.Workspace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conf.update(AUTO_ATTACH_SETTING, value, target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return vscode.ConfigurationTarget.Global;
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoAttachWithJsDebug() {
|
async function toggleAutoAttachSetting(scope?: vscode.ConfigurationTarget): Promise<void> {
|
||||||
const jsDebugConfig = vscode.workspace.getConfiguration(JS_DEBUG_SETTINGS);
|
const section = vscode.workspace.getConfiguration(SETTING_SECTION);
|
||||||
return jsDebugConfig.get(JS_DEBUG_USEPREVIEWAA, true);
|
scope = scope || getDefaultScope(section.inspect(SETTING_STATE));
|
||||||
|
|
||||||
|
const stateItems = [State.Always, State.Smart, State.OnlyWithFlag, State.Disabled].map(state => ({
|
||||||
|
state,
|
||||||
|
label: state.slice(0, 1).toUpperCase() + state.slice(1),
|
||||||
|
description: TEXT_STATE_DESCRIPTION[state],
|
||||||
|
alwaysShow: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const scopeItem =
|
||||||
|
scope === vscode.ConfigurationTarget.Global
|
||||||
|
? {
|
||||||
|
label: localize('scope.workspace', 'Toggle in this workspace $(arrow-right)'),
|
||||||
|
scope: vscode.ConfigurationTarget.Workspace,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
label: localize('scope.global', 'Toggle for this machine $(arrow-right)'),
|
||||||
|
scope: vscode.ConfigurationTarget.Global,
|
||||||
|
};
|
||||||
|
|
||||||
|
const quickPick = vscode.window.createQuickPick<StatePickItem>();
|
||||||
|
// todo: have a separator here, see https://github.com/microsoft/vscode/issues/74967
|
||||||
|
quickPick.items = [...stateItems, scopeItem];
|
||||||
|
|
||||||
|
quickPick.show();
|
||||||
|
const current = readCurrentState();
|
||||||
|
quickPick.activeItems = stateItems.filter(i => i.state === current);
|
||||||
|
|
||||||
|
const result = await new Promise<StatePickItem | undefined>(resolve => {
|
||||||
|
quickPick.onDidAccept(() => resolve(quickPick.selectedItems[0]));
|
||||||
|
quickPick.onDidHide(() => resolve());
|
||||||
|
});
|
||||||
|
|
||||||
|
quickPick.dispose();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('scope' in result) {
|
||||||
|
return await toggleAutoAttachSetting(result.scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('state' in result) {
|
||||||
|
section.update(SETTING_STATE, result.state, scope);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function readCurrentState(): State {
|
function readCurrentState(): State {
|
||||||
const nodeConfig = vscode.workspace.getConfiguration(NODE_DEBUG_SETTINGS);
|
const section = vscode.workspace.getConfiguration(SETTING_SECTION);
|
||||||
const autoAttachState = <AUTO_ATTACH_VALUES>nodeConfig.get(AUTO_ATTACH_SETTING);
|
return section.get<State>(SETTING_STATE) ?? State.Disabled;
|
||||||
switch (autoAttachState) {
|
|
||||||
case 'off':
|
|
||||||
return State.Off;
|
|
||||||
case 'on':
|
|
||||||
return autoAttachWithJsDebug() ? State.OnWithJsDebug : State.OnWithNodeDebug;
|
|
||||||
case 'disabled':
|
|
||||||
default:
|
|
||||||
return State.Disabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,7 +165,7 @@ function ensureStatusBarExists(context: vscode.ExtensionContext) {
|
||||||
statusItem.command = TOGGLE_COMMAND;
|
statusItem.command = TOGGLE_COMMAND;
|
||||||
statusItem.tooltip = localize(
|
statusItem.tooltip = localize(
|
||||||
'status.tooltip.auto.attach',
|
'status.tooltip.auto.attach',
|
||||||
'Automatically attach to node.js processes in debug mode'
|
'Automatically attach to node.js processes in debug mode',
|
||||||
);
|
);
|
||||||
statusItem.show();
|
statusItem.show();
|
||||||
context.subscriptions.push(statusItem);
|
context.subscriptions.push(statusItem);
|
||||||
|
@ -146,69 +177,27 @@ function ensureStatusBarExists(context: vscode.ExtensionContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearJsDebugAttachState(context: vscode.ExtensionContext) {
|
async function clearJsDebugAttachState(context: vscode.ExtensionContext) {
|
||||||
await context.workspaceState.update(JS_DEBUG_IPC_KEY, undefined);
|
await context.workspaceState.update(STORAGE_IPC, undefined);
|
||||||
await vscode.commands.executeCommand('extension.js-debug.clearAutoAttachVariables');
|
await vscode.commands.executeCommand('extension.js-debug.clearAutoAttachVariables');
|
||||||
|
await destroyAttachServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CachedIpcState {
|
|
||||||
ipcAddress: string;
|
|
||||||
jsDebugPath: string;
|
|
||||||
settingsValue: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StateTransition<StateData> {
|
|
||||||
onActivate?(context: vscode.ExtensionContext, currentState: State): Promise<void>;
|
|
||||||
exit?(context: vscode.ExtensionContext, stateData: StateData): Promise<void> | void;
|
|
||||||
enter?(context: vscode.ExtensionContext): Promise<StateData> | StateData;
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeTransition = <T>(tsn: StateTransition<T>) => tsn; // helper to apply generic type
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of logic that happens when auto attach states are entered and exited.
|
* Turns auto attach on, and returns the server auto attach is listening on
|
||||||
* All state transitions are queued and run in order; promises are awaited.
|
* if it's successful.
|
||||||
*/
|
*/
|
||||||
const transitions: { [S in State]: StateTransition<unknown> } = {
|
async function createAttachServer(context: vscode.ExtensionContext) {
|
||||||
[State.Disabled]: makeTransition({
|
|
||||||
async enter(context) {
|
|
||||||
statusItem?.hide();
|
|
||||||
await clearJsDebugAttachState(context);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
[State.Off]: makeTransition({
|
|
||||||
enter(context) {
|
|
||||||
const statusItem = ensureStatusBarExists(context);
|
|
||||||
statusItem.text = OFF_TEXT;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
[State.OnWithNodeDebug]: makeTransition({
|
|
||||||
async enter(context) {
|
|
||||||
const statusItem = ensureStatusBarExists(context);
|
|
||||||
const vscode_pid = process.env['VSCODE_PID'];
|
|
||||||
const rootPid = vscode_pid ? parseInt(vscode_pid) : 0;
|
|
||||||
await vscode.commands.executeCommand('extension.node-debug.startAutoAttach', rootPid);
|
|
||||||
statusItem.text = ON_TEXT;
|
|
||||||
},
|
|
||||||
|
|
||||||
async exit() {
|
|
||||||
await vscode.commands.executeCommand('extension.node-debug.stopAutoAttach');
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
[State.OnWithJsDebug]: makeTransition<Server | null>({
|
|
||||||
async enter(context) {
|
|
||||||
const ipcAddress = await getIpcAddress(context);
|
const ipcAddress = await getIpcAddress(context);
|
||||||
if (!ipcAddress) {
|
if (!ipcAddress) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = await new Promise<Server>((resolve, reject) => {
|
server = new Promise<Server>((resolve, reject) => {
|
||||||
const s = createServer((socket) => {
|
const s = createServer(socket => {
|
||||||
let data: Buffer[] = [];
|
let data: Buffer[] = [];
|
||||||
socket.on('data', async (chunk) => {
|
socket.on('data', async chunk => {
|
||||||
if (chunk[chunk.length - 1] !== 0) { // terminated with NUL byte
|
if (chunk[chunk.length - 1] !== 0) {
|
||||||
|
// terminated with NUL byte
|
||||||
data.push(chunk);
|
data.push(chunk);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +207,7 @@ const transitions: { [S in State]: StateTransition<unknown> } = {
|
||||||
try {
|
try {
|
||||||
await vscode.commands.executeCommand(
|
await vscode.commands.executeCommand(
|
||||||
'extension.js-debug.autoAttachToProcess',
|
'extension.js-debug.autoAttachToProcess',
|
||||||
JSON.parse(Buffer.concat(data).toString())
|
JSON.parse(Buffer.concat(data).toString()),
|
||||||
);
|
);
|
||||||
socket.write(Buffer.from([0]));
|
socket.write(Buffer.from([0]));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -229,51 +218,70 @@ const transitions: { [S in State]: StateTransition<unknown> } = {
|
||||||
})
|
})
|
||||||
.on('error', reject)
|
.on('error', reject)
|
||||||
.listen(ipcAddress, () => resolve(s));
|
.listen(ipcAddress, () => resolve(s));
|
||||||
}).catch(console.error);
|
}).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
return await server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the auto-attach server, if it's running.
|
||||||
|
*/
|
||||||
|
async function destroyAttachServer() {
|
||||||
|
const instance = await server;
|
||||||
|
if (instance) {
|
||||||
|
await new Promise(r => instance.close(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CachedIpcState {
|
||||||
|
ipcAddress: string;
|
||||||
|
jsDebugPath: string;
|
||||||
|
settingsValue: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of logic that happens when auto attach states are entered and exited.
|
||||||
|
* All state transitions are queued and run in order; promises are awaited.
|
||||||
|
*/
|
||||||
|
const transitions: { [S in State]: (context: vscode.ExtensionContext) => Promise<void> } = {
|
||||||
|
async [State.Disabled](context) {
|
||||||
|
await clearJsDebugAttachState(context);
|
||||||
|
statusItem?.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
async [State.OnlyWithFlag](context) {
|
||||||
|
await createAttachServer(context);
|
||||||
const statusItem = ensureStatusBarExists(context);
|
const statusItem = ensureStatusBarExists(context);
|
||||||
statusItem.text = ON_TEXT;
|
statusItem.text = TEXT_WITH_FLAG;
|
||||||
return server || null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async exit(context, server) {
|
async [State.Smart](context) {
|
||||||
// we don't need to clear the environment variables--the bootloader will
|
await createAttachServer(context);
|
||||||
// no-op if the debug server is closed. This prevents having to reload
|
const statusItem = ensureStatusBarExists(context);
|
||||||
// terminals if users want to turn it back on.
|
statusItem.text = TEXT_SMART;
|
||||||
if (server) {
|
|
||||||
await new Promise((resolve) => server.close(resolve));
|
|
||||||
}
|
|
||||||
|
|
||||||
// but if they toggled auto attach use js-debug off, go ahead and do so
|
|
||||||
if (!autoAttachWithJsDebug()) {
|
|
||||||
await clearJsDebugAttachState(context);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async onActivate(context, currentState) {
|
async [State.Always](context) {
|
||||||
if (currentState === State.OnWithNodeDebug || currentState === State.Disabled) {
|
await createAttachServer(context);
|
||||||
await clearJsDebugAttachState(context);
|
const statusItem = ensureStatusBarExists(context);
|
||||||
}
|
statusItem.text = TEXT_ALWAYS;
|
||||||
}
|
},
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the auto attach feature based on the user or workspace setting
|
* Updates the auto attach feature based on the user or workspace setting
|
||||||
*/
|
*/
|
||||||
function updateAutoAttach() {
|
function updateAutoAttach(newState: State) {
|
||||||
const newState = readCurrentState();
|
currentState = currentState.then(async ({ context, state: oldState }) => {
|
||||||
|
|
||||||
currentState = currentState.then(async ({ context, state: oldState, transitionData }) => {
|
|
||||||
if (newState === oldState) {
|
if (newState === oldState) {
|
||||||
return { context, state: oldState, transitionData };
|
return { context, state: oldState };
|
||||||
}
|
}
|
||||||
|
|
||||||
await transitions[oldState].exit?.(context, transitionData);
|
await transitions[newState](context);
|
||||||
const newData = await transitions[newState].enter?.(context);
|
return { context, state: newState };
|
||||||
await context.workspaceState.update(LAST_STATE_STORAGE_KEY, newState);
|
|
||||||
|
|
||||||
return { context, state: newState, transitionData: newData };
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,41 +293,43 @@ async function getIpcAddress(context: vscode.ExtensionContext) {
|
||||||
// Iff the `cachedData` is present, the js-debug registered environment
|
// Iff the `cachedData` is present, the js-debug registered environment
|
||||||
// variables for this workspace--cachedData is set after successfully
|
// variables for this workspace--cachedData is set after successfully
|
||||||
// invoking the attachment command.
|
// invoking the attachment command.
|
||||||
const cachedIpc = context.workspaceState.get<CachedIpcState>(JS_DEBUG_IPC_KEY);
|
const cachedIpc = context.workspaceState.get<CachedIpcState>(STORAGE_IPC);
|
||||||
|
|
||||||
// We invalidate the IPC data if the js-debug path changes, since that
|
// We invalidate the IPC data if the js-debug path changes, since that
|
||||||
// indicates the extension was updated or reinstalled and the
|
// indicates the extension was updated or reinstalled and the
|
||||||
// environment variables will have been lost.
|
// environment variables will have been lost.
|
||||||
// todo: make a way in the API to read environment data directly without activating js-debug?
|
// todo: make a way in the API to read environment data directly without activating js-debug?
|
||||||
const jsDebugPath = vscode.extensions.getExtension('ms-vscode.js-debug-nightly')?.extensionPath
|
const jsDebugPath =
|
||||||
|| vscode.extensions.getExtension('ms-vscode.js-debug')?.extensionPath;
|
vscode.extensions.getExtension('ms-vscode.js-debug-nightly')?.extensionPath ||
|
||||||
|
vscode.extensions.getExtension('ms-vscode.js-debug')?.extensionPath;
|
||||||
|
|
||||||
const settingsValue = getJsDebugSettingKey();
|
const settingsValue = getJsDebugSettingKey();
|
||||||
if (cachedIpc && cachedIpc.jsDebugPath === jsDebugPath && cachedIpc.settingsValue === settingsValue) {
|
if (cachedIpc?.jsDebugPath === jsDebugPath && cachedIpc?.settingsValue === settingsValue) {
|
||||||
return cachedIpc.ipcAddress;
|
return cachedIpc.ipcAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await vscode.commands.executeCommand<{ ipcAddress: string; }>(
|
const result = await vscode.commands.executeCommand<{ ipcAddress: string }>(
|
||||||
'extension.js-debug.setAutoAttachVariables',
|
'extension.js-debug.setAutoAttachVariables',
|
||||||
cachedIpc?.ipcAddress
|
cachedIpc?.ipcAddress,
|
||||||
);
|
);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ipcAddress = result.ipcAddress;
|
const ipcAddress = result.ipcAddress;
|
||||||
await context.workspaceState.update(
|
await context.workspaceState.update(STORAGE_IPC, {
|
||||||
JS_DEBUG_IPC_KEY,
|
ipcAddress,
|
||||||
{ ipcAddress, jsDebugPath, settingsValue } as CachedIpcState,
|
jsDebugPath,
|
||||||
);
|
settingsValue,
|
||||||
|
} as CachedIpcState);
|
||||||
|
|
||||||
return ipcAddress;
|
return ipcAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getJsDebugSettingKey() {
|
function getJsDebugSettingKey() {
|
||||||
let o: { [key: string]: unknown } = {};
|
let o: { [key: string]: unknown } = {};
|
||||||
const config = vscode.workspace.getConfiguration(JS_DEBUG_SETTINGS);
|
const config = vscode.workspace.getConfiguration(SETTING_SECTION);
|
||||||
for (const setting of JS_DEBUG_REFRESH_SETTINGS) {
|
for (const setting of SETTINGS_CAUSE_REFRESH) {
|
||||||
o[setting] = config.get(setting);
|
o[setting] = config.get(setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue