Compare commits
22 commits
main
...
dev/austdi
Author | SHA1 | Date | |
---|---|---|---|
53d93be6f8 | |||
09e56d1fd9 | |||
3a247fa195 | |||
934bae9e3f | |||
470c01b7ea | |||
da61b9d3b9 | |||
dcc8a98a24 | |||
b812ec8a21 | |||
2ad4103726 | |||
79b6d3854b | |||
87f1ca432e | |||
2ea9ebba2f | |||
14cbd1335b | |||
4da9094dd6 | |||
387186d280 | |||
dcb0334724 | |||
f6550b1028 | |||
b8b87262c0 | |||
f1d29097ba | |||
033814c2bc | |||
4ae0dd1500 | |||
899dccfec9 |
|
@ -60,7 +60,7 @@ CommandListPopup::CommandListPopup(SCREEN_INFORMATION& screenInfo, const Command
|
|||
}
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandListPopup::_handlePopupKeys(COOKED_READ_DATA& cookedReadData, const wchar_t wch, const DWORD modifiers) noexcept
|
||||
NTSTATUS CommandListPopup::_handlePopupKeys(CookedRead& cookedReadData, const wchar_t wch, const DWORD modifiers) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -147,7 +147,7 @@ void CommandListPopup::_setBottomIndex()
|
|||
}
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandListPopup::_deleteSelection(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
NTSTATUS CommandListPopup::_deleteSelection(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -177,7 +177,7 @@ NTSTATUS CommandListPopup::_deleteSelection(COOKED_READ_DATA& cookedReadData) no
|
|||
// Arguments:
|
||||
// - cookedReadData - the read wait object to operate upon
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandListPopup::_swapUp(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
NTSTATUS CommandListPopup::_swapUp(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -200,7 +200,7 @@ NTSTATUS CommandListPopup::_swapUp(COOKED_READ_DATA& cookedReadData) noexcept
|
|||
// Arguments:
|
||||
// - cookedReadData - the read wait object to operate upon
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandListPopup::_swapDown(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
NTSTATUS CommandListPopup::_swapDown(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -218,57 +218,15 @@ NTSTATUS CommandListPopup::_swapDown(COOKED_READ_DATA& cookedReadData) noexcept
|
|||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void CommandListPopup::_handleReturn(COOKED_READ_DATA& cookedReadData)
|
||||
void CommandListPopup::_handleReturn(CookedRead& cookedReadData)
|
||||
{
|
||||
short Index = 0;
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
DWORD LineCount = 1;
|
||||
Index = _currentCommand;
|
||||
const short index = _currentCommand;
|
||||
CommandLine::Instance().EndCurrentPopup();
|
||||
SetCurrentCommandLine(cookedReadData, (SHORT)Index);
|
||||
cookedReadData.ProcessInput(UNICODE_CARRIAGERETURN, 0, Status);
|
||||
// complete read
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
// check for alias
|
||||
cookedReadData.ProcessAliases(LineCount);
|
||||
}
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
size_t NumBytes;
|
||||
if (cookedReadData.BytesRead() > cookedReadData.UserBufferSize() || LineCount > 1)
|
||||
{
|
||||
if (LineCount > 1)
|
||||
{
|
||||
const wchar_t* Tmp;
|
||||
for (Tmp = cookedReadData.BufferStartPtr(); *Tmp != UNICODE_LINEFEED; Tmp++)
|
||||
{
|
||||
FAIL_FAST_IF(!(Tmp < (cookedReadData.BufferStartPtr() + cookedReadData.BytesRead())));
|
||||
}
|
||||
NumBytes = (Tmp - cookedReadData.BufferStartPtr() + 1) * sizeof(*Tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
NumBytes = cookedReadData.UserBufferSize();
|
||||
}
|
||||
|
||||
// Copy what we can fit into the user buffer
|
||||
const size_t bytesWritten = cookedReadData.SavePromptToUserBuffer(NumBytes / sizeof(wchar_t));
|
||||
|
||||
// Store all of the remaining as pending until the next read operation.
|
||||
cookedReadData.SavePendingInput(NumBytes / sizeof(wchar_t), LineCount > 1);
|
||||
NumBytes = bytesWritten;
|
||||
}
|
||||
else
|
||||
{
|
||||
NumBytes = cookedReadData.BytesRead();
|
||||
NumBytes = cookedReadData.SavePromptToUserBuffer(NumBytes / sizeof(wchar_t));
|
||||
}
|
||||
|
||||
cookedReadData.SetReportedByteCount(NumBytes);
|
||||
SetCurrentCommandLine(cookedReadData, index);
|
||||
cookedReadData.BufferInput(UNICODE_CARRIAGERETURN);
|
||||
}
|
||||
|
||||
void CommandListPopup::_cycleSelectionToMatchingCommands(COOKED_READ_DATA& cookedReadData, const wchar_t wch)
|
||||
void CommandListPopup::_cycleSelectionToMatchingCommands(CookedRead& cookedReadData, const wchar_t wch)
|
||||
{
|
||||
short Index = 0;
|
||||
if (cookedReadData.History().FindMatchingCommand({ &wch, 1 },
|
||||
|
@ -286,7 +244,7 @@ void CommandListPopup::_cycleSelectionToMatchingCommands(COOKED_READ_DATA& cooke
|
|||
// - CONSOLE_STATUS_WAIT - we ran out of input, so a wait block was created
|
||||
// - CONSOLE_STATUS_READ_COMPLETE - user hit return
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandListPopup::Process(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
NTSTATUS CommandListPopup::Process(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
CommandListPopup(SCREEN_INFORMATION& screenInfo, const CommandHistory& history);
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS Process(COOKED_READ_DATA& cookedReadData) noexcept override;
|
||||
NTSTATUS Process(CookedRead& cookedReadData) noexcept override;
|
||||
|
||||
protected:
|
||||
void _DrawContent() override;
|
||||
|
@ -34,17 +34,17 @@ private:
|
|||
void _update(const SHORT delta, const bool wrap = false);
|
||||
void _updateHighlight(const SHORT oldCommand, const SHORT newCommand);
|
||||
|
||||
void _handleReturn(COOKED_READ_DATA& cookedReadData);
|
||||
void _cycleSelectionToMatchingCommands(COOKED_READ_DATA& cookedReadData, const wchar_t wch);
|
||||
void _handleReturn(CookedRead& cookedReadData);
|
||||
void _cycleSelectionToMatchingCommands(CookedRead& cookedReadData, const wchar_t wch);
|
||||
void _setBottomIndex();
|
||||
[[nodiscard]]
|
||||
NTSTATUS _handlePopupKeys(COOKED_READ_DATA& cookedReadData, const wchar_t wch, const DWORD modifiers) noexcept;
|
||||
NTSTATUS _handlePopupKeys(CookedRead& cookedReadData, const wchar_t wch, const DWORD modifiers) noexcept;
|
||||
[[nodiscard]]
|
||||
NTSTATUS _deleteSelection(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
NTSTATUS _deleteSelection(CookedRead& cookedReadData) noexcept;
|
||||
[[nodiscard]]
|
||||
NTSTATUS _swapUp(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
NTSTATUS _swapUp(CookedRead& cookedReadData) noexcept;
|
||||
[[nodiscard]]
|
||||
NTSTATUS _swapDown(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
NTSTATUS _swapDown(CookedRead& cookedReadData) noexcept;
|
||||
|
||||
SHORT _currentCommand;
|
||||
SHORT _bottomIndex; // number of command displayed on last line of popup
|
||||
|
|
|
@ -27,7 +27,7 @@ CommandNumberPopup::CommandNumberPopup(SCREEN_INFORMATION& screenInfo) :
|
|||
// Arguments:
|
||||
// - cookedReadData - read data to operate on
|
||||
// - wch - digit to handle
|
||||
void CommandNumberPopup::_handleNumber(COOKED_READ_DATA& cookedReadData, const wchar_t wch) noexcept
|
||||
void CommandNumberPopup::_handleNumber(CookedRead& cookedReadData, const wchar_t wch) noexcept
|
||||
{
|
||||
if (_userInput.size() < COMMAND_NUMBER_LENGTH)
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ void CommandNumberPopup::_handleNumber(COOKED_READ_DATA& cookedReadData, const w
|
|||
&wch,
|
||||
&CharsToWrite,
|
||||
&NumSpaces,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.PromptStartLocation().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
nullptr));
|
||||
cookedReadData.ScreenInfo().SetAttributes(realAttributes);
|
||||
|
@ -57,7 +57,7 @@ void CommandNumberPopup::_handleNumber(COOKED_READ_DATA& cookedReadData, const w
|
|||
// - handles backspace user input. removes a digit from the user input
|
||||
// Arguments:
|
||||
// - cookedReadData - read data to operate on
|
||||
void CommandNumberPopup::_handleBackspace(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandNumberPopup::_handleBackspace(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
if (_userInput.size() > 0)
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ void CommandNumberPopup::_handleBackspace(COOKED_READ_DATA& cookedReadData) noex
|
|||
&backspace,
|
||||
&CharsToWrite,
|
||||
&NumSpaces,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.PromptStartLocation().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
nullptr));
|
||||
cookedReadData.ScreenInfo().SetAttributes(realAttributes);
|
||||
|
@ -84,7 +84,7 @@ void CommandNumberPopup::_handleBackspace(COOKED_READ_DATA& cookedReadData) noex
|
|||
// - handles escape user input. cancels the popup
|
||||
// Arguments:
|
||||
// - cookedReadData - read data to operate on
|
||||
void CommandNumberPopup::_handleEscape(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandNumberPopup::_handleEscape(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
CommandLine::Instance().EndAllPopups();
|
||||
|
||||
|
@ -92,14 +92,14 @@ void CommandNumberPopup::_handleEscape(COOKED_READ_DATA& cookedReadData) noexcep
|
|||
// We want to use the position before the cursor was moved for this popup handler specifically, which may
|
||||
// be *anywhere* in the edit line and will be synchronized with the pointers in the cookedReadData
|
||||
// structure (BufPtr, etc.)
|
||||
LOG_IF_FAILED(cookedReadData.ScreenInfo().SetCursorPosition(cookedReadData.BeforeDialogCursorPosition(), TRUE));
|
||||
LOG_IF_FAILED(cookedReadData.ScreenInfo().SetCursorPosition(cookedReadData.BeforePopupCursorPosition(), TRUE));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - handles return user input. sets the prompt to the history item indicated
|
||||
// Arguments:
|
||||
// - cookedReadData - read data to operate on
|
||||
void CommandNumberPopup::_handleReturn(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandNumberPopup::_handleReturn(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
const short commandNumber = gsl::narrow<short>(std::min(static_cast<size_t>(_parse()),
|
||||
cookedReadData.History().GetNumberOfCommands() - 1));
|
||||
|
@ -114,7 +114,7 @@ void CommandNumberPopup::_handleReturn(COOKED_READ_DATA& cookedReadData) noexcep
|
|||
// - CONSOLE_STATUS_WAIT - we ran out of input, so a wait block was created
|
||||
// - CONSOLE_STATUS_READ_COMPLETE - user hit return
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandNumberPopup::Process(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
NTSTATUS CommandNumberPopup::Process(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
WCHAR wch = UNICODE_NULL;
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
CommandNumberPopup(SCREEN_INFORMATION& screenInfo);
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS Process(COOKED_READ_DATA& cookedReadData) noexcept override;
|
||||
NTSTATUS Process(CookedRead& cookedReadData) noexcept override;
|
||||
|
||||
protected:
|
||||
void _DrawContent() override;
|
||||
|
@ -32,10 +32,10 @@ protected:
|
|||
private:
|
||||
std::wstring _userInput;
|
||||
|
||||
void _handleNumber(COOKED_READ_DATA& cookedReadData, const wchar_t wch) noexcept;
|
||||
void _handleBackspace(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
void _handleEscape(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
void _handleReturn(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
void _handleNumber(CookedRead& cookedReadData, const wchar_t wch) noexcept;
|
||||
void _handleBackspace(CookedRead& cookedReadData) noexcept;
|
||||
void _handleEscape(CookedRead& cookedReadData) noexcept;
|
||||
void _handleReturn(CookedRead& cookedReadData) noexcept;
|
||||
|
||||
void _push(const wchar_t wch);
|
||||
void _pop() noexcept;
|
||||
|
|
|
@ -20,7 +20,7 @@ CopyFromCharPopup::CopyFromCharPopup(SCREEN_INFORMATION& screenInfo) :
|
|||
// - CONSOLE_STATUS_WAIT - we ran out of input, so a wait block was created
|
||||
// - CONSOLE_STATUS_READ_COMPLETE - user hit return
|
||||
[[nodiscard]]
|
||||
NTSTATUS CopyFromCharPopup::Process(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
NTSTATUS CopyFromCharPopup::Process(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
// get user input
|
||||
WCHAR Char = UNICODE_NULL;
|
||||
|
@ -39,7 +39,7 @@ NTSTATUS CopyFromCharPopup::Process(COOKED_READ_DATA& cookedReadData) noexcept
|
|||
return CONSOLE_STATUS_WAIT_NO_BLOCK;
|
||||
}
|
||||
|
||||
const auto span = cookedReadData.SpanAtPointer();
|
||||
const auto span = cookedReadData.PromptFromInsertionIndex();
|
||||
const auto foundLocation = std::find(std::next(span.begin()), span.end(), Char);
|
||||
if (foundLocation == span.end())
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
CopyFromCharPopup(SCREEN_INFORMATION& screenInfo);
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS Process(COOKED_READ_DATA& cookedReadData) noexcept override;
|
||||
NTSTATUS Process(CookedRead& cookedReadData) noexcept override;
|
||||
|
||||
protected:
|
||||
void _DrawContent() override;
|
||||
|
|
|
@ -22,15 +22,15 @@ CopyToCharPopup::CopyToCharPopup(SCREEN_INFORMATION& screenInfo) :
|
|||
// - cookedReadData - the read data to operate on
|
||||
// - LastCommand - the most recent command run
|
||||
// - wch - the wchar to copy up to
|
||||
void CopyToCharPopup::_copyToChar(COOKED_READ_DATA& cookedReadData, const std::wstring_view LastCommand, const wchar_t wch)
|
||||
void CopyToCharPopup::_copyToChar(CookedRead& cookedReadData, const std::wstring_view LastCommand, const wchar_t wch)
|
||||
{
|
||||
// make sure that there it is possible to copy any found text over
|
||||
if (cookedReadData.InsertionPoint() >= LastCommand.size())
|
||||
if (cookedReadData.InsertionIndex() >= LastCommand.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto searchStart = std::next(LastCommand.cbegin(), cookedReadData.InsertionPoint() + 1);
|
||||
const auto searchStart = std::next(LastCommand.cbegin(), cookedReadData.InsertionIndex() + 1);
|
||||
auto location = std::find(searchStart, LastCommand.cend(), wch);
|
||||
|
||||
// didn't find wch so copy nothing
|
||||
|
@ -39,10 +39,10 @@ void CopyToCharPopup::_copyToChar(COOKED_READ_DATA& cookedReadData, const std::w
|
|||
return;
|
||||
}
|
||||
|
||||
const auto startIt = std::next(LastCommand.cbegin(), cookedReadData.InsertionPoint());
|
||||
const auto endIt = location;
|
||||
const std::wstring_view::const_iterator startIt = std::next(LastCommand.cbegin(), cookedReadData.InsertionIndex());
|
||||
const std::wstring_view::const_iterator endIt = location;
|
||||
|
||||
cookedReadData.Write({ &*startIt, gsl::narrow<size_t>(std::distance(startIt, endIt)) });
|
||||
cookedReadData.Overwrite(startIt, endIt);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -51,7 +51,7 @@ void CopyToCharPopup::_copyToChar(COOKED_READ_DATA& cookedReadData, const std::w
|
|||
// - CONSOLE_STATUS_WAIT - we ran out of input, so a wait block was created
|
||||
// - CONSOLE_STATUS_READ_COMPLETE - user hit return
|
||||
[[nodiscard]]
|
||||
NTSTATUS CopyToCharPopup::Process(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
NTSTATUS CopyToCharPopup::Process(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
wchar_t wch = UNICODE_NULL;
|
||||
bool popupKey = false;
|
||||
|
|
|
@ -23,10 +23,10 @@ public:
|
|||
CopyToCharPopup(SCREEN_INFORMATION& screenInfo);
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS Process(COOKED_READ_DATA& cookedReadData) noexcept override;
|
||||
NTSTATUS Process(CookedRead& cookedReadData) noexcept override;
|
||||
protected:
|
||||
void _DrawContent() override;
|
||||
|
||||
private:
|
||||
void _copyToChar(COOKED_READ_DATA& cookedReadData, const std::wstring_view LastCommand, const wchar_t wch);
|
||||
void _copyToChar(CookedRead& cookedReadData, const std::wstring_view LastCommand, const wchar_t wch);
|
||||
};
|
||||
|
|
|
@ -41,13 +41,13 @@ struct case_insensitive_equality
|
|||
}
|
||||
};
|
||||
|
||||
std::unordered_map<std::wstring,
|
||||
std::unordered_map<std::wstring,
|
||||
std::wstring,
|
||||
case_insensitive_hash,
|
||||
case_insensitive_equality>,
|
||||
case_insensitive_hash,
|
||||
case_insensitive_equality> g_aliasData;
|
||||
static std::unordered_map<std::wstring,
|
||||
std::unordered_map<std::wstring,
|
||||
std::wstring,
|
||||
case_insensitive_hash,
|
||||
case_insensitive_equality>,
|
||||
case_insensitive_hash,
|
||||
case_insensitive_equality> g_aliasData;
|
||||
|
||||
// Routine Description:
|
||||
// - Adds a command line alias to the global set.
|
||||
|
@ -318,7 +318,7 @@ HRESULT GetConsoleAliasesLengthWImplHelper(const std::wstring_view exeName,
|
|||
{
|
||||
// Ensure output variables are initialized
|
||||
bufferRequired = 0;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
const std::wstring exeNameString(exeName);
|
||||
|
|
|
@ -221,109 +221,28 @@ void CommandLine::EndAllPopups()
|
|||
}
|
||||
}
|
||||
|
||||
void DeleteCommandLine(COOKED_READ_DATA& cookedReadData, const bool fUpdateFields)
|
||||
void DeleteCommandLine(CookedRead& cookedReadData, const bool fUpdateFields)
|
||||
{
|
||||
size_t CharsToWrite = cookedReadData.VisibleCharCount();
|
||||
COORD coordOriginalCursor = cookedReadData.OriginalCursorPosition();
|
||||
const COORD coordBufferSize = cookedReadData.ScreenInfo().GetBufferSize().Dimensions();
|
||||
|
||||
// catch the case where the current command has scrolled off the top of the screen.
|
||||
if (coordOriginalCursor.Y < 0)
|
||||
{
|
||||
CharsToWrite += coordBufferSize.X * coordOriginalCursor.Y;
|
||||
CharsToWrite += cookedReadData.OriginalCursorPosition().X; // account for prompt
|
||||
cookedReadData.OriginalCursorPosition().X = 0;
|
||||
cookedReadData.OriginalCursorPosition().Y = 0;
|
||||
coordOriginalCursor.X = 0;
|
||||
coordOriginalCursor.Y = 0;
|
||||
}
|
||||
|
||||
if (!CheckBisectStringW(cookedReadData.BufferStartPtr(),
|
||||
CharsToWrite,
|
||||
coordBufferSize.X - cookedReadData.OriginalCursorPosition().X))
|
||||
{
|
||||
CharsToWrite++;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
cookedReadData.ScreenInfo().Write(OutputCellIterator(UNICODE_SPACE, CharsToWrite), coordOriginalCursor);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (fUpdateFields)
|
||||
{
|
||||
cookedReadData.Erase();
|
||||
}
|
||||
|
||||
LOG_IF_FAILED(cookedReadData.ScreenInfo().SetCursorPosition(cookedReadData.OriginalCursorPosition(), true));
|
||||
else
|
||||
{
|
||||
cookedReadData.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
void RedrawCommandLine(COOKED_READ_DATA& cookedReadData)
|
||||
void RedrawCommandLine(CookedRead& cookedReadData)
|
||||
{
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
// Draw the command line
|
||||
cookedReadData.OriginalCursorPosition() = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
|
||||
SHORT ScrollY = 0;
|
||||
#pragma prefast(suppress:28931, "Status is not unused. It's used in debug assertions.")
|
||||
NTSTATUS Status = WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY);
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(Status);
|
||||
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
|
||||
// Move the cursor back to the right position
|
||||
COORD CursorPosition = cookedReadData.OriginalCursorPosition();
|
||||
CursorPosition.X += (SHORT)RetrieveTotalNumberOfSpaces(cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint());
|
||||
if (CheckBisectStringW(cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint(),
|
||||
cookedReadData.ScreenInfo().GetBufferSize().Width() - cookedReadData.OriginalCursorPosition().X))
|
||||
{
|
||||
CursorPosition.X++;
|
||||
}
|
||||
Status = AdjustCursorPosition(cookedReadData.ScreenInfo(), CursorPosition, TRUE, nullptr);
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(Status);
|
||||
}
|
||||
cookedReadData.Show();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This routine copies the commandline specified by Index into the cooked read buffer
|
||||
void SetCurrentCommandLine(COOKED_READ_DATA& cookedReadData, _In_ SHORT Index) // index, not command number
|
||||
void SetCurrentCommandLine(CookedRead& cookedReadData, _In_ SHORT Index) // index, not command number
|
||||
{
|
||||
DeleteCommandLine(cookedReadData, TRUE);
|
||||
FAIL_FAST_IF_FAILED(cookedReadData.History().RetrieveNth(Index,
|
||||
cookedReadData.SpanWholeBuffer(),
|
||||
cookedReadData.BytesRead()));
|
||||
FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
SHORT ScrollY = 0;
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
}
|
||||
|
||||
size_t const CharsToWrite = cookedReadData.BytesRead() / sizeof(WCHAR);
|
||||
cookedReadData.InsertionPoint() = CharsToWrite;
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + CharsToWrite);
|
||||
cookedReadData.SetPromptToCommand(gsl::narrow<size_t>(Index));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -332,7 +251,7 @@ void SetCurrentCommandLine(COOKED_READ_DATA& cookedReadData, _In_ SHORT Index) /
|
|||
// - CONSOLE_STATUS_WAIT - we ran out of input, so a wait block was created
|
||||
// - STATUS_SUCCESS - read was fully completed (user hit return)
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandLine::_startCommandListPopup(COOKED_READ_DATA& cookedReadData)
|
||||
NTSTATUS CommandLine::_startCommandListPopup(CookedRead& cookedReadData)
|
||||
{
|
||||
if (cookedReadData.HasHistory() &&
|
||||
cookedReadData.History().GetNumberOfCommands())
|
||||
|
@ -358,7 +277,7 @@ NTSTATUS CommandLine::_startCommandListPopup(COOKED_READ_DATA& cookedReadData)
|
|||
// - CONSOLE_STATUS_WAIT - we ran out of input, so a wait block was created
|
||||
// - STATUS_SUCCESS - read was fully completed (user hit return)
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandLine::_startCopyFromCharPopup(COOKED_READ_DATA& cookedReadData)
|
||||
NTSTATUS CommandLine::_startCopyFromCharPopup(CookedRead& cookedReadData)
|
||||
{
|
||||
// Delete the current command from cursor position to the
|
||||
// letter specified by the user. The user is prompted via
|
||||
|
@ -386,7 +305,7 @@ NTSTATUS CommandLine::_startCopyFromCharPopup(COOKED_READ_DATA& cookedReadData)
|
|||
// - STATUS_SUCCESS - read was fully completed (user hit return)
|
||||
// - S_FALSE - if we couldn't make a popup because we had no commands
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandLine::_startCopyToCharPopup(COOKED_READ_DATA& cookedReadData)
|
||||
NTSTATUS CommandLine::_startCopyToCharPopup(CookedRead& cookedReadData)
|
||||
{
|
||||
// copy the previous command to the current command, up to but
|
||||
// not including the character specified by the user. the user
|
||||
|
@ -414,7 +333,7 @@ NTSTATUS CommandLine::_startCopyToCharPopup(COOKED_READ_DATA& cookedReadData)
|
|||
// - STATUS_SUCCESS - read was fully completed (user hit return)
|
||||
// - S_FALSE - if we couldn't make a popup because we had no commands or it wouldn't fit.
|
||||
[[nodiscard]]
|
||||
HRESULT CommandLine::StartCommandNumberPopup(COOKED_READ_DATA& cookedReadData)
|
||||
HRESULT CommandLine::StartCommandNumberPopup(CookedRead& cookedReadData)
|
||||
{
|
||||
if (cookedReadData.HasHistory() &&
|
||||
cookedReadData.History().GetNumberOfCommands() &&
|
||||
|
@ -426,7 +345,7 @@ HRESULT CommandLine::StartCommandNumberPopup(COOKED_READ_DATA& cookedReadData)
|
|||
popup.Draw();
|
||||
|
||||
// Save the original cursor position in case the user cancels out of the dialog
|
||||
cookedReadData.BeforeDialogCursorPosition() = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
//cookedReadData.BeforeDialogCursorPosition() = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
|
||||
// Move the cursor into the dialog so the user can type multiple characters for the command number
|
||||
const COORD CursorPosition = popup.GetCursorPosition();
|
||||
|
@ -451,10 +370,9 @@ HRESULT CommandLine::StartCommandNumberPopup(COOKED_READ_DATA& cookedReadData)
|
|||
// - searchDirection - Direction in history to search
|
||||
// Note:
|
||||
// - May throw exceptions
|
||||
void CommandLine::_processHistoryCycling(COOKED_READ_DATA& cookedReadData,
|
||||
void CommandLine::_processHistoryCycling(CookedRead& cookedReadData,
|
||||
const CommandHistory::SearchDirection searchDirection)
|
||||
{
|
||||
|
||||
// for doskey compatibility, buffer isn't circular. don't do anything if attempting
|
||||
// to cycle history past the bounds of the history buffer
|
||||
if (!cookedReadData.HasHistory())
|
||||
|
@ -472,28 +390,7 @@ void CommandLine::_processHistoryCycling(COOKED_READ_DATA& cookedReadData,
|
|||
return;
|
||||
}
|
||||
|
||||
DeleteCommandLine(cookedReadData, true);
|
||||
THROW_IF_FAILED(cookedReadData.History().Retrieve(searchDirection,
|
||||
cookedReadData.SpanWholeBuffer(),
|
||||
cookedReadData.BytesRead()));
|
||||
FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
short ScrollY = 0;
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
}
|
||||
const size_t CharsToWrite = cookedReadData.BytesRead() / sizeof(WCHAR);
|
||||
cookedReadData.InsertionPoint() = CharsToWrite;
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + CharsToWrite);
|
||||
cookedReadData.SetPromptToCommand(searchDirection);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -502,34 +399,9 @@ void CommandLine::_processHistoryCycling(COOKED_READ_DATA& cookedReadData,
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Note:
|
||||
// - May throw exceptions
|
||||
void CommandLine::_setPromptToOldestCommand(COOKED_READ_DATA& cookedReadData)
|
||||
void CommandLine::_setPromptToOldestCommand(CookedRead& cookedReadData)
|
||||
{
|
||||
if (cookedReadData.HasHistory() && cookedReadData.History().GetNumberOfCommands())
|
||||
{
|
||||
DeleteCommandLine(cookedReadData, true);
|
||||
const short commandNumber = 0;
|
||||
THROW_IF_FAILED(cookedReadData.History().RetrieveNth(commandNumber,
|
||||
cookedReadData.SpanWholeBuffer(),
|
||||
cookedReadData.BytesRead()));
|
||||
FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
short ScrollY = 0;
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
}
|
||||
size_t CharsToWrite = cookedReadData.BytesRead() / sizeof(WCHAR);
|
||||
cookedReadData.InsertionPoint() = CharsToWrite;
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + CharsToWrite);
|
||||
}
|
||||
cookedReadData.SetPromptToOldestCommand();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -538,56 +410,18 @@ void CommandLine::_setPromptToOldestCommand(COOKED_READ_DATA& cookedReadData)
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Note:
|
||||
// - May throw exceptions
|
||||
void CommandLine::_setPromptToNewestCommand(COOKED_READ_DATA& cookedReadData)
|
||||
void CommandLine::_setPromptToNewestCommand(CookedRead& cookedReadData)
|
||||
{
|
||||
DeleteCommandLine(cookedReadData, true);
|
||||
if (cookedReadData.HasHistory() && cookedReadData.History().GetNumberOfCommands())
|
||||
{
|
||||
const short commandNumber = (SHORT)(cookedReadData.History().GetNumberOfCommands() - 1);
|
||||
THROW_IF_FAILED(cookedReadData.History().RetrieveNth(commandNumber,
|
||||
cookedReadData.SpanWholeBuffer(),
|
||||
cookedReadData.BytesRead()));
|
||||
FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
short ScrollY = 0;
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
}
|
||||
size_t CharsToWrite = cookedReadData.BytesRead() / sizeof(WCHAR);
|
||||
cookedReadData.InsertionPoint() = CharsToWrite;
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + CharsToWrite);
|
||||
}
|
||||
cookedReadData.SetPromptToNewestCommand();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Deletes all prompt text to the right of the cursor
|
||||
// Arguments:
|
||||
// - cookedReadData - The cooked read data to operate on
|
||||
void CommandLine::DeletePromptAfterCursor(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandLine::DeletePromptAfterCursor(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
DeleteCommandLine(cookedReadData, false);
|
||||
cookedReadData.BytesRead() = cookedReadData.InsertionPoint() * sizeof(WCHAR);
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
nullptr));
|
||||
}
|
||||
cookedReadData.DeletePromptAfterInsertionIndex();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -596,26 +430,13 @@ void CommandLine::DeletePromptAfterCursor(COOKED_READ_DATA& cookedReadData) noex
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - The new cursor position
|
||||
COORD CommandLine::_deletePromptBeforeCursor(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
COORD CommandLine::_deletePromptBeforeCursor(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
DeleteCommandLine(cookedReadData, false);
|
||||
cookedReadData.BytesRead() -= cookedReadData.InsertionPoint() * sizeof(WCHAR);
|
||||
cookedReadData.InsertionPoint() = 0;
|
||||
memmove(cookedReadData.BufferStartPtr(), cookedReadData.BufferCurrentPtr(), cookedReadData.BytesRead());
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr());
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
nullptr));
|
||||
}
|
||||
return cookedReadData.OriginalCursorPosition();
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
const size_t cellsMoved = cookedReadData.DeletePromptBeforeInsertionIndex();
|
||||
// the cursor is adjusted to be within the bounds of the screen later, don't need to worry about it here
|
||||
cursorPosition.X -= gsl::narrow<short>(cellsMoved);
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -624,24 +445,12 @@ COORD CommandLine::_deletePromptBeforeCursor(COOKED_READ_DATA& cookedReadData) n
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - The new cursor position
|
||||
COORD CommandLine::_moveCursorToEndOfPrompt(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
COORD CommandLine::_moveCursorToEndOfPrompt(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
cookedReadData.InsertionPoint() = cookedReadData.BytesRead() / sizeof(WCHAR);
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + cookedReadData.InsertionPoint());
|
||||
COORD cursorPosition{ 0, 0 };
|
||||
cursorPosition.X = (SHORT)(cookedReadData.OriginalCursorPosition().X + cookedReadData.VisibleCharCount());
|
||||
cursorPosition.Y = cookedReadData.OriginalCursorPosition().Y;
|
||||
|
||||
const SHORT sScreenBufferSizeX = cookedReadData.ScreenInfo().GetBufferSize().Width();
|
||||
if (CheckBisectProcessW(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint(),
|
||||
sScreenBufferSizeX - cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
true))
|
||||
{
|
||||
cursorPosition.X++;
|
||||
}
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
const size_t cellsMoved = cookedReadData.MoveInsertionIndexToEnd();
|
||||
// the cursor is adjusted to be within the bounds of the screen later, don't need to worry about it here
|
||||
cursorPosition.X += gsl::narrow<short>(cellsMoved);
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
|
@ -651,11 +460,10 @@ COORD CommandLine::_moveCursorToEndOfPrompt(COOKED_READ_DATA& cookedReadData) no
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - The new cursor position
|
||||
COORD CommandLine::_moveCursorToStartOfPrompt(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
COORD CommandLine::_moveCursorToStartOfPrompt(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
cookedReadData.InsertionPoint() = 0;
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr());
|
||||
return cookedReadData.OriginalCursorPosition();
|
||||
cookedReadData.MoveInsertionIndexToStart();
|
||||
return cookedReadData.PromptStartLocation();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -664,82 +472,12 @@ COORD CommandLine::_moveCursorToStartOfPrompt(COOKED_READ_DATA& cookedReadData)
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - New cursor position
|
||||
COORD CommandLine::_moveCursorLeftByWord(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
COORD CommandLine::_moveCursorLeftByWord(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
PWCHAR LastWord;
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
if (cookedReadData.BufferCurrentPtr() != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
// A bit better word skipping.
|
||||
LastWord = cookedReadData.BufferCurrentPtr() - 1;
|
||||
if (LastWord != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
if (*LastWord == L' ')
|
||||
{
|
||||
// Skip spaces, until the non-space character is found.
|
||||
while (--LastWord != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
FAIL_FAST_IF(!(LastWord > cookedReadData.BufferStartPtr()));
|
||||
if (*LastWord != L' ')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (LastWord != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
if (IsWordDelim(*LastWord))
|
||||
{
|
||||
// Skip WORD_DELIMs until space or non WORD_DELIM is found.
|
||||
while (--LastWord != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
FAIL_FAST_IF(!(LastWord > cookedReadData.BufferStartPtr()));
|
||||
if (*LastWord == L' ' || !IsWordDelim(*LastWord))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip the regular words
|
||||
while (--LastWord != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
FAIL_FAST_IF(!(LastWord > cookedReadData.BufferStartPtr()));
|
||||
if (IsWordDelim(*LastWord))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FAIL_FAST_IF(!(LastWord >= cookedReadData.BufferStartPtr()));
|
||||
if (LastWord != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
|
||||
// LastWord is currently pointing to the last character
|
||||
// of the previous word, unless it backed up to the beginning
|
||||
// of the buffer.
|
||||
// Let's increment LastWord so that it points to the expeced
|
||||
// insertion point.
|
||||
++LastWord;
|
||||
}
|
||||
cookedReadData.SetBufferCurrentPtr(LastWord);
|
||||
}
|
||||
cookedReadData.InsertionPoint() = (ULONG)(cookedReadData.BufferCurrentPtr() - cookedReadData.BufferStartPtr());
|
||||
cursorPosition = cookedReadData.OriginalCursorPosition();
|
||||
cursorPosition.X = (SHORT)(cursorPosition.X +
|
||||
RetrieveTotalNumberOfSpaces(cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint()));
|
||||
const SHORT sScreenBufferSizeX = cookedReadData.ScreenInfo().GetBufferSize().Width();
|
||||
if (CheckBisectStringW(cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint() + 1,
|
||||
sScreenBufferSizeX - cookedReadData.OriginalCursorPosition().X))
|
||||
{
|
||||
cursorPosition.X++;
|
||||
}
|
||||
}
|
||||
const size_t cellsMoved = cookedReadData.MoveInsertionIndexLeftByWord();
|
||||
// the cursor is adjusted to be within the bounds of the screen later, don't need to worry about it here
|
||||
cursorPosition.X -= gsl::narrow<short>(cellsMoved);
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
|
@ -749,33 +487,12 @@ COORD CommandLine::_moveCursorLeftByWord(COOKED_READ_DATA& cookedReadData) noexc
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - New cursor position
|
||||
COORD CommandLine::_moveCursorLeft(COOKED_READ_DATA& cookedReadData)
|
||||
COORD CommandLine::_moveCursorLeft(CookedRead& cookedReadData)
|
||||
{
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
if (cookedReadData.BufferCurrentPtr() != cookedReadData.BufferStartPtr())
|
||||
{
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferCurrentPtr() - 1);
|
||||
cookedReadData.InsertionPoint()--;
|
||||
cursorPosition.X = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition().X;
|
||||
cursorPosition.Y = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition().Y;
|
||||
cursorPosition.X = (SHORT)(cursorPosition.X -
|
||||
RetrieveNumberOfSpaces(cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint()));
|
||||
const SHORT sScreenBufferSizeX = cookedReadData.ScreenInfo().GetBufferSize().Width();
|
||||
if (CheckBisectProcessW(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint() + 2,
|
||||
sScreenBufferSizeX - cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
true))
|
||||
{
|
||||
if ((cursorPosition.X == -2) || (cursorPosition.X == -1))
|
||||
{
|
||||
cursorPosition.X--;
|
||||
}
|
||||
}
|
||||
}
|
||||
const size_t cellsMoved = cookedReadData.MoveInsertionIndexLeft();
|
||||
// the cursor is adjusted to be within the bounds of the screen later, don't need to worry about it here
|
||||
cursorPosition.X -= gsl::narrow<short>(cellsMoved);
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
|
@ -785,70 +502,12 @@ COORD CommandLine::_moveCursorLeft(COOKED_READ_DATA& cookedReadData)
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - The new cursor position
|
||||
COORD CommandLine::_moveCursorRightByWord(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
COORD CommandLine::_moveCursorRightByWord(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
if (cookedReadData.InsertionPoint() < (cookedReadData.BytesRead() / sizeof(WCHAR)))
|
||||
{
|
||||
PWCHAR NextWord = cookedReadData.BufferCurrentPtr();
|
||||
|
||||
// A bit better word skipping.
|
||||
PWCHAR BufLast = cookedReadData.BufferStartPtr() + cookedReadData.BytesRead() / sizeof(WCHAR);
|
||||
|
||||
FAIL_FAST_IF(!(NextWord < BufLast));
|
||||
if (*NextWord == L' ')
|
||||
{
|
||||
// If the current character is space, skip to the next non-space character.
|
||||
while (NextWord < BufLast)
|
||||
{
|
||||
if (*NextWord != L' ')
|
||||
{
|
||||
break;
|
||||
}
|
||||
++NextWord;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip the body part.
|
||||
bool fStartFromDelim = IsWordDelim(*NextWord);
|
||||
|
||||
while (++NextWord < BufLast)
|
||||
{
|
||||
if (fStartFromDelim != IsWordDelim(*NextWord))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the space block.
|
||||
if (NextWord < BufLast && *NextWord == L' ')
|
||||
{
|
||||
while (++NextWord < BufLast)
|
||||
{
|
||||
if (*NextWord != L' ')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cookedReadData.SetBufferCurrentPtr(NextWord);
|
||||
cookedReadData.InsertionPoint() = (ULONG)(cookedReadData.BufferCurrentPtr() - cookedReadData.BufferStartPtr());
|
||||
cursorPosition = cookedReadData.OriginalCursorPosition();
|
||||
cursorPosition.X = (SHORT)(cursorPosition.X +
|
||||
RetrieveTotalNumberOfSpaces(cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint()));
|
||||
const SHORT sScreenBufferSizeX = cookedReadData.ScreenInfo().GetBufferSize().Width();
|
||||
if (CheckBisectStringW(cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint() + 1,
|
||||
sScreenBufferSizeX - cookedReadData.OriginalCursorPosition().X))
|
||||
{
|
||||
cursorPosition.X++;
|
||||
}
|
||||
}
|
||||
const size_t cellsMoved = cookedReadData.MoveInsertionIndexRightByWord();
|
||||
// the cursor is adjusted to be within the bounds of the screen later, don't need to worry about it here
|
||||
cursorPosition.X += gsl::narrow<short>(cellsMoved);
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
|
@ -858,71 +517,12 @@ COORD CommandLine::_moveCursorRightByWord(COOKED_READ_DATA& cookedReadData) noex
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - The new cursor position
|
||||
COORD CommandLine::_moveCursorRight(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
COORD CommandLine::_moveCursorRight(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
const SHORT sScreenBufferSizeX = cookedReadData.ScreenInfo().GetBufferSize().Width();
|
||||
// If not at the end of the line, move cursor position right.
|
||||
if (cookedReadData.InsertionPoint() < (cookedReadData.BytesRead() / sizeof(WCHAR)))
|
||||
{
|
||||
cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
cursorPosition.X = (SHORT)(cursorPosition.X +
|
||||
RetrieveNumberOfSpaces(cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint()));
|
||||
if (CheckBisectProcessW(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint() + 2,
|
||||
sScreenBufferSizeX - cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
true))
|
||||
{
|
||||
if (cursorPosition.X == (sScreenBufferSizeX - 1))
|
||||
cursorPosition.X++;
|
||||
}
|
||||
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferCurrentPtr() + 1);
|
||||
cookedReadData.InsertionPoint()++;
|
||||
}
|
||||
// if at the end of the line, copy a character from the same position in the last command
|
||||
else if (cookedReadData.HasHistory())
|
||||
{
|
||||
size_t NumSpaces;
|
||||
const auto LastCommand = cookedReadData.History().GetLastCommand();
|
||||
if (!LastCommand.empty() && LastCommand.size() > cookedReadData.InsertionPoint())
|
||||
{
|
||||
*cookedReadData.BufferCurrentPtr() = LastCommand[cookedReadData.InsertionPoint()];
|
||||
cookedReadData.BytesRead() += sizeof(WCHAR);
|
||||
cookedReadData.InsertionPoint()++;
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
short ScrollY = 0;
|
||||
size_t CharsToWrite = sizeof(WCHAR);
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&CharsToWrite,
|
||||
&NumSpaces,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
cookedReadData.VisibleCharCount() += NumSpaces;
|
||||
// update reported cursor position
|
||||
if (ScrollY != 0)
|
||||
{
|
||||
cursorPosition.X = 0;
|
||||
cursorPosition.Y += ScrollY;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursorPosition.X += 1;
|
||||
}
|
||||
}
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferCurrentPtr() + 1);
|
||||
}
|
||||
}
|
||||
const size_t cellsMoved = cookedReadData.MoveInsertionIndexRight();
|
||||
// the cursor is adjusted to be within the bounds of the screen later, don't need to worry about it here
|
||||
cursorPosition.X += gsl::narrow<short>(cellsMoved);
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
|
@ -930,37 +530,16 @@ COORD CommandLine::_moveCursorRight(COOKED_READ_DATA& cookedReadData) noexcept
|
|||
// - Place a ctrl-z in the current command line
|
||||
// Arguments:
|
||||
// - cookedReadData - The cooked read data to operate on
|
||||
void CommandLine::_insertCtrlZ(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandLine::_insertCtrlZ(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
size_t NumSpaces = 0;
|
||||
|
||||
*cookedReadData.BufferCurrentPtr() = (WCHAR)0x1a; // ctrl-z
|
||||
cookedReadData.BytesRead() += sizeof(WCHAR);
|
||||
cookedReadData.InsertionPoint()++;
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
short ScrollY = 0;
|
||||
size_t CharsToWrite = sizeof(WCHAR);
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&CharsToWrite,
|
||||
&NumSpaces,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
cookedReadData.VisibleCharCount() += NumSpaces;
|
||||
}
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferCurrentPtr() + 1);
|
||||
cookedReadData.InsertCtrlZ();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Empties the command history for cookedReadData
|
||||
// Arguments:
|
||||
// - cookedReadData - The cooked read data to operate on
|
||||
void CommandLine::_deleteCommandHistory(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandLine::_deleteCommandHistory(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
if (cookedReadData.HasHistory())
|
||||
{
|
||||
|
@ -973,39 +552,9 @@ void CommandLine::_deleteCommandHistory(COOKED_READ_DATA& cookedReadData) noexce
|
|||
// - Copy the remainder of the previous command to the current command.
|
||||
// Arguments:
|
||||
// - cookedReadData - The cooked read data to operate on
|
||||
void CommandLine::_fillPromptWithPreviousCommandFragment(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandLine::_fillPromptWithPreviousCommandFragment(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
if (cookedReadData.HasHistory())
|
||||
{
|
||||
size_t NumSpaces, cchCount;
|
||||
|
||||
const auto LastCommand = cookedReadData.History().GetLastCommand();
|
||||
if (!LastCommand.empty() && LastCommand.size() > cookedReadData.InsertionPoint())
|
||||
{
|
||||
cchCount = LastCommand.size() - cookedReadData.InsertionPoint();
|
||||
const auto bufferSpan = cookedReadData.SpanAtPointer();
|
||||
std::copy_n(LastCommand.cbegin() + cookedReadData.InsertionPoint(), cchCount, bufferSpan.begin());
|
||||
cookedReadData.InsertionPoint() += cchCount;
|
||||
cchCount *= sizeof(WCHAR);
|
||||
cookedReadData.BytesRead() = std::max(LastCommand.size() * sizeof(wchar_t), cookedReadData.BytesRead());
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
short ScrollY = 0;
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&cchCount,
|
||||
&NumSpaces,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
cookedReadData.VisibleCharCount() += NumSpaces;
|
||||
}
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferCurrentPtr() + cchCount / sizeof(WCHAR));
|
||||
}
|
||||
}
|
||||
cookedReadData.FillPromptWithPreviousCommandFragment();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -1014,50 +563,9 @@ void CommandLine::_fillPromptWithPreviousCommandFragment(COOKED_READ_DATA& cooke
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - The new cursor position
|
||||
COORD CommandLine::_cycleMatchingCommandHistoryToPrompt(COOKED_READ_DATA& cookedReadData)
|
||||
void CommandLine::_cycleMatchingCommandHistoryToPrompt(CookedRead& cookedReadData)
|
||||
{
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
if (cookedReadData.HasHistory())
|
||||
{
|
||||
SHORT index;
|
||||
if (cookedReadData.History().FindMatchingCommand({ cookedReadData.BufferStartPtr(), cookedReadData.InsertionPoint() },
|
||||
cookedReadData.History().LastDisplayed,
|
||||
index,
|
||||
CommandHistory::MatchOptions::None))
|
||||
{
|
||||
SHORT CurrentPos;
|
||||
|
||||
// save cursor position
|
||||
CurrentPos = (SHORT)cookedReadData.InsertionPoint();
|
||||
|
||||
DeleteCommandLine(cookedReadData, true);
|
||||
THROW_IF_FAILED(cookedReadData.History().RetrieveNth((SHORT)index,
|
||||
cookedReadData.SpanWholeBuffer(),
|
||||
cookedReadData.BytesRead()));
|
||||
FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
short ScrollY = 0;
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
&ScrollY));
|
||||
cookedReadData.OriginalCursorPosition().Y += ScrollY;
|
||||
cursorPosition.Y += ScrollY;
|
||||
}
|
||||
|
||||
// restore cursor position
|
||||
cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + CurrentPos);
|
||||
cookedReadData.InsertionPoint() = CurrentPos;
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(cookedReadData.ScreenInfo().SetCursorPosition(cursorPosition, true));
|
||||
}
|
||||
}
|
||||
return cursorPosition;
|
||||
cookedReadData.SetPromptToMatchingHistoryCommand();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -1066,55 +574,9 @@ COORD CommandLine::_cycleMatchingCommandHistoryToPrompt(COOKED_READ_DATA& cooked
|
|||
// - cookedReadData - The cooked read data to operate on
|
||||
// Return Value:
|
||||
// - The new cursor position
|
||||
COORD CommandLine::DeleteFromRightOfCursor(COOKED_READ_DATA& cookedReadData) noexcept
|
||||
void CommandLine::DeleteFromRightOfCursor(CookedRead& cookedReadData) noexcept
|
||||
{
|
||||
// save cursor position
|
||||
COORD cursorPosition = cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().GetPosition();
|
||||
|
||||
if (!cookedReadData.AtEol())
|
||||
{
|
||||
// Delete commandline.
|
||||
#pragma prefast(suppress:__WARNING_BUFFER_OVERFLOW, "Not sure why prefast is getting confused here")
|
||||
DeleteCommandLine(cookedReadData, false);
|
||||
|
||||
// Delete char.
|
||||
cookedReadData.BytesRead() -= sizeof(WCHAR);
|
||||
memmove(cookedReadData.BufferCurrentPtr(),
|
||||
cookedReadData.BufferCurrentPtr() + 1,
|
||||
cookedReadData.BytesRead() - (cookedReadData.InsertionPoint() * sizeof(WCHAR)));
|
||||
|
||||
{
|
||||
PWCHAR buf = (PWCHAR)((PBYTE)cookedReadData.BufferStartPtr() + cookedReadData.BytesRead());
|
||||
*buf = (WCHAR)' ';
|
||||
}
|
||||
|
||||
// Write commandline.
|
||||
if (cookedReadData.IsEchoInput())
|
||||
{
|
||||
FAIL_FAST_IF_NTSTATUS_FAILED(WriteCharsLegacy(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
&cookedReadData.BytesRead(),
|
||||
&cookedReadData.VisibleCharCount(),
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
|
||||
nullptr));
|
||||
}
|
||||
|
||||
// restore cursor position
|
||||
const SHORT sScreenBufferSizeX = cookedReadData.ScreenInfo().GetBufferSize().Width();
|
||||
if (CheckBisectProcessW(cookedReadData.ScreenInfo(),
|
||||
cookedReadData.BufferStartPtr(),
|
||||
cookedReadData.InsertionPoint() + 1,
|
||||
sScreenBufferSizeX - cookedReadData.OriginalCursorPosition().X,
|
||||
cookedReadData.OriginalCursorPosition().X,
|
||||
true))
|
||||
{
|
||||
cursorPosition.X++;
|
||||
}
|
||||
}
|
||||
return cursorPosition;
|
||||
cookedReadData.DeleteFromRightOfInsertionIndex();
|
||||
}
|
||||
|
||||
// TODO: [MSFT:4586207] Clean up this mess -- needs helpers. http://osgvsowi/4586207
|
||||
|
@ -1125,7 +587,7 @@ COORD CommandLine::DeleteFromRightOfCursor(COOKED_READ_DATA& cookedReadData) noe
|
|||
// - CONSOLE_STATUS_READ_COMPLETE - user hit <enter> in CommandListPopup
|
||||
// - STATUS_SUCCESS - everything's cool
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandLine::ProcessCommandLine(COOKED_READ_DATA& cookedReadData,
|
||||
NTSTATUS CommandLine::ProcessCommandLine(CookedRead& cookedReadData,
|
||||
_In_ WCHAR wch,
|
||||
const DWORD dwKeyState)
|
||||
{
|
||||
|
@ -1281,6 +743,7 @@ NTSTATUS CommandLine::ProcessCommandLine(COOKED_READ_DATA& cookedReadData,
|
|||
if (!ctrlPressed && !altPressed)
|
||||
{
|
||||
Status = _startCommandListPopup(cookedReadData);
|
||||
return Status;
|
||||
}
|
||||
else if (altPressed)
|
||||
{
|
||||
|
@ -1291,8 +754,7 @@ NTSTATUS CommandLine::ProcessCommandLine(COOKED_READ_DATA& cookedReadData,
|
|||
case VK_F8:
|
||||
try
|
||||
{
|
||||
cursorPosition = _cycleMatchingCommandHistoryToPrompt(cookedReadData);
|
||||
UpdateCursorPosition = true;
|
||||
_cycleMatchingCommandHistoryToPrompt(cookedReadData);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
@ -1324,8 +786,7 @@ NTSTATUS CommandLine::ProcessCommandLine(COOKED_READ_DATA& cookedReadData,
|
|||
cookedReadData.ScreenInfo().SetCursorDBMode(cookedReadData.IsInsertMode() != gci.GetInsertMode());
|
||||
break;
|
||||
case VK_DELETE:
|
||||
cursorPosition = DeleteFromRightOfCursor(cookedReadData);
|
||||
UpdateCursorPosition = true;
|
||||
DeleteFromRightOfCursor(cookedReadData);
|
||||
break;
|
||||
default:
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
|
|
|
@ -60,8 +60,8 @@ Notes:
|
|||
|
||||
#include "history.h"
|
||||
#include "alias.h"
|
||||
#include "readDataCooked.hpp"
|
||||
#include "popup.h"
|
||||
#include "cookedRead.hpp"
|
||||
|
||||
|
||||
class CommandLine
|
||||
|
@ -77,12 +77,12 @@ public:
|
|||
bool IsVisible() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS ProcessCommandLine(COOKED_READ_DATA& cookedReadData,
|
||||
NTSTATUS ProcessCommandLine(CookedRead& cookedReadData,
|
||||
_In_ WCHAR wch,
|
||||
const DWORD dwKeyState);
|
||||
|
||||
[[nodiscard]]
|
||||
HRESULT StartCommandNumberPopup(COOKED_READ_DATA& cookedReadData);
|
||||
HRESULT StartCommandNumberPopup(CookedRead& cookedReadData);
|
||||
|
||||
bool HasPopup() const noexcept;
|
||||
Popup& GetPopup();
|
||||
|
@ -95,8 +95,8 @@ public:
|
|||
void EndCurrentPopup();
|
||||
void EndAllPopups();
|
||||
|
||||
void DeletePromptAfterCursor(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
COORD DeleteFromRightOfCursor(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
void DeletePromptAfterCursor(CookedRead& cookedReadData) noexcept;
|
||||
void DeleteFromRightOfCursor(CookedRead& cookedReadData) noexcept;
|
||||
protected:
|
||||
CommandLine();
|
||||
|
||||
|
@ -105,26 +105,26 @@ protected:
|
|||
CommandLine& operator=(CommandLine const&) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandLine::_startCommandListPopup(COOKED_READ_DATA& cookedReadData);
|
||||
NTSTATUS CommandLine::_startCommandListPopup(CookedRead& cookedReadData);
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandLine::_startCopyFromCharPopup(COOKED_READ_DATA& cookedReadData);
|
||||
NTSTATUS CommandLine::_startCopyFromCharPopup(CookedRead& cookedReadData);
|
||||
[[nodiscard]]
|
||||
NTSTATUS CommandLine::_startCopyToCharPopup(COOKED_READ_DATA& cookedReadData);
|
||||
NTSTATUS CommandLine::_startCopyToCharPopup(CookedRead& cookedReadData);
|
||||
|
||||
void _processHistoryCycling(COOKED_READ_DATA& cookedReadData, const CommandHistory::SearchDirection searchDirection);
|
||||
void _setPromptToOldestCommand(COOKED_READ_DATA& cookedReadData);
|
||||
void _setPromptToNewestCommand(COOKED_READ_DATA& cookedReadData);
|
||||
COORD _deletePromptBeforeCursor(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
COORD _moveCursorToEndOfPrompt(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
COORD _moveCursorToStartOfPrompt(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
COORD _moveCursorLeftByWord(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
COORD _moveCursorLeft(COOKED_READ_DATA& cookedReadData);
|
||||
COORD _moveCursorRightByWord(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
COORD _moveCursorRight(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
void _insertCtrlZ(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
void _deleteCommandHistory(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
void _fillPromptWithPreviousCommandFragment(COOKED_READ_DATA& cookedReadData) noexcept;
|
||||
COORD _cycleMatchingCommandHistoryToPrompt(COOKED_READ_DATA& cookedReadData);
|
||||
void _processHistoryCycling(CookedRead& cookedReadData, const CommandHistory::SearchDirection searchDirection);
|
||||
void _setPromptToOldestCommand(CookedRead& cookedReadData);
|
||||
void _setPromptToNewestCommand(CookedRead& cookedReadData);
|
||||
COORD _deletePromptBeforeCursor(CookedRead& cookedReadData) noexcept;
|
||||
COORD _moveCursorToEndOfPrompt(CookedRead& cookedReadData) noexcept;
|
||||
COORD _moveCursorToStartOfPrompt(CookedRead& cookedReadData) noexcept;
|
||||
COORD _moveCursorLeftByWord(CookedRead& cookedReadData) noexcept;
|
||||
COORD _moveCursorLeft(CookedRead& cookedReadData);
|
||||
COORD _moveCursorRightByWord(CookedRead& cookedReadData) noexcept;
|
||||
COORD _moveCursorRight(CookedRead& cookedReadData) noexcept;
|
||||
void _insertCtrlZ(CookedRead& cookedReadData) noexcept;
|
||||
void _deleteCommandHistory(CookedRead& cookedReadData) noexcept;
|
||||
void _fillPromptWithPreviousCommandFragment(CookedRead& cookedReadData) noexcept;
|
||||
void _cycleMatchingCommandHistoryToPrompt(CookedRead& cookedReadData);
|
||||
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
|
@ -139,9 +139,9 @@ private:
|
|||
};
|
||||
|
||||
|
||||
void DeleteCommandLine(COOKED_READ_DATA& cookedReadData, const bool fUpdateFields);
|
||||
void DeleteCommandLine(CookedRead& cookedReadData, const bool fUpdateFields);
|
||||
|
||||
void RedrawCommandLine(COOKED_READ_DATA& cookedReadData);
|
||||
void RedrawCommandLine(CookedRead& cookedReadData);
|
||||
|
||||
// Values for WriteChars(), WriteCharsLegacy() dwFlags
|
||||
#define WC_DESTRUCTIVE_BACKSPACE 0x01
|
||||
|
@ -166,4 +166,4 @@ HRESULT DoSrvSetConsoleTitleW(const std::wstring_view title) noexcept;
|
|||
|
||||
bool IsValidStringBuffer(_In_ bool Unicode, _In_reads_bytes_(Size) PVOID Buffer, _In_ ULONG Size, _In_ ULONG Count, ...);
|
||||
|
||||
void SetCurrentCommandLine(COOKED_READ_DATA& cookedReadData, _In_ SHORT Index);
|
||||
void SetCurrentCommandLine(CookedRead& cookedReadData, _In_ SHORT Index);
|
||||
|
|
|
@ -163,17 +163,17 @@ bool CONSOLE_INFORMATION::HasPendingCookedRead() const noexcept
|
|||
return _cookedReadData != nullptr;
|
||||
}
|
||||
|
||||
const COOKED_READ_DATA& CONSOLE_INFORMATION::CookedReadData() const noexcept
|
||||
const CookedRead& CONSOLE_INFORMATION::CookedReadData() const noexcept
|
||||
{
|
||||
return *_cookedReadData;
|
||||
}
|
||||
|
||||
COOKED_READ_DATA& CONSOLE_INFORMATION::CookedReadData() noexcept
|
||||
CookedRead& CONSOLE_INFORMATION::CookedReadData() noexcept
|
||||
{
|
||||
return *_cookedReadData;
|
||||
}
|
||||
|
||||
void CONSOLE_INFORMATION::SetCookedReadData(COOKED_READ_DATA* readData) noexcept
|
||||
void CONSOLE_INFORMATION::SetCookedReadData(CookedRead* readData) noexcept
|
||||
{
|
||||
_cookedReadData = readData;
|
||||
}
|
||||
|
|
1112
src/host/cookedRead.cpp
Normal file
1112
src/host/cookedRead.cpp
Normal file
File diff suppressed because it is too large
Load diff
172
src/host/cookedRead.hpp
Normal file
172
src/host/cookedRead.hpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "readData.hpp"
|
||||
#include "screenInfo.hpp"
|
||||
#include "history.h"
|
||||
|
||||
class CookedRead final : public ReadData
|
||||
{
|
||||
public:
|
||||
|
||||
CookedRead(InputBuffer* const pInputBuffer,
|
||||
INPUT_READ_HANDLE_DATA* const pInputReadHandleData,
|
||||
SCREEN_INFORMATION& screenInfo,
|
||||
std::shared_ptr<CommandHistory> pCommandHistory,
|
||||
wchar_t* userBuffer,
|
||||
const size_t cchUserBuffer,
|
||||
const ULONG ctrlWakeupMask,
|
||||
const std::wstring_view exeName
|
||||
);
|
||||
|
||||
bool Notify(const WaitTerminationReason TerminationReason,
|
||||
const bool fIsUnicode,
|
||||
_Out_ NTSTATUS* const pReplyStatus,
|
||||
_Out_ size_t* const pNumBytes,
|
||||
_Out_ DWORD* const pControlKeyState,
|
||||
_Out_ void* const pOutputData);
|
||||
|
||||
[[nodiscard]]
|
||||
NTSTATUS Read(const bool isUnicode,
|
||||
size_t& numBytes,
|
||||
ULONG& controlKeyState) noexcept;
|
||||
|
||||
void Erase();
|
||||
void Hide();
|
||||
void Show();
|
||||
|
||||
|
||||
bool IsInsertMode() const noexcept;
|
||||
void SetInsertMode(const bool mode) noexcept;
|
||||
|
||||
bool IsEchoInput() const noexcept;
|
||||
|
||||
COORD PromptStartLocation() const noexcept;
|
||||
COORD BeforePopupCursorPosition() const noexcept;
|
||||
size_t VisibleCharCount() const;
|
||||
|
||||
size_t MoveInsertionIndexLeft();
|
||||
size_t MoveInsertionIndexRight();
|
||||
void MoveInsertionIndexToStart() noexcept;
|
||||
size_t MoveInsertionIndexToEnd();
|
||||
size_t MoveInsertionIndexLeftByWord();
|
||||
size_t MoveInsertionIndexRightByWord();
|
||||
|
||||
void SetPromptToOldestCommand();
|
||||
void SetPromptToNewestCommand();
|
||||
void SetPromptToCommand(const size_t index);
|
||||
void SetPromptToCommand(const CommandHistory::SearchDirection searchDirection);
|
||||
void SetPromptToMatchingHistoryCommand();
|
||||
void FillPromptWithPreviousCommandFragment();
|
||||
|
||||
void Overwrite(const std::wstring_view::const_iterator startIt, const std::wstring_view::const_iterator endIt);
|
||||
|
||||
size_t DeletePromptBeforeInsertionIndex();
|
||||
void DeletePromptAfterInsertionIndex();
|
||||
void DeleteFromRightOfInsertionIndex();
|
||||
|
||||
void InsertCtrlZ();
|
||||
|
||||
SCREEN_INFORMATION& ScreenInfo();
|
||||
|
||||
CommandHistory& History() noexcept;
|
||||
bool HasHistory() const noexcept;
|
||||
|
||||
void BufferInput(const wchar_t wch);
|
||||
|
||||
std::wstring_view Prompt();
|
||||
std::wstring_view PromptFromInsertionIndex();
|
||||
|
||||
size_t InsertionIndex() const noexcept;
|
||||
|
||||
private:
|
||||
enum class ReadState
|
||||
{
|
||||
Ready, // ready to read more user input
|
||||
Wait, // need to wait for more input
|
||||
Error, // something went wrong
|
||||
GotChar, // read a character from the user
|
||||
Complete, // the prompt has been completed (user pressed enter)
|
||||
CommandKey, // the user pressed a command editing key
|
||||
Popup
|
||||
};
|
||||
|
||||
static bool _isSurrogatePairAt(const std::wstring_view wstrView, const size_t index);
|
||||
static size_t _visibleCharCountOf(const std::wstring_view wstrView);
|
||||
|
||||
bool _isTailSurrogatePair() const;
|
||||
bool _isSurrogatePairAt(const size_t index) const;
|
||||
bool _isCtrlWakeupMaskTriggered(const wchar_t wch) const noexcept;
|
||||
|
||||
bool _insertMode;
|
||||
|
||||
|
||||
// the buffer that the prompt data will go into in order to finish the ReadConsole api call
|
||||
wchar_t* _userBuffer;
|
||||
size_t _cchUserBuffer;
|
||||
|
||||
SCREEN_INFORMATION& _screenInfo;
|
||||
// storage for the prompt text data
|
||||
std::wstring _prompt;
|
||||
// the location of where the interactive portion of the prompt starts
|
||||
COORD _promptStartLocation;
|
||||
// the location of the cursor before a popup is launched
|
||||
COORD _beforePopupCursorPosition;
|
||||
std::shared_ptr<CommandHistory> _pCommandHistory;
|
||||
// mask of control keys that if pressed will end the cooked read early
|
||||
const ULONG _ctrlWakeupMask;
|
||||
// current state of the CookedRead
|
||||
ReadState _state;
|
||||
NTSTATUS _status;
|
||||
// index of _prompt for character insertion
|
||||
size_t _insertionIndex;
|
||||
|
||||
// used for special command line keys
|
||||
bool _commandLineEditingKeys;
|
||||
DWORD _keyState;
|
||||
wchar_t _commandKeyChar;
|
||||
|
||||
bool _echoInput;
|
||||
|
||||
std::deque<wchar_t> _bufferedInput;
|
||||
|
||||
// name of the application that requested the read. used for alias transformation.
|
||||
std::wstring _exeName;
|
||||
|
||||
|
||||
void _readChar(std::deque<wchar_t>& unprocessedChars);
|
||||
void _processChars(std::deque<wchar_t>& unprocessedChars);
|
||||
void _error();
|
||||
void _wait();
|
||||
void _complete(size_t& numBytes);
|
||||
void _commandKey();
|
||||
void _popup();
|
||||
|
||||
void _backspace();
|
||||
void _clearPromptCells();
|
||||
void _writeToPrompt(std::deque<wchar_t>& unprocessedChars);
|
||||
void _writeToScreen(const bool resetCursor);
|
||||
|
||||
size_t _calculatePromptCellLength(const bool wholePrompt) const;
|
||||
|
||||
bool _isInsertionIndexAtPromptBegin();
|
||||
bool _isInsertionIndexAtPromptEnd();
|
||||
|
||||
void _adjustCursorToInsertionIndex();
|
||||
|
||||
#if UNIT_TESTING
|
||||
friend class CommandLineTests;
|
||||
friend class CopyFromCharPopupTests;
|
||||
friend class CopyToCharPopupTests;
|
||||
friend class CommandNumberPopupTests;
|
||||
friend class CommandListPopupTests;
|
||||
friend class PopupTestHelper;
|
||||
|
||||
public:
|
||||
inline void SetPromptStartLocation(const COORD location)
|
||||
{
|
||||
_promptStartLocation = location;
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
|
@ -19,6 +19,7 @@
|
|||
#include "ApiRoutines.h"
|
||||
|
||||
#include "..\interactivity\inc\ServiceLocator.hpp"
|
||||
#include "cookedRead.hpp"
|
||||
|
||||
#pragma hdrstop
|
||||
|
||||
|
@ -347,6 +348,7 @@ HRESULT ApiRoutines::SetConsoleInputModeImpl(InputBuffer& context, const ULONG m
|
|||
if (gci.HasPendingCookedRead())
|
||||
{
|
||||
gci.CookedReadData().SetInsertMode(gci.GetInsertMode());
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,16 +28,16 @@
|
|||
// (where other collections like deque do not.)
|
||||
// If CommandHistory::s_Allocate and friends stop shuffling elements
|
||||
// for maintaining LRU, then this datatype can be changed.
|
||||
std::list<CommandHistory> CommandHistory::s_historyLists;
|
||||
std::list<std::shared_ptr<CommandHistory>> CommandHistory::s_historyLists;
|
||||
|
||||
CommandHistory* CommandHistory::s_Find(const HANDLE processHandle)
|
||||
std::shared_ptr<CommandHistory> CommandHistory::s_Find(const HANDLE processHandle)
|
||||
{
|
||||
for (auto& historyList : s_historyLists)
|
||||
for (auto historyList : s_historyLists)
|
||||
{
|
||||
if (historyList._processHandle == processHandle)
|
||||
if (historyList->_processHandle == processHandle)
|
||||
{
|
||||
FAIL_FAST_IF(WI_IsFlagClear(historyList.Flags, CLE_ALLOCATED));
|
||||
return &historyList;
|
||||
FAIL_FAST_IF(WI_IsFlagClear(historyList->Flags, CLE_ALLOCATED));
|
||||
return historyList;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ CommandHistory* CommandHistory::s_Find(const HANDLE processHandle)
|
|||
// - processHandle - handle to client process.
|
||||
void CommandHistory::s_Free(const HANDLE processHandle)
|
||||
{
|
||||
CommandHistory* const History = CommandHistory::s_Find(processHandle);
|
||||
const std::shared_ptr<CommandHistory>& History = CommandHistory::s_Find(processHandle);
|
||||
if (History)
|
||||
{
|
||||
WI_ClearFlag(History->Flags, CLE_ALLOCATED);
|
||||
|
@ -64,9 +64,9 @@ void CommandHistory::s_ResizeAll(const size_t commands)
|
|||
FAIL_FAST_IF(commands > SHORT_MAX);
|
||||
gci.SetHistoryBufferSize(gsl::narrow<UINT>(commands));
|
||||
|
||||
for (auto& historyList : s_historyLists)
|
||||
for (auto historyList : s_historyLists)
|
||||
{
|
||||
historyList.Realloc(commands);
|
||||
historyList->Realloc(commands);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,10 +297,10 @@ void CommandHistory::s_ReallocExeToFront(const std::wstring_view appName, const
|
|||
{
|
||||
for (auto it = s_historyLists.begin(); it != s_historyLists.end(); it++)
|
||||
{
|
||||
if (WI_IsFlagSet(it->Flags, CLE_ALLOCATED) && it->IsAppNameMatch(appName))
|
||||
if (WI_IsFlagSet((*it)->Flags, CLE_ALLOCATED) && (*it)->IsAppNameMatch(appName))
|
||||
{
|
||||
CommandHistory backup = *it;
|
||||
backup.Realloc(commands);
|
||||
std::shared_ptr<CommandHistory> backup = *it;
|
||||
backup->Realloc(commands);
|
||||
|
||||
s_historyLists.erase(it);
|
||||
s_historyLists.push_front(backup);
|
||||
|
@ -310,13 +310,13 @@ void CommandHistory::s_ReallocExeToFront(const std::wstring_view appName, const
|
|||
}
|
||||
}
|
||||
|
||||
CommandHistory* CommandHistory::s_FindByExe(const std::wstring_view appName)
|
||||
std::shared_ptr<CommandHistory> CommandHistory::s_FindByExe(const std::wstring_view appName)
|
||||
{
|
||||
for (auto& historyList : s_historyLists)
|
||||
for (auto historyList : s_historyLists)
|
||||
{
|
||||
if (WI_IsFlagSet(historyList.Flags, CLE_ALLOCATED) && historyList.IsAppNameMatch(appName))
|
||||
if (WI_IsFlagSet(historyList->Flags, CLE_ALLOCATED) && historyList->IsAppNameMatch(appName))
|
||||
{
|
||||
return &historyList;
|
||||
return historyList;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -333,20 +333,20 @@ size_t CommandHistory::s_CountOfHistories()
|
|||
// - Console - pointer to console.
|
||||
// Return Value:
|
||||
// - Pointer to command history buffer. if none are available, returns nullptr.
|
||||
CommandHistory* CommandHistory::s_Allocate(const std::wstring_view appName, const HANDLE processHandle)
|
||||
std::shared_ptr<CommandHistory> CommandHistory::s_Allocate(const std::wstring_view appName, const HANDLE processHandle)
|
||||
{
|
||||
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
// Reuse a history buffer. The buffer must be !CLE_ALLOCATED.
|
||||
// If possible, the buffer should have the same app name.
|
||||
std::optional<CommandHistory> BestCandidate;
|
||||
std::shared_ptr<CommandHistory> BestCandidate = nullptr;
|
||||
bool SameApp = false;
|
||||
|
||||
for (auto it = s_historyLists.cbegin(); it != s_historyLists.cend(); it++)
|
||||
{
|
||||
if (WI_IsFlagClear(it->Flags, CLE_ALLOCATED))
|
||||
if (WI_IsFlagClear((*it)->Flags, CLE_ALLOCATED))
|
||||
{
|
||||
// use LRU history buffer with same app name
|
||||
if (it->IsAppNameMatch(appName))
|
||||
if ((*it)->IsAppNameMatch(appName))
|
||||
{
|
||||
BestCandidate = *it;
|
||||
SameApp = true;
|
||||
|
@ -360,21 +360,21 @@ CommandHistory* CommandHistory::s_Allocate(const std::wstring_view appName, cons
|
|||
// command history buffers hasn't been allocated, allocate a new one.
|
||||
if (!SameApp && s_historyLists.size() < gci.GetNumberOfHistoryBuffers())
|
||||
{
|
||||
CommandHistory History;
|
||||
std::shared_ptr<CommandHistory> History = std::make_shared<CommandHistory>();
|
||||
|
||||
History._appName = appName;
|
||||
History.Flags = CLE_ALLOCATED;
|
||||
History.LastDisplayed = -1;
|
||||
History._maxCommands = gsl::narrow<SHORT>(gci.GetHistoryBufferSize());
|
||||
History._processHandle = processHandle;
|
||||
return &s_historyLists.emplace_front(History);
|
||||
History->_appName = appName;
|
||||
History->Flags = CLE_ALLOCATED;
|
||||
History->LastDisplayed = -1;
|
||||
History->_maxCommands = gsl::narrow<SHORT>(gci.GetHistoryBufferSize());
|
||||
History->_processHandle = processHandle;
|
||||
return s_historyLists.emplace_front(History);
|
||||
}
|
||||
else if (!BestCandidate.has_value() && s_historyLists.size() > 0)
|
||||
else if (BestCandidate == nullptr && s_historyLists.size() > 0)
|
||||
{
|
||||
// If we have no candidate already and we need one, take the LRU (which is the back/last one) which isn't allocated.
|
||||
for (auto it = s_historyLists.crbegin(); it != s_historyLists.crend(); it++)
|
||||
{
|
||||
if (WI_IsFlagClear(it->Flags, CLE_ALLOCATED))
|
||||
if (WI_IsFlagClear((*it)->Flags, CLE_ALLOCATED))
|
||||
{
|
||||
BestCandidate = *it;
|
||||
s_historyLists.erase(std::next(it).base()); // trickery to turn reverse iterator into forward iterator for erase.
|
||||
|
@ -385,7 +385,7 @@ CommandHistory* CommandHistory::s_Allocate(const std::wstring_view appName, cons
|
|||
}
|
||||
|
||||
// If the app name doesn't match, copy in the new app name and free the old commands.
|
||||
if (BestCandidate.has_value())
|
||||
if (BestCandidate)
|
||||
{
|
||||
if (!SameApp)
|
||||
{
|
||||
|
@ -397,7 +397,7 @@ CommandHistory* CommandHistory::s_Allocate(const std::wstring_view appName, cons
|
|||
BestCandidate->_processHandle = processHandle;
|
||||
WI_SetFlag(BestCandidate->Flags, CLE_ALLOCATED);
|
||||
|
||||
return &s_historyLists.emplace_front(BestCandidate.value());
|
||||
return s_historyLists.emplace_front(BestCandidate);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -678,7 +678,7 @@ HRESULT GetConsoleCommandHistoryLengthImplHelper(const std::wstring_view exeName
|
|||
LockConsole();
|
||||
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
|
||||
|
||||
CommandHistory* const pCommandHistory = CommandHistory::s_FindByExe(exeName);
|
||||
const std::shared_ptr<CommandHistory> pCommandHistory = CommandHistory::s_FindByExe(exeName);
|
||||
if (nullptr != pCommandHistory)
|
||||
{
|
||||
size_t cchNeeded = 0;
|
||||
|
@ -783,7 +783,7 @@ HRESULT GetConsoleCommandHistoryWImplHelper(const std::wstring_view exeName,
|
|||
historyBuffer.at(0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
CommandHistory* const CommandHistory = CommandHistory::s_FindByExe(exeName);
|
||||
const std::shared_ptr<CommandHistory> CommandHistory = CommandHistory::s_FindByExe(exeName);
|
||||
|
||||
if (nullptr != CommandHistory)
|
||||
{
|
||||
|
|
|
@ -19,9 +19,9 @@ Abstract:
|
|||
class CommandHistory
|
||||
{
|
||||
public:
|
||||
static CommandHistory* s_Allocate(const std::wstring_view appName, const HANDLE processHandle);
|
||||
static CommandHistory* s_Find(const HANDLE processHandle);
|
||||
static CommandHistory* s_FindByExe(const std::wstring_view appName);
|
||||
static std::shared_ptr<CommandHistory> s_Allocate(const std::wstring_view appName, const HANDLE processHandle);
|
||||
static std::shared_ptr<CommandHistory> s_Find(const HANDLE processHandle);
|
||||
static std::shared_ptr<CommandHistory> s_FindByExe(const std::wstring_view appName);
|
||||
static void s_ReallocExeToFront(const std::wstring_view appName, const size_t commands);
|
||||
static void s_Free(const HANDLE processHandle);
|
||||
static void s_ResizeAll(const size_t commands);
|
||||
|
@ -93,7 +93,7 @@ private:
|
|||
std::wstring _appName;
|
||||
HANDLE _processHandle;
|
||||
|
||||
static std::list<CommandHistory> s_historyLists;
|
||||
static std::list<std::shared_ptr<CommandHistory>> s_historyLists;
|
||||
|
||||
public:
|
||||
DWORD Flags;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<ClCompile Include="..\conattrs.cpp" />
|
||||
<ClCompile Include="..\ConsoleArguments.cpp" />
|
||||
<ClCompile Include="..\CursorBlinker.cpp" />
|
||||
<ClCompile Include="..\readDataCooked.cpp" />
|
||||
<ClCompile Include="..\cookedRead.cpp" />
|
||||
<ClCompile Include="..\conareainfo.cpp" />
|
||||
<ClCompile Include="..\conimeinfo.cpp" />
|
||||
<ClCompile Include="..\consoleInformation.cpp" />
|
||||
|
@ -95,7 +95,7 @@
|
|||
<ClInclude Include="..\popup.h" />
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
<ClInclude Include="..\readData.hpp" />
|
||||
<ClInclude Include="..\readDataCooked.hpp" />
|
||||
<ClInclude Include="..\cookedRead.hpp" />
|
||||
<ClInclude Include="..\rectreadDataDirect.hpp" />
|
||||
<ClInclude Include="..\readDataRaw.hpp" />
|
||||
<ClInclude Include="..\registry.hpp" />
|
||||
|
|
|
@ -333,7 +333,7 @@ void Popup::SetUserInputFunction(UserInputFunction function) noexcept
|
|||
// - wch - on completion, the char read from the user
|
||||
// Return Value:
|
||||
// - relevant NTSTATUS
|
||||
NTSTATUS Popup::_getUserInput(COOKED_READ_DATA& cookedReadData, bool& popupKey, DWORD& modifiers, wchar_t& wch) noexcept
|
||||
NTSTATUS Popup::_getUserInput(CookedRead& cookedReadData, bool& popupKey, DWORD& modifiers, wchar_t& wch) noexcept
|
||||
{
|
||||
return _userInputFunction(cookedReadData, popupKey, modifiers, wch);
|
||||
}
|
||||
|
@ -346,7 +346,7 @@ NTSTATUS Popup::_getUserInput(COOKED_READ_DATA& cookedReadData, bool& popupKey,
|
|||
// - wch - on completion, the char read from the user
|
||||
// Return Value:
|
||||
// - relevant NTSTATUS
|
||||
NTSTATUS Popup::_getUserInputInternal(COOKED_READ_DATA& cookedReadData,
|
||||
NTSTATUS Popup::_getUserInputInternal(CookedRead& cookedReadData,
|
||||
bool& popupKey,
|
||||
DWORD& modifiers,
|
||||
wchar_t& wch) noexcept
|
||||
|
@ -360,7 +360,7 @@ NTSTATUS Popup::_getUserInputInternal(COOKED_READ_DATA& cookedReadData,
|
|||
&modifiers);
|
||||
if (!NT_SUCCESS(Status) && Status != CONSOLE_STATUS_WAIT)
|
||||
{
|
||||
cookedReadData.BytesRead() = 0;
|
||||
cookedReadData.Erase();
|
||||
}
|
||||
return Status;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ Revision History:
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "readDataCooked.hpp"
|
||||
#include "cookedRead.hpp"
|
||||
#include "screenInfo.hpp"
|
||||
#include "readDataCooked.hpp"
|
||||
|
||||
|
@ -34,12 +34,12 @@ class Popup
|
|||
{
|
||||
public:
|
||||
|
||||
using UserInputFunction = std::function<NTSTATUS(COOKED_READ_DATA&, bool&, DWORD&, wchar_t&)>;
|
||||
using UserInputFunction = std::function<NTSTATUS(CookedRead&, bool&, DWORD&, wchar_t&)>;
|
||||
|
||||
Popup(SCREEN_INFORMATION& screenInfo, const COORD proposedSize);
|
||||
virtual ~Popup();
|
||||
[[nodiscard]]
|
||||
virtual NTSTATUS Process(COOKED_READ_DATA& cookedReadData) noexcept = 0;
|
||||
virtual NTSTATUS Process(CookedRead& cookedReadData) noexcept = 0;
|
||||
|
||||
void Draw();
|
||||
|
||||
|
@ -66,7 +66,7 @@ protected:
|
|||
friend class CommandListPopupTests;
|
||||
#endif
|
||||
|
||||
NTSTATUS _getUserInput(COOKED_READ_DATA& cookedReadData, bool& popupKey, DWORD& modifiers, wchar_t& wch) noexcept;
|
||||
NTSTATUS _getUserInput(CookedRead& cookedReadData, bool& popupKey, DWORD& modifiers, wchar_t& wch) noexcept;
|
||||
void _DrawPrompt(const UINT id);
|
||||
virtual void _DrawContent() = 0;
|
||||
|
||||
|
@ -81,7 +81,7 @@ private:
|
|||
|
||||
void _DrawBorder();
|
||||
|
||||
static NTSTATUS _getUserInputInternal(COOKED_READ_DATA& cookedReadData,
|
||||
static NTSTATUS _getUserInputInternal(CookedRead& cookedReadData,
|
||||
bool& popupKey,
|
||||
DWORD& modifiers,
|
||||
wchar_t& wch) noexcept;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "precomp.h"
|
||||
|
||||
#include "search.h"
|
||||
#include "cookedRead.hpp"
|
||||
|
||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||
#include "../types/inc/convert.hpp"
|
||||
|
@ -954,8 +955,8 @@ bool Selection::s_GetInputLineBoundaries(_Out_opt_ COORD* const pcoordInputStart
|
|||
}
|
||||
|
||||
const auto& cookedRead = gci.CookedReadData();
|
||||
const COORD coordStart = cookedRead.OriginalCursorPosition();
|
||||
COORD coordEnd = cookedRead.OriginalCursorPosition();
|
||||
const COORD coordStart = cookedRead.PromptStartLocation();
|
||||
COORD coordEnd = coordStart;
|
||||
|
||||
if (coordEnd.X < 0 && coordEnd.Y < 0)
|
||||
{
|
||||
|
|
|
@ -69,7 +69,7 @@ Revision History:
|
|||
|
||||
#define CONSOLE_SUSPENDED (CONSOLE_OUTPUT_SUSPENDED)
|
||||
|
||||
class COOKED_READ_DATA;
|
||||
class CookedRead;
|
||||
class CommandHistory;
|
||||
|
||||
class CONSOLE_INFORMATION :
|
||||
|
@ -125,9 +125,9 @@ public:
|
|||
|
||||
bool IsInVtIoMode() const;
|
||||
bool HasPendingCookedRead() const noexcept;
|
||||
const COOKED_READ_DATA& CookedReadData() const noexcept;
|
||||
COOKED_READ_DATA& CookedReadData() noexcept;
|
||||
void SetCookedReadData(COOKED_READ_DATA* readData) noexcept;
|
||||
const CookedRead& CookedReadData() const noexcept;
|
||||
CookedRead& CookedReadData() noexcept;
|
||||
void SetCookedReadData(CookedRead* readData) noexcept;
|
||||
|
||||
COLORREF GetDefaultForeground() const noexcept;
|
||||
COLORREF GetDefaultBackground() const noexcept;
|
||||
|
@ -160,7 +160,7 @@ private:
|
|||
std::wstring _OriginalTitle;
|
||||
std::wstring _LinkTitle; // Path to .lnk file
|
||||
SCREEN_INFORMATION* pCurrentScreenBuffer;
|
||||
COOKED_READ_DATA* _cookedReadData; // non-ownership pointer
|
||||
CookedRead* _cookedReadData; // non-ownership pointer
|
||||
|
||||
Microsoft::Console::VirtualTerminal::VtIo _vtIo;
|
||||
Microsoft::Console::CursorBlinker _blinker;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "..\interactivity\inc\ServiceLocator.hpp"
|
||||
|
||||
#include "cookedRead.hpp"
|
||||
|
||||
#pragma hdrstop
|
||||
|
||||
// Routine Description:
|
||||
|
@ -445,7 +447,7 @@ static NTSTATUS _ReadPendingInput(InputBuffer& inputBuffer,
|
|||
// - bytesRead - on output, the number of bytes read into pwchBuffer
|
||||
// - controlKeyState - set by a cooked read
|
||||
// - initialData - text of initial data found in the read message
|
||||
// - ctrlWakeupMask - used by COOKED_READ_DATA
|
||||
// - ctrlWakeupMask - used by COOKED_READ_DATA to trigger an early return of reading if masked key is pressed
|
||||
// - readHandleState - input read handle data associated with this read operation
|
||||
// - exeName - name of the exe requesting the read
|
||||
// - unicode - true if read should be unicode, false otherwise
|
||||
|
@ -461,7 +463,7 @@ static HRESULT _ReadLineInput(InputBuffer& inputBuffer,
|
|||
gsl::span<char> buffer,
|
||||
size_t& bytesRead,
|
||||
DWORD& controlKeyState,
|
||||
const std::string_view initialData,
|
||||
const std::string_view /*initialData*/,
|
||||
const DWORD ctrlWakeupMask,
|
||||
INPUT_READ_HANDLE_DATA& readHandleState,
|
||||
const std::wstring_view exeName,
|
||||
|
@ -472,22 +474,22 @@ static HRESULT _ReadLineInput(InputBuffer& inputBuffer,
|
|||
RETURN_HR_IF(E_FAIL, !gci.HasActiveOutputBuffer());
|
||||
|
||||
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
|
||||
CommandHistory* const pCommandHistory = CommandHistory::s_Find(processData);
|
||||
const std::shared_ptr<CommandHistory> pCommandHistory = CommandHistory::s_Find(processData);
|
||||
|
||||
try
|
||||
{
|
||||
auto cookedReadData = std::make_unique<COOKED_READ_DATA>(&inputBuffer, // pInputBuffer
|
||||
&readHandleState, // pInputReadHandleData
|
||||
screenInfo, // pScreenInfo
|
||||
buffer.size_bytes(), // UserBufferSize
|
||||
reinterpret_cast<wchar_t*>(buffer.data()), // UserBuffer
|
||||
ctrlWakeupMask, // CtrlWakeupMask
|
||||
pCommandHistory, // CommandHistory
|
||||
exeName, // exe name
|
||||
initialData);
|
||||
|
||||
// TODO hook up other arguments to CookedRead
|
||||
auto cookedReadData = std::make_unique<CookedRead>(&inputBuffer,
|
||||
&readHandleState,
|
||||
screenInfo,
|
||||
pCommandHistory,
|
||||
reinterpret_cast<wchar_t*>(buffer.data()),
|
||||
buffer.size_bytes() / sizeof(wchar_t),
|
||||
ctrlWakeupMask,
|
||||
exeName);
|
||||
gci.SetCookedReadData(cookedReadData.get());
|
||||
bytesRead = buffer.size_bytes(); // This parameter on the way in is the size to read, on the way out, it will be updated to what is actually read.
|
||||
// bytesRead on the way in is the size to read, on the way out, it will be updated to what is actually read.
|
||||
bytesRead = buffer.size_bytes();
|
||||
if (CONSOLE_STATUS_WAIT == cookedReadData->Read(unicode, bytesRead, controlKeyState))
|
||||
{
|
||||
// memory will be cleaned up by wait queue
|
||||
|
@ -497,6 +499,7 @@ static HRESULT _ReadLineInput(InputBuffer& inputBuffer,
|
|||
{
|
||||
gci.SetCookedReadData(nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ constexpr size_t PROMPT_SIZE = 512;
|
|||
class CommandLineTests
|
||||
{
|
||||
std::unique_ptr<CommonState> m_state;
|
||||
CommandHistory* m_pHistory;
|
||||
std::shared_ptr<CommandHistory> m_pHistory;
|
||||
|
||||
TEST_CLASS(CommandLineTests);
|
||||
|
||||
|
@ -49,6 +49,7 @@ class CommandLineTests
|
|||
{
|
||||
return false;
|
||||
}
|
||||
VERIFY_ARE_EQUAL(m_pHistory->GetNumberOfCommands(), 0u);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -56,6 +57,7 @@ class CommandLineTests
|
|||
{
|
||||
CommandHistory::s_Free((HANDLE)0);
|
||||
m_pHistory = nullptr;
|
||||
CommandHistory::s_ClearHistoryListStorage();
|
||||
m_state->CleanupCookedReadData();
|
||||
m_state->CleanupReadHandle();
|
||||
m_state->CleanupGlobalInputBuffer();
|
||||
|
@ -63,53 +65,37 @@ class CommandLineTests
|
|||
return true;
|
||||
}
|
||||
|
||||
void VerifyPromptText(COOKED_READ_DATA& cookedReadData, const std::wstring wstr)
|
||||
void VerifyPromptText(CookedRead& cookedReadData, const std::wstring wstr)
|
||||
{
|
||||
const auto span = cookedReadData.SpanWholeBuffer();
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, wstr.size() * sizeof(wchar_t));
|
||||
for (size_t i = 0; i < wstr.size(); ++i)
|
||||
{
|
||||
VERIFY_ARE_EQUAL(span.at(i), wstr.at(i));
|
||||
}
|
||||
const auto prompt = cookedReadData.Prompt();
|
||||
VERIFY_ARE_EQUAL(prompt, wstr);
|
||||
}
|
||||
|
||||
void InitCookedReadData(COOKED_READ_DATA& cookedReadData,
|
||||
CommandHistory* pHistory,
|
||||
wchar_t* pBuffer,
|
||||
const size_t cchBuffer)
|
||||
void InitCookedReadData(CookedRead& cookedReadData,
|
||||
std::shared_ptr<CommandHistory> pHistory,
|
||||
const std::wstring prompt)
|
||||
{
|
||||
cookedReadData._commandHistory = pHistory;
|
||||
cookedReadData._userBuffer = pBuffer;
|
||||
cookedReadData._userBufferSize = cchBuffer * sizeof(wchar_t);
|
||||
cookedReadData._bufferSize = cchBuffer * sizeof(wchar_t);
|
||||
cookedReadData._backupLimit = pBuffer;
|
||||
cookedReadData._bufPtr = pBuffer;
|
||||
cookedReadData._exeName = L"cmd.exe";
|
||||
cookedReadData.OriginalCursorPosition() = { 0, 0 };
|
||||
cookedReadData._pCommandHistory = pHistory;
|
||||
cookedReadData._prompt = prompt;
|
||||
cookedReadData._promptStartLocation = { 0, 0 };
|
||||
cookedReadData._insertionIndex = 0;
|
||||
}
|
||||
|
||||
void SetPrompt(COOKED_READ_DATA& cookedReadData, const std::wstring text)
|
||||
void SetPrompt(CookedRead& cookedReadData, const std::wstring text)
|
||||
{
|
||||
std::copy(text.begin(), text.end(), cookedReadData._backupLimit);
|
||||
cookedReadData._bytesRead = text.size() * sizeof(wchar_t);
|
||||
cookedReadData._currentPosition = text.size();
|
||||
cookedReadData._bufPtr += text.size();
|
||||
cookedReadData._visibleCharCount = text.size();
|
||||
cookedReadData._prompt = text;
|
||||
cookedReadData._insertionIndex = cookedReadData._prompt.size();
|
||||
}
|
||||
|
||||
void MoveCursor(COOKED_READ_DATA& cookedReadData, const size_t column)
|
||||
void MoveCursor(CookedRead& cookedReadData, const size_t column)
|
||||
{
|
||||
cookedReadData._currentPosition = column;
|
||||
cookedReadData._bufPtr = cookedReadData._backupLimit + column;
|
||||
cookedReadData._insertionIndex = column;
|
||||
}
|
||||
|
||||
TEST_METHOD(CanCycleCommandHistory)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, m_pHistory, L"");
|
||||
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
||||
|
@ -118,7 +104,7 @@ class CommandLineTests
|
|||
auto& commandLine = CommandLine::Instance();
|
||||
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Next);
|
||||
// should not have anything on the prompt
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, 0u);
|
||||
VERIFY_IS_TRUE(cookedReadData._prompt.empty());
|
||||
|
||||
// go back one history item
|
||||
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Previous);
|
||||
|
@ -152,11 +138,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanSetPromptToOldestHistory)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, m_pHistory, L"");
|
||||
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
||||
|
@ -174,11 +157,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanSetPromptToNewestHistory)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, m_pHistory, L"");
|
||||
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
||||
|
@ -196,11 +176,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanDeletePromptAfterCursor)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
auto expected = L"test word blah";
|
||||
SetPrompt(cookedReadData, expected);
|
||||
|
@ -215,11 +192,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanDeletePromptBeforeCursor)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
auto expected = L"test word blah";
|
||||
SetPrompt(cookedReadData, expected);
|
||||
|
@ -229,111 +203,95 @@ class CommandLineTests
|
|||
MoveCursor(cookedReadData, 5);
|
||||
auto& commandLine = CommandLine::Instance();
|
||||
const COORD cursorPos = commandLine._deletePromptBeforeCursor(cookedReadData);
|
||||
cookedReadData._currentPosition = cursorPos.X;
|
||||
//cookedReadData._currentPosition = cursorPos.X;
|
||||
VerifyPromptText(cookedReadData, L"word blah");
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveCursorToEndOfPrompt)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
auto expected = L"test word blah";
|
||||
SetPrompt(cookedReadData, expected);
|
||||
VerifyPromptText(cookedReadData, expected);
|
||||
|
||||
// make sure the cursor is not at the start of the prompt
|
||||
VERIFY_ARE_NOT_EQUAL(cookedReadData._currentPosition, 0u);
|
||||
VERIFY_ARE_NOT_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
||||
VERIFY_ARE_NOT_EQUAL(cookedReadData._insertionIndex, 0u);
|
||||
|
||||
// save current position for later checking
|
||||
const auto expectedCursorPos = cookedReadData._currentPosition;
|
||||
const auto expectedBufferPos = cookedReadData._bufPtr;
|
||||
const auto expectedCursorPos = cookedReadData._insertionIndex;
|
||||
|
||||
MoveCursor(cookedReadData, 0);
|
||||
|
||||
auto& commandLine = CommandLine::Instance();
|
||||
const COORD cursorPos = commandLine._moveCursorToEndOfPrompt(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(cursorPos.X, gsl::narrow<short>(expectedCursorPos));
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, expectedCursorPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, expectedBufferPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, expectedCursorPos);
|
||||
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveCursorToStartOfPrompt)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
auto expected = L"test word blah";
|
||||
SetPrompt(cookedReadData, expected);
|
||||
VerifyPromptText(cookedReadData, expected);
|
||||
|
||||
// make sure the cursor is not at the start of the prompt
|
||||
VERIFY_ARE_NOT_EQUAL(cookedReadData._currentPosition, 0u);
|
||||
VERIFY_ARE_NOT_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
||||
VERIFY_ARE_NOT_EQUAL(cookedReadData._insertionIndex, 0u);
|
||||
VERIFY_IS_FALSE(cookedReadData._prompt.empty());
|
||||
|
||||
auto& commandLine = CommandLine::Instance();
|
||||
const COORD cursorPos = commandLine._moveCursorToStartOfPrompt(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(cursorPos.X, 0);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, 0u);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, 0u);
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveCursorLeftByWord)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
auto expected = L"test word blah";
|
||||
const std::wstring expected = L"test word blah";
|
||||
SetPrompt(cookedReadData, expected);
|
||||
VerifyPromptText(cookedReadData, expected);
|
||||
|
||||
auto& commandLine = CommandLine::Instance();
|
||||
cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().SetPosition({ gsl::narrow<short>(expected.size()), 0 });
|
||||
// cursor position at beginning of "blah"
|
||||
short expectedPos = 10;
|
||||
COORD cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, gsl::narrow<size_t>(expectedPos));
|
||||
|
||||
// move again
|
||||
cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().SetPosition({ expectedPos, 0 });
|
||||
expectedPos = 5; // before "word"
|
||||
cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, gsl::narrow<size_t>(expectedPos));
|
||||
|
||||
// move again
|
||||
cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().SetPosition({ expectedPos, 0 });
|
||||
expectedPos = 0; // before "test"
|
||||
cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, gsl::narrow<size_t>(expectedPos));
|
||||
|
||||
// try to move again, nothing should happen
|
||||
cookedReadData.ScreenInfo().GetTextBuffer().GetCursor().SetPosition({ expectedPos, 0 });
|
||||
cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, gsl::narrow<size_t>(expectedPos));
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveCursorLeft)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
const std::wstring expected = L"test word blah";
|
||||
SetPrompt(cookedReadData, expected);
|
||||
|
@ -344,28 +302,23 @@ class CommandLineTests
|
|||
for (auto it = expected.crbegin(); it != expected.crend(); ++it)
|
||||
{
|
||||
const COORD cursorPos = commandLine._moveCursorLeft(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(*cookedReadData._bufPtr, *it);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._prompt.at(cookedReadData._insertionIndex), *it);
|
||||
}
|
||||
// should now be at the start of the prompt
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, 0u);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, 0u);
|
||||
|
||||
// try to move left a final time, nothing should change
|
||||
const COORD cursorPos = commandLine._moveCursorLeft(cookedReadData);
|
||||
VERIFY_ARE_EQUAL(cursorPos.X, 0);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, 0u);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._insertionIndex, 0u);
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO MSFT:11285829 tcome back and turn these on once the system cursor isn't needed
|
||||
TEST_METHOD(CanMoveCursorRightByWord)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
auto expected = L"test word blah";
|
||||
SetPrompt(cookedReadData, expected);
|
||||
|
@ -398,11 +351,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanInsertCtrlZ)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, nullptr, L"");
|
||||
|
||||
auto& commandLine = CommandLine::Instance();
|
||||
commandLine._insertCtrlZ(cookedReadData);
|
||||
|
@ -411,11 +361,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanDeleteCommandHistory)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, m_pHistory, L"");
|
||||
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
||||
|
@ -428,11 +375,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanFillPromptWithPreviousCommandFragment)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, m_pHistory, L"");
|
||||
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"I'm a little teapot", false));
|
||||
SetPrompt(cookedReadData, L"short and stout");
|
||||
|
@ -444,11 +388,8 @@ class CommandLineTests
|
|||
|
||||
TEST_METHOD(CanCycleMatchingCommandHistory)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
|
||||
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
||||
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, m_pHistory, L"");
|
||||
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"I'm a little teapot", false));
|
||||
VERIFY_SUCCEEDED(m_pHistory->Add(L"short and stout", false));
|
||||
|
@ -472,12 +413,10 @@ class CommandLineTests
|
|||
TEST_METHOD(CmdlineCtrlHomeFullwidthChars)
|
||||
{
|
||||
Log::Comment(L"Set up buffers, create cooked read data, get screen information.");
|
||||
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
||||
VERIFY_IS_NOT_NULL(buffer.get());
|
||||
auto& consoleInfo = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& screenInfo = consoleInfo.GetActiveOutputBuffer();
|
||||
auto& cookedReadData = consoleInfo.CookedReadData();
|
||||
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
||||
InitCookedReadData(cookedReadData, m_pHistory, L"");
|
||||
|
||||
Log::Comment(L"Create Japanese text string and calculate the distance we expect the cursor to move.");
|
||||
const std::wstring text(L"\x30ab\x30ac\x30ad\x30ae\x30af"); // katakana KA GA KI GI KU
|
||||
|
@ -490,8 +429,13 @@ class CommandLineTests
|
|||
}
|
||||
|
||||
Log::Comment(L"Write the text into the buffer using the cooked read structures as if it came off of someone's input.");
|
||||
const auto written = cookedReadData.Write(text);
|
||||
VERIFY_ARE_EQUAL(text.length(), written);
|
||||
for (const auto& wch : text)
|
||||
{
|
||||
cookedReadData.BufferInput(wch);
|
||||
}
|
||||
size_t numBytes = 0;
|
||||
ULONG ctrlKeyState = 0;
|
||||
VERIFY_ARE_EQUAL(cookedReadData.Read(true, numBytes, ctrlKeyState), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT));
|
||||
|
||||
Log::Comment(L"Retrieve the position of the cursor after insertion and check that it moved as far as we expected.");
|
||||
const auto cursorAfter = screenInfo.GetTextBuffer().GetCursor().GetPosition();
|
||||
|
|
|
@ -25,7 +25,7 @@ class CommandListPopupTests
|
|||
TEST_CLASS(CommandListPopupTests);
|
||||
|
||||
std::unique_ptr<CommonState> m_state;
|
||||
CommandHistory* m_pHistory;
|
||||
std::shared_ptr<CommandHistory> m_pHistory;
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
|
@ -72,24 +72,10 @@ class CommandListPopupTests
|
|||
return true;
|
||||
}
|
||||
|
||||
void InitReadData(COOKED_READ_DATA& cookedReadData,
|
||||
wchar_t* const pBuffer,
|
||||
const size_t cchBuffer,
|
||||
const size_t cursorPosition)
|
||||
{
|
||||
cookedReadData._bufferSize = cchBuffer * sizeof(wchar_t);
|
||||
cookedReadData._bufPtr = pBuffer + cursorPosition;
|
||||
cookedReadData._backupLimit = pBuffer;
|
||||
cookedReadData.OriginalCursorPosition() = { 0, 0 };
|
||||
cookedReadData._bytesRead = cursorPosition * sizeof(wchar_t);
|
||||
cookedReadData._currentPosition = cursorPosition;
|
||||
cookedReadData.VisibleCharCount() = cursorPosition;
|
||||
}
|
||||
|
||||
TEST_METHOD(CanDismiss)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
modifiers = 0;
|
||||
|
@ -105,19 +91,14 @@ class CommandListPopupTests
|
|||
|
||||
// prepare cookedReadData
|
||||
const std::wstring testString = L"hello world";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should not be changed
|
||||
const std::wstring resultString(buffer, buffer + testString.size());
|
||||
VERIFY_ARE_EQUAL(testString, resultString);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, testString.size() * sizeof(wchar_t));
|
||||
// the prompt should not be changed
|
||||
VERIFY_ARE_EQUAL(testString, cookedReadData._prompt);
|
||||
|
||||
// popup has been dismissed
|
||||
VERIFY_IS_FALSE(CommandLine::Instance().HasPopup());
|
||||
|
@ -126,7 +107,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(UpMovesSelection)
|
||||
{
|
||||
// function to simulate user pressing up arrow
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -150,11 +131,9 @@ class CommandListPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
const short commandNumberBefore = popup._currentCommand;
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
@ -165,7 +144,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(DownMovesSelection)
|
||||
{
|
||||
// function to simulate user pressing down arrow
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -191,11 +170,9 @@ class CommandListPopupTests
|
|||
popup._currentCommand = 0;
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
const short commandNumberBefore = popup._currentCommand;
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
@ -206,7 +183,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(EndMovesSelectionToEnd)
|
||||
{
|
||||
// function to simulate user pressing end key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -232,11 +209,9 @@ class CommandListPopupTests
|
|||
popup._currentCommand = 0;
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
// selection should have moved to the bottom line
|
||||
|
@ -246,7 +221,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(HomeMovesSelectionToStart)
|
||||
{
|
||||
// function to simulate user pressing home key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -270,11 +245,9 @@ class CommandListPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
// selection should have moved to the bottom line
|
||||
|
@ -284,7 +257,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(PageUpMovesSelection)
|
||||
{
|
||||
// function to simulate user pressing page up key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -308,11 +281,9 @@ class CommandListPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
// selection should have moved up a page
|
||||
|
@ -322,7 +293,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(PageDownMovesSelection)
|
||||
{
|
||||
// function to simulate user pressing page down key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -348,11 +319,9 @@ class CommandListPopupTests
|
|||
popup._currentCommand = 0;
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
// selection should have moved up a page
|
||||
|
@ -362,7 +331,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(SideArrowsFillsPrompt)
|
||||
{
|
||||
// function to simulate user pressing right arrow key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
wch = VK_RIGHT;
|
||||
popupKey = true;
|
||||
|
@ -379,23 +348,21 @@ class CommandListPopupTests
|
|||
popup._currentCommand = 0;
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
// prompt should have history item in prompt
|
||||
const std::wstring_view historyItem = m_pHistory->GetLastCommand();
|
||||
const std::wstring_view resultText{ buffer, historyItem.size() };
|
||||
const std::wstring_view resultText{ cookedReadData._prompt.c_str(), cookedReadData._prompt.size() };
|
||||
VERIFY_ARE_EQUAL(historyItem, resultText);
|
||||
}
|
||||
|
||||
TEST_METHOD(CanLaunchCommandNumberPopup)
|
||||
{
|
||||
// function to simulate user pressing F9
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
wch = VK_F9;
|
||||
popupKey = true;
|
||||
|
@ -410,11 +377,9 @@ class CommandListPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
auto& commandLine = CommandLine::Instance();
|
||||
VERIFY_IS_FALSE(commandLine.HasPopup());
|
||||
|
@ -428,7 +393,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(CanDeleteFromCommandHistory)
|
||||
{
|
||||
// function to simulate user pressing the delete key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -452,11 +417,9 @@ class CommandListPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
const size_t startHistorySize = m_pHistory->GetNumberOfCommands();
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
@ -466,7 +429,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(CanReorderHistoryUp)
|
||||
{
|
||||
// function to simulate user pressing shift + up arrow
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static bool firstTime = true;
|
||||
if (firstTime)
|
||||
|
@ -491,11 +454,9 @@ class CommandListPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(m_pHistory->GetLastCommand(), L"here is my spout");
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
@ -506,7 +467,7 @@ class CommandListPopupTests
|
|||
TEST_METHOD(CanReorderHistoryDown)
|
||||
{
|
||||
// function to simulate user pressing the up arrow, then shift + down arrow, then escape
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static unsigned int count = 0;
|
||||
if (count == 0)
|
||||
|
@ -536,11 +497,9 @@ class CommandListPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(m_pHistory->GetLastCommand(), L"here is my spout");
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
|
|
@ -25,7 +25,7 @@ class CommandNumberPopupTests
|
|||
TEST_CLASS(CommandNumberPopupTests);
|
||||
|
||||
std::unique_ptr<CommonState> m_state;
|
||||
CommandHistory* m_pHistory;
|
||||
std::shared_ptr<CommandHistory> m_pHistory;
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
|
@ -58,6 +58,7 @@ class CommandNumberPopupTests
|
|||
{
|
||||
CommandHistory::s_Free((HANDLE)0);
|
||||
m_pHistory = nullptr;
|
||||
CommandHistory::s_ClearHistoryListStorage();
|
||||
m_state->CleanupCookedReadData();
|
||||
m_state->CleanupReadHandle();
|
||||
m_state->CleanupGlobalInputBuffer();
|
||||
|
@ -68,7 +69,7 @@ class CommandNumberPopupTests
|
|||
TEST_METHOD(CanDismiss)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = VK_ESCAPE;
|
||||
|
@ -83,20 +84,15 @@ class CommandNumberPopupTests
|
|||
|
||||
// prepare cookedReadData
|
||||
const std::wstring testString = L"hello world";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should not be changed
|
||||
const std::wstring resultString(buffer, buffer + testString.size());
|
||||
VERIFY_ARE_EQUAL(testString, resultString);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, testString.size() * sizeof(wchar_t));
|
||||
VERIFY_ARE_EQUAL(testString, cookedReadData._prompt);
|
||||
|
||||
// popup has been dismissed
|
||||
VERIFY_IS_FALSE(CommandLine::Instance().HasPopup());
|
||||
|
@ -108,7 +104,7 @@ class CommandNumberPopupTests
|
|||
// CommanNumberPopup is the only popup that can act as a 2nd popup. make sure that it dismisses all
|
||||
// popups when exiting
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = VK_ESCAPE;
|
||||
|
@ -117,6 +113,13 @@ class CommandNumberPopupTests
|
|||
};
|
||||
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
// prepare cookedReadData
|
||||
const std::wstring testString = L"hello world";
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
// add popups to CommandLine
|
||||
auto& commandLine = CommandLine::Instance();
|
||||
commandLine._popups.emplace_front(std::make_unique<CommandListPopup>(gci.GetActiveOutputBuffer(), *m_pHistory));
|
||||
|
@ -125,17 +128,6 @@ class CommandNumberPopupTests
|
|||
numberPopup.SetUserInputFunction(fn);
|
||||
|
||||
VERIFY_ARE_EQUAL(commandLine._popups.size(), 2u);
|
||||
|
||||
// prepare cookedReadData
|
||||
const std::wstring testString = L"hello world";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(numberPopup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
VERIFY_IS_FALSE(commandLine.HasPopup());
|
||||
|
||||
|
@ -144,7 +136,7 @@ class CommandNumberPopupTests
|
|||
TEST_METHOD(EmptyInputCountsAsOldestHistory)
|
||||
{
|
||||
Log::Comment(L"hitting enter with no input should grab the oldest history item");
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = false;
|
||||
wch = UNICODE_CARRIAGERETURN;
|
||||
|
@ -158,20 +150,17 @@ class CommandNumberPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should contain the least recent history item
|
||||
|
||||
const std::wstring_view expected = m_pHistory->GetLastCommand();
|
||||
const std::wstring resultString(buffer, buffer + expected.size());
|
||||
VERIFY_ARE_EQUAL(expected, resultString);
|
||||
VERIFY_ARE_EQUAL(expected, cookedReadData._prompt);
|
||||
}
|
||||
|
||||
TEST_METHOD(CanSelectHistoryItem)
|
||||
|
@ -179,7 +168,7 @@ class CommandNumberPopupTests
|
|||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
for (unsigned int historyIndex = 0; historyIndex < m_pHistory->GetNumberOfCommands(); ++historyIndex)
|
||||
{
|
||||
Popup::UserInputFunction fn = [historyIndex](COOKED_READ_DATA& /*cookedReadData*/,
|
||||
Popup::UserInputFunction fn = [historyIndex](CookedRead& /*cookedReadData*/,
|
||||
bool& popupKey,
|
||||
DWORD& modifiers,
|
||||
wchar_t& wch)
|
||||
|
@ -207,19 +196,16 @@ class CommandNumberPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should contain the correct nth history item
|
||||
|
||||
const auto expected = m_pHistory->GetNth(gsl::narrow<short>(historyIndex));
|
||||
const std::wstring resultString(buffer, buffer + expected.size());
|
||||
VERIFY_ARE_EQUAL(expected, resultString);
|
||||
VERIFY_ARE_EQUAL(expected, cookedReadData._prompt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +214,7 @@ class CommandNumberPopupTests
|
|||
Log::Comment(L"entering a number larger than the number of history items should grab the most recent history item");
|
||||
|
||||
// simulates user pressing 1, 2, 3, 4, 5, enter
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
static int num = 1;
|
||||
popupKey = false;
|
||||
|
@ -252,20 +238,17 @@ class CommandNumberPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0);
|
||||
PopupTestHelper::InitReadData(cookedReadData, L"");
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should contain the most recent history item
|
||||
|
||||
const std::wstring_view expected = m_pHistory->GetLastCommand();
|
||||
const std::wstring resultString(buffer, buffer + expected.size());
|
||||
VERIFY_ARE_EQUAL(expected, resultString);
|
||||
VERIFY_ARE_EQUAL(expected, cookedReadData._prompt);
|
||||
}
|
||||
|
||||
TEST_METHOD(InputIsLimited)
|
||||
|
|
|
@ -59,7 +59,7 @@ class CopyFromCharPopupTests
|
|||
TEST_METHOD(CanDismiss)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = VK_ESCAPE;
|
||||
|
@ -74,18 +74,13 @@ class CopyFromCharPopupTests
|
|||
|
||||
// prepare cookedReadData
|
||||
std::wstring testString = L"hello world";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should not be changed
|
||||
std::wstring resultString(buffer, buffer + testString.size());
|
||||
VERIFY_ARE_EQUAL(testString, resultString);
|
||||
VERIFY_ARE_EQUAL(cookedReadData.BytesRead(), testString.size() * sizeof(wchar_t));
|
||||
VERIFY_ARE_EQUAL(testString, cookedReadData._prompt);
|
||||
|
||||
// popup has been dismissed
|
||||
VERIFY_IS_FALSE(CommandLine::Instance().HasPopup());
|
||||
|
@ -93,7 +88,7 @@ class CopyFromCharPopupTests
|
|||
|
||||
TEST_METHOD(DeleteAllWhenCharNotFound)
|
||||
{
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = false;
|
||||
wch = L'x';
|
||||
|
@ -108,23 +103,20 @@ class CopyFromCharPopupTests
|
|||
|
||||
// prepare cookedReadData
|
||||
std::wstring testString = L"hello world";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
// move cursor to beginning of prompt text
|
||||
cookedReadData.InsertionPoint() = 0;
|
||||
cookedReadData.MoveInsertionIndexToStart();
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// all text to the right of the cursor should be gone
|
||||
VERIFY_ARE_EQUAL(cookedReadData.BytesRead(), 0u);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._prompt.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_METHOD(CanDeletePartialLine)
|
||||
{
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = false;
|
||||
wch = L'f';
|
||||
|
@ -139,21 +131,15 @@ class CopyFromCharPopupTests
|
|||
|
||||
// prepare cookedReadData
|
||||
std::wstring testString = L"By the rude bridge that arched the flood";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
// move cursor to index 12
|
||||
const size_t index = 12;
|
||||
cookedReadData.SetBufferCurrentPtr(buffer + index);
|
||||
cookedReadData.InsertionPoint() = index;
|
||||
cookedReadData._insertionIndex = index;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
std::wstring expectedText = L"By the rude flood";
|
||||
VERIFY_ARE_EQUAL(cookedReadData.BytesRead(), expectedText.size() * sizeof(wchar_t));
|
||||
std::wstring resultText(buffer, buffer + expectedText.size());
|
||||
VERIFY_ARE_EQUAL(resultText, expectedText);
|
||||
VERIFY_ARE_EQUAL(expectedText, cookedReadData._prompt);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ class CopyToCharPopupTests
|
|||
TEST_CLASS(CopyToCharPopupTests);
|
||||
|
||||
std::unique_ptr<CommonState> m_state;
|
||||
CommandHistory* m_pHistory;
|
||||
std::shared_ptr<CommandHistory> m_pHistory;
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
|
@ -57,6 +57,7 @@ class CopyToCharPopupTests
|
|||
{
|
||||
CommandHistory::s_Free((HANDLE)0);
|
||||
m_pHistory = nullptr;
|
||||
CommandHistory::s_ClearHistoryListStorage();
|
||||
m_state->CleanupCookedReadData();
|
||||
m_state->CleanupReadHandle();
|
||||
m_state->CleanupGlobalInputBuffer();
|
||||
|
@ -67,7 +68,7 @@ class CopyToCharPopupTests
|
|||
TEST_METHOD(CanDismiss)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = VK_ESCAPE;
|
||||
|
@ -82,20 +83,15 @@ class CopyToCharPopupTests
|
|||
|
||||
// prepare cookedReadData
|
||||
const std::wstring testString = L"hello world";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should not be changed
|
||||
const std::wstring resultString(buffer, buffer + testString.size());
|
||||
VERIFY_ARE_EQUAL(testString, resultString);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, testString.size() * sizeof(wchar_t));
|
||||
VERIFY_ARE_EQUAL(testString, cookedReadData._prompt);
|
||||
|
||||
// popup has been dismissed
|
||||
VERIFY_IS_FALSE(CommandLine::Instance().HasPopup());
|
||||
|
@ -104,7 +100,7 @@ class CopyToCharPopupTests
|
|||
TEST_METHOD(NothingHappensWhenCharNotFound)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = L'x';
|
||||
|
@ -118,24 +114,20 @@ class CopyToCharPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0u);
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// the buffer should not be changed
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, 0u);
|
||||
VERIFY_IS_TRUE(cookedReadData._prompt.empty());
|
||||
}
|
||||
|
||||
TEST_METHOD(CanCopyToEmptyPrompt)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = L's';
|
||||
|
@ -149,31 +141,21 @@ class CopyToCharPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), 0u);
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
const std::wstring expectedText = L"here i";
|
||||
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedText.size());
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, expectedText.size() * sizeof(wchar_t));
|
||||
|
||||
// make sure that the text matches
|
||||
const std::wstring resultText(buffer, buffer + expectedText.size());
|
||||
VERIFY_ARE_EQUAL(resultText, expectedText);
|
||||
// make sure that more wasn't copied
|
||||
VERIFY_ARE_EQUAL(buffer[expectedText.size()], UNICODE_SPACE);
|
||||
VERIFY_ARE_EQUAL(expectedText, cookedReadData._prompt);
|
||||
}
|
||||
|
||||
TEST_METHOD(WontCopyTextBeforeCursor)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = L's';
|
||||
|
@ -187,37 +169,27 @@ class CopyToCharPopupTests
|
|||
popup.SetUserInputFunction(fn);
|
||||
|
||||
// prepare cookedReadData with a string longer than the previous history
|
||||
const std::wstring testString = L"Whose woods there are I think I know.";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
const std::wstring testString = L"Whose woods these are I think I know.";
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
const wchar_t* const expectedBufPtr = cookedReadData._bufPtr;
|
||||
const size_t expectedBytesRead = cookedReadData._bytesRead;
|
||||
const auto beforeText = cookedReadData._prompt;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
// nothing should have changed
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, expectedBufPtr);
|
||||
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, expectedBytesRead);
|
||||
const std::wstring resultText(buffer, buffer + testString.size());
|
||||
VERIFY_ARE_EQUAL(resultText, testString);
|
||||
// make sure that more wasn't copied
|
||||
VERIFY_ARE_EQUAL(buffer[testString.size()], UNICODE_SPACE);
|
||||
|
||||
VERIFY_ARE_EQUAL(beforeText, cookedReadData._prompt);
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMergeLine)
|
||||
{
|
||||
// function to simulate user pressing escape key
|
||||
Popup::UserInputFunction fn = [](COOKED_READ_DATA& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
Popup::UserInputFunction fn = [](CookedRead& /*cookedReadData*/, bool& popupKey, DWORD& modifiers, wchar_t& wch)
|
||||
{
|
||||
popupKey = true;
|
||||
wch = L's';
|
||||
wch = L' ';
|
||||
modifiers = 0;
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
@ -229,21 +201,15 @@ class CopyToCharPopupTests
|
|||
|
||||
// prepare cookedReadData with a string longer than the previous history
|
||||
const std::wstring testString = L"fear ";
|
||||
wchar_t buffer[BUFFER_SIZE];
|
||||
std::fill(std::begin(buffer), std::end(buffer), UNICODE_SPACE);
|
||||
std::copy(testString.begin(), testString.end(), std::begin(buffer));
|
||||
auto& cookedReadData = gci.CookedReadData();
|
||||
PopupTestHelper::InitReadData(cookedReadData, buffer, ARRAYSIZE(buffer), testString.size());
|
||||
PopupTestHelper::InitReadData(cookedReadData, testString);
|
||||
PopupTestHelper::InitHistory(*m_pHistory);
|
||||
cookedReadData._commandHistory = m_pHistory;
|
||||
cookedReadData._pCommandHistory = m_pHistory;
|
||||
|
||||
VERIFY_ARE_EQUAL(popup.Process(cookedReadData), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT_NO_BLOCK));
|
||||
|
||||
const std::wstring expectedText = L"fear is";
|
||||
const std::wstring resultText(buffer, buffer + testString.size());
|
||||
VERIFY_ARE_EQUAL(resultText, testString);
|
||||
// make sure that more wasn't copied
|
||||
VERIFY_ARE_EQUAL(buffer[expectedText.size()], UNICODE_SPACE);
|
||||
VERIFY_ARE_EQUAL(expectedText, cookedReadData._prompt);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<ClCompile Include="AttrRowTests.cpp" />
|
||||
<ClCompile Include="ClipboardTests.cpp" />
|
||||
<ClCompile Include="ConsoleArgumentsTests.cpp" />
|
||||
<ClCompile Include="CommandLineTests.cpp" />
|
||||
<ClCompile Include="CodepointWidthDetectorTests.cpp" />
|
||||
<ClCompile Include="CommandListPopupTests.cpp" />
|
||||
<ClCompile Include="CommandNumberPopupTests.cpp" />
|
||||
<ClCompile Include="CopyFromCharPopupTests.cpp" />
|
||||
<ClCompile Include="CopyToCharPopupTests.cpp" />
|
||||
<ClCompile Include="CommandLineTests.cpp" />
|
||||
<ClCompile Include="DbcsTests.cpp" />
|
||||
<ClCompile Include="HistoryTests.cpp" />
|
||||
<ClCompile Include="InitTests.cpp" />
|
||||
|
@ -105,4 +105,4 @@
|
|||
<Import Project="$(SolutionDir)src\common.build.dll.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.tests.props" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -18,25 +18,19 @@ Author(s):
|
|||
#pragma once
|
||||
|
||||
#include "../history.h"
|
||||
#include "../readDataCooked.hpp"
|
||||
#include "../cookedRead.hpp"
|
||||
|
||||
|
||||
class PopupTestHelper final
|
||||
{
|
||||
public:
|
||||
|
||||
static void InitReadData(COOKED_READ_DATA& cookedReadData,
|
||||
wchar_t* const pBuffer,
|
||||
const size_t cchBuffer,
|
||||
const size_t cursorPosition) noexcept
|
||||
static void InitReadData(CookedRead& cookedReadData,
|
||||
const std::wstring& wstr) noexcept
|
||||
{
|
||||
cookedReadData._bufferSize = cchBuffer * sizeof(wchar_t);
|
||||
cookedReadData._bufPtr = pBuffer + cursorPosition;
|
||||
cookedReadData._backupLimit = pBuffer;
|
||||
cookedReadData.OriginalCursorPosition() = { 0, 0 };
|
||||
cookedReadData._bytesRead = cursorPosition * sizeof(wchar_t);
|
||||
cookedReadData._currentPosition = cursorPosition;
|
||||
cookedReadData.VisibleCharCount() = cursorPosition;
|
||||
cookedReadData._insertionIndex = 0;
|
||||
cookedReadData._prompt = wstr;
|
||||
cookedReadData.MoveInsertionIndexToEnd();
|
||||
}
|
||||
|
||||
static void InitHistory(CommandHistory& history) noexcept
|
||||
|
@ -44,7 +38,7 @@ public:
|
|||
history.Empty();
|
||||
history.Flags |= CLE_ALLOCATED;
|
||||
VERIFY_SUCCEEDED(history.Add(L"I'm a little teapot", false));
|
||||
VERIFY_SUCCEEDED(history.Add(L"hear me shout", false));
|
||||
VERIFY_SUCCEEDED(history.Add(L"short and stout", false));
|
||||
VERIFY_SUCCEEDED(history.Add(L"here is my handle", false));
|
||||
VERIFY_SUCCEEDED(history.Add(L"here is my spout", false));
|
||||
VERIFY_ARE_EQUAL(history.GetNumberOfCommands(), 4u);
|
||||
|
|
|
@ -443,6 +443,10 @@ class SelectionInputTests
|
|||
bool fResult = Selection::s_GetInputLineBoundaries(nullptr, nullptr);
|
||||
VERIFY_IS_FALSE(fResult);
|
||||
|
||||
// prepare input buffer
|
||||
m_state->PrepareGlobalInputBuffer();
|
||||
auto cleanupInputBuffer = wil::scope_exit([&]() { m_state->CleanupGlobalInputBuffer(); });
|
||||
|
||||
// prepare some read data
|
||||
m_state->PrepareReadHandle();
|
||||
auto cleanupReadHandle = wil::scope_exit([&]() { m_state->CleanupReadHandle(); });
|
||||
|
@ -451,7 +455,7 @@ class SelectionInputTests
|
|||
// set up to clean up read data later
|
||||
auto cleanupCookedRead = wil::scope_exit([&]() { m_state->CleanupCookedReadData(); });
|
||||
|
||||
COOKED_READ_DATA& readData = gci.CookedReadData();
|
||||
CookedRead& readData = gci.CookedReadData();
|
||||
|
||||
// backup text info position over remainder of text execution duration
|
||||
TextBuffer& textBuffer = gci.GetActiveOutputBuffer().GetTextBuffer();
|
||||
|
@ -460,10 +464,13 @@ class SelectionInputTests
|
|||
coordOldTextInfoPos.Y = textBuffer.GetCursor().GetPosition().Y;
|
||||
|
||||
// set various cursor positions
|
||||
readData.OriginalCursorPosition().X = 15;
|
||||
readData.OriginalCursorPosition().Y = 3;
|
||||
readData.SetPromptStartLocation({ 15, 3 });
|
||||
|
||||
readData.VisibleCharCount() = 200;
|
||||
// place some data into the prompt
|
||||
readData.BufferInput(L't');
|
||||
size_t numBytes = 0;
|
||||
ULONG ctrlKeyState = 0;
|
||||
VERIFY_ARE_EQUAL(readData.Read(true, numBytes, ctrlKeyState), static_cast<NTSTATUS>(CONSOLE_STATUS_WAIT));
|
||||
|
||||
textBuffer.GetCursor().SetXPosition(35);
|
||||
textBuffer.GetCursor().SetYPosition(35);
|
||||
|
@ -480,8 +487,8 @@ class SelectionInputTests
|
|||
VERIFY_IS_TRUE(fResult);
|
||||
|
||||
// starting position/boundary should always be where the input line started
|
||||
VERIFY_ARE_EQUAL(coordStart.X, readData.OriginalCursorPosition().X);
|
||||
VERIFY_ARE_EQUAL(coordStart.Y, readData.OriginalCursorPosition().Y);
|
||||
VERIFY_ARE_EQUAL(coordStart.X, readData.PromptStartLocation().X);
|
||||
VERIFY_ARE_EQUAL(coordStart.Y, readData.PromptStartLocation().Y);
|
||||
|
||||
// ending position can vary. it's in one of two spots
|
||||
// 1. If the original cooked cursor was valid (which it was this first time), it's NumberOfVisibleChars ahead.
|
||||
|
@ -489,15 +496,14 @@ class SelectionInputTests
|
|||
|
||||
const short cCharsToAdjust = ((short)readData.VisibleCharCount() - 1); // then -1 to be on the last piece of text, not past it
|
||||
|
||||
coordFinalPos.X = (readData.OriginalCursorPosition().X + cCharsToAdjust) % sRowWidth;
|
||||
coordFinalPos.Y = readData.OriginalCursorPosition().Y + ((readData.OriginalCursorPosition().X + cCharsToAdjust) / sRowWidth);
|
||||
coordFinalPos.X = (readData.PromptStartLocation().X + cCharsToAdjust) % sRowWidth;
|
||||
coordFinalPos.Y = readData.PromptStartLocation().Y + ((readData.PromptStartLocation().X + cCharsToAdjust) / sRowWidth);
|
||||
|
||||
VERIFY_ARE_EQUAL(coordEnd.X, coordFinalPos.X);
|
||||
VERIFY_ARE_EQUAL(coordEnd.Y, coordFinalPos.Y);
|
||||
|
||||
// 2. if the original cooked cursor is invalid, then it's the text info cursor position
|
||||
readData.OriginalCursorPosition().X = -1;
|
||||
readData.OriginalCursorPosition().Y = -1;
|
||||
readData.SetPromptStartLocation({ -1, -1 });
|
||||
|
||||
fResult = Selection::s_GetInputLineBoundaries(nullptr, &coordEnd);
|
||||
VERIFY_IS_TRUE(fResult);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <iomanip>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
|
||||
// WIL
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ unit testing projects in the codebase without a bunch of overhead.
|
|||
#include "../host/inputReadHandleData.h"
|
||||
#include "../buffer/out/CharRow.hpp"
|
||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||
#include "../host/cookedRead.hpp"
|
||||
|
||||
class CommonState
|
||||
{
|
||||
|
@ -125,15 +126,14 @@ public:
|
|||
void PrepareCookedReadData()
|
||||
{
|
||||
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto* readData = new COOKED_READ_DATA(gci.pInputBuffer,
|
||||
m_readHandle.get(),
|
||||
gci.GetActiveOutputBuffer(),
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
L"",
|
||||
{});
|
||||
auto* readData = new CookedRead(gci.pInputBuffer,
|
||||
m_readHandle.get(),
|
||||
gci.GetActiveOutputBuffer(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
{});
|
||||
gci.SetCookedReadData(readData);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ const wchar_t UNICODE_BOX_DRAW_LIGHT_VERTICAL = 0x2502;
|
|||
const wchar_t UNICODE_BOX_DRAW_LIGHT_UP_AND_RIGHT = 0x2514;
|
||||
const wchar_t UNICODE_BOX_DRAW_LIGHT_UP_AND_LEFT = 0x2518;
|
||||
const wchar_t UNICODE_INVALID = 0xFFFF;
|
||||
const wchar_t UNICODE_CTRL_Z = 0x1a;
|
||||
|
||||
// This is the "Ctrl+C" character.
|
||||
// With VKey='C', it generates a CTRL_C_EVENT
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "..\..\host\server.h"
|
||||
#include "..\..\host\scrolling.hpp"
|
||||
#include "..\..\host\telemetry.hpp"
|
||||
#include "..\..\host\cookedRead.hpp"
|
||||
|
||||
#include "..\inc\ServiceLocator.hpp"
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ std::string ConvertToA(const UINT codepage, const std::wstring_view source)
|
|||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
int iSource; // convert to int because Wc2Mb requires it.
|
||||
THROW_IF_FAILED(SizeTToInt(source.size(), &iSource));
|
||||
|
||||
|
@ -347,7 +347,12 @@ std::deque<std::unique_ptr<KeyEvent>> SynthesizeNumpadEvents(const wchar_t wch,
|
|||
CodepointWidth GetQuickCharWidth(const wchar_t wch) noexcept
|
||||
{
|
||||
// 0x00-0x1F is ambiguous by font
|
||||
if (0x20 <= wch && wch <= 0x7e)
|
||||
// ... but we render ctrl+z wide
|
||||
if (wch == 0x1a)
|
||||
{
|
||||
return CodepointWidth::Wide;
|
||||
}
|
||||
else if (0x20 <= wch && wch <= 0x7e)
|
||||
{
|
||||
/* ASCII */
|
||||
return CodepointWidth::Narrow;
|
||||
|
|
Loading…
Reference in a new issue