-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Add support for bracketed paste mode in ConHost #15155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,29 +79,29 @@ class CONSOLE_INFORMATION : | |
public Microsoft::Console::IIoProvider | ||
{ | ||
public: | ||
CONSOLE_INFORMATION(); | ||
CONSOLE_INFORMATION() = default; | ||
CONSOLE_INFORMATION(const CONSOLE_INFORMATION& c) = delete; | ||
CONSOLE_INFORMATION& operator=(const CONSOLE_INFORMATION& c) = delete; | ||
|
||
ConsoleProcessList ProcessHandleList; | ||
InputBuffer* pInputBuffer; | ||
InputBuffer* pInputBuffer = nullptr; | ||
|
||
SCREEN_INFORMATION* ScreenBuffers; // singly linked list | ||
SCREEN_INFORMATION* ScreenBuffers = nullptr; // singly linked list | ||
ConsoleWaitQueue OutputQueue; | ||
|
||
DWORD Flags; | ||
DWORD Flags = 0; | ||
|
||
std::atomic<WORD> PopupCount; | ||
std::atomic<WORD> PopupCount = 0; | ||
|
||
// the following fields are used for ansi-unicode translation | ||
UINT CP; | ||
UINT OutputCP; | ||
UINT CP = 0; | ||
UINT OutputCP = 0; | ||
|
||
ULONG CtrlFlags; // indicates outstanding ctrl requests | ||
ULONG LimitingProcessId; | ||
ULONG CtrlFlags = 0; // indicates outstanding ctrl requests | ||
ULONG LimitingProcessId = 0; | ||
|
||
CPINFO CPInfo; | ||
CPINFO OutputCPInfo; | ||
CPINFO CPInfo = {}; | ||
CPINFO OutputCPInfo = {}; | ||
|
||
ConsoleImeInfo ConsoleIme; | ||
|
||
|
@@ -125,6 +125,9 @@ class CONSOLE_INFORMATION : | |
COOKED_READ_DATA& CookedReadData() noexcept; | ||
void SetCookedReadData(COOKED_READ_DATA* readData) noexcept; | ||
|
||
bool GetBracketedPasteMode() const noexcept; | ||
void SetBracketedPasteMode(const bool enabled) noexcept; | ||
|
||
void SetTitle(const std::wstring_view newTitle); | ||
void SetTitlePrefix(const std::wstring_view newTitlePrefix); | ||
void SetOriginalTitle(const std::wstring_view originalTitle); | ||
|
@@ -155,8 +158,9 @@ class CONSOLE_INFORMATION : | |
std::wstring _TitleAndPrefix; | ||
std::wstring _OriginalTitle; | ||
std::wstring _LinkTitle; // Path to .lnk file | ||
SCREEN_INFORMATION* pCurrentScreenBuffer; | ||
COOKED_READ_DATA* _cookedReadData; // non-ownership pointer | ||
SCREEN_INFORMATION* pCurrentScreenBuffer = nullptr; | ||
COOKED_READ_DATA* _cookedReadData = nullptr; // non-ownership pointer | ||
bool _bracketedPasteMode = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am so glad you made this change. Thanks. I mean, all of the initializers. |
||
|
||
Microsoft::Console::VirtualTerminal::VtIo _vtIo; | ||
Microsoft::Console::CursorBlinker _blinker; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,7 +108,9 @@ void Clipboard::StringPaste(_In_reads_(cchData) const wchar_t* const pData, | |
|
||
try | ||
{ | ||
auto inEvents = TextToKeyEvents(pData, cchData); | ||
const auto vtInputMode = gci.pInputBuffer->IsInVirtualTerminalInputMode(); | ||
const auto bracketedPasteMode = gci.GetBracketedPasteMode(); | ||
auto inEvents = TextToKeyEvents(pData, cchData, vtInputMode && bracketedPasteMode); | ||
gci.pInputBuffer->Write(inEvents); | ||
} | ||
catch (...) | ||
|
@@ -127,16 +129,31 @@ void Clipboard::StringPaste(_In_reads_(cchData) const wchar_t* const pData, | |
// Arguments: | ||
// - pData - the text to convert | ||
// - cchData - the size of pData, in wchars | ||
// - bracketedPaste - should this be bracketed with paste control sequences | ||
// Return Value: | ||
// - deque of KeyEvents that represent the string passed in | ||
// Note: | ||
// - will throw exception on error | ||
std::deque<std::unique_ptr<IInputEvent>> Clipboard::TextToKeyEvents(_In_reads_(cchData) const wchar_t* const pData, | ||
const size_t cchData) | ||
const size_t cchData, | ||
const bool bracketedPaste) | ||
{ | ||
THROW_HR_IF_NULL(E_INVALIDARG, pData); | ||
|
||
std::deque<std::unique_ptr<IInputEvent>> keyEvents; | ||
const auto pushControlSequence = [&](const std::wstring_view sequence) { | ||
std::for_each(sequence.begin(), sequence.end(), [&](const auto wch) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A range-for should be simpler I think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main reason I used That said, it's not a big deal to change if you really don't like it this way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If you knew. 🥲 K&R gang 4 life.
Oh interesting. I was always under the impression that compilers produce better assembly for range-for loops if they can infer the bounds easily (like for span/string_view). At least in my little test it produces the same assembly though: https://godbolt.org/z/crj8aM1qr (it's the function next to the
No, please feel free to keep it (unless anyone else insists on it of course). Although you might be interested in using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Absolutely!
That is interesting. That looks nothing like the assembly I'm seeing generated in Visual Studio. I don't think my version of VS is that out of date (the C/C++ Compiler is 19.34.31937), but maybe it's just the other code in the method that alters the way those lambdas are optimized. Not that I'm seeing a huge difference between the two anyway. I just found it interesting that the
Yes, I was hoping there was something like that, so I'm glad you brought that up. Unfortunately it also introduces additional overhead in the generated assembly. For the most part it looks almost identical to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On looking at your godbolt link again, I realised now why it didn't look anything like what I was expecting. The function immediately below In godbolt those too lambdas are massively different, but that's mostly because the one seems to have partially inlined the |
||
keyEvents.push_back(std::make_unique<KeyEvent>(true, 1ui16, 0ui16, 0ui16, wch, 0)); | ||
keyEvents.push_back(std::make_unique<KeyEvent>(false, 1ui16, 0ui16, 0ui16, wch, 0)); | ||
}); | ||
}; | ||
|
||
// When a bracketed paste is requested, we need to wrap the text with | ||
// control sequences which indicate that the content has been pasted. | ||
if (bracketedPaste) | ||
{ | ||
pushControlSequence(L"\x1b[200~"); | ||
} | ||
|
||
for (size_t i = 0; i < cchData; ++i) | ||
{ | ||
|
@@ -148,8 +165,10 @@ std::deque<std::unique_ptr<IInputEvent>> Clipboard::TextToKeyEvents(_In_reads_(c | |
const auto skipLinefeed = (i != 0 && | ||
currentChar == UNICODE_LINEFEED && | ||
pData[i - 1] == UNICODE_CARRIAGERETURN); | ||
// filter out escape if bracketed paste mode is enabled | ||
const auto skipEscape = (bracketedPaste && currentChar == UNICODE_ESC); | ||
|
||
if (!charAllowed || skipLinefeed) | ||
if (!charAllowed || skipLinefeed || skipEscape) | ||
{ | ||
continue; | ||
} | ||
|
@@ -182,6 +201,11 @@ std::deque<std::unique_ptr<IInputEvent>> Clipboard::TextToKeyEvents(_In_reads_(c | |
convertedEvents.pop_front(); | ||
} | ||
} | ||
|
||
if (bracketedPaste) | ||
{ | ||
pushControlSequence(L"\x1b[201~"); | ||
} | ||
return keyEvents; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.