Skip to content

Commit 604b1f9

Browse files
committed
Finish Get-Credential support
1 parent 6f9b20c commit 604b1f9

16 files changed

+793
-573
lines changed

src/PowerShellEditorServices.Protocol/Server/PromptHandlers.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
1010
using Microsoft.PowerShell.EditorServices.Utility;
1111
using System.Threading.Tasks;
12+
using System.Threading;
13+
using System.Security;
1214

1315
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
1416
{
@@ -44,6 +46,7 @@ internal class ProtocolChoicePromptHandler : ConsoleChoicePromptHandler
4446
{
4547
private IMessageSender messageSender;
4648
private ConsoleService consoleService;
49+
private TaskCompletionSource<string> readLineTask;
4750

4851
public ProtocolChoicePromptHandler(
4952
IMessageSender messageSender,
@@ -73,6 +76,12 @@ protected override void ShowPrompt(PromptStyle promptStyle)
7376
.ConfigureAwait(false);
7477
}
7578

79+
protected override Task<string> ReadInputString(CancellationToken cancellationToken)
80+
{
81+
this.readLineTask = new TaskCompletionSource<string>();
82+
return this.readLineTask.Task;
83+
}
84+
7685
private void HandlePromptResponse(
7786
Task<ShowChoicePromptResponse> responseTask)
7887
{
@@ -82,9 +91,7 @@ private void HandlePromptResponse(
8291

8392
if (!response.PromptCancelled)
8493
{
85-
this.consoleService.ReceivePromptResponse(
86-
response.ResponseText,
87-
false);
94+
this.readLineTask.TrySetResult(response.ResponseText);
8895
}
8996
else
9097
{
@@ -106,13 +113,16 @@ private void HandlePromptResponse(
106113
// Cancel the current prompt
107114
this.consoleService.SendControlC();
108115
}
116+
117+
this.readLineTask = null;
109118
}
110119
}
111120

112121
internal class ProtocolInputPromptHandler : ConsoleInputPromptHandler
113122
{
114123
private IMessageSender messageSender;
115124
private ConsoleService consoleService;
125+
private TaskCompletionSource<string> readLineTask;
116126

117127
public ProtocolInputPromptHandler(
118128
IMessageSender messageSender,
@@ -153,6 +163,12 @@ protected override void ShowFieldPrompt(FieldDetails fieldDetails)
153163
.ConfigureAwait(false);
154164
}
155165

166+
protected override Task<string> ReadInputString(CancellationToken cancellationToken)
167+
{
168+
this.readLineTask = new TaskCompletionSource<string>();
169+
return this.readLineTask.Task;
170+
}
171+
156172
private void HandlePromptResponse(
157173
Task<ShowInputPromptResponse> responseTask)
158174
{
@@ -162,9 +178,7 @@ private void HandlePromptResponse(
162178

163179
if (!response.PromptCancelled)
164180
{
165-
this.consoleService.ReceivePromptResponse(
166-
response.ResponseText,
167-
true);
181+
this.readLineTask.TrySetResult(response.ResponseText);
168182
}
169183
else
170184
{
@@ -186,6 +200,8 @@ private void HandlePromptResponse(
186200
// Cancel the current prompt
187201
this.consoleService.SendControlC();
188202
}
203+
204+
this.readLineTask = null;
189205
}
190206
}
191207
}

src/PowerShellEditorServices/Console/ChoicePromptHandler.cs

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

66
using System;
77
using System.Collections.Generic;
8-
using System.Collections.ObjectModel;
98
using System.Linq;
109
using System.Threading;
1110
using System.Threading.Tasks;
@@ -40,7 +39,8 @@ public abstract class ChoicePromptHandler : PromptHandler
4039
{
4140
#region Private Fields
4241

43-
private TaskCompletionSource<int[]> promptTask;
42+
private CancellationTokenSource promptCancellationTokenSource =
43+
new CancellationTokenSource();
4444

4545
#endregion
4646

@@ -110,7 +110,6 @@ public Task<int> PromptForChoice(
110110
this.Caption = promptCaption;
111111
this.Message = promptMessage;
112112
this.Choices = choices;
113-
this.promptTask = new TaskCompletionSource<int[]>();
114113

115114
this.DefaultChoices =
116115
defaultChoice == -1
@@ -120,12 +119,23 @@ public Task<int> PromptForChoice(
120119
// Cancel the TaskCompletionSource if the caller cancels the task
121120
cancellationToken.Register(this.CancelPrompt, true);
122121

123-
// Show the prompt to the user
124-
this.ShowPrompt(PromptStyle.Full);
125-
126122
// Convert the int[] result to int
127-
return this.promptTask.Task.ContinueWith(
128-
t => t.Result.DefaultIfEmpty(-1).First());
123+
return
124+
this.StartPromptLoop(this.promptCancellationTokenSource.Token)
125+
.ContinueWith(
126+
task =>
127+
{
128+
if (task.IsFaulted)
129+
{
130+
throw task.Exception;
131+
}
132+
else if (task.IsCanceled)
133+
{
134+
throw new TaskCanceledException(task);
135+
}
136+
137+
return this.GetSingleResult(task.Result);
138+
});
129139
}
130140

131141
/// <summary>
@@ -165,16 +175,51 @@ public Task<int[]> PromptForChoice(
165175
this.Choices = choices;
166176
this.DefaultChoices = defaultChoices;
167177
this.IsMultiChoice = true;
168-
this.promptTask = new TaskCompletionSource<int[]>();
169178

170179
// Cancel the TaskCompletionSource if the caller cancels the task
171180
cancellationToken.Register(this.CancelPrompt, true);
172181

182+
return this.StartPromptLoop(this.promptCancellationTokenSource.Token);
183+
}
184+
185+
private async Task<int[]> StartPromptLoop(
186+
CancellationToken cancellationToken)
187+
{
188+
int[] choiceIndexes = null;
189+
173190
// Show the prompt to the user
174191
this.ShowPrompt(PromptStyle.Full);
175192

176-
return this.promptTask.Task;
193+
while (!cancellationToken.IsCancellationRequested)
194+
{
195+
string responseString = await this.ReadInputString(cancellationToken);
196+
if (responseString == null)
197+
{
198+
// If the response string is null, the prompt has been cancelled
199+
break;
200+
}
201+
202+
// If the handler returns null it means we should prompt again
203+
choiceIndexes = this.HandleResponse(responseString);
204+
if (choiceIndexes != null)
205+
{
206+
break;
207+
}
208+
209+
// The user did not respond with a valid choice,
210+
// show the prompt again to give another chance
211+
this.ShowPrompt(PromptStyle.Minimal);
212+
}
213+
214+
if (cancellationToken.IsCancellationRequested)
215+
{
216+
// Throw a TaskCanceledException to stop the pipeline
217+
throw new TaskCanceledException();
218+
}
219+
220+
return choiceIndexes?.ToArray();
177221
}
222+
178223
/// <summary>
179224
/// Implements behavior to handle the user's response.
180225
/// </summary>
@@ -183,7 +228,7 @@ public Task<int[]> PromptForChoice(
183228
/// True if the prompt is complete, false if the prompt is
184229
/// still waiting for a valid response.
185230
/// </returns>
186-
public override bool HandleResponse(string responseString)
231+
protected virtual int[] HandleResponse(string responseString)
187232
{
188233
List<int> choiceIndexes = new List<int>();
189234

@@ -215,12 +260,10 @@ public override bool HandleResponse(string responseString)
215260
{
216261
// The user did not respond with a valid choice,
217262
// show the prompt again to give another chance
218-
this.ShowPrompt(PromptStyle.Minimal);
219-
return false;
263+
return null;
220264
}
221265

222-
this.promptTask.SetResult(choiceIndexes.ToArray());
223-
return true;
266+
return choiceIndexes.ToArray();
224267
}
225268

226269
/// <summary>
@@ -229,7 +272,7 @@ public override bool HandleResponse(string responseString)
229272
protected override void OnPromptCancelled()
230273
{
231274
// Cancel the prompt task
232-
this.promptTask.TrySetCanceled();
275+
this.promptCancellationTokenSource.Cancel();
233276
}
234277

235278
#endregion
@@ -244,6 +287,30 @@ protected override void OnPromptCancelled()
244287
/// </param>
245288
protected abstract void ShowPrompt(PromptStyle promptStyle);
246289

290+
/// <summary>
291+
/// Reads an input string asynchronously from the console.
292+
/// </summary>
293+
/// <param name="cancellationToken">
294+
/// A CancellationToken that can be used to cancel the read.
295+
/// </param>
296+
/// <returns>
297+
/// A Task instance that can be monitored for completion to get
298+
/// the user's input.
299+
/// </returns>
300+
protected abstract Task<string> ReadInputString(CancellationToken cancellationToken);
301+
302+
#endregion
303+
304+
#region Private Methods
305+
306+
private int GetSingleResult(int[] choiceArray)
307+
{
308+
return
309+
choiceArray != null
310+
? choiceArray.DefaultIfEmpty(-1).First()
311+
: -1;
312+
}
313+
247314
#endregion
248315
}
249316
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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 System;
7+
using System.Collections;
8+
9+
namespace Microsoft.PowerShell.EditorServices.Console
10+
{
11+
public class CollectionFieldDetails : FieldDetails
12+
{
13+
#region Private Fields
14+
15+
private bool isArray;
16+
private bool isEntryComplete;
17+
private string fieldName;
18+
private int currentCollectionIndex;
19+
private ArrayList collectionItems = new ArrayList();
20+
21+
#endregion
22+
23+
#region Constructors
24+
25+
public CollectionFieldDetails(
26+
string name,
27+
string label,
28+
Type fieldType,
29+
bool isMandatory,
30+
object defaultValue)
31+
: base(name, label, fieldType, isMandatory, defaultValue)
32+
{
33+
this.fieldName = name;
34+
35+
this.FieldType = typeof(object);
36+
37+
if (fieldType.IsArray)
38+
{
39+
this.isArray = true;
40+
this.FieldType = fieldType.GetElementType();
41+
}
42+
43+
this.Name =
44+
string.Format(
45+
"{0}[{1}]",
46+
this.fieldName,
47+
this.currentCollectionIndex);
48+
}
49+
50+
#endregion
51+
52+
#region Public Methods
53+
54+
public override FieldDetails GetNextField()
55+
{
56+
if (!this.isEntryComplete)
57+
{
58+
// Get the next collection field
59+
this.currentCollectionIndex++;
60+
this.Name =
61+
string.Format(
62+
"{0}[{1}]",
63+
this.fieldName,
64+
this.currentCollectionIndex);
65+
66+
return this;
67+
}
68+
else
69+
{
70+
return null;
71+
}
72+
}
73+
74+
public override void SetValue(object fieldValue, bool hasValue)
75+
{
76+
if (hasValue)
77+
{
78+
// Add the item to the collection
79+
this.collectionItems.Add(fieldValue);
80+
}
81+
else
82+
{
83+
this.isEntryComplete = true;
84+
}
85+
}
86+
87+
protected override object OnGetValue()
88+
{
89+
object collection = this.collectionItems;
90+
91+
// Should the result collection be an array?
92+
if (this.isArray)
93+
{
94+
// Convert the ArrayList to an array
95+
collection =
96+
this.collectionItems.ToArray(
97+
this.FieldType);
98+
}
99+
100+
return collection;
101+
}
102+
103+
#endregion
104+
}
105+
}

0 commit comments

Comments
 (0)