Skip to content
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

Skip whitespace removal for multiline pastes #13698

Merged
7 commits merged into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2163,18 +2163,12 @@ namespace winrt::TerminalApp::implementation

if (_settings.GlobalSettings().TrimPaste())
{
std::wstring_view textView{ text };
const auto pos = textView.find_last_not_of(L"\t\n\v\f\r ");
if (pos == textView.npos)
text = { Utils::TrimPaste(text) };
if (text.empty())
{
// Text is all white space, nothing to paste
co_return;
}
else if (const auto toRemove = textView.size() - 1 - pos; toRemove > 0)
{
textView.remove_suffix(toRemove);
text = { textView };
}
}

// If the requesting terminal is in bracketed paste mode, then we don't need to warn about a multi-line paste.
Expand Down
5 changes: 5 additions & 0 deletions src/types/inc/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,9 @@ namespace Microsoft::Console::Utils
std::tuple<std::wstring, std::wstring> MangleStartingDirectoryForWSL(std::wstring_view commandLine,
std::wstring_view startingDirectory);

// Similar to MangleStartingDirectoryForWSL, this function is only ever used
// in TerminalPage::_PasteFromClipboardHandler, but putting it here makes
// testing easier.
std::wstring_view TrimPaste(std::wstring_view textView) noexcept;

}
41 changes: 41 additions & 0 deletions src/types/ut_types/UtilsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class UtilsTests
TEST_METHOD(TestMangleWSLPaths);
#endif

TEST_METHOD(TestTrimTrailingWhitespace);
TEST_METHOD(TestDontTrimTrailingWhitespace);

void _VerifyXTermColorResult(const std::wstring_view wstr, DWORD colorValue);
void _VerifyXTermColorInvalid(const std::wstring_view wstr);
};
Expand Down Expand Up @@ -505,3 +508,41 @@ void UtilsTests::TestMangleWSLPaths()
}
}
#endif

void UtilsTests::TestTrimTrailingWhitespace()
{
// Continue on failures
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;

// Tests for GH #11473
VERIFY_ARE_EQUAL(L"Foo", TrimPaste(L"Foo "));
VERIFY_ARE_EQUAL(L"Foo", TrimPaste(L"Foo\n"));
VERIFY_ARE_EQUAL(L"Foo", TrimPaste(L"Foo\n\n"));
VERIFY_ARE_EQUAL(L"Foo", TrimPaste(L"Foo\r\n"));
VERIFY_ARE_EQUAL(L"Foo Bar", TrimPaste(L"Foo Bar\n"));
VERIFY_ARE_EQUAL(L"Foo\tBar", TrimPaste(L"Foo\tBar\n"));

VERIFY_ARE_EQUAL(L"Foo Bar", TrimPaste(L"Foo Bar\t"), L"Trim when there is a tab at the end.");
VERIFY_ARE_EQUAL(L"Foo Bar", TrimPaste(L"Foo Bar\t\t"), L"Trim when there are tabs at the end.");
VERIFY_ARE_EQUAL(L"Foo Bar", TrimPaste(L"Foo Bar\t\n"), L"Trim when there are tabs at the start of the whitespace at the end.");
VERIFY_ARE_EQUAL(L"Foo\tBar", TrimPaste(L"Foo\tBar\t\n"), L"Trim when there are tabs in the middle of the string, and in the whitespace at the end.");
VERIFY_ARE_EQUAL(L"Foo\tBar", TrimPaste(L"Foo\tBar\n\t"), L"Trim when there are tabs in the middle of the string, and in the whitespace at the end.");
VERIFY_ARE_EQUAL(L"Foo\tBar", TrimPaste(L"Foo\tBar\t\n\t"), L"Trim when there are tabs in the middle of the string, and in the whitespace at the end.");
}
void UtilsTests::TestDontTrimTrailingWhitespace()
{
// Continue on failures
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;

VERIFY_ARE_EQUAL(L"Foo\tBar", TrimPaste(L"Foo\tBar"));

// Tests for GH #12387
VERIFY_ARE_EQUAL(L"Foo\nBar\n", TrimPaste(L"Foo\nBar\n"));
VERIFY_ARE_EQUAL(L"Foo Baz\nBar\n", TrimPaste(L"Foo Baz\nBar\n"));
VERIFY_ARE_EQUAL(L"Foo\tBaz\nBar\n", TrimPaste(L"Foo\tBaz\nBar\n"), L"Don't trim when there's a trailing newline, and tabs in the middle");
VERIFY_ARE_EQUAL(L"Foo\tBaz\nBar\t\n", TrimPaste(L"Foo\tBaz\nBar\t\n"), L"Don't trim when there's a trailing newline, and tabs in the middle");

// We need to both
// * trim when there's a tab followed by only whitespace
// * not trim then there's a tab in the middle, and the string ends in whitespace
}
30 changes: 30 additions & 0 deletions src/types/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,3 +772,33 @@ std::tuple<std::wstring, std::wstring> Utils::MangleStartingDirectoryForWSL(std:
std::wstring{ startingDirectory }
};
}

std::wstring_view Utils::TrimPaste(std::wstring_view textView) noexcept
{
const auto lastNonSpace = textView.find_last_not_of(L"\t\n\v\f\r ");
const auto firstNewline = textView.find_first_of(L"\n\v\f\r");

const bool isOnlyWhitespace = lastNonSpace == textView.npos;
const bool isMultiline = firstNewline < lastNonSpace;

if (isOnlyWhitespace)
{
// Text is all white space, nothing to paste
return L"";
}

if (isMultiline)
{
// In this case, the user totally wanted to paste multiple lines of text,
// and that likely includes the trailing newline.
// DON'T trim it in this case.
return textView;
}

if (const auto toRemove = textView.size() - 1 - lastNonSpace; toRemove > 0)
{
textView.remove_suffix(toRemove);
}

return textView;
serd2011 marked this conversation as resolved.
Show resolved Hide resolved
}