Skip to content

Commit 5f0a237

Browse files
committed
Fix broken Run Selection (F8) behavior
This change fixes the Run Selection (F8) behavior that got broken after the big PSHost implementation refactoring. The 'evaluate' message handler was not being registered correctly anymore so these requests were never being executed.
1 parent 874a0c8 commit 5f0a237

File tree

6 files changed

+92
-101
lines changed

6 files changed

+92
-101
lines changed

src/PowerShellEditorServices.Host/EditorServicesHost.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ private EditorSession CreateSession(
334334
EditorServicesPSHostUserInterface hostUserInterface =
335335
enableConsoleRepl
336336
? (EditorServicesPSHostUserInterface) new TerminalPSHostUserInterface(powerShellContext, this.logger)
337-
: new ProtocolPSHostUserInterface(powerShellContext, messageSender, messageHandlers, this.logger);
337+
: new ProtocolPSHostUserInterface(powerShellContext, messageSender, this.logger);
338338

339339
EditorServicesPSHost psHost =
340340
new EditorServicesPSHost(
@@ -374,7 +374,7 @@ private EditorSession CreateDebugSession(
374374
EditorServicesPSHostUserInterface hostUserInterface =
375375
enableConsoleRepl
376376
? (EditorServicesPSHostUserInterface) new TerminalPSHostUserInterface(powerShellContext, this.logger)
377-
: new ProtocolPSHostUserInterface(powerShellContext, messageSender, messageHandlers, this.logger);
377+
: new ProtocolPSHostUserInterface(powerShellContext, messageSender, this.logger);
378378

379379
EditorServicesPSHost psHost =
380380
new EditorServicesPSHost(

src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs

Lines changed: 6 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
88
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
99
using Microsoft.PowerShell.EditorServices.Protocol.Server;
10+
using Microsoft.PowerShell.EditorServices.Utility;
1011
using System;
1112
using System.Threading;
1213
using System.Threading.Tasks;
13-
using Microsoft.PowerShell.EditorServices.Utility;
1414

1515
namespace Microsoft.PowerShell.EditorServices.Host
1616
{
@@ -20,7 +20,6 @@ internal class ProtocolPSHostUserInterface : EditorServicesPSHostUserInterface
2020

2121
private IMessageSender messageSender;
2222
private OutputDebouncer outputDebouncer;
23-
private TaskCompletionSource<string> commandLineInputTask;
2423

2524
#endregion
2625

@@ -34,16 +33,11 @@ internal class ProtocolPSHostUserInterface : EditorServicesPSHostUserInterface
3433
public ProtocolPSHostUserInterface(
3534
PowerShellContext powerShellContext,
3635
IMessageSender messageSender,
37-
IMessageHandlers messageHandlers,
3836
ILogger logger)
3937
: base(powerShellContext, new SimplePSHostRawUserInterface(logger), logger)
4038
{
4139
this.messageSender = messageSender;
4240
this.outputDebouncer = new OutputDebouncer(messageSender);
43-
44-
messageHandlers.SetRequestHandler(
45-
EvaluateRequest.Type,
46-
this.HandleEvaluateRequest);
4741
}
4842

4943
public void Dispose()
@@ -108,10 +102,12 @@ protected override void UpdateProgress(
108102
{
109103
}
110104

111-
protected override async Task<string> ReadCommandLine(CancellationToken cancellationToken)
105+
protected override Task<string> ReadCommandLine(CancellationToken cancellationToken)
112106
{
113-
this.commandLineInputTask = new TaskCompletionSource<string>();
114-
return await this.commandLineInputTask.Task;
107+
// This currently does nothing because the "evaluate" request
108+
// will cancel the current prompt and execute the user's
109+
// script selection.
110+
return new TaskCompletionSource<string>().Task;
115111
}
116112

117113
protected override InputPromptHandler OnCreateInputPromptHandler()
@@ -123,66 +119,5 @@ protected override ChoicePromptHandler OnCreateChoicePromptHandler()
123119
{
124120
return new ProtocolChoicePromptHandler(this.messageSender, this, this, this.Logger);
125121
}
126-
127-
protected async Task HandleEvaluateRequest(
128-
EvaluateRequestArguments evaluateParams,
129-
RequestContext<EvaluateResponseBody> requestContext)
130-
{
131-
// TODO: This needs to respect debug mode!
132-
133-
var evaluateResponse =
134-
new EvaluateResponseBody
135-
{
136-
Result = "",
137-
VariablesReference = 0
138-
};
139-
140-
if (this.commandLineInputTask != null)
141-
{
142-
this.commandLineInputTask.SetResult(evaluateParams.Expression);
143-
await requestContext.SendResult(evaluateResponse);
144-
}
145-
else
146-
{
147-
// Check for special commands
148-
if (string.Equals("!ctrlc", evaluateParams.Expression, StringComparison.CurrentCultureIgnoreCase))
149-
{
150-
this.powerShellContext.AbortExecution();
151-
await requestContext.SendResult(evaluateResponse);
152-
}
153-
else if (string.Equals("!break", evaluateParams.Expression, StringComparison.CurrentCultureIgnoreCase))
154-
{
155-
// TODO: Need debugger commands interface
156-
//editorSession.DebugService.Break();
157-
await requestContext.SendResult(evaluateResponse);
158-
}
159-
else
160-
{
161-
// We don't await the result of the execution here because we want
162-
// to be able to receive further messages while the current script
163-
// is executing. This important in cases where the pipeline thread
164-
// gets blocked by something in the script like a prompt to the user.
165-
var executeTask =
166-
this.powerShellContext.ExecuteScriptString(
167-
evaluateParams.Expression,
168-
writeInputToHost: true,
169-
writeOutputToHost: true,
170-
addToHistory: true);
171-
172-
// Return the execution result after the task completes so that the
173-
// caller knows when command execution completed.
174-
Task unusedTask =
175-
executeTask.ContinueWith(
176-
(task) =>
177-
{
178-
// Return an empty result since the result value is irrelevant
179-
// for this request in the LanguageServer
180-
return
181-
requestContext.SendResult(
182-
evaluateResponse);
183-
});
184-
}
185-
}
186-
}
187122
}
188123
}

src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -767,8 +767,6 @@ protected async Task HandleEvaluateRequest(
767767

768768
if (isFromRepl)
769769
{
770-
// TODO: Do we send the input through the command handler?
771-
// Send the input through the console service
772770
var notAwaited =
773771
this.editorSession
774772
.PowerShellContext

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
using System.Threading;
2121
using System.Threading.Tasks;
2222

23+
using DebugAdapterMessages = Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
24+
2325
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
2426
{
2527
public class LanguageServer
@@ -116,6 +118,8 @@ public void Start()
116118
this.messageHandlers.SetRequestHandler(NewProjectFromTemplateRequest.Type, this.HandleNewProjectFromTemplateRequest);
117119
this.messageHandlers.SetRequestHandler(GetProjectTemplatesRequest.Type, this.HandleGetProjectTemplatesRequest);
118120

121+
this.messageHandlers.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequest);
122+
119123
this.messageHandlers.SetRequestHandler(GetPSSARulesRequest.Type, this.HandleGetPSSARulesRequest);
120124
this.messageHandlers.SetRequestHandler(SetPSSARulesRequest.Type, this.HandleSetPSSARulesRequest);
121125

@@ -1150,6 +1154,40 @@ await requestContext.SendResult(
11501154
codeActionCommands.ToArray());
11511155
}
11521156

1157+
protected Task HandleEvaluateRequest(
1158+
DebugAdapterMessages.EvaluateRequestArguments evaluateParams,
1159+
RequestContext<DebugAdapterMessages.EvaluateResponseBody> requestContext)
1160+
{
1161+
// We don't await the result of the execution here because we want
1162+
// to be able to receive further messages while the current script
1163+
// is executing. This important in cases where the pipeline thread
1164+
// gets blocked by something in the script like a prompt to the user.
1165+
var executeTask =
1166+
this.editorSession.PowerShellContext.ExecuteScriptString(
1167+
evaluateParams.Expression,
1168+
writeInputToHost: true,
1169+
writeOutputToHost: true,
1170+
addToHistory: true);
1171+
1172+
// Return the execution result after the task completes so that the
1173+
// caller knows when command execution completed.
1174+
executeTask.ContinueWith(
1175+
(task) =>
1176+
{
1177+
// Return an empty result since the result value is irrelevant
1178+
// for this request in the LanguageServer
1179+
return
1180+
requestContext.SendResult(
1181+
new DebugAdapterMessages.EvaluateResponseBody
1182+
{
1183+
Result = "",
1184+
VariablesReference = 0
1185+
});
1186+
});
1187+
1188+
return Task.FromResult(true);
1189+
}
1190+
11531191
#endregion
11541192

11551193
#region Event Handlers

test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,9 @@ public async Task ServiceExecutesReplCommandAndReceivesOutput()
582582
{
583583
OutputReader outputReader = new OutputReader(this.messageHandlers);
584584

585+
// Send the configuration request to initiate the command loop
586+
await this.SendConfigurationRequest(outputReader);
587+
585588
await
586589
this.SendRequest(
587590
EvaluateRequest.Type,
@@ -591,7 +594,6 @@ public async Task ServiceExecutesReplCommandAndReceivesOutput()
591594
});
592595

593596
Assert.Equal("1 + 2", await outputReader.ReadLine());
594-
await outputReader.ReadLine(); // Skip the empty line
595597
Assert.Equal("3", await outputReader.ReadLine());
596598
}
597599

@@ -611,6 +613,9 @@ public async Task ServiceExecutesReplCommandAndReceivesChoicePrompt()
611613
{
612614
OutputReader outputReader = new OutputReader(this.messageHandlers);
613615

616+
// Send the configuration request to initiate the command loop
617+
await this.SendConfigurationRequest(outputReader);
618+
614619
string choiceScript =
615620
@"
616621
$caption = ""Test Choice"";
@@ -648,8 +653,8 @@ await requestContext.SendResult(
648653
ResponseText = "a"
649654
});
650655

651-
// Skip the initial script and prompt lines (6 script lines plus 2 blank lines and 3 prompt lines)
652-
string[] outputLines = await outputReader.ReadLines(11);
656+
// Skip the initial script and prompt lines (6 script lines plus 3 prompt lines)
657+
string[] outputLines = await outputReader.ReadLines(9);
653658

654659
// Wait for the selection to appear as output
655660
await evaluateTask;
@@ -661,6 +666,9 @@ public async Task ServiceExecutesReplCommandAndReceivesInputPrompt()
661666
{
662667
OutputReader outputReader = new OutputReader(this.messageHandlers);
663668

669+
// Send the configuration request to initiate the command loop
670+
await this.SendConfigurationRequest(outputReader);
671+
664672
string promptScript =
665673
@"
666674
$NameField = New-Object System.Management.Automation.Host.FieldDescription ""Name""
@@ -696,8 +704,8 @@ await requestContext.SendResult(
696704
ResponseText = "John"
697705
});
698706

699-
// Skip the initial script lines (4 script lines plus 2 blank lines)
700-
string[] scriptLines = await outputReader.ReadLines(6);
707+
// Skip the initial 4 script lines
708+
string[] scriptLines = await outputReader.ReadLines(4);
701709

702710
// Verify the first line
703711
Assert.Equal("Name: John", await outputReader.ReadLine());
@@ -773,25 +781,7 @@ public async Task ServiceLoadsProfilesOnDemand()
773781
OutputReader outputReader = new OutputReader(this.messageHandlers);
774782

775783
// Send the configuration change to cause profiles to be loaded
776-
await this.languageServiceClient.SendEvent(
777-
DidChangeConfigurationNotification<LanguageServerSettingsWrapper>.Type,
778-
new DidChangeConfigurationParams<LanguageServerSettingsWrapper>
779-
{
780-
Settings = new LanguageServerSettingsWrapper
781-
{
782-
Powershell = new LanguageServerSettings
783-
{
784-
EnableProfileLoading = true,
785-
ScriptAnalysis = new ScriptAnalysisSettings
786-
{
787-
Enable = false
788-
}
789-
}
790-
}
791-
});
792-
793-
// Wait for the prompt to be written once the profile loads
794-
Assert.StartsWith("PS ", await outputReader.ReadLine(waitForNewLine: false));
784+
await this.SendConfigurationRequest(outputReader, true);
795785

796786
Task<EvaluateResponseBody> evaluateTask =
797787
this.SendRequest(
@@ -871,5 +861,31 @@ await this.SendEvent(
871861
await diagnosticWaitTask;
872862
}
873863
}
864+
865+
private async Task SendConfigurationRequest(
866+
OutputReader outputReader,
867+
bool enableProfileLoading = false)
868+
{
869+
// Send the configuration change to cause profiles to be loaded
870+
await this.languageServiceClient.SendEvent(
871+
DidChangeConfigurationNotification<LanguageServerSettingsWrapper>.Type,
872+
new DidChangeConfigurationParams<LanguageServerSettingsWrapper>
873+
{
874+
Settings = new LanguageServerSettingsWrapper
875+
{
876+
Powershell = new LanguageServerSettings
877+
{
878+
EnableProfileLoading = enableProfileLoading,
879+
ScriptAnalysis = new ScriptAnalysisSettings
880+
{
881+
Enable = false
882+
}
883+
}
884+
}
885+
});
886+
887+
// Wait for the prompt to be written once the profile loads
888+
Assert.StartsWith("PS ", await outputReader.ReadLine(waitForNewLine: false));
889+
}
874890
}
875891
}

test/PowerShellEditorServices.Test.Host/OutputReader.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.PowerShell.EditorServices.Utility;
99
using System;
1010
using System.Collections.Generic;
11+
using System.Diagnostics;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314
using Xunit;
@@ -37,7 +38,10 @@ public async Task<string> ReadLine(string expectedOutputCategory = "stdout", boo
3738
string nextOutputString = string.Empty;
3839

3940
// Wait no longer than 7 seconds for output to come back
40-
CancellationTokenSource cancellationSource = new CancellationTokenSource(7000);
41+
CancellationToken cancellationToken =
42+
Debugger.IsAttached
43+
? CancellationToken.None
44+
: new CancellationTokenSource(7000).Token;
4145

4246
// Any lines in the buffer?
4347
if (this.bufferedOutput.Count > 0)
@@ -56,7 +60,7 @@ public async Task<string> ReadLine(string expectedOutputCategory = "stdout", boo
5660
// Execution reaches this point if a buffered line wasn't available
5761
Task<OutputEventBody> outputTask =
5862
this.outputQueue.DequeueAsync(
59-
cancellationSource.Token);
63+
cancellationToken);
6064
OutputEventBody nextOutputEvent = await outputTask;
6165

6266
// Verify that the output is of the expected type

0 commit comments

Comments
 (0)