terminal/src/host/cmdline.h
James Holderness c69757ec9e
Remove unneeded VT-specific control character handling (#4289)
## Summary of the Pull Request

This PR removes all of the VT-specific functionality from the `WriteCharsLegacy` function that dealt with control characters, since those controls are now handled in the state machine when in VT mode. It also removes most of the control character handling from the `Terminal::_WriteBuffer` method for the same reason.

## References

This is a followup to PR #4171

## PR Checklist
* [x] Closes #3971
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Requires documentation to be updated
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: https://github.com/microsoft/terminal/issues/780#issuecomment-570287435

## Detailed Description of the Pull Request / Additional comments

There are four changes to the `WriteCharsLegacy` implementation:

1. The `TAB` character had special case handling in VT mode which is now no longer required. This fixes a bug in the Python REPL editor (when run from a cmd shell in Windows Terminal), which would prevent you tabbing past the end of the line. It also fixes #3971.

2. Following on from point 1, the `WC_NONDESTRUCTIVE_TAB` flag could also now be removed. It only ever applied in VT mode, in which case the `TAB` character isn't handled in `WriteCharsLegacy`, so there isn't a need for a non-destructive version.

3. There used to be special case handling for a `BS` character at the beginning of the line when in VT mode, and that is also no longer required. This fixes an edge-case bug which would prevent a glyph being output for code point 8 when `ENABLE_PROCESSED_OUTPUT` was disabled. 

4. There was quite a lot of special case handling for control characters in the "end-of-line wrap" implementation, which is no longer required. This fixes a bug which would prevent "low ASCII" characters from wrapping when output at the end of a line.

Then in the `Terminal::_WriteBuffer` implementation, I've simply removed all control character handling, except for `LF`. The Terminal is always in VT mode, so the control characters are always handled by the state machine. The exception for the `LF` character is simply because it doesn't have a proper implementation yet, so it still passes the character through to `_WriteBuffer`. That will get cleaned up eventually, but I thought that could wait for a later PR.

Finally, with the removal of the VT mode handling in `WriteCharsLegacy`, there was no longer a need for the `SCREEN_INFORMATION::InVTMode` method to be publicly accessible. That has now been made private.

## Validation Steps Performed

I've only tested manually, making sure the conhost and Windows Terminal still basically work, and confirming that the above-mentioned bugs are fixed by these changes.
2020-01-29 19:18:46 +00:00

161 lines
5.6 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- cmdline.h
Abstract:
- This file contains the internal structures and definitions used by command line input and editing.
Author:
- Therese Stowell (ThereseS) 15-Nov-1991
Revision History:
- Mike Griese (migrie) Jan 2018:
Refactored the history and alias functionality into their own files.
- Michael Niksa (miniksa) May 2018:
Split apart popup information. Started encapsulating command line things. Removed 0 length buffers.
Notes:
The input model for the command line editing popups is complex.
Here is the relevant pseudocode:
CookedReadWaitRoutine
if (CookedRead->Popup)
Status = (*CookedRead->Popup->Callback)();
if (Status == CONSOLE_STATUS_READ_COMPLETE)
return STATUS_SUCCESS;
return Status;
CookedRead
if (Command Line Editing Key)
ProcessCommandLine
else
process regular key
ProcessCommandLine
if F7
return Popup
Popup
draw popup
return ProcessCommandListInput
ProcessCommandListInput
while (TRUE)
GetChar
if (wait)
return wait
switch (char)
.
.
.
--*/
#pragma once
#include "input.h"
#include "screenInfo.hpp"
#include "server.h"
#include "history.h"
#include "alias.h"
#include "readDataCooked.hpp"
#include "popup.h"
class CommandLine
{
public:
~CommandLine();
static CommandLine& Instance();
bool IsEditLineEmpty() const;
void Hide(const bool fUpdateFields);
void Show();
bool IsVisible() const noexcept;
[[nodiscard]] NTSTATUS ProcessCommandLine(COOKED_READ_DATA& cookedReadData,
_In_ WCHAR wch,
const DWORD dwKeyState);
[[nodiscard]] HRESULT StartCommandNumberPopup(COOKED_READ_DATA& cookedReadData);
bool HasPopup() const noexcept;
Popup& GetPopup();
void UpdatePopups(const TextAttribute& NewAttributes,
const TextAttribute& NewPopupAttributes,
const TextAttribute& OldAttributes,
const TextAttribute& OldPopupAttributes);
void EndCurrentPopup();
void EndAllPopups();
void DeletePromptAfterCursor(COOKED_READ_DATA& cookedReadData) noexcept;
COORD DeleteFromRightOfCursor(COOKED_READ_DATA& cookedReadData) noexcept;
protected:
CommandLine();
// delete these because we don't want to accidentally get copies of the singleton
CommandLine(CommandLine const&) = delete;
CommandLine& operator=(CommandLine const&) = delete;
[[nodiscard]] NTSTATUS _startCommandListPopup(COOKED_READ_DATA& cookedReadData);
[[nodiscard]] NTSTATUS _startCopyFromCharPopup(COOKED_READ_DATA& cookedReadData);
[[nodiscard]] NTSTATUS _startCopyToCharPopup(COOKED_READ_DATA& 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);
#ifdef UNIT_TESTING
friend class CommandLineTests;
friend class CommandNumberPopupTests;
#endif
private:
std::deque<std::unique_ptr<Popup>> _popups;
bool _isVisible;
};
void DeleteCommandLine(COOKED_READ_DATA& cookedReadData, const bool fUpdateFields);
void RedrawCommandLine(COOKED_READ_DATA& cookedReadData);
// Values for WriteChars(), WriteCharsLegacy() dwFlags
#define WC_DESTRUCTIVE_BACKSPACE 0x01
#define WC_KEEP_CURSOR_VISIBLE 0x02
#define WC_ECHO 0x04
// This is no longer necessary. The buffer will always be Unicode. We don't need to perform special work to check if we're in a raster font
// and convert the entire buffer to match (and all insertions).
//#define WC_FALSIFY_UNICODE 0x08
#define WC_LIMIT_BACKSPACE 0x10
//#define WC_NONDESTRUCTIVE_TAB 0x20 - This is not needed anymore, because the VT code handles tabs internally now.
//#define WC_NEWLINE_SAVE_X 0x40 - This has been replaced with an output mode flag instead as it's line discipline behavior that may not necessarily be coupled with VT.
#define WC_DELAY_EOL_WRAP 0x80
// Word delimiters
bool IsWordDelim(const wchar_t wch);
bool IsWordDelim(const std::wstring_view charData);
[[nodiscard]] 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);