diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index b30596637b04..44b427a48bfb 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -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 && diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 62e5f50d6c6d..b73735c087f9 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -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: @@ -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(); }