Skip to content

Commit b51cc75

Browse files
Address remaining feedback
1 parent 86ab115 commit b51cc75

11 files changed

+91
-52
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ registered_data.ini
2929
.dotnet/
3030
module/Plaster
3131
module/PSScriptAnalyzer
32+
module/PSReadLine
3233
docs/_site/
3334
docs/_repo/
3435
docs/metadata/

src/PowerShellEditorServices/Console/ConsoleReadLine.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ public ConsoleReadLine(PowerShellContext powerShellContext)
3939

4040
public Task<string> ReadCommandLine(CancellationToken cancellationToken)
4141
{
42-
return this.ReadLine(true, cancellationToken);
42+
return this.ReadLineAsync(true, cancellationToken);
4343
}
4444

4545
public Task<string> ReadSimpleLine(CancellationToken cancellationToken)
4646
{
47-
return this.ReadLine(false, cancellationToken);
47+
return this.ReadLineAsync(false, cancellationToken);
4848
}
4949

5050
public async Task<SecureString> ReadSecureLine(CancellationToken cancellationToken)
@@ -135,9 +135,9 @@ private static async Task<ConsoleKeyInfo> ReadKeyAsync(CancellationToken cancell
135135
return await ConsoleProxy.ReadKeyAsync(cancellationToken);
136136
}
137137

138-
private async Task<string> ReadLine(bool isCommandLine, CancellationToken cancellationToken)
138+
private async Task<string> ReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
139139
{
140-
return await this.powerShellContext.InvokeReadLine(isCommandLine, cancellationToken);
140+
return await this.powerShellContext.InvokeReadLineAsync(isCommandLine, cancellationToken);
141141
}
142142

143143
/// <summary>
@@ -155,7 +155,7 @@ private async Task<string> ReadLine(bool isCommandLine, CancellationToken cancel
155155
/// A task object representing the asynchronus operation. The Result property on
156156
/// the task object returns the user input string.
157157
/// </returns>
158-
internal async Task<string> InvokeLegacyReadLine(bool isCommandLine, CancellationToken cancellationToken)
158+
internal async Task<string> InvokeLegacyReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
159159
{
160160
string inputBeforeCompletion = null;
161161
string inputAfterCompletion = null;

src/PowerShellEditorServices/Console/WindowsConsoleOperations.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
using System;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using Microsoft.PowerShell.EditorServices.Utility;
910

1011
namespace Microsoft.PowerShell.EditorServices.Console
1112
{
1213
internal class WindowsConsoleOperations : IConsoleOperations
1314
{
1415
private ConsoleKeyInfo? _bufferedKey;
1516

16-
private SemaphoreSlim _readKeyHandle = new SemaphoreSlim(1, 1);
17+
private SemaphoreSlim _readKeyHandle = AsyncUtils.CreateSimpleLockingSemaphore();
1718

1819
public int GetCursorLeft() => System.Console.CursorLeft;
1920

src/PowerShellEditorServices/Session/IPromptContext.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public interface IPromptContext
2424
/// A task object that represents the completion of reading input. The Result property will
2525
/// return the input string.
2626
/// </returns>
27-
Task<string> InvokeReadLine(bool isCommandLine, CancellationToken cancellationToken);
27+
Task<string> InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken);
2828

2929
/// <summary>
3030
/// Performs any additional actions required to cancel the current ReadLine invocation.

src/PowerShellEditorServices/Session/LegacyReadLineContext.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ public Task AbortReadLineAsync()
2323
return Task.FromResult(true);
2424
}
2525

26-
public async Task<string> InvokeReadLine(bool isCommandLine, CancellationToken cancellationToken)
26+
public async Task<string> InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
2727
{
28-
return await _legacyReadLine.InvokeLegacyReadLine(isCommandLine, cancellationToken);
28+
return await _legacyReadLine.InvokeLegacyReadLineAsync(isCommandLine, cancellationToken);
2929
}
3030

3131
public Task WaitForReadLineExitAsync()

src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs

+8-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System;
1111
using System.Management.Automation.Runspaces;
1212
using Microsoft.PowerShell.EditorServices.Console;
13+
using Microsoft.PowerShell.EditorServices.Utility;
1314

1415
namespace Microsoft.PowerShell.EditorServices.Session {
1516
using System.Management.Automation;
@@ -75,7 +76,10 @@ internal PSReadLinePromptContext(
7576
#endif
7677
}
7778

78-
internal static bool TryGetPSReadLineProxy(Runspace runspace, out PSReadLineProxy readLineProxy)
79+
internal static bool TryGetPSReadLineProxy(
80+
ILogger logger,
81+
Runspace runspace,
82+
out PSReadLineProxy readLineProxy)
7983
{
8084
readLineProxy = null;
8185
using (var pwsh = PowerShell.Create())
@@ -93,7 +97,7 @@ internal static bool TryGetPSReadLineProxy(Runspace runspace, out PSReadLineProx
9397

9498
try
9599
{
96-
readLineProxy = new PSReadLineProxy(psReadLineType);
100+
readLineProxy = new PSReadLineProxy(psReadLineType, logger);
97101
}
98102
catch (InvalidOperationException)
99103
{
@@ -107,7 +111,7 @@ internal static bool TryGetPSReadLineProxy(Runspace runspace, out PSReadLineProx
107111
return true;
108112
}
109113

110-
public async Task<string> InvokeReadLine(bool isCommandLine, CancellationToken cancellationToken)
114+
public async Task<string> InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
111115
{
112116
_readLineCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
113117
var localTokenSource = _readLineCancellationSource;
@@ -120,7 +124,7 @@ public async Task<string> InvokeReadLine(bool isCommandLine, CancellationToken c
120124
{
121125
if (!isCommandLine)
122126
{
123-
return await _consoleReadLine.InvokeLegacyReadLine(
127+
return await _consoleReadLine.InvokeLegacyReadLineAsync(
124128
false,
125129
_readLineCancellationSource.Token);
126130
}

src/PowerShellEditorServices/Session/PSReadLineProxy.cs

+49-8
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@
55

66
using System;
77
using System.Reflection;
8+
using Microsoft.PowerShell.EditorServices.Utility;
89

910
namespace Microsoft.PowerShell.EditorServices.Session
1011
{
1112
internal class PSReadLineProxy
1213
{
14+
private const string FieldMemberType = "field";
15+
16+
private const string MethodMemberType = "method";
17+
1318
private const string AddToHistoryMethodName = "AddToHistory";
1419

1520
private const string SetKeyHandlerMethodName = "SetKeyHandler";
@@ -20,19 +25,19 @@ internal class PSReadLineProxy
2025

2126
private const string ForcePSEventHandlingMethodName = "ForcePSEventHandling";
2227

23-
private static readonly Type[] s_setKeyHandlerTypes = new Type[4]
28+
private static readonly Type[] s_setKeyHandlerTypes =
2429
{
2530
typeof(string[]),
2631
typeof(Action<ConsoleKeyInfo?, object>),
2732
typeof(string),
2833
typeof(string)
2934
};
3035

31-
private static readonly Type[] s_addToHistoryTypes = new Type[1] { typeof(string) };
36+
private static readonly Type[] s_addToHistoryTypes = { typeof(string) };
3237

3338
private readonly FieldInfo _readKeyOverrideField;
3439

35-
internal PSReadLineProxy(Type psConsoleReadLine)
40+
internal PSReadLineProxy(Type psConsoleReadLine, ILogger logger)
3641
{
3742
ForcePSEventHandling =
3843
(Action)psConsoleReadLine.GetMethod(
@@ -55,12 +60,36 @@ internal PSReadLineProxy(Type psConsoleReadLine)
5560
.GetType(VirtualTerminalTypeName)
5661
?.GetField(ReadKeyOverrideFieldName, BindingFlags.Static | BindingFlags.NonPublic);
5762

58-
if (_readKeyOverrideField == null ||
59-
SetKeyHandler == null ||
60-
AddToHistory == null ||
61-
ForcePSEventHandling == null)
63+
if (_readKeyOverrideField == null)
64+
{
65+
throw NewInvalidPSReadLineVersionException(
66+
FieldMemberType,
67+
ReadKeyOverrideFieldName,
68+
logger);
69+
}
70+
71+
if (SetKeyHandler == null)
72+
{
73+
throw NewInvalidPSReadLineVersionException(
74+
MethodMemberType,
75+
SetKeyHandlerMethodName,
76+
logger);
77+
}
78+
79+
if (AddToHistory == null)
6280
{
63-
throw new InvalidOperationException();
81+
throw NewInvalidPSReadLineVersionException(
82+
MethodMemberType,
83+
AddToHistoryMethodName,
84+
logger);
85+
}
86+
87+
if (ForcePSEventHandling == null)
88+
{
89+
throw NewInvalidPSReadLineVersionException(
90+
MethodMemberType,
91+
ForcePSEventHandlingMethodName,
92+
logger);
6493
}
6594
}
6695

@@ -74,5 +103,17 @@ internal void OverrideReadKey(Func<bool, ConsoleKeyInfo> readKeyFunc)
74103
{
75104
_readKeyOverrideField.SetValue(null, readKeyFunc);
76105
}
106+
107+
private static InvalidOperationException NewInvalidPSReadLineVersionException(
108+
string memberType,
109+
string memberName,
110+
ILogger logger)
111+
{
112+
logger.Write(
113+
LogLevel.Error,
114+
$"The loaded version of PSReadLine is not supported. The {memberType} \"{memberName}\" was not found.");
115+
116+
return new InvalidOperationException();
117+
}
77118
}
78119
}

src/PowerShellEditorServices/Session/PowerShell3Operations.cs

-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ public IEnumerable<TResult> ExecuteCommandInDebugger<TResult>(
7373

7474
public void StopCommandInDebugger(PowerShellContext powerShellContext)
7575
{
76-
// TODO: Possibly save the pipeline to a field and initiate stop here. Or just throw.
7776
}
7877

7978
public bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace)

src/PowerShellEditorServices/Session/PowerShellContext.cs

+17-17
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,26 @@
33
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
44
//
55

6-
using Microsoft.PowerShell.EditorServices.Utility;
76
using System;
8-
using System.Globalization;
97
using System.Collections.Generic;
108
using System.Collections.ObjectModel;
9+
using System.Globalization;
10+
using System.IO;
1111
using System.Linq;
12+
using System.Management.Automation.Host;
13+
using System.Management.Automation.Remoting;
14+
using System.Management.Automation.Runspaces;
1215
using System.Text;
1316
using System.Text.RegularExpressions;
1417
using System.Threading;
1518
using System.Threading.Tasks;
19+
using Microsoft.PowerShell.EditorServices.Session;
20+
using Microsoft.PowerShell.EditorServices.Session.Capabilities;
21+
using Microsoft.PowerShell.EditorServices.Utility;
1622

1723
namespace Microsoft.PowerShell.EditorServices
1824
{
19-
using Session;
2025
using System.Management.Automation;
21-
using System.Management.Automation.Host;
22-
using System.Management.Automation.Runspaces;
23-
using Microsoft.PowerShell.EditorServices.Session.Capabilities;
24-
using System.IO;
25-
using System.Management.Automation.Remoting;
2626

2727
/// <summary>
2828
/// Manages the lifetime and usage of a PowerShell session.
@@ -33,7 +33,7 @@ public class PowerShellContext : IDisposable, IHostSupportsInteractiveSession
3333
{
3434
#region Fields
3535

36-
private readonly SemaphoreSlim resumeRequestHandle = new SemaphoreSlim(1, 1);
36+
private readonly SemaphoreSlim resumeRequestHandle = AsyncUtils.CreateSimpleLockingSemaphore();
3737

3838
private bool isPSReadLineEnabled;
3939
private ILogger logger;
@@ -48,7 +48,7 @@ public class PowerShellContext : IDisposable, IHostSupportsInteractiveSession
4848

4949
private Stack<RunspaceDetails> runspaceStack = new Stack<RunspaceDetails>();
5050

51-
private bool isCommandLoopRestarterSet;
51+
private int isCommandLoopRestarterSet;
5252

5353
#endregion
5454

@@ -307,11 +307,11 @@ public void Initialize(
307307
this.powerShell,
308308
this.ConsoleReader,
309309
this.versionSpecificOperations);
310-
this.InvocationEventQueue = InvocationEventQueue.Create(this, this.PromptNest);
310+
this.InvocationEventQueue = InvocationEventQueue.Create(this, this.PromptNest, this.logger);
311311

312312
if (powerShellVersion.Major >= 5 &&
313313
this.isPSReadLineEnabled &&
314-
PSReadLinePromptContext.TryGetPSReadLineProxy(initialRunspace, out PSReadLineProxy proxy))
314+
PSReadLinePromptContext.TryGetPSReadLineProxy(logger, initialRunspace, out PSReadLineProxy proxy))
315315
{
316316
this.PromptContext = new PSReadLinePromptContext(
317317
this,
@@ -1006,9 +1006,9 @@ internal async Task InvokeOnPipelineThread(Action<PowerShell> invocationAction)
10061006
invocationAction.Invoke(this.PromptNest.GetPowerShell());
10071007
}
10081008

1009-
internal async Task<string> InvokeReadLine(bool isCommandLine, CancellationToken cancellationToken)
1009+
internal async Task<string> InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
10101010
{
1011-
return await PromptContext.InvokeReadLine(
1011+
return await PromptContext.InvokeReadLineAsync(
10121012
isCommandLine,
10131013
cancellationToken);
10141014
}
@@ -2060,7 +2060,7 @@ private void HandleRunspaceStateChanged(object sender, RunspaceStateEventArgs ar
20602060

20612061
private void StartCommandLoopOnRunspaceAvailable()
20622062
{
2063-
if (this.isCommandLoopRestarterSet)
2063+
if (Interlocked.CompareExchange(ref this.isCommandLoopRestarterSet, 1, 1) == 1)
20642064
{
20652065
return;
20662066
}
@@ -2075,12 +2075,12 @@ private void StartCommandLoopOnRunspaceAvailable()
20752075
}
20762076

20772077
((Runspace)runspace).AvailabilityChanged -= handler;
2078-
this.isCommandLoopRestarterSet = false;
2078+
Interlocked.Exchange(ref this.isCommandLoopRestarterSet, 0);
20792079
this.ConsoleReader?.StartCommandLoop();
20802080
};
20812081

20822082
this.CurrentRunspace.Runspace.AvailabilityChanged += handler;
2083-
this.isCommandLoopRestarterSet = true;
2083+
Interlocked.Exchange(ref this.isCommandLoopRestarterSet, 1);
20842084
}
20852085

20862086
private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)

src/PowerShellEditorServices/Session/PromptNestFrame.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ protected virtual void Dispose(bool disposing)
9292
PowerShell.Runspace = null;
9393
PowerShell.Dispose();
9494
},
95-
null);
95+
state: null);
9696
}
9797
else
9898
{

0 commit comments

Comments
 (0)