Skip to content

Commit e435301

Browse files
committed
Merge pull request #109 from PowerShell/daviwil/choice-prompt-support
Add choice prompt handling for PSHostUserInterface
2 parents 5958b44 + 2f49a79 commit e435301

34 files changed

+2007
-945
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
7+
8+
namespace Microsoft.PowerShell.EditorServices.Protocol.Messages
9+
{
10+
public class ShowChoicePromptNotification
11+
{
12+
public static readonly
13+
EventType<ShowChoicePromptNotification> Type =
14+
EventType<ShowChoicePromptNotification>.Create("powerShell/showChoicePrompt");
15+
16+
public string Caption { get; set; }
17+
18+
public string Message { get; set; }
19+
20+
public ChoiceDetails[] Choices { get; set; }
21+
22+
public int DefaultChoice { get; set; }
23+
}
24+
25+
public class CompleteChoicePromptNotification
26+
{
27+
public static readonly
28+
EventType<CompleteChoicePromptNotification> Type =
29+
EventType<CompleteChoicePromptNotification>.Create("powerShell/completeChoicePrompt");
30+
31+
public bool PromptCancelled { get; set; }
32+
33+
public string ChosenItem { get; set; }
34+
}
35+
}
36+

src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<Compile Include="DebugAdapter\AttachRequest.cs" />
6565
<Compile Include="DebugAdapter\Breakpoint.cs" />
6666
<Compile Include="DebugAdapter\ContinueRequest.cs" />
67+
<Compile Include="Messages\PromptEvents.cs" />
6768
<Compile Include="Server\DebugAdapter.cs" />
6869
<Compile Include="Server\DebugAdapterBase.cs" />
6970
<Compile Include="Client\DebugAdapterClientBase.cs" />
@@ -101,6 +102,7 @@
101102
<Compile Include="LanguageServer\ExpandAliasRequest.cs" />
102103
<Compile Include="LanguageServer\Hover.cs" />
103104
<Compile Include="Client\LanguageClientBase.cs" />
105+
<Compile Include="Server\IEventWriter.cs" />
104106
<Compile Include="Server\LanguageServer.cs" />
105107
<Compile Include="Server\LanguageServerBase.cs" />
106108
<Compile Include="LanguageServer\ShowOnlineHelpRequest.cs" />
@@ -132,6 +134,7 @@
132134
<Compile Include="Properties\AssemblyInfo.cs" />
133135
<Compile Include="LanguageServer\References.cs" />
134136
<Compile Include="Server\LanguageServerSettings.cs" />
137+
<Compile Include="Server\PromptHandlers.cs" />
135138
<Compile Include="Server\ProtocolServer.cs" />
136139
</ItemGroup>
137140
<ItemGroup>

src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public DebugAdapter(ChannelBase serverChannel) : base(serverChannel)
2929
this.editorSession = new EditorSession();
3030
this.editorSession.StartSession();
3131
this.editorSession.DebugService.DebuggerStopped += this.DebugService_DebuggerStopped;
32-
this.editorSession.PowerShellContext.OutputWritten += this.powerShellContext_OutputWritten;
32+
this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
3333
}
3434

3535
protected override void Initialize()
@@ -312,27 +312,37 @@ protected async Task HandleEvaluateRequest(
312312
EvaluateRequestArguments evaluateParams,
313313
RequestContext<EvaluateResponseBody> requestContext)
314314
{
315+
string valueString = null;
316+
int variableId = 0;
317+
315318
bool isFromRepl =
316319
string.Equals(
317320
evaluateParams.Context,
318321
"repl",
319322
StringComparison.InvariantCultureIgnoreCase);
320323

321-
VariableDetails result =
322-
await editorSession.DebugService.EvaluateExpression(
324+
if (isFromRepl)
325+
{
326+
// Send the input through the console service
327+
editorSession.ConsoleService.ReceiveInputString(
323328
evaluateParams.Expression,
324-
evaluateParams.FrameId,
325-
isFromRepl);
326-
327-
string valueString = null;
328-
int variableId = 0;
329-
330-
if (result != null)
329+
false);
330+
}
331+
else
331332
{
332-
valueString = result.ValueString;
333-
variableId =
334-
result.IsExpandable ?
335-
result.Id : 0;
333+
VariableDetails result =
334+
await editorSession.DebugService.EvaluateExpression(
335+
evaluateParams.Expression,
336+
evaluateParams.FrameId,
337+
isFromRepl);
338+
339+
if (result != null)
340+
{
341+
valueString = result.ValueString;
342+
variableId =
343+
result.IsExpandable ?
344+
result.Id : 0;
345+
}
336346
}
337347

338348
await requestContext.SendResult(
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
7+
using System.Threading.Tasks;
8+
9+
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
10+
{
11+
internal interface IEventWriter
12+
{
13+
Task SendEvent<TParams>(
14+
EventType<TParams> eventType,
15+
TParams eventParams);
16+
}
17+
}
18+

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@
66
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
77
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
88
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
9+
using Microsoft.PowerShell.EditorServices.Protocol.Messages;
910
using Microsoft.PowerShell.EditorServices.Utility;
1011
using Nito.AsyncEx;
1112
using System;
1213
using System.Collections.Generic;
1314
using System.IO;
1415
using System.Linq;
1516
using System.Management.Automation;
16-
using System.Management.Automation.Language;
1717
using System.Text.RegularExpressions;
1818
using System.Threading;
1919
using System.Threading.Tasks;
2020
using DebugAdapterMessages = Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
2121

2222
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
2323
{
24-
public class LanguageServer : LanguageServerBase
24+
public class LanguageServer : LanguageServerBase, IEventWriter
2525
{
2626
private static CancellationTokenSource existingRequestCancellation;
2727

@@ -36,7 +36,13 @@ public LanguageServer(ChannelBase serverChannel) : base(serverChannel)
3636
{
3737
this.editorSession = new EditorSession();
3838
this.editorSession.StartSession();
39-
this.editorSession.PowerShellContext.OutputWritten += this.powerShellContext_OutputWritten;
39+
this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
40+
41+
// Always send console prompts through the UI in the language service
42+
// TODO: This will change later once we have a general REPL available
43+
// in VS Code.
44+
this.editorSession.ConsoleService.PushPromptHandlerContext(
45+
new ProtocolPromptHandlerContext(this));
4046
}
4147

4248
protected override void Initialize()
@@ -62,6 +68,7 @@ protected override void Initialize()
6268

6369
this.SetRequestHandler(ShowOnlineHelpRequest.Type, this.HandleShowOnlineHelpRequest);
6470
this.SetRequestHandler(ExpandAliasRequest.Type, this.HandleExpandAliasRequest);
71+
this.SetEventHandler(CompleteChoicePromptNotification.Type, this.HandleCompleteChoicePromptNotification);
6572

6673
this.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequest);
6774
}
@@ -166,6 +173,25 @@ Sort Start -Descending
166173
await requestContext.SendResult(result.First().ToString());
167174
}
168175

176+
protected Task HandleCompleteChoicePromptNotification(
177+
CompleteChoicePromptNotification completeChoicePromptParams,
178+
EventContext eventContext)
179+
{
180+
if (!completeChoicePromptParams.PromptCancelled)
181+
{
182+
this.editorSession.ConsoleService.ReceiveInputString(
183+
completeChoicePromptParams.ChosenItem,
184+
false);
185+
}
186+
else
187+
{
188+
// Cancel the current prompt
189+
this.editorSession.ConsoleService.SendControlC();
190+
}
191+
192+
return Task.FromResult(true);
193+
}
194+
169195
protected Task HandleDidOpenTextDocumentNotification(
170196
DidOpenTextDocumentNotification openParams,
171197
EventContext eventContext)
@@ -676,11 +702,15 @@ protected async Task HandleEvaluateRequest(
676702
DebugAdapterMessages.EvaluateRequestArguments evaluateParams,
677703
RequestContext<DebugAdapterMessages.EvaluateResponseBody> requestContext)
678704
{
679-
var results =
680-
await this.editorSession.PowerShellContext.ExecuteScriptString(
705+
// We don't await the result of the execution here because we want
706+
// to be able to receive further messages while the current script
707+
// is executing. This important in cases where the pipeline thread
708+
// gets blocked by something in the script like a prompt to the user.
709+
var executeTask =
710+
this.editorSession.PowerShellContext.ExecuteScriptString(
681711
evaluateParams.Expression,
682712
true,
683-
true);
713+
true).ConfigureAwait(false);
684714

685715
// Return an empty result since the result value is irrelevant
686716
// for this request in the LanguageServer
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using Microsoft.PowerShell.EditorServices.Console;
7+
using Microsoft.PowerShell.EditorServices.Protocol.Messages;
8+
9+
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
10+
{
11+
internal class ProtocolPromptHandlerContext : IPromptHandlerContext
12+
{
13+
private IEventWriter eventWriter;
14+
15+
public ProtocolPromptHandlerContext(IEventWriter eventWriter)
16+
{
17+
this.eventWriter = eventWriter;
18+
}
19+
20+
public ChoicePromptHandler GetChoicePromptHandler()
21+
{
22+
return new ProtocolChoicePromptHandler(this.eventWriter);
23+
}
24+
}
25+
26+
internal class ProtocolChoicePromptHandler : ChoicePromptHandler
27+
{
28+
private IEventWriter eventWriter;
29+
30+
public ProtocolChoicePromptHandler(IEventWriter eventWriter)
31+
{
32+
this.eventWriter = eventWriter;
33+
}
34+
35+
protected override void ShowPrompt(PromptStyle promptStyle)
36+
{
37+
eventWriter.SendEvent(
38+
ShowChoicePromptNotification.Type,
39+
new ShowChoicePromptNotification
40+
{
41+
Caption = this.Caption,
42+
Message = this.Message,
43+
Choices = this.Choices,
44+
DefaultChoice = this.DefaultChoice
45+
}).ConfigureAwait(false);
46+
}
47+
}
48+
}
49+

src/PowerShellEditorServices.Protocol/Server/ProtocolServer.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
77
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
8-
using Newtonsoft.Json.Linq;
98
using System;
109
using System.Threading;
1110
using System.Threading.Tasks;
@@ -110,15 +109,19 @@ public Task SendEvent<TParams>(
110109

111110
if (!this.serverChannel.MessageDispatcher.InMessageLoopThread)
112111
{
112+
TaskCompletionSource<bool> writeTask = new TaskCompletionSource<bool>();
113+
113114
this.serverChannel.MessageDispatcher.SynchronizationContext.Post(
114115
async (obj) =>
115116
{
116117
await this.serverChannel.MessageWriter.WriteEvent(
117118
eventType,
118119
eventParams);
120+
121+
writeTask.SetResult(true);
119122
}, null);
120123

121-
return Task.FromResult(true);
124+
return writeTask.Task;
122125
}
123126
else
124127
{

0 commit comments

Comments
 (0)