Return to ground when we flush the last char (#2823)
## Summary of the Pull Request The InputStateMachineEngine was incorrectly not returning to the ground state after flushing the last sequence. That means that something like alt+backspace would leave us in the Escape state, not the ground state. This makes sure we return to ground. Additionally removes the "Parser.UnitTests-common.vcxproj" file, which was originally used for a theoretical time when we only open-sourced the parser. It's unnecessary now, and we can get rid of it. Also includes a small patch to bcz.cmd, to make sure bx works with projects with a space in their name. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist * [x] Closes #2746 * [x] I work here * [x] Tests added/passed * [n/a] Requires documentation to be updated <hr> * Return to ground when we flush the last char The InputStateMachineEngine was incorrectly not returning to the ground state after flushing the last sequence. That means that something like alt+backspace would leave us in the Escape state, not the ground state. This makes sure we return to ground. Fixes #2746. Additionally removes the "Parser.UnitTests-common.vcxproj" file, which was originally used for a theoretical time when we only open-sourced the parser. It's unnecessary now, and we can get rid of it. Also includes a small patch to bcz.cmd, to make sure bx works with projects with a space in their name. * Update src/terminal/parser/stateMachine.cpp Co-Authored-By: Dustin L. Howett (MSFT) <duhowett@microsoft.com> * add the comment @miniksa wanted
This commit is contained in:
parent
505ceaccf6
commit
53c81a08f8
|
@ -1399,6 +1399,25 @@ void StateMachine::ProcessString(const wchar_t* const rgwch, const size_t cch)
|
||||||
}
|
}
|
||||||
else if (s_fProcessIndividually)
|
else if (s_fProcessIndividually)
|
||||||
{
|
{
|
||||||
|
// One of the "weird things" in VT input is the case of something like
|
||||||
|
// <kbd>alt+[</kbd>. 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,
|
||||||
|
// there's no way to know for sure. For an <kbd>alt+[</kbd> keypress,
|
||||||
|
// the parser originally would just sit in the `CsiEntry` state after
|
||||||
|
// processing it, which would pollute the following keypress (e.g.
|
||||||
|
// <kbd>alt+[</kbd>, <kbd>A</kbd> 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 <kbd>alt+[</kbd>.
|
||||||
|
|
||||||
if (_pEngine->FlushAtEndOfString())
|
if (_pEngine->FlushAtEndOfString())
|
||||||
{
|
{
|
||||||
// Reset our state, and put all but the last char in again.
|
// Reset our state, and put all but the last char in again.
|
||||||
|
@ -1413,25 +1432,31 @@ void StateMachine::ProcessString(const wchar_t* const rgwch, const size_t cch)
|
||||||
switch (_state)
|
switch (_state)
|
||||||
{
|
{
|
||||||
case VTStates::Ground:
|
case VTStates::Ground:
|
||||||
return _ActionExecute(*pwch);
|
_ActionExecute(*pwch);
|
||||||
|
break;
|
||||||
case VTStates::Escape:
|
case VTStates::Escape:
|
||||||
case VTStates::EscapeIntermediate:
|
case VTStates::EscapeIntermediate:
|
||||||
return _ActionEscDispatch(*pwch);
|
_ActionEscDispatch(*pwch);
|
||||||
|
break;
|
||||||
case VTStates::CsiEntry:
|
case VTStates::CsiEntry:
|
||||||
case VTStates::CsiIntermediate:
|
case VTStates::CsiIntermediate:
|
||||||
case VTStates::CsiIgnore:
|
case VTStates::CsiIgnore:
|
||||||
case VTStates::CsiParam:
|
case VTStates::CsiParam:
|
||||||
return _ActionCsiDispatch(*pwch);
|
_ActionCsiDispatch(*pwch);
|
||||||
|
break;
|
||||||
case VTStates::OscParam:
|
case VTStates::OscParam:
|
||||||
case VTStates::OscString:
|
case VTStates::OscString:
|
||||||
case VTStates::OscTermination:
|
case VTStates::OscTermination:
|
||||||
return _ActionOscDispatch(*pwch);
|
_ActionOscDispatch(*pwch);
|
||||||
|
break;
|
||||||
case VTStates::Ss3Entry:
|
case VTStates::Ss3Entry:
|
||||||
case VTStates::Ss3Param:
|
case VTStates::Ss3Param:
|
||||||
return _ActionSs3Dispatch(*pwch);
|
_ActionSs3Dispatch(*pwch);
|
||||||
default:
|
break;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
// microsoft/terminal#2746: Make sure to return to the ground state
|
||||||
|
// after dispatching the characters
|
||||||
|
_EnterGround();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,6 +230,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
|
||||||
TEST_METHOD(AltBackspaceTest);
|
TEST_METHOD(AltBackspaceTest);
|
||||||
TEST_METHOD(AltCtrlDTest);
|
TEST_METHOD(AltCtrlDTest);
|
||||||
TEST_METHOD(AltIntermediateTest);
|
TEST_METHOD(AltIntermediateTest);
|
||||||
|
TEST_METHOD(AltBackspaceEnterTest);
|
||||||
|
|
||||||
friend class TestInteractDispatch;
|
friend class TestInteractDispatch;
|
||||||
};
|
};
|
||||||
|
@ -826,3 +827,54 @@ void InputEngineTest::AltIntermediateTest()
|
||||||
Log::Comment(NoThrowString().Format(L"Processing \"\\x05\""));
|
Log::Comment(NoThrowString().Format(L"Processing \"\\x05\""));
|
||||||
stateMachine->ProcessString(seq);
|
stateMachine->ProcessString(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputEngineTest::AltBackspaceEnterTest()
|
||||||
|
{
|
||||||
|
// Created as a test for microsoft/terminal#2746. See that issue for mode
|
||||||
|
// details. We're going to send an Alt+Backspace to conpty, followed by an
|
||||||
|
// enter. The enter should be processed as just a single VK_ENTER, not a
|
||||||
|
// alt+enter.
|
||||||
|
|
||||||
|
TestState testState;
|
||||||
|
auto pfn = std::bind(&TestState::TestInputCallback, &testState, std::placeholders::_1);
|
||||||
|
|
||||||
|
auto inputEngine = std::make_unique<InputStateMachineEngine>(new TestInteractDispatch(pfn, &testState));
|
||||||
|
auto _stateMachine = std::make_unique<StateMachine>(inputEngine.release());
|
||||||
|
VERIFY_IS_NOT_NULL(_stateMachine);
|
||||||
|
testState._stateMachine = _stateMachine.get();
|
||||||
|
|
||||||
|
INPUT_RECORD inputRec;
|
||||||
|
|
||||||
|
inputRec.EventType = KEY_EVENT;
|
||||||
|
inputRec.Event.KeyEvent.bKeyDown = TRUE;
|
||||||
|
inputRec.Event.KeyEvent.dwControlKeyState = LEFT_ALT_PRESSED;
|
||||||
|
inputRec.Event.KeyEvent.wRepeatCount = 1;
|
||||||
|
inputRec.Event.KeyEvent.wVirtualKeyCode = VK_BACK;
|
||||||
|
inputRec.Event.KeyEvent.wVirtualScanCode = static_cast<WORD>(MapVirtualKeyW(VK_BACK, MAPVK_VK_TO_VSC));
|
||||||
|
inputRec.Event.KeyEvent.uChar.UnicodeChar = L'\x08';
|
||||||
|
|
||||||
|
// First, expect a alt+backspace.
|
||||||
|
testState.vExpectedInput.push_back(inputRec);
|
||||||
|
|
||||||
|
std::wstring seq = L"\x1b\x7f";
|
||||||
|
Log::Comment(NoThrowString().Format(L"Processing \"\\x1b\\x7f\""));
|
||||||
|
_stateMachine->ProcessString(seq);
|
||||||
|
|
||||||
|
// Ensure the state machine has correctly returned to the ground state
|
||||||
|
VERIFY_ARE_EQUAL(StateMachine::VTStates::Ground, _stateMachine->_state);
|
||||||
|
|
||||||
|
inputRec.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||||
|
inputRec.Event.KeyEvent.dwControlKeyState = 0;
|
||||||
|
inputRec.Event.KeyEvent.wVirtualScanCode = static_cast<WORD>(MapVirtualKeyW(VK_RETURN, MAPVK_VK_TO_VSC));
|
||||||
|
inputRec.Event.KeyEvent.uChar.UnicodeChar = L'\x0d'; //maybe \xa
|
||||||
|
|
||||||
|
// Then, expect a enter
|
||||||
|
testState.vExpectedInput.push_back(inputRec);
|
||||||
|
|
||||||
|
seq = L"\x0d";
|
||||||
|
Log::Comment(NoThrowString().Format(L"Processing \"\\x0d\""));
|
||||||
|
_stateMachine->ProcessString(seq);
|
||||||
|
|
||||||
|
// Ensure the state machine has correctly returned to the ground state
|
||||||
|
VERIFY_ARE_EQUAL(StateMachine::VTStates::Ground, _stateMachine->_state);
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="OutputEngineTest.cpp" />
|
|
||||||
<ClCompile Include="..\precomp.cpp">
|
|
||||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="..\precomp.h" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemDefinitionGroup>
|
|
||||||
<ClCompile>
|
|
||||||
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
</Project>
|
|
|
@ -2,14 +2,27 @@
|
||||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||||
|
|
||||||
<Import Project="Parser.UnitTests-common.vcxproj" />
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\precomp.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Only add closed-source files, dependencies to this file.
|
<!-- Only add closed-source files, dependencies to this file.
|
||||||
Any open-source files can go in Parser.UnitTests-common.vcxproj -->
|
Any open-source files can go in Parser.UnitTests-common.vcxproj -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="InputEngineTest.cpp" />
|
<ClCompile Include="InputEngineTest.cpp" />
|
||||||
|
<ClCompile Include="OutputEngineTest.cpp" />
|
||||||
|
<ClCompile Include="..\precomp.cpp">
|
||||||
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\types\lib\types.vcxproj">
|
<ProjectReference Include="..\..\..\types\lib\types.vcxproj">
|
||||||
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
|
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
|
||||||
|
|
|
@ -68,7 +68,7 @@ if "%_EXCLUSIVE%" == "1" (
|
||||||
echo Performing nuget restore...
|
echo Performing nuget restore...
|
||||||
nuget.exe restore %OPENCON%\OpenConsole.sln
|
nuget.exe restore %OPENCON%\OpenConsole.sln
|
||||||
|
|
||||||
set _BUILD_CMDLINE="%MSBUILD%" %OPENCON%\OpenConsole.sln /t:%_MSBUILD_TARGET% /m /p:Configuration=%_LAST_BUILD_CONF% /p:Platform=%ARCH% %_APPX_ARGS%
|
set _BUILD_CMDLINE="%MSBUILD%" %OPENCON%\OpenConsole.sln /t:"%_MSBUILD_TARGET%" /m /p:Configuration=%_LAST_BUILD_CONF% /p:Platform=%ARCH% %_APPX_ARGS%
|
||||||
|
|
||||||
echo %_BUILD_CMDLINE%
|
echo %_BUILD_CMDLINE%
|
||||||
echo Starting build...
|
echo Starting build...
|
||||||
|
|
Loading…
Reference in a new issue