@@ -126,6 +126,7 @@ export class DebugSessionFeature extends LanguageClientConsumer
126
126
private sessionCount = 1 ;
127
127
private tempDebugProcess : PowerShellProcess | undefined ;
128
128
private tempSessionDetails : IEditorServicesSessionDetails | undefined ;
129
+ private commands : Disposable [ ] ;
129
130
private handlers : Disposable [ ] = [ ] ;
130
131
131
132
constructor ( context : ExtensionContext , private sessionManager : SessionManager , private logger : ILogger ) {
@@ -138,15 +139,34 @@ export class DebugSessionFeature extends LanguageClientConsumer
138
139
context . subscriptions . push ( debug . registerDebugConfigurationProvider ( "PowerShell" , this , triggerKind ) ) ;
139
140
} ) ;
140
141
context . subscriptions . push ( debug . registerDebugAdapterDescriptorFactory ( "PowerShell" , this ) ) ;
142
+
143
+ // NOTE: While process and runspace IDs are numbers, command
144
+ // substitutions in VS Code debug configurations are required to return
145
+ // strings. Hence to the `toString()` on these.
146
+ this . commands = [
147
+ commands . registerCommand ( "PowerShell.PickPSHostProcess" , async ( ) => {
148
+
149
+ const processId = await this . pickPSHostProcess ( ) ;
150
+ return processId ?. toString ( ) ;
151
+ } ) ,
152
+ commands . registerCommand ( "PowerShell.PickRunspace" , async ( processId ) => {
153
+ const runspace = await this . pickRunspace ( processId ) ;
154
+ return runspace ?. toString ( ) ;
155
+ } , this ) ,
156
+ ] ;
141
157
}
142
158
143
159
public dispose ( ) : void {
160
+ for ( const command of this . commands ) {
161
+ command . dispose ( ) ;
162
+ }
163
+
144
164
for ( const handler of this . handlers ) {
145
165
handler . dispose ( ) ;
146
166
}
147
167
}
148
168
149
- public override setLanguageClient ( languageClient : LanguageClient ) : void {
169
+ public override onLanguageClientSet ( languageClient : LanguageClient ) : void {
150
170
this . handlers = [
151
171
languageClient . onNotification (
152
172
StartDebuggerNotificationType ,
@@ -477,7 +497,7 @@ export class DebugSessionFeature extends LanguageClientConsumer
477
497
478
498
// If nothing is set, prompt for the processId.
479
499
if ( ! config . customPipeName && ! config . processId ) {
480
- config . processId = await commands . executeCommand ( "PowerShell.PickPSHostProcess" ) ;
500
+ config . processId = await this . pickPSHostProcess ( ) ;
481
501
// No process selected. Cancel attach.
482
502
if ( ! config . processId ) {
483
503
return PREVENT_DEBUG_START ;
@@ -511,10 +531,66 @@ export class DebugSessionFeature extends LanguageClientConsumer
511
531
512
532
return config ;
513
533
}
534
+
535
+ private async pickPSHostProcess ( ) : Promise < number | undefined > {
536
+ // Start with the current PowerShell process in the list.
537
+ const items : IProcessItem [ ] = [ ] ;
538
+
539
+ const client = await LanguageClientConsumer . getLanguageClient ( ) ;
540
+ const response = await client . sendRequest ( GetPSHostProcessesRequestType , { } ) ;
541
+ for ( const process of response ) {
542
+ let windowTitle = "" ;
543
+ if ( process . mainWindowTitle ) {
544
+ windowTitle = `, ${ process . mainWindowTitle } ` ;
545
+ }
546
+
547
+ items . push ( {
548
+ label : process . processName ,
549
+ description : `PID: ${ process . processId . toString ( ) } ${ windowTitle } ` ,
550
+ processId : process . processId ,
551
+ } ) ;
552
+ }
553
+
554
+ if ( items . length === 0 ) {
555
+ return Promise . reject ( "There are no PowerShell host processes to attach." ) ;
556
+ }
557
+
558
+ const options : QuickPickOptions = {
559
+ placeHolder : "Select a PowerShell host process to attach." ,
560
+ matchOnDescription : true ,
561
+ matchOnDetail : true ,
562
+ } ;
563
+
564
+ const item = await window . showQuickPick ( items , options ) ;
565
+
566
+ return item ?. processId ?? undefined ;
567
+ }
568
+
569
+ private async pickRunspace ( processId : number ) : Promise < number | undefined > {
570
+ const client = await LanguageClientConsumer . getLanguageClient ( ) ;
571
+ const response = await client . sendRequest ( GetRunspaceRequestType , { processId } ) ;
572
+ const items : IRunspaceItem [ ] = [ ] ;
573
+ for ( const runspace of response ) {
574
+ items . push ( {
575
+ label : runspace . name ,
576
+ description : `ID: ${ runspace . id } - ${ runspace . availability } ` ,
577
+ id : runspace . id ,
578
+ } ) ;
579
+ }
580
+
581
+ const options : QuickPickOptions = {
582
+ placeHolder : "Select PowerShell runspace to debug" ,
583
+ matchOnDescription : true ,
584
+ matchOnDetail : true ,
585
+ } ;
586
+
587
+ const item = await window . showQuickPick ( items , options ) ;
588
+
589
+ return item ?. id ?? undefined ;
590
+ }
514
591
}
515
592
516
593
export class SpecifyScriptArgsFeature implements Disposable {
517
-
518
594
private command : Disposable ;
519
595
private context : ExtensionContext ;
520
596
@@ -572,111 +648,6 @@ interface IPSHostProcessInfo {
572
648
export const GetPSHostProcessesRequestType =
573
649
new RequestType < IGetPSHostProcessesArguments , IPSHostProcessInfo [ ] , string > ( "powerShell/getPSHostProcesses" ) ;
574
650
575
- export class PickPSHostProcessFeature extends LanguageClientConsumer {
576
- private command : Disposable ;
577
- private waitingForClientToken ?: CancellationTokenSource ;
578
- private getLanguageClientResolve ?: ( value : LanguageClient ) => void ;
579
-
580
- constructor ( private logger : ILogger ) {
581
- super ( ) ;
582
-
583
- this . command = commands . registerCommand ( "PowerShell.PickPSHostProcess" , async ( ) => {
584
- return this . pickPSHostProcess ( ) ;
585
- } ) ;
586
- }
587
-
588
- public override setLanguageClient ( languageClient : LanguageClient ) : void {
589
- this . languageClient = languageClient ;
590
-
591
- if ( this . waitingForClientToken && this . getLanguageClientResolve ) {
592
- this . getLanguageClientResolve ( this . languageClient ) ;
593
- this . clearWaitingToken ( ) ;
594
- }
595
- }
596
-
597
- public dispose ( ) : void {
598
- this . command . dispose ( ) ;
599
- }
600
-
601
- private getLanguageClient ( ) : Promise < LanguageClient > {
602
- if ( this . languageClient !== undefined ) {
603
- return Promise . resolve ( this . languageClient ) ;
604
- } else {
605
- // If PowerShell isn't finished loading yet, show a loading message
606
- // until the LanguageClient is passed on to us
607
- this . waitingForClientToken = new CancellationTokenSource ( ) ;
608
-
609
- return new Promise < LanguageClient > (
610
- ( resolve , reject ) => {
611
- this . getLanguageClientResolve = resolve ;
612
-
613
- void window
614
- . showQuickPick (
615
- [ "Cancel" ] ,
616
- { placeHolder : "Attach to PowerShell host process: Please wait, starting PowerShell..." } ,
617
- this . waitingForClientToken ?. token )
618
- . then ( ( response ) => {
619
- if ( response === "Cancel" ) {
620
- this . clearWaitingToken ( ) ;
621
- reject ( ) ;
622
- }
623
- } , undefined ) ;
624
-
625
- // Cancel the loading prompt after 60 seconds
626
- setTimeout ( ( ) => {
627
- if ( this . waitingForClientToken ) {
628
- this . clearWaitingToken ( ) ;
629
- reject ( ) ;
630
-
631
- void this . logger . writeAndShowError ( "Attach to PowerShell host process: PowerShell session took too long to start." ) ;
632
- }
633
- } , 60000 ) ;
634
- } ,
635
- ) ;
636
- }
637
- }
638
-
639
- private async pickPSHostProcess ( ) : Promise < number | undefined > {
640
- // We need the language client in order to send the request.
641
- await this . getLanguageClient ( ) ;
642
-
643
- // Start with the current PowerShell process in the list.
644
- const items : IProcessItem [ ] = [ ] ;
645
-
646
- const response = await this . languageClient ?. sendRequest ( GetPSHostProcessesRequestType , { } ) ;
647
- for ( const process of response ?? [ ] ) {
648
- let windowTitle = "" ;
649
- if ( process . mainWindowTitle ) {
650
- windowTitle = `, ${ process . mainWindowTitle } ` ;
651
- }
652
-
653
- items . push ( {
654
- label : process . processName ,
655
- description : `PID: ${ process . processId . toString ( ) } ${ windowTitle } ` ,
656
- processId : process . processId ,
657
- } ) ;
658
- }
659
-
660
- if ( items . length === 0 ) {
661
- return Promise . reject ( "There are no PowerShell host processes to attach to." ) ;
662
- }
663
-
664
- const options : QuickPickOptions = {
665
- placeHolder : "Select a PowerShell host process to attach to" ,
666
- matchOnDescription : true ,
667
- matchOnDetail : true ,
668
- } ;
669
-
670
- const item = await window . showQuickPick ( items , options ) ;
671
-
672
- return item ?. processId ?? undefined ;
673
- }
674
-
675
- private clearWaitingToken ( ) : void {
676
- this . waitingForClientToken ?. dispose ( ) ;
677
- this . waitingForClientToken = undefined ;
678
- }
679
- }
680
651
681
652
interface IRunspaceItem extends QuickPickItem {
682
653
id : number ; // payload for the QuickPick UI
@@ -694,99 +665,3 @@ interface IRunspace {
694
665
695
666
export const GetRunspaceRequestType =
696
667
new RequestType < IGetRunspaceRequestArguments , IRunspace [ ] , string > ( "powerShell/getRunspace" ) ;
697
-
698
- export class PickRunspaceFeature extends LanguageClientConsumer {
699
- private command : Disposable ;
700
- private waitingForClientToken ?: CancellationTokenSource ;
701
- private getLanguageClientResolve ?: ( value : LanguageClient ) => void ;
702
-
703
- constructor ( private logger : ILogger ) {
704
- super ( ) ;
705
-
706
- this . command = commands . registerCommand ( "PowerShell.PickRunspace" ,
707
- async ( processId ) => { return this . pickRunspace ( processId ) ; } ,
708
- this ) ;
709
- }
710
-
711
- public override setLanguageClient ( languageClient : LanguageClient ) : void {
712
- this . languageClient = languageClient ;
713
-
714
- if ( this . waitingForClientToken && this . getLanguageClientResolve ) {
715
- this . getLanguageClientResolve ( this . languageClient ) ;
716
- this . clearWaitingToken ( ) ;
717
- }
718
- }
719
-
720
- public dispose ( ) : void {
721
- this . command . dispose ( ) ;
722
- }
723
-
724
- private getLanguageClient ( ) : Promise < LanguageClient > {
725
- if ( this . languageClient ) {
726
- return Promise . resolve ( this . languageClient ) ;
727
- } else {
728
- // If PowerShell isn't finished loading yet, show a loading message
729
- // until the LanguageClient is passed on to us
730
- this . waitingForClientToken = new CancellationTokenSource ( ) ;
731
-
732
- return new Promise < LanguageClient > (
733
- ( resolve , reject ) => {
734
- this . getLanguageClientResolve = resolve ;
735
-
736
- void window
737
- . showQuickPick (
738
- [ "Cancel" ] ,
739
- { placeHolder : "Attach to PowerShell host process: Please wait, starting PowerShell..." } ,
740
- this . waitingForClientToken ?. token )
741
- . then ( ( response ) => {
742
- if ( response === "Cancel" ) {
743
- this . clearWaitingToken ( ) ;
744
- reject ( ) ;
745
- }
746
- } , undefined ) ;
747
-
748
- // Cancel the loading prompt after 60 seconds
749
- setTimeout ( ( ) => {
750
- if ( this . waitingForClientToken ) {
751
- this . clearWaitingToken ( ) ;
752
- reject ( ) ;
753
-
754
- void this . logger . writeAndShowError (
755
- "Attach to PowerShell host process: PowerShell session took too long to start." ) ;
756
- }
757
- } , 60000 ) ;
758
- } ,
759
- ) ;
760
- }
761
- }
762
-
763
- private async pickRunspace ( processId : number ) : Promise < number | undefined > {
764
- // We need the language client in order to send the request.
765
- await this . getLanguageClient ( ) ;
766
-
767
- const response = await this . languageClient ?. sendRequest ( GetRunspaceRequestType , { processId } ) ;
768
- const items : IRunspaceItem [ ] = [ ] ;
769
- for ( const runspace of response ?? [ ] ) {
770
- items . push ( {
771
- label : runspace . name ,
772
- description : `ID: ${ runspace . id } - ${ runspace . availability } ` ,
773
- id : runspace . id ,
774
- } ) ;
775
- }
776
-
777
- const options : QuickPickOptions = {
778
- placeHolder : "Select PowerShell runspace to debug" ,
779
- matchOnDescription : true ,
780
- matchOnDetail : true ,
781
- } ;
782
-
783
- const item = await window . showQuickPick ( items , options ) ;
784
-
785
- return item ?. id ?? undefined ;
786
- }
787
-
788
- private clearWaitingToken ( ) : void {
789
- this . waitingForClientToken ?. dispose ( ) ;
790
- this . waitingForClientToken = undefined ;
791
- }
792
- }
0 commit comments