@@ -16,6 +16,8 @@ public class Channel : IDisposable
16
16
private readonly Type _psrlType ;
17
17
private readonly Runspace _runspace ;
18
18
private readonly MethodInfo _psrlInsert , _psrlRevertLine , _psrlAcceptLine ;
19
+ private readonly FieldInfo _psrlHandleResizing , _psrlReadLineReady ;
20
+ private readonly object _psrlSingleton ;
19
21
private readonly ManualResetEvent _connSetupWaitHandler ;
20
22
private readonly Predictor _predictor ;
21
23
private readonly ScriptBlock _onIdleAction ;
@@ -40,10 +42,17 @@ private Channel(Runspace runspace, Type psConsoleReadLineType)
40
42
. Append ( Path . GetFileNameWithoutExtension ( Environment . ProcessPath ) )
41
43
. ToString ( ) ;
42
44
43
- BindingFlags bindingFlags = BindingFlags . Static | BindingFlags . Public ;
44
- _psrlInsert = _psrlType . GetMethod ( "Insert" , bindingFlags , [ typeof ( string ) ] ) ;
45
- _psrlRevertLine = _psrlType . GetMethod ( "RevertLine" , bindingFlags ) ;
46
- _psrlAcceptLine = _psrlType . GetMethod ( "AcceptLine" , bindingFlags ) ;
45
+ BindingFlags methodFlags = BindingFlags . Static | BindingFlags . Public ;
46
+ _psrlInsert = _psrlType . GetMethod ( "Insert" , methodFlags , [ typeof ( string ) ] ) ;
47
+ _psrlRevertLine = _psrlType . GetMethod ( "RevertLine" , methodFlags ) ;
48
+ _psrlAcceptLine = _psrlType . GetMethod ( "AcceptLine" , methodFlags ) ;
49
+
50
+ FieldInfo singletonInfo = _psrlType . GetField ( "_singleton" , BindingFlags . Static | BindingFlags . NonPublic ) ;
51
+ _psrlSingleton = singletonInfo . GetValue ( null ) ;
52
+
53
+ BindingFlags fieldFlags = BindingFlags . Instance | BindingFlags . NonPublic ;
54
+ _psrlReadLineReady = _psrlType . GetField ( "_readLineReady" , fieldFlags ) ;
55
+ _psrlHandleResizing = _psrlType . GetField ( "_handlePotentialResizing" , fieldFlags ) ;
47
56
48
57
_predictor = new Predictor ( ) ;
49
58
_onIdleAction = ScriptBlock . Create ( "[AIShell.Integration.Channel]::Singleton.OnIdleHandler()" ) ;
@@ -217,10 +226,9 @@ private void OnPostCode(PostCodeMessage postCodeMessage)
217
226
codeToInsert = sb . ToString ( ) ;
218
227
}
219
228
220
- // When PSReadLine is actively running, 'TreatControlCAsInput' would be set to 'true' because
221
- // it handles 'Ctrl+c' as regular input.
229
+ // When PSReadLine is actively running, its '_readLineReady' field should be set to 'true'.
222
230
// When the value is 'false', it means PowerShell is still busy running scripts or commands.
223
- if ( Console . TreatControlCAsInput )
231
+ if ( _psrlReadLineReady . GetValue ( _psrlSingleton ) is true )
224
232
{
225
233
PSRLRevertLine ( ) ;
226
234
PSRLInsert ( codeToInsert ) ;
@@ -268,18 +276,58 @@ private void OnAskConnection(ShellClientPipe clientPipe, Exception exception)
268
276
269
277
private void PSRLInsert ( string text )
270
278
{
279
+ using var _ = new NoWindowResizingCheck ( ) ;
271
280
_psrlInsert . Invoke ( null , [ text ] ) ;
272
281
}
273
282
274
283
private void PSRLRevertLine ( )
275
284
{
285
+ using var _ = new NoWindowResizingCheck ( ) ;
276
286
_psrlRevertLine . Invoke ( null , [ null , null ] ) ;
277
287
}
278
288
279
289
private void PSRLAcceptLine ( )
280
290
{
291
+ using var _ = new NoWindowResizingCheck ( ) ;
281
292
_psrlAcceptLine . Invoke ( null , [ null , null ] ) ;
282
293
}
294
+
295
+ /// <summary>
296
+ /// We assume the terminal window will not resize during the code-post operation and hence disable the window resizing check on macOS.
297
+ /// This is to avoid reading console cursor positions while PSReadLine is already blocked on 'Console.ReadKey', because on Unix system,
298
+ /// when we are already blocked on key input, reading cursor position on another thread will be blocked too until a key is pressed.
299
+ ///
300
+ /// We do need window resizing check on Windows due to how 'Start-AIShell' works differently:
301
+ /// - On Windows, 'Start-AIShell' returns way BEFORE the current tab gets splitted for the sidecar pane, and PowerShell has already
302
+ /// called into PSReadLine when the splitting actually happens. So, it's literally a window resizing for PSReadLine at that point
303
+ /// and hence we need the window resizing check to correct the initial coordinates ('_initialX' and '_initialY').
304
+ /// - On macOS, however, 'Start-AIShell' returns AFTER the current tab gets splitted for the sidecar pane. So, window resizing will
305
+ /// be done before PowerShell calls into PSReadLine and hence there is no need for window resizing check on macOS.
306
+ /// Also, On Windows we can read cursor position without blocking even if another thread is blocked on calling 'ReadKey'.
307
+ /// </summary>
308
+ private class NoWindowResizingCheck : IDisposable
309
+ {
310
+ private readonly object _originalValue ;
311
+
312
+ internal NoWindowResizingCheck ( )
313
+ {
314
+ if ( OperatingSystem . IsMacOS ( ) )
315
+ {
316
+ Channel channel = Singleton ;
317
+ _originalValue = channel . _psrlHandleResizing . GetValue ( channel . _psrlSingleton ) ;
318
+ channel . _psrlHandleResizing . SetValue ( channel . _psrlSingleton , false ) ;
319
+ }
320
+ }
321
+
322
+ public void Dispose ( )
323
+ {
324
+ if ( OperatingSystem . IsMacOS ( ) )
325
+ {
326
+ Channel channel = Singleton ;
327
+ channel . _psrlHandleResizing . SetValue ( channel . _psrlSingleton , _originalValue ) ;
328
+ }
329
+ }
330
+ }
283
331
}
284
332
285
333
internal record CodePostData ( string CodeToInsert , List < PredictionCandidate > PredictionCandidates ) ;
0 commit comments