-
Notifications
You must be signed in to change notification settings - Fork 8.5k
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
Refactor TerminalInput to return strings #15611
Refactor TerminalInput to return strings #15611
Conversation
@@ -24,8 +24,6 @@ | |||
#include "../buffer/out/search.h" | |||
#include "../buffer/out/TextColor.h" | |||
|
|||
#include <til/ticket_lock.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is a little less bad if you suppress whitespace changes: https://github.com/microsoft/terminal/pull/15611/files?w=1
namespace Microsoft::Console::VirtualTerminal | ||
{ | ||
class TerminalInput final | ||
{ | ||
public: | ||
TerminalInput(_In_ std::function<void(std::deque<std::unique_ptr<IInputEvent>>&)> pfn); | ||
using StringType = std::wstring; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The choice of std::wstring
is inoptimal because it'll heap-allocate on every single key press. I figured it doesn't really matter though, because we already do that and more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you elected not to use memory_buffer
? aw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's 7 chars due to the terminating null character, but the win32 key sequence is longer than that. (I mean it's also longer than 8 characters...)
I'm using std::wstring
because memory_buffer
is not comparable, which makes it harder to use for unit tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, win32 input sequences. bah.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add an operator==
to the global namespace for memory_buffer?
is it comparable in fmt 9?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I know it isn't. I'd probably just use til::small_vector
instead, but it lacks an append(some_range)
method. I think we should improve on this at some other time. It realistically doesn't make any difference in our overall application.
625f121
to
376a993
Compare
376a993
to
f73fe75
Compare
if (const auto out = _termInput.HandleFocus(focused)) | ||
{ | ||
_HandleTerminalInputCallback(*out); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this pattern is different from the one in TerminalCore, since it takes *out
instead of out
. Should we converge?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... No, I don't think so. I feel like the _WriteBuffer
loop makes more sense this way.
namespace Microsoft::Console::VirtualTerminal | ||
{ | ||
class TerminalInput final | ||
{ | ||
public: | ||
TerminalInput(_In_ std::function<void(std::deque<std::unique_ptr<IInputEvent>>&)> pfn); | ||
using StringType = std::wstring; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you elected not to use memory_buffer
? aw
namespace Microsoft::Console::VirtualTerminal | ||
{ | ||
class TerminalInput final | ||
{ | ||
public: | ||
TerminalInput(_In_ std::function<void(std::deque<std::unique_ptr<IInputEvent>>&)> pfn); | ||
using StringType = std::wstring; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
for (const auto& wch : text) | ||
{ | ||
_storage.push_back(std::make_unique<KeyEvent>(true, 1ui16, 0ui16, 0ui16, wch, 0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now for THIS we are gonna heap alloc every time but WHATEVER I HATE IT TOO
if (til::is_leading_surrogate(ch)) | ||
{ | ||
if (_leadingSurrogate.has_value()) | ||
{ | ||
// we already were storing a leading surrogate but we got another one. Go ahead and send the | ||
// saved surrogate piece and save the new one | ||
const auto formatted = wil::str_printf<std::wstring>(L"%I32u", _leadingSurrogate.value()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what in blazes? so we would literally just send the raw value of the leading surrogate as an integer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a behavior change, but i can't see anyone relying on it.
the code on the right just sends the leading surrogate as a character instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
noting of course that this is the broken case where we got a Lead + Lead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was quite confused as well. I looked up where it came from and it's been there ever since it got introduced with the WPF PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it so that it now discard invalid surrogate pairs entirely. It's rather unexpected that our input handling code would have to deal with unpaired surrogate pairs anyways. Discarding them is probably about as good as sending U+FFFD.
This comment has been minimized.
This comment has been minimized.
src/terminal/input/terminalInput.hpp
Outdated
TerminalInput() = delete; | ||
TerminalInput(const TerminalInput& old) = default; | ||
TerminalInput(TerminalInput&& moved) = default; | ||
static_assert(StringType{} == StringType{}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah woops that's a leftover from when I was testing which StringType
would work with the unit tests. I needed a class that as a ==
operator. I'll remove it. 😅
return true; | ||
// Note that we must return an empty string here to imply that we've handled | ||
// the event, otherwise the key press can still end up being submitted. | ||
return MakeOutput({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear MakeOutput({})
returns the empty string, whereas MakeUnhandled()
returns OutputType{}
, which is... what exactly? A null string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah kind of. It's like returning null
vs. ""
in JS. null
in this case means "we didn't handle it", where ""
means we did handle it and the result is simply an empty string.
if (success) | ||
{ | ||
_SendInputSequence(sequence); | ||
success = true; | ||
} | ||
if (_inputMode.any(Mode::ButtonEventMouseTracking, Mode::AnyEventMouseTracking)) | ||
{ | ||
_mouseInputState.lastPos.x = position.x; | ||
_mouseInputState.lastPos.y = position.y; | ||
_mouseInputState.lastButton = button; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't this get lost in the change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code change is a little (very) confusing tbh. This if condition has moved up in the scope, so that I could use early returns and remove the local success
variable. It's fine to move the if
condition up because none of the called functions depend on _mouseInputState
(they're all 3 static
functions).
TerminalInput
is configurable, but almost entirely state-less.As such it isn't helpful that it emits its output via a callback.
It makes tracing the flow of data harder purely from reading the code
and also raises uncertainty about when
TerminalInput
may generateoutput. This commit makes it more robust by having
TerminalInput
simply return its data. Furthermore, it returns that data as a string
instead of converting back and forth between text and
IInputEvent
.This change will help me make conhost's
InputBuffer
implementationleaner and help me confidently make more difficult changes to it
with the goal to improve our Unicode support/correctness.
Validation Steps Performed
showkey -a
✅