Skip to content

Commit

Permalink
Fix multi line paste detection and filtering (microsoft#8634)
Browse files Browse the repository at this point in the history
- Detect `\r` when warning about multi line paste
- Translate `\n` to `\r` on paste

## PR Checklist
* [x] Closes microsoft#8601
* [x] Closes microsoft#5821

## Validation Steps Performed
Manual testing
  • Loading branch information
PankajBhojwani authored and mpela81 committed Jan 28, 2021
1 parent 46c8ced commit 4fd557c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
5 changes: 3 additions & 2 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1935,8 +1935,9 @@ namespace winrt::TerminalApp::implementation
}
}

const bool hasNewLine = std::find(text.cbegin(), text.cend(), L'\n') != text.cend();
const bool warnMultiLine = hasNewLine && _settings.GlobalSettings().WarnAboutMultiLinePaste();
const auto isNewLineLambda = [](auto c) { return c == L'\n' || c == L'\r'; };
const auto hasNewLine = std::find_if(text.cbegin(), text.cend(), isNewLineLambda) != text.cend();
const auto warnMultiLine = hasNewLine && _settings.GlobalSettings().WarnAboutMultiLinePaste();

constexpr const std::size_t minimumSizeForWarning = 1024 * 5; // 5 KiB
const bool warnLargeText = text.size() > minimumSizeForWarning &&
Expand Down
46 changes: 39 additions & 7 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - Pre-process text pasted (presumably from the clipboard)
// before sending it over the terminal's connection, converting
// Windows-space \r\n line-endings to \r line-endings
// - Also converts \n line-endings to \r line-endings
void TermControl::_SendPastedTextToConnection(const std::wstring& wstr)
{
// Some notes on this implementation:
Expand All @@ -2034,20 +2035,51 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// performance guarantees aren't exactly stellar)
// - The STL doesn't have a simple string search/replace method.
// This fact is lamentable.
// - This line-ending conversion is intentionally fairly
// conservative, to avoid stripping out lone \n characters
// where they could conceivably be intentional.
// - We search for \n, and when we find it we copy the string up to
// the \n (but not including it). Then, we check the if the
// previous character is \r, if its not, then we had a lone \n
// and so we append our own \r

std::wstring stripped{ wstr };
std::wstring stripped;
stripped.reserve(wstr.length());

std::wstring::size_type pos = 0;
std::wstring::size_type begin = 0;

while ((pos = stripped.find(L"\r\n", pos)) != std::wstring::npos)
while ((pos = wstr.find(L"\n", pos)) != std::wstring::npos)
{
stripped.replace(pos, 2, L"\r");
// copy up to but not including the \n
stripped.append(wstr.cbegin() + begin, wstr.cbegin() + pos);
if (!(pos > 0 && (wstr.at(pos - 1) == L'\r')))
{
// there was no \r before the \n we did not copy,
// so append our own \r (this effectively replaces the \n
// with a \r)
stripped.push_back(L'\r');
}
++pos;
begin = pos;
}

// If we entered the while loop even once, begin would be non-zero
// (because we set begin = pos right after incrementing pos)
// So, if begin is still zero at this point it means we never found a newline
// and we can just write the original string
if (begin == 0)
{
_connection.WriteInput(wstr);
}
else
{
// copy over the part after the last \n
stripped.append(wstr.cbegin() + begin, wstr.cend());

// we may have removed some characters, so we may not need as much space
// as we reserved earlier
stripped.shrink_to_fit();
_connection.WriteInput(stripped);
}

_connection.WriteInput(stripped);
_terminal->TrySnapOnInput();
}

Expand Down

0 comments on commit 4fd557c

Please sign in to comment.