Skip to content

Commit

Permalink
Skip whitespace removal for multiline pastes (#13698)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request
Skips whitespace removal if pasted string is multiline

## PR Checklist
* [x] Closes #12387
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] 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: #xxx

## Validation Steps Performed
Tests passed
The last command in multiline paste now executes
  • Loading branch information
serd2011 authored Aug 17, 2022
1 parent 8cf5697 commit 1e18ab9
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 8 deletions.
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
}
25 changes: 25 additions & 0 deletions src/types/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,3 +772,28 @@ 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;
}

return textView.substr(0, lastNonSpace + 1);
}

0 comments on commit 1e18ab9

Please sign in to comment.