Skip to content

Commit 568edf4

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 3a283f3 commit 568edf4

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
@@ -31,6 +31,7 @@
3131
"onCommand:PowerShell.NewProjectFromTemplate",
3232
"onCommand:PowerShell.OpenExamplesFolder",
3333
"onCommand:PowerShell.PickPSHostProcess",
34+
"onCommand:PowerShell.PickRunspace",
3435
"onCommand:PowerShell.SpecifyScriptArgs",
3536
"onCommand:PowerShell.ShowSessionConsole",
3637
"onCommand:PowerShell.ShowSessionMenu",
@@ -313,6 +314,7 @@
313314
"runtime": "node",
314315
"variables": {
315316
"PickPSHostProcess": "PowerShell.PickPSHostProcess",
317+
"PickRunspace": "PowerShell.PickRunspace",
316318
"SpecifyScriptArgs": "PowerShell.SpecifyScriptArgs"
317319
},
318320
"languages": [
@@ -401,6 +403,16 @@
401403
"request": "launch",
402404
"cwd": ""
403405
}
406+
},
407+
{
408+
"label": "PowerShell: Attach Interactive Session Runspace",
409+
"description": "Open runspace picker to select runspace to attach debugger to",
410+
"body": {
411+
"name": "PowerShell Attach Interactive Session Runspace",
412+
"type": "PowerShell",
413+
"request": "attach",
414+
"processId": "current"
415+
}
404416
}
405417
],
406418
"configurationAttributes": {
@@ -442,9 +454,9 @@
442454
"default": null
443455
},
444456
"runspaceId": {
445-
"type": "number",
457+
"type":["string","number"],
446458
"description": "Optional: The ID of the runspace to debug in the attached process. Defaults to 1. Works only on PowerShell 5 and above.",
447-
"default": 1
459+
"default": null
448460
},
449461
"customPipeName": {
450462
"type": "string",
@@ -485,14 +497,19 @@
485497
{
486498
"name": "PowerShell Attach to Host Process",
487499
"type": "PowerShell",
488-
"request": "attach",
489-
"runspaceId": 1
500+
"request": "attach"
490501
},
491502
{
492503
"name": "PowerShell Interactive Session",
493504
"type": "PowerShell",
494505
"request": "launch",
495506
"cwd": ""
507+
},
508+
{
509+
"name": "PowerShell Attach Interactive Session Runspace",
510+
"type": "PowerShell",
511+
"request": "attach",
512+
"processId": "current"
496513
}
497514
]
498515
}

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";
@@ -138,6 +139,7 @@ export function activate(context: vscode.ExtensionContext): void {
138139
new SpecifyScriptArgsFeature(context),
139140
new HelpCompletionFeature(logger),
140141
new CustomViewsFeature(),
142+
new PickRunspaceFeature(),
141143
];
142144

143145
sessionManager.setExtensionFeatures(extensionFeatures);

0 commit comments

Comments
 (0)