diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp
index eb22ff9538..643409afda 100644
--- a/src/terminal/parser/stateMachine.cpp
+++ b/src/terminal/parser/stateMachine.cpp
@@ -2127,6 +2127,8 @@ void StateMachine::ProcessString(const std::wstring_view string)
// If we're at the end of the string and have remaining un-printed characters,
if (_state != VTStates::Ground)
{
+ const auto run = _CurrentRun();
+
// One of the "weird things" in VT input is the case of something like
// alt+[. In VT, that's encoded as `\x1b[`. However, that's
// also the start of a CSI, and could be the start of a longer sequence,
@@ -2136,60 +2138,28 @@ void StateMachine::ProcessString(const std::wstring_view string)
// alt+[, A would be processed like `\x1b[A`,
// which is _wrong_).
//
- // Fortunately, for VT input, each keystroke comes in as an individual
- // write operation. So, if at the end of processing a string for the
- // InputEngine, we find that we're not in the Ground state, that implies
- // that we've processed some input, but not dispatched it yet. This
- // block at the end of `ProcessString` will then re-process the
- // undispatched string, but it will ensure that it dispatches on the
- // last character of the string. For our previous `\x1b[` scenario, that
- // means we'll make sure to call `_ActionEscDispatch('[')`., which will
- // properly decode the string as alt+[.
- const auto run = _CurrentRun();
-
+ // At the same time, input may be broken up arbitrarily, depending on the pipe's
+ // buffer size, our read-buffer size, the sender's write-buffer size, and more.
+ // In fact, with the current WSL, input is broken up in 16 byte chunks (Why? :(),
+ // which breaks up many of our longer sequences, like our Win32InputMode ones.
+ //
+ // As a heuristic, this code specifically checks for a trailing Esc or Alt+key.
if (_isEngineForInput)
{
- // Reset our state, and put all but the last char in again.
- ResetState();
- _processingLastCharacter = false;
- // Chars to flush are [pwchSequenceStart, pwchCurr)
- auto wchIter = run.cbegin();
- while (wchIter < run.cend() - 1)
+ if (run.size() <= 2 && run.front() == L'\x1b')
{
- ProcessCharacter(*wchIter);
- wchIter++;
+ _EnterGround();
+ if (run.size() == 1)
+ {
+ _ActionExecute(L'\x1b');
+ }
+ else
+ {
+ _EnterEscape();
+ _ActionEscDispatch(run.back());
+ }
+ _EnterGround();
}
- // Manually execute the last char [pwchCurr]
- _processingLastCharacter = true;
- switch (_state)
- {
- case VTStates::Ground:
- _ActionExecute(*wchIter);
- break;
- case VTStates::Escape:
- case VTStates::EscapeIntermediate:
- _ActionEscDispatch(*wchIter);
- break;
- case VTStates::CsiEntry:
- case VTStates::CsiIntermediate:
- case VTStates::CsiIgnore:
- case VTStates::CsiParam:
- case VTStates::CsiSubParam:
- _ActionCsiDispatch(*wchIter);
- break;
- case VTStates::OscParam:
- case VTStates::OscString:
- case VTStates::OscTermination:
- _ActionOscDispatch(*wchIter);
- break;
- case VTStates::Ss3Entry:
- case VTStates::Ss3Param:
- _ActionSs3Dispatch(*wchIter);
- break;
- }
- // microsoft/terminal#2746: Make sure to return to the ground state
- // after dispatching the characters
- _EnterGround();
}
else if (_state != VTStates::SosPmApcString && _state != VTStates::DcsPassThrough && _state != VTStates::DcsIgnore)
{
diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp
index 2e21c77c74..fcb23734be 100644
--- a/src/terminal/parser/ut_parser/InputEngineTest.cpp
+++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp
@@ -260,6 +260,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
TEST_METHOD(AltCtrlDTest);
TEST_METHOD(AltIntermediateTest);
TEST_METHOD(AltBackspaceEnterTest);
+ TEST_METHOD(ChunkedSequence);
TEST_METHOD(SGRMouseTest_ButtonClick);
TEST_METHOD(SGRMouseTest_Modifiers);
TEST_METHOD(SGRMouseTest_Movement);
@@ -1041,6 +1042,19 @@ void InputEngineTest::AltBackspaceEnterTest()
VerifyExpectedInputDrained();
}
+void InputEngineTest::ChunkedSequence()
+{
+ // This test ensures that a DSC sequence that's split up into multiple chunks isn't
+ // confused with a single Alt+key combination like in the AltBackspaceEnterTest().
+ // Basically, it tests the selectivity of the AltBackspaceEnterTest() fix.
+
+ auto dispatch = std::make_unique(nullptr, nullptr);
+ auto inputEngine = std::make_unique(std::move(dispatch));
+ StateMachine stateMachine{ std::move(inputEngine) };
+ stateMachine.ProcessString(L"\x1b[1");
+ VERIFY_ARE_EQUAL(StateMachine::VTStates::CsiParam, stateMachine._state);
+}
+
// Method Description:
// - Writes an SGR VT sequence based on the necessary parameters
// Arguments: