Skip to content

Commit b1c5f5f

Browse files
adamdriscollTylerLeonhardt
authored andcommitted
Add Debug Runspace command (#1782)
* Add Debug Runspace command * Update package.json based on feedback. * Update response to match PSES response. * Change how we ask for RunspaceId. * Clean up.
1 parent 4e061ea commit b1c5f5f

File tree

3 files changed

+156
-4
lines changed

3 files changed

+156
-4
lines changed

package.json

+21-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"onCommand:PowerShell.NewProjectFromTemplate",
3333
"onCommand:PowerShell.OpenExamplesFolder",
3434
"onCommand:PowerShell.PickPSHostProcess",
35+
"onCommand:PowerShell.PickRunspace",
3536
"onCommand:PowerShell.SpecifyScriptArgs",
3637
"onCommand:PowerShell.ShowSessionConsole",
3738
"onCommand:PowerShell.ShowSessionMenu",
@@ -318,6 +319,7 @@
318319
"runtime": "node",
319320
"variables": {
320321
"PickPSHostProcess": "PowerShell.PickPSHostProcess",
322+
"PickRunspace": "PowerShell.PickRunspace",
321323
"SpecifyScriptArgs": "PowerShell.SpecifyScriptArgs"
322324
},
323325
"languages": [
@@ -406,6 +408,16 @@
406408
"request": "launch",
407409
"cwd": ""
408410
}
411+
},
412+
{
413+
"label": "PowerShell: Attach Interactive Session Runspace",
414+
"description": "Open runspace picker to select runspace to attach debugger to",
415+
"body": {
416+
"name": "PowerShell Attach Interactive Session Runspace",
417+
"type": "PowerShell",
418+
"request": "attach",
419+
"processId": "current"
420+
}
409421
}
410422
],
411423
"configurationAttributes": {
@@ -447,9 +459,9 @@
447459
"default": null
448460
},
449461
"runspaceId": {
450-
"type": "number",
462+
"type":["string","number"],
451463
"description": "Optional: The ID of the runspace to debug in the attached process. Defaults to 1. Works only on PowerShell 5 and above.",
452-
"default": 1
464+
"default": null
453465
},
454466
"customPipeName": {
455467
"type": "string",
@@ -490,14 +502,19 @@
490502
{
491503
"name": "PowerShell Attach to Host Process",
492504
"type": "PowerShell",
493-
"request": "attach",
494-
"runspaceId": 1
505+
"request": "attach"
495506
},
496507
{
497508
"name": "PowerShell Interactive Session",
498509
"type": "PowerShell",
499510
"request": "launch",
500511
"cwd": ""
512+
},
513+
{
514+
"name": "PowerShell Attach Interactive Session Runspace",
515+
"type": "PowerShell",
516+
"request": "attach",
517+
"processId": "current"
501518
}
502519
]
503520
}

src/features/DebugSession.ts

+133
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,20 @@ export class DebugSessionFeature implements IFeature, DebugConfigurationProvider
8080
// if nothing is set, prompt for the processId
8181
if (!config.customPipeName && !config.processId) {
8282
config.processId = await vscode.commands.executeCommand("PowerShell.PickPSHostProcess");
83+
84+
// No process selected. Cancel attach.
85+
if (!config.processId) {
86+
return null;
87+
}
88+
}
89+
90+
if (!config.runspaceId) {
91+
config.runspaceId = await vscode.commands.executeCommand("PowerShell.PickRunspace", config.processId);
92+
93+
// No runspace selected. Cancel attach.
94+
if (!config.runspaceId) {
95+
return null;
96+
}
8397
}
8498
}
8599

@@ -386,3 +400,122 @@ export class PickPSHostProcessFeature implements IFeature {
386400
}
387401
}
388402
}
403+
404+
interface IRunspaceItem extends vscode.QuickPickItem {
405+
id: string; // payload for the QuickPick UI
406+
}
407+
408+
interface IRunspace {
409+
id: number;
410+
name: string;
411+
availability: string;
412+
}
413+
414+
export const GetRunspaceRequestType =
415+
new RequestType<any, IRunspace[], string, void>("powerShell/getRunspace");
416+
417+
export class PickRunspaceFeature implements IFeature {
418+
419+
private command: vscode.Disposable;
420+
private languageClient: LanguageClient;
421+
private waitingForClientToken: vscode.CancellationTokenSource;
422+
private getLanguageClientResolve: (value?: LanguageClient | Thenable<LanguageClient>) => void;
423+
424+
constructor() {
425+
this.command =
426+
vscode.commands.registerCommand("PowerShell.PickRunspace", (processId) => {
427+
return this.getLanguageClient()
428+
.then((_) => this.pickRunspace(processId), (_) => undefined);
429+
}, this);
430+
}
431+
432+
public setLanguageClient(languageClient: LanguageClient) {
433+
this.languageClient = languageClient;
434+
435+
if (this.waitingForClientToken) {
436+
this.getLanguageClientResolve(this.languageClient);
437+
this.clearWaitingToken();
438+
}
439+
}
440+
441+
public dispose() {
442+
this.command.dispose();
443+
}
444+
445+
private getLanguageClient(): Thenable<LanguageClient> {
446+
if (this.languageClient) {
447+
return Promise.resolve(this.languageClient);
448+
} else {
449+
// If PowerShell isn't finished loading yet, show a loading message
450+
// until the LanguageClient is passed on to us
451+
this.waitingForClientToken = new vscode.CancellationTokenSource();
452+
453+
return new Promise<LanguageClient>(
454+
(resolve, reject) => {
455+
this.getLanguageClientResolve = resolve;
456+
457+
vscode.window
458+
.showQuickPick(
459+
["Cancel"],
460+
{ placeHolder: "Attach to PowerShell host process: Please wait, starting PowerShell..." },
461+
this.waitingForClientToken.token)
462+
.then((response) => {
463+
if (response === "Cancel") {
464+
this.clearWaitingToken();
465+
reject();
466+
}
467+
});
468+
469+
// Cancel the loading prompt after 60 seconds
470+
setTimeout(() => {
471+
if (this.waitingForClientToken) {
472+
this.clearWaitingToken();
473+
reject();
474+
475+
vscode.window.showErrorMessage(
476+
"Attach to PowerShell host process: PowerShell session took too long to start.");
477+
}
478+
}, 60000);
479+
},
480+
);
481+
}
482+
}
483+
484+
private pickRunspace(processId): Thenable<string> {
485+
return this.languageClient.sendRequest(GetRunspaceRequestType, processId).then((response) => {
486+
const items: IRunspaceItem[] = [];
487+
488+
for (const runspace of response) {
489+
// Skip default runspace
490+
if (runspace.id === 1 || runspace.name === "PSAttachRunspace") {
491+
continue;
492+
}
493+
494+
items.push({
495+
label: runspace.name,
496+
description: `ID: ${runspace.id} - ${runspace.availability}`,
497+
id: runspace.id.toString(),
498+
});
499+
}
500+
501+
const options: vscode.QuickPickOptions = {
502+
placeHolder: "Select PowerShell runspace to debug",
503+
matchOnDescription: true,
504+
matchOnDetail: true,
505+
};
506+
507+
return vscode.window.showQuickPick(items, options).then((item) => {
508+
// Return undefined when user presses Esc.
509+
// This prevents VSCode from opening launch.json in this case which happens if we return "".
510+
return item ? `${item.id}` : undefined;
511+
});
512+
});
513+
}
514+
515+
private clearWaitingToken() {
516+
if (this.waitingForClientToken) {
517+
this.waitingForClientToken.dispose();
518+
this.waitingForClientToken = undefined;
519+
}
520+
}
521+
}

src/main.ts

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ConsoleFeature } from "./features/Console";
1313
import { CustomViewsFeature } from "./features/CustomViews";
1414
import { DebugSessionFeature } from "./features/DebugSession";
1515
import { PickPSHostProcessFeature } from "./features/DebugSession";
16+
import { PickRunspaceFeature } from "./features/DebugSession";
1617
import { SpecifyScriptArgsFeature } from "./features/DebugSession";
1718
import { DocumentFormatterFeature } from "./features/DocumentFormatter";
1819
import { ExamplesFeature } from "./features/Examples";
@@ -142,6 +143,7 @@ export function activate(context: vscode.ExtensionContext): void {
142143
new SpecifyScriptArgsFeature(context),
143144
new HelpCompletionFeature(logger),
144145
new CustomViewsFeature(),
146+
new PickRunspaceFeature(),
145147
];
146148

147149
sessionManager.setExtensionFeatures(extensionFeatures);

0 commit comments

Comments
 (0)