Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a -UseHostReadKey switch to instruct LegacyReadLine to use the … #1748

Closed
wants to merge 3 commits into from

Conversation

dkattan
Copy link
Contributor

@dkattan dkattan commented Mar 24, 2022

…ReadKey provided by the host instead of Console.ReadKey

@dkattan
Copy link
Contributor Author

dkattan commented Mar 24, 2022

I removed ConsoleProxy because the static constructor was making it difficult to use PSES in a web server where everything is hosted in one process.

I moved the operations of ConsoleProxy directly into EditorServicesConsolePSHostUserInterface.cs since that was the only place it appears to be used, and then added -UseHostReadKey which prevents the instantiation of _consoleOperations, leaving it null.

I then added null checks to all usages of _consoleOperations with fallbacks to underlyingHostUI. Most importantly for ReadKey as this was famously hardcoded to Console.ReadKey under the assumption that this library would never be used outside of a local terminal.

I feel like this fits rather nicely for people like me who have taken the time to properly implement a custom PSHostRawUserInterface

@andyleejordan
Copy link
Member

Reviewing your PR has me noticing that ReadKeyAsync is implemented...yet never used. @dkattan a few issues I'm investigating seem to be caused by our override of ReadKey (for PSReadLine): PowerShell/vscode-powershell#3881, PowerShell/vscode-powershell#2741, and PowerShell/vscode-powershell#3876. So...I'm very intrigued here because I'm already working in this area and trying to come up with a better solution than "gobble everything and pretend we didn't."

@dkattan
Copy link
Contributor Author

dkattan commented Mar 25, 2022

Reviewing your PR has me noticing that ReadKeyAsync is implemented...yet never used. @dkattan a few issues I'm investigating seem to be caused by our override of ReadKey (for PSReadLine): PowerShell/vscode-powershell#3881, PowerShell/vscode-powershell#2741, and PowerShell/vscode-powershell#3876. So...I'm very intrigued here because I'm already working in this area and trying to come up with a better solution than "gobble everything and pretend we didn't."

I have so many questions.

  1. Why do we need that override in the first place?
  2. Why does PSReadLine use System.Console.ReadKey() instead of PSHostRawUserInterface.ReadKey?

@SeeminglyScience
Copy link
Collaborator

  1. Why do we need that override in the first place?

Two reasons, broken up by platform:

On Windows it's so we can make ReadKey cancellable, sorta. It's a hack since there isn't any real support for cancellation but we put it in another thread and store the result in a field if it's cancelled. That way it doesn't block, and when we cancel it we can reuse the result next invocation.

On non-Windows it's due to an internal stdin lock in the BCL. If one thread is blocking on Console.ReadKey and another thread calls Console.CursorTop (or similar) both threads will block until input is received. PSRL internally checks cursor position after processing events to see if it needs to redraw due to host output from an event subscriber. In practice this means that Console.ReadKey becomes synchronous and we lose the ability to cancel it.

  1. Why does PSReadLine use System.Console.RawUI.ReadKey() instead of PSHostRawUserInterface.ReadKey?

Assuming you mean System.Console.ReadKey() for the former. The latter just works quite differently depending on the version, and platform where ReadKey is pretty predictable. Every time I've tried to use RUI.ReadKey I've found some issue with it that makes it less desirable.

Also tbh PSRL really only works in a console based host, so using the host API is typically not very important. If the host isn't console based, it's probably better for PSRL to throw.

@dkattan
Copy link
Contributor Author

dkattan commented Mar 25, 2022

Every time I've tried to use RUI.ReadKey I've found some issue with it that makes it less desirable.

I'm curious what these issues were and if they still exist. I hate to harp on it, I'm just trying to find a way to make a REPL loop work reliably in the browser in front of a net 6.0 backend.

@andyleejordan
Copy link
Member

Every time I've tried to use RUI.ReadKey I've found some issue with it that makes it less desirable.

I'm curious what these issues were and if they still exist. I hate to harp on it, I'm just trying to find a way to make a REPL loop work reliably in the browser in front of a net 6.0 backend.

I'd also like to know why this doesn't work.

@@ -17,7 +17,7 @@ public enum ConsoleReplKind
/// <summary>Use a REPL with the legacy readline implementation. This is generally used when PSReadLine is unavailable.</summary>
LegacyReadLine = 1,
/// <summary>Use a REPL with the PSReadLine module for console interaction.</summary>
PSReadLine = 2,
PSReadLine = 2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PSReadLine = 2
PSReadLine = 2,

Trailing comma is on purpose here

@@ -88,7 +91,7 @@ public sealed class EditorServicesConfig
/// (including none to disable the integrated console).
/// </summary>
public ConsoleReplKind ConsoleRepl { get; set; } = ConsoleReplKind.None;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Extra whitespace

@SeeminglyScience
Copy link
Collaborator

Couldn't remember so I went to go test some things, and the biggest blocker I found pretty quickly is that shift on it's own returns as a key press (on Windows).

It's technically correct, but not the behavior we need. Technically we could just ignore those but it's a lot of extra logic and I'm not really sure what it buys us.

I can keep lookin' if y'all want but it's a super different API that we're going to end up just trying to get working like Console.ReadKey already does.

Really, this is why we had a different host that was not terminal focused. It was torn out recently because it wasn't referenced by anything, but that should probably just be brought back with an extra switch.

Another question, you really want a custom ReadKey? Or do you want a custom ReadLine?

@dkattan
Copy link
Contributor Author

dkattan commented Apr 5, 2022

My end game is to have xterm.js in the browser with tab completion support, so I'm pretty sure I need ReadKey.

I wouldn't be opposed to my ReadKey being called after your buffering logic, I think I stand to save myself a lot of work if you guys or PSReadline was handling the buffering/cancellation/etc.

PSReadline is going to take more work because they have a singleton for clipboard purposes and that breaks my ability to have multiple consoles as when new consoles get spawned they get control of the previous instance and start sharing output.

In the interim if I could just LegacyReadline to honor my hosts ReadKey, I think I'd be set.

@andyleejordan
Copy link
Member

So sorry @dkattan, I rewrote the readkey implementation, you'll need to redo this BUT it should be way, way easier. Thank you for the inspiration.

@andyleejordan
Copy link
Member

@dkattan did you want to redo this PR or should I close it? Really sorry about the re-work I caused you, BUT hey, it's so much simpler now, no?!

@dkattan
Copy link
Contributor Author

dkattan commented May 18, 2022

@dkattan did you want to redo this PR or should I close it? Really sorry about the re-work I caused you, BUT hey, it's so much simpler now, no?!

I definitely do, just been a bit busy. I'll see if I can rebase it this afternoon.

}
else
{
KeyInfo keyInfo = _hostInfo.PSHost.UI.RawUI.ReadKey();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silly question: Could we just make the default RawUI.ReadKey in PSES be Console.ReadKey()? I ask because I see that's how you're supplying a custom ReadKey, which is exactly what we're also trying to do.

@andyleejordan
Copy link
Member

@dkattan I'm totally still willing to take the change in spirit, it just needs to be re-written. Let me know if you get to it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants