mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-23 06:31:29 +00:00
Compare commits
4 Commits
dev/cazamo
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a83c0f167 | ||
|
|
b2666fb346 | ||
|
|
8f4fdf9751 | ||
|
|
33a80191c1 |
@@ -1143,6 +1143,13 @@ til::CoordType ROW::GetTrailingColumnAtCharOffset(const ptrdiff_t offset) const
|
||||
return _createCharToColumnMapper(offset).GetTrailingColumnAt(offset);
|
||||
}
|
||||
|
||||
uint16_t ROW::GetCharOffset(til::CoordType col) const noexcept
|
||||
{
|
||||
const auto columns = GetReadableColumnCount();
|
||||
const auto colBeg = clamp(col, 0, columns);
|
||||
return _uncheckedCharOffset(gsl::narrow_cast<size_t>(colBeg));
|
||||
}
|
||||
|
||||
DelimiterClass ROW::DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept
|
||||
{
|
||||
const auto col = _clampedColumn(column);
|
||||
|
||||
@@ -172,6 +172,7 @@ public:
|
||||
std::wstring_view GetText(til::CoordType columnBegin, til::CoordType columnEnd) const noexcept;
|
||||
til::CoordType GetLeadingColumnAtCharOffset(ptrdiff_t offset) const noexcept;
|
||||
til::CoordType GetTrailingColumnAtCharOffset(ptrdiff_t offset) const noexcept;
|
||||
uint16_t GetCharOffset(til::CoordType col) const noexcept;
|
||||
DelimiterClass DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept;
|
||||
|
||||
auto AttrBegin() const noexcept { return _attr.begin(); }
|
||||
|
||||
@@ -308,6 +308,11 @@ namespace TerminalAppLocalTests
|
||||
// TerminalPage and not only create them successfully, but also create a
|
||||
// tab using those settings successfully.
|
||||
|
||||
// - - - IMPORTANT - - -
|
||||
// GH#14623: "closeOnExit": "never" is important for all test profiles. Without
|
||||
// it, the spawned process exits immediately in the UAP test environment,
|
||||
// and the default "automatic" close-on-exit behavior removes the
|
||||
// tab/pane asynchronously, racing against test assertions.
|
||||
static constexpr std::wstring_view settingsJson0{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
@@ -315,12 +320,14 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -347,10 +354,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TryDuplicateBadTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _DuplicateFocusedTab on tab 1
|
||||
@@ -365,12 +368,14 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -382,7 +387,8 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -438,10 +444,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TryDuplicateBadPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _SplitPane(Duplicate) on tab 1
|
||||
@@ -456,12 +458,14 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -473,7 +477,8 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -572,25 +577,29 @@ namespace TerminalAppLocalTests
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 0",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 1",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile2",
|
||||
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 2",
|
||||
"historySize": 3
|
||||
"historySize": 3,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile3",
|
||||
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 3",
|
||||
"historySize": 4
|
||||
"historySize": 4,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
],
|
||||
"schemes":
|
||||
@@ -693,7 +702,6 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -733,10 +741,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::MoveFocusFromZoomedPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second pane");
|
||||
@@ -782,10 +786,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::CloseZoomedPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second pane");
|
||||
@@ -841,10 +841,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::SwapPanes()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Setup 4 panes.");
|
||||
@@ -1051,31 +1047,31 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::NextMRUTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// This is a test for GH#8025 - we want to make sure that we can do both
|
||||
// in-order and MRU tab traversal, using the tab switcher and with the
|
||||
// tab switcher disabled.
|
||||
// This is a test for GH#8025 - we want to make sure that MRU tab
|
||||
// ordering works correctly and that in-order/disabled switching works.
|
||||
//
|
||||
// Note: We test MRU ordering directly rather than going through the
|
||||
// command palette tab switcher, because the palette's anchor key
|
||||
// handling auto-dismisses when no modifier keys are held (which we
|
||||
// can't simulate in the test environment).
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second tab");
|
||||
Log::Comment(L"Create Tab[1]");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 1 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create a third tab");
|
||||
Log::Comment(L"Create Tab[2]");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 2 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create a fourth tab");
|
||||
Log::Comment(L"Create Tab[3]");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 3 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
@@ -1084,99 +1080,89 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Select the second tab");
|
||||
Log::Comment(L"Select Tab[1]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectTab(1);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify Tab[1] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to MRU switching");
|
||||
// MRU order should now be: Tab[1], Tab[3], Tab[2], Tab[0]
|
||||
// Verify the MRU list directly.
|
||||
Log::Comment(L"Verify MRU order: MRU[0]=Tab[1], MRU[1]=Tab[3]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
uint32_t mruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(0), mruIdx);
|
||||
VERIFY_ARE_EQUAL(1u, mruIdx, L"MRU[0] should be Tab[1] (most recent)");
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), mruIdx);
|
||||
VERIFY_ARE_EQUAL(3u, mruIdx, L"MRU[1] should be Tab[3] (last tab added)");
|
||||
});
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
|
||||
Log::Comment(L"Select MRU[1]=Tab[3] directly");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Hide the command palette, to confirm the selection");
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
// The next MRU tab after Tab[1] is Tab[3]
|
||||
uint32_t nextMruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), nextMruIdx);
|
||||
page->_SelectTab(nextMruIdx);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the second tab");
|
||||
Log::Comment(L"Select MRU[1]=Tab[1] directly");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Hide the command palette, to confirm the selection");
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
uint32_t nextMruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), nextMruIdx);
|
||||
page->_SelectTab(nextMruIdx);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to in-order switching");
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::InOrder);
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify the third tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify Tab[1] is focused");
|
||||
});
|
||||
|
||||
// The Disabled tab switcher mode uses direct index-based switching
|
||||
// without the command palette, so it works in the test environment.
|
||||
Log::Comment(L"Change the tab switch order to not use the tab switcher (which is in-order always)");
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::Disabled);
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
|
||||
Log::Comment(L"Switch to the next in-order tab: Tab[2]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify Tab[2] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab: Tab[3]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::VerifyCommandPaletteTabSwitcherOrder()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// This is a test for GH#8188 - we want to make sure that the order of tabs
|
||||
// is preserved in the CommandPalette's TabSwitcher
|
||||
// This is a test for GH#8188 - we want to make sure that the MRU
|
||||
// ordering is correctly maintained as tabs are selected.
|
||||
//
|
||||
// Note: We verify MRU ordering directly rather than going through
|
||||
// the command palette tab switcher, because the palette's anchor key
|
||||
// handling auto-dismisses when no modifier keys are held (which we
|
||||
// can't simulate in the test environment).
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
@@ -1189,7 +1175,7 @@ namespace TerminalAppLocalTests
|
||||
});
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
|
||||
Log::Comment(L"give alphabetical names to all switch tab actions");
|
||||
Log::Comment(L"give alphabetical names to all tabs");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_GetTabImpl(page->_tabs.GetAt(0))->Title(L"a");
|
||||
});
|
||||
@@ -1211,18 +1197,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"c", page->_tabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_tabs.GetAt(3).Title());
|
||||
|
||||
// MRU order after creating Tab[0]-Tab[3]: MRU[0]=Tab[3], MRU[3]=Tab[0]
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to MRU switching");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
||||
});
|
||||
|
||||
Log::Comment(L"Select the tabs from 0 to 3");
|
||||
Log::Comment(L"Select Tab[0] through Tab[3] to establish MRU order");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(0));
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(1));
|
||||
@@ -1230,47 +1212,31 @@ namespace TerminalAppLocalTests
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(3));
|
||||
});
|
||||
|
||||
Log::Comment(L"Verify MRU order: MRU[0]='d', MRU[1]='c', MRU[2]='b', MRU[3]='a'");
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
// In the course of a single tick, the Command Palette will:
|
||||
// * open
|
||||
// * select the proper tab from the mru's list
|
||||
// * raise an event for _filteredActionsView().SelectionChanged to
|
||||
// immediately preview the new tab
|
||||
// * raise a _SwitchToTabRequestedHandlers event
|
||||
// * then dismiss itself, because we can't fake holing down an
|
||||
// anchor key in the tests
|
||||
Log::Comment(L"Select Tab[2]='c' (MRU[1] after 'd')");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectTab(2);
|
||||
});
|
||||
|
||||
Log::Comment(L"Verify MRU order updated: MRU[0]='c', MRU[1]='d', MRU[2]='b', MRU[3]='a'");
|
||||
TestOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->LoadCommandPalette());
|
||||
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode");
|
||||
// At this point, the contents of the command palette's _mruTabs list is
|
||||
// still the _old_ ordering (d, c, b, a). The ordering is only updated
|
||||
// in TerminalPage::_SelectNextTab, but as we saw before, the palette
|
||||
// will also dismiss itself immediately when that's called. So we can't
|
||||
// really inspect the contents of the list in this test, unfortunately.
|
||||
}
|
||||
|
||||
void TabTests::TestWindowRenameSuccessful()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1303,7 +1269,6 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1336,10 +1301,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewCommitScheme()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1402,10 +1363,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewDismissScheme()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then dismissed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1454,10 +1411,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewSchemeWhilePreviewing()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme, then preview another scheme. ");
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
@@ -1525,10 +1478,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestClampSwitchToTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Test that switching to a tab index higher than the number of tabs just clamps to the last tab.");
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
@@ -672,9 +672,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Dieser Linktyp wird derzeit nicht unterstützt:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Abbrechen</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Dieser Link kann zu einem unsicheren Speicherort führen. Links können ihren Computer und Ihre Daten beschädigen. Klicken Sie zum Schutz Des Computers nur auf Links aus vertrauenswürdigen Quellen.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Trotzdem öffnen</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Einstellungen</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Este tipo de vínculo no se admite actualmente:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancelar</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Este vínculo puede dar lugar a una ubicación no segura. Los hipervínculos pueden ser perjudiciales para el equipo y los datos. Para proteger el equipo, haga clic solo en vínculos de orígenes de confianza.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Abrir de todas formas</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Configuración</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ce type de lien n’est actuellement pas pris en charge :</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Annuler</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ce lien peut entraîner un emplacement non sécurisé. Les liens hypertexte peuvent endommager votre ordinateur et vos données. Pour protéger votre ordinateur, cliquez uniquement sur des liens provenant de sources fiables.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Ouvrir quand même</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Paramètres</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Questo tipo di collegamento non è al momento supportato:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Annulla</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Questo collegamento potrebbe causare un percorso non sicuro. I collegamenti ipertestuali possono essere dannosi per il computer e i dati. Per proteggere il computer, fare clic solo su collegamenti da origini attendibili.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Apri comunque</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Impostazioni</value>
|
||||
</data>
|
||||
|
||||
@@ -670,9 +670,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>このリンクの種類は現在サポートされていません:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>キャンセル</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>このリンクは安全でない可能性があります。ハイパーリンクは、コンピューターやデータに問題を起こす可能性があります。コンピューターを保護するには、信頼できるソースからのリンクのみをクリックしてください。</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>開く</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>이 링크 형식은 현재 지원되지 않습니다.</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>취소</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>이 링크로 인해 안전하지 않은 위치가 발생할 수 있습니다. 하이퍼링크는 컴퓨터와 데이터를 손상시킬 수 있습니다. 컴퓨터를 보호하려면 신뢰할 수 있는 원본의 링크만 클릭하세요.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>열기</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>설정</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Não há suporte para este tipo de link no momento:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancelar</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Este link pode levar a um local não seguro. Hiperlinks podem ser prejudiciais ao computador e aos dados. Para proteger o computador, clique somente em links de fontes confiáveis.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Abrir mesmo assim</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Configurações</value>
|
||||
</data>
|
||||
|
||||
@@ -669,8 +669,14 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Сąñс℮ł !</value>
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Çдπсёľ !</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ŧђīś ℓîŋќ мαў ľêãδ τб áń úʼnšàƒé ℓоćάŧίоñ. Ĥўрзŗℓĭŋķѕ çâⁿ ъέ ђąřмƒúļ τό ўôця ċómφύŧèґ аňδ ðáťǻ. Ţб ρгøťėçŧ ўòύг ςömφùţĕŕ, ŏŋľỳ čℓΐςķ łίŋκѕ ƒřöм ťŗμѕŧєđ śόυяčêś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Őρέй ǻпŷŵãγ !!!</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Śëţťĩпğś !!</value>
|
||||
|
||||
@@ -669,8 +669,14 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Сąñс℮ł !</value>
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Çдπсёľ !</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ŧђīś ℓîŋќ мαў ľêãδ τб áń úʼnšàƒé ℓоćάŧίоñ. Ĥўрзŗℓĭŋķѕ çâⁿ ъέ ђąřмƒúļ τό ўôця ċómφύŧèґ аňδ ðáťǻ. Ţб ρгøťėçŧ ўòύг ςömφùţĕŕ, ŏŋľỳ čℓΐςķ łίŋκѕ ƒřöм ťŗμѕŧєđ śόυяčêś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Őρέй ǻпŷŵãγ !!!</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Śëţťĩпğś !!</value>
|
||||
|
||||
@@ -669,8 +669,14 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Сąñс℮ł !</value>
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Çдπсёľ !</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ŧђīś ℓîŋќ мαў ľêãδ τб áń úʼnšàƒé ℓоćάŧίоñ. Ĥўрзŗℓĭŋķѕ çâⁿ ъέ ђąřмƒúļ τό ўôця ċómφύŧèґ аňδ ðáťǻ. Ţб ρгøťėçŧ ўòύг ςömφùţĕŕ, ŏŋľỳ čℓΐςķ łίŋκѕ ƒřöм ťŗμѕŧєđ śόυяčêś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Őρέй ǻпŷŵãγ !!!</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Śëţťĩпğś !!</value>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Этот тип связи в настоящее время не поддерживается:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Отмена</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Эта ссылка может привести к небезопасному расположению. Гиперссылки могут нанести вред компьютеру и данным. Чтобы защитить компьютер, щелкните ссылки только из надежных источников.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Все равно открыть</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Параметры</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>当前不支持此链接类型:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>此链接可能会导致不安全的位置。超链接可能对你的计算机和数据有害。若要保护你的计算机,请仅单击来自受信任源的链接。</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>仍然打开</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>目前不支援此連結類型:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>此連結可能通往不安全地點。超連結可能對你的電腦和資料造成傷害。為了保護你的電腦,只點擊來自可信來源的連結。</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>一律開啟</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
|
||||
@@ -2906,8 +2906,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do we ever get here (= uninitialized terminal)? If so: How?
|
||||
assert(false);
|
||||
return { 10, 10 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C :\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C :\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL(C:\ -> /mnt/c)</value>
|
||||
<value>WSL(C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin(C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin(C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2(C:\ -> /c)</value>
|
||||
<value>MSYS2(C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW(C:\ -> C:/)</value>
|
||||
<value>MinGW(C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2610,19 +2610,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c) !!! !!!</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c) !!! !!!</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c) !!! !!! !!</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c) !!! !!! !!</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c) !!! !!</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c) !!! !!</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/) !!! !!</value>
|
||||
<value>MinGW (C:\ 🡒 C:/) !!! !!</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2610,19 +2610,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c) !!! !!!</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c) !!! !!!</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c) !!! !!! !!</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c) !!! !!! !!</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c) !!! !!</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c) !!! !!</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/) !!! !!</value>
|
||||
<value>MinGW (C:\ 🡒 C:/) !!! !!</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2610,19 +2610,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c) !!! !!!</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c) !!! !!!</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c) !!! !!! !!</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c) !!! !!! !!</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c) !!! !!</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c) !!! !!</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/) !!! !!</value>
|
||||
<value>MinGW (C:\ 🡒 C:/) !!! !!</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -1843,15 +1843,6 @@ namespace SettingsModelUnitTests
|
||||
VERIFY_ARE_EQUAL(settings->ProfileDefaults().HasTabTitle(), copyImpl->ProfileDefaults().HasTabTitle());
|
||||
VERIFY_ARE_NOT_EQUAL(settings->ProfileDefaults().TabTitle(), copyImpl->ProfileDefaults().TabTitle());
|
||||
|
||||
// Verify HasXxx independence: setting a previously-inherited value on the clone
|
||||
// should make HasXxx true on the clone but remain false on the original.
|
||||
// SnapOnInput is not set in the JSON, so both should inherit the default.
|
||||
VERIFY_IS_FALSE(settings->AllProfiles().GetAt(0).HasSnapOnInput());
|
||||
VERIFY_IS_FALSE(copyImpl->AllProfiles().GetAt(0).HasSnapOnInput());
|
||||
copyImpl->AllProfiles().GetAt(0).SnapOnInput(false);
|
||||
VERIFY_IS_FALSE(settings->AllProfiles().GetAt(0).HasSnapOnInput());
|
||||
VERIFY_IS_TRUE(copyImpl->AllProfiles().GetAt(0).HasSnapOnInput());
|
||||
|
||||
Log::Comment(L"Test empty profiles.defaults");
|
||||
static constexpr std::string_view emptyPDJson{ R"(
|
||||
{
|
||||
|
||||
@@ -105,9 +105,10 @@ namespace SettingsModelUnitTests
|
||||
TEST_METHOD(RealResolverUrlCases);
|
||||
TEST_METHOD(RealResolverUNCCases);
|
||||
|
||||
static constexpr std::wstring_view pingCommandline{ LR"(C:\Windows\System32\PING.EXE)" }; // Normalized by Profile (this is the casing that Windows stores on disk)
|
||||
static constexpr std::wstring_view overrideCommandline{ LR"(C:\Windows\System32\cscript.exe)" };
|
||||
static constexpr std::wstring_view cmdCommandline{ LR"(C:\Windows\System32\cmd.exe)" }; // The default commandline for a profile
|
||||
// These are normalized by NormalizeCommandLine, which resolves to the on-disk casing.
|
||||
// They are used in test cases where media paths fall back to profile command lines.
|
||||
static inline std::wstring overrideCommandline;
|
||||
static inline std::wstring cmdCommandline;
|
||||
static constexpr std::wstring_view fragmentBasePath1{ LR"(C:\Windows\Media)" };
|
||||
|
||||
private:
|
||||
@@ -218,6 +219,9 @@ namespace SettingsModelUnitTests
|
||||
// Some of our tests use paths under system32. Just don't redirect them.
|
||||
Wow64DisableWow64FsRedirection(&redirectionFlag);
|
||||
#endif
|
||||
// Normalize these AFTER the call above so that we get the correctly redirected paths.
|
||||
overrideCommandline = implementation::Profile::NormalizeCommandLine(LR"(C:\Windows\System32\cscript.exe)");
|
||||
cmdCommandline = implementation::Profile::NormalizeCommandLine(LR"(C:\Windows\System32\cmd.exe)");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,6 @@ namespace SettingsModelUnitTests
|
||||
TEST_METHOD(TestGenGuidsForProfiles);
|
||||
TEST_METHOD(TestCorrectOldDefaultShellPaths);
|
||||
TEST_METHOD(ProfileDefaultsProhibitedSettings);
|
||||
|
||||
TEST_METHOD(SettingInheritanceFallback);
|
||||
TEST_METHOD(ClearSettingRestoresInheritance);
|
||||
TEST_METHOD(HasSettingAtSpecificLayer);
|
||||
};
|
||||
|
||||
void ProfileTests::ProfileGeneratesGuid()
|
||||
@@ -536,130 +532,4 @@ namespace SettingsModelUnitTests
|
||||
VERIFY_ARE_NOT_EQUAL(L"Default Profile Source", allProfiles.GetAt(2).Source());
|
||||
VERIFY_ARE_NOT_EQUAL(L"foo.exe", allProfiles.GetAt(2).Commandline());
|
||||
}
|
||||
|
||||
void ProfileTests::SettingInheritanceFallback()
|
||||
{
|
||||
// Verify that when no layer defines a setting, the default value is used.
|
||||
// Also verify that when only user defaults defines it, profiles inherit from there.
|
||||
static constexpr std::string_view userSettings{ R"({
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"historySize": 5000
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
{
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"snapOnInput": false
|
||||
}
|
||||
]
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings);
|
||||
const auto allProfiles = settings->AllProfiles();
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, allProfiles.Size());
|
||||
|
||||
// profile0: historySize inherited from defaults
|
||||
VERIFY_ARE_EQUAL(5000, allProfiles.GetAt(0).HistorySize());
|
||||
// profile0: snapOnInput not set anywhere, falls back to default (true)
|
||||
VERIFY_ARE_EQUAL(true, allProfiles.GetAt(0).SnapOnInput());
|
||||
|
||||
// profile1: historySize inherited from defaults
|
||||
VERIFY_ARE_EQUAL(5000, allProfiles.GetAt(1).HistorySize());
|
||||
// profile1: snapOnInput explicitly set to false
|
||||
VERIFY_ARE_EQUAL(false, allProfiles.GetAt(1).SnapOnInput());
|
||||
}
|
||||
|
||||
void ProfileTests::ClearSettingRestoresInheritance()
|
||||
{
|
||||
// Verify that clearing a setting at the profile layer causes it to
|
||||
// fall back to the parent's value.
|
||||
static constexpr std::string_view parentString{ R"({
|
||||
"name": "parent",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1000,
|
||||
"tabTitle": "ParentTitle"
|
||||
})" };
|
||||
static constexpr std::string_view childString{ R"({
|
||||
"name": "child",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2000,
|
||||
"tabTitle": "ChildTitle"
|
||||
})" };
|
||||
|
||||
const auto parentJson = VerifyParseSucceeded(parentString);
|
||||
const auto childJson = VerifyParseSucceeded(childString);
|
||||
|
||||
auto parent = implementation::Profile::FromJson(parentJson);
|
||||
auto child = parent->CreateChild();
|
||||
child->LayerJson(childJson);
|
||||
|
||||
// Verify child has its own values
|
||||
VERIFY_ARE_EQUAL(2000, child->HistorySize());
|
||||
VERIFY_ARE_EQUAL(L"ChildTitle", child->TabTitle());
|
||||
VERIFY_IS_TRUE(child->HasHistorySize());
|
||||
VERIFY_IS_TRUE(child->HasTabTitle());
|
||||
|
||||
// Clear historySize on child: should fall back to parent
|
||||
child->ClearHistorySize();
|
||||
VERIFY_IS_FALSE(child->HasHistorySize());
|
||||
VERIFY_ARE_EQUAL(1000, child->HistorySize());
|
||||
|
||||
// Clear tabTitle on child: should fall back to parent
|
||||
child->ClearTabTitle();
|
||||
VERIFY_IS_FALSE(child->HasTabTitle());
|
||||
VERIFY_ARE_EQUAL(L"ParentTitle", child->TabTitle());
|
||||
}
|
||||
|
||||
void ProfileTests::HasSettingAtSpecificLayer()
|
||||
{
|
||||
// Verify that HasXxx() correctly reports whether a setting is defined
|
||||
// at the current layer vs inherited from a parent.
|
||||
static constexpr std::string_view userSettings{ R"({
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"historySize": 5000,
|
||||
"tabTitle": "DefaultTitle"
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 9001
|
||||
},
|
||||
{
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
]
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings);
|
||||
const auto allProfiles = settings->AllProfiles();
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, allProfiles.Size());
|
||||
|
||||
// profile0: historySize is explicitly set
|
||||
VERIFY_IS_TRUE(allProfiles.GetAt(0).HasHistorySize());
|
||||
VERIFY_ARE_EQUAL(9001, allProfiles.GetAt(0).HistorySize());
|
||||
|
||||
// profile0: tabTitle is NOT set at this layer (inherited from defaults)
|
||||
VERIFY_IS_FALSE(allProfiles.GetAt(0).HasTabTitle());
|
||||
VERIFY_ARE_EQUAL(L"DefaultTitle", allProfiles.GetAt(0).TabTitle());
|
||||
|
||||
// profile1: historySize is NOT set at this layer (inherited from defaults)
|
||||
VERIFY_IS_FALSE(allProfiles.GetAt(1).HasHistorySize());
|
||||
VERIFY_ARE_EQUAL(5000, allProfiles.GetAt(1).HistorySize());
|
||||
|
||||
// ProfileDefaults: historySize is set
|
||||
VERIFY_IS_TRUE(settings->ProfileDefaults().HasHistorySize());
|
||||
VERIFY_ARE_EQUAL(5000, settings->ProfileDefaults().HistorySize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,12 +60,6 @@ namespace SettingsModelUnitTests
|
||||
|
||||
TEST_METHOD(ProfileWithInvalidIcon);
|
||||
|
||||
TEST_METHOD(ModifyProfileSettingAndRoundtrip);
|
||||
TEST_METHOD(ModifyGlobalSettingAndRoundtrip);
|
||||
TEST_METHOD(ModifyColorSchemeAndRoundtrip);
|
||||
TEST_METHOD(FixupUserSettingsDetectsChanges);
|
||||
TEST_METHOD(FixupCommandlinePatching);
|
||||
|
||||
private:
|
||||
// Method Description:
|
||||
// - deserializes and reserializes a json string representing a settings object model of type T
|
||||
@@ -1331,288 +1325,4 @@ namespace SettingsModelUnitTests
|
||||
// what was written in the settings file.
|
||||
VERIFY_ARE_EQUAL(R"(c:\this_icon_had_better_not_exist.tiff)", newResult["profiles"]["list"][0]["icon"].asString());
|
||||
}
|
||||
|
||||
void SerializationTests::ModifyProfileSettingAndRoundtrip()
|
||||
{
|
||||
// Load settings, modify a profile setting via setter, serialize,
|
||||
// and verify the JSON output reflects the change.
|
||||
static constexpr std::string_view settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1000,
|
||||
"commandline": "cmd.exe"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsJson) };
|
||||
|
||||
// Verify initial value
|
||||
VERIFY_ARE_EQUAL(1000, settings->AllProfiles().GetAt(0).HistorySize());
|
||||
|
||||
// Modify the setting
|
||||
settings->AllProfiles().GetAt(0).HistorySize(5000);
|
||||
VERIFY_ARE_EQUAL(5000, settings->AllProfiles().GetAt(0).HistorySize());
|
||||
|
||||
// Serialize and verify the change is reflected in JSON
|
||||
const auto result{ settings->ToJson() };
|
||||
VERIFY_ARE_EQUAL(5000, result["profiles"]["list"][0]["historySize"].asInt());
|
||||
|
||||
// Verify other settings are preserved
|
||||
VERIFY_ARE_EQUAL("cmd.exe", result["profiles"]["list"][0]["commandline"].asString());
|
||||
|
||||
// Also verify: modify a setting that wasn't previously set
|
||||
settings->AllProfiles().GetAt(0).TabTitle(L"NewTitle");
|
||||
const auto result2{ settings->ToJson() };
|
||||
VERIFY_ARE_EQUAL("NewTitle", result2["profiles"]["list"][0]["tabTitle"].asString());
|
||||
}
|
||||
|
||||
void SerializationTests::ModifyGlobalSettingAndRoundtrip()
|
||||
{
|
||||
// Load settings, modify a global setting, serialize, verify JSON.
|
||||
static constexpr std::string_view settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"initialRows": 30,
|
||||
"alwaysOnTop": false,
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsJson) };
|
||||
|
||||
// Verify initial values
|
||||
VERIFY_ARE_EQUAL(30, settings->GlobalSettings().InitialRows());
|
||||
VERIFY_ARE_EQUAL(false, settings->GlobalSettings().AlwaysOnTop());
|
||||
|
||||
// Modify global settings
|
||||
settings->GlobalSettings().InitialRows(50);
|
||||
settings->GlobalSettings().AlwaysOnTop(true);
|
||||
|
||||
// Verify in-memory changes
|
||||
VERIFY_ARE_EQUAL(50, settings->GlobalSettings().InitialRows());
|
||||
VERIFY_ARE_EQUAL(true, settings->GlobalSettings().AlwaysOnTop());
|
||||
|
||||
// Serialize and verify
|
||||
const auto result{ settings->ToJson() };
|
||||
VERIFY_ARE_EQUAL(50, result["initialRows"].asInt());
|
||||
VERIFY_ARE_EQUAL(true, result["alwaysOnTop"].asBool());
|
||||
}
|
||||
|
||||
void SerializationTests::ModifyColorSchemeAndRoundtrip()
|
||||
{
|
||||
// Load settings with a user color scheme, modify it, serialize, verify.
|
||||
static constexpr std::string_view settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{
|
||||
"name": "MyScheme",
|
||||
"foreground": "#CCCCCC",
|
||||
"background": "#0C0C0C",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#0C0C0C",
|
||||
"red": "#C50F1F",
|
||||
"green": "#13A10E",
|
||||
"yellow": "#C19C00",
|
||||
"blue": "#0037DA",
|
||||
"purple": "#881798",
|
||||
"cyan": "#3A96DD",
|
||||
"white": "#CCCCCC",
|
||||
"brightBlack": "#767676",
|
||||
"brightRed": "#E74856",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightWhite": "#F2F2F2"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsJson) };
|
||||
|
||||
// Find and modify the color scheme
|
||||
const auto schemes = settings->GlobalSettings().ColorSchemes();
|
||||
VERIFY_IS_TRUE(schemes.HasKey(L"MyScheme"));
|
||||
auto myScheme = schemes.Lookup(L"MyScheme");
|
||||
|
||||
const auto origForeground = myScheme.Foreground();
|
||||
myScheme.Foreground(til::color{ 0xAA, 0xBB, 0xCC });
|
||||
|
||||
// Serialize and verify the change persists
|
||||
const auto result{ settings->ToJson() };
|
||||
const auto& schemesJson = result["schemes"];
|
||||
bool found = false;
|
||||
for (const auto& scheme : schemesJson)
|
||||
{
|
||||
if (scheme["name"].asString() == "MyScheme")
|
||||
{
|
||||
VERIFY_ARE_EQUAL("#AABBCC", scheme["foreground"].asString());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
VERIFY_IS_TRUE(found, L"MyScheme should be present in serialized output");
|
||||
}
|
||||
|
||||
void SerializationTests::FixupUserSettingsDetectsChanges()
|
||||
{
|
||||
// Verify that FixupUserSettings returns true when settings need
|
||||
// to be written back (e.g., migration), and false when clean.
|
||||
static constexpr std::string_view cleanSettingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"commandline": "cmd.exe"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
// Load, fixup, serialize. Reload and verify fixup returns false.
|
||||
implementation::SettingsLoader loader1{ cleanSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
||||
loader1.MergeInboxIntoUserSettings();
|
||||
loader1.FinalizeLayering();
|
||||
loader1.FixupUserSettings();
|
||||
const auto settings1 = winrt::make_self<implementation::CascadiaSettings>(std::move(loader1));
|
||||
const auto result1{ settings1->ToJson() };
|
||||
|
||||
// Reload from the serialized output (should be stable)
|
||||
implementation::SettingsLoader loader2{ toString(result1), implementation::LoadStringResource(IDR_DEFAULTS) };
|
||||
loader2.MergeInboxIntoUserSettings();
|
||||
loader2.FinalizeLayering();
|
||||
const auto fixupNeeded = loader2.FixupUserSettings();
|
||||
|
||||
// After a clean roundtrip, no further fixups should be needed
|
||||
VERIFY_IS_FALSE(fixupNeeded, L"A clean roundtrip should not require further fixups");
|
||||
}
|
||||
|
||||
void SerializationTests::FixupCommandlinePatching()
|
||||
{
|
||||
// Verify that FixupUserSettings patches "cmd.exe" to the full path
|
||||
// for the Command Prompt profile, and "powershell.exe" for the
|
||||
// Windows PowerShell profile, and returns true to indicate changes.
|
||||
|
||||
// Case 1: CMD profile with short commandline should be patched
|
||||
static constexpr std::string_view cmdSettingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "Command Prompt",
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"commandline": "cmd.exe"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
{
|
||||
implementation::SettingsLoader loader{ cmdSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
||||
loader.MergeInboxIntoUserSettings();
|
||||
loader.FinalizeLayering();
|
||||
const auto fixupNeeded = loader.FixupUserSettings();
|
||||
VERIFY_IS_TRUE(fixupNeeded, L"FixupUserSettings should return true when cmd.exe is patched");
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
||||
const auto cmdProfile = settings->FindProfile(Utils::GuidFromString(L"{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"));
|
||||
VERIFY_IS_NOT_NULL(cmdProfile);
|
||||
VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\cmd.exe", cmdProfile.Commandline());
|
||||
}
|
||||
|
||||
// Case 2: PowerShell profile with short commandline should be patched
|
||||
static constexpr std::string_view psSettingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "Windows PowerShell",
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"commandline": "powershell.exe"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
{
|
||||
implementation::SettingsLoader loader{ psSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
||||
loader.MergeInboxIntoUserSettings();
|
||||
loader.FinalizeLayering();
|
||||
const auto fixupNeeded = loader.FixupUserSettings();
|
||||
VERIFY_IS_TRUE(fixupNeeded, L"FixupUserSettings should return true when powershell.exe is patched");
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
||||
const auto psProfile = settings->FindProfile(Utils::GuidFromString(L"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"));
|
||||
VERIFY_IS_NOT_NULL(psProfile);
|
||||
VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", psProfile.Commandline());
|
||||
}
|
||||
|
||||
// Case 3: CMD profile with the full path should NOT trigger fixup
|
||||
static constexpr std::string_view cleanCmdSettingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "Command Prompt",
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
{
|
||||
implementation::SettingsLoader loader{ cleanCmdSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
||||
loader.MergeInboxIntoUserSettings();
|
||||
loader.FinalizeLayering();
|
||||
const auto fixupNeeded = loader.FixupUserSettings();
|
||||
VERIFY_IS_FALSE(fixupNeeded, L"FixupUserSettings should return false when no patching is needed");
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
||||
const auto cmdProfile = settings->FindProfile(Utils::GuidFromString(L"{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"));
|
||||
VERIFY_IS_NOT_NULL(cmdProfile);
|
||||
// Should still resolve to the full path via inbox defaults
|
||||
VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\cmd.exe", cmdProfile.Commandline());
|
||||
}
|
||||
|
||||
// Case 4: A non-builtin profile with "cmd.exe" should NOT be patched
|
||||
static constexpr std::string_view customCmdSettingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "My Custom CMD",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"commandline": "cmd.exe"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
{
|
||||
implementation::SettingsLoader loader{ customCmdSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
||||
loader.MergeInboxIntoUserSettings();
|
||||
loader.FinalizeLayering();
|
||||
loader.FixupUserSettings();
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
||||
const auto customProfile = settings->FindProfile(Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}"));
|
||||
VERIFY_IS_NOT_NULL(customProfile);
|
||||
// Custom profile should keep "cmd.exe" unchanged
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", customProfile.Commandline());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,38 +1061,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||
ROW* rowBackup = nullptr;
|
||||
if (row == compositionRow)
|
||||
{
|
||||
auto& scratch = buffer.GetScratchpadRow();
|
||||
scratch.CopyFrom(r);
|
||||
rowBackup = &scratch;
|
||||
|
||||
std::wstring_view text{ activeComposition.text };
|
||||
RowWriteState state{
|
||||
.columnLimit = r.GetReadableColumnCount(),
|
||||
.columnEnd = _compositionCache->absoluteOrigin.x,
|
||||
};
|
||||
|
||||
size_t off = 0;
|
||||
for (const auto& range : activeComposition.attributes)
|
||||
{
|
||||
const auto len = range.len;
|
||||
auto attr = range.attr;
|
||||
|
||||
// Use the color at the cursor if TSF didn't specify any explicit color.
|
||||
if (attr.GetBackground().IsDefault())
|
||||
{
|
||||
attr.SetBackground(_compositionCache->baseAttribute.GetBackground());
|
||||
}
|
||||
if (attr.GetForeground().IsDefault())
|
||||
{
|
||||
attr.SetForeground(_compositionCache->baseAttribute.GetForeground());
|
||||
}
|
||||
|
||||
state.text = text.substr(off, len);
|
||||
state.columnBegin = state.columnEnd;
|
||||
const_cast<ROW&>(r).ReplaceText(state);
|
||||
const_cast<ROW&>(r).ReplaceAttributes(state.columnBegin, state.columnEnd, attr);
|
||||
off += len;
|
||||
}
|
||||
rowBackup = _PaintBufferOutputComposition(buffer, r, activeComposition);
|
||||
}
|
||||
const auto restore = wil::scope_exit([&] {
|
||||
if (rowBackup)
|
||||
@@ -1132,6 +1101,107 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||
}
|
||||
}
|
||||
|
||||
ROW* Renderer::_PaintBufferOutputComposition(TextBuffer& buffer, const ROW& r, const Composition& activeComposition)
|
||||
{
|
||||
auto& scratch = buffer.GetScratchpadRow();
|
||||
scratch.CopyFrom(r);
|
||||
|
||||
// *Overwrite* the original text with the active composition...
|
||||
til::CoordType compositionEnd = 0;
|
||||
{
|
||||
std::wstring_view text{ activeComposition.text };
|
||||
RowWriteState state{
|
||||
.columnLimit = r.GetReadableColumnCount(),
|
||||
.columnEnd = _compositionCache->absoluteOrigin.x,
|
||||
};
|
||||
|
||||
size_t off = 0;
|
||||
for (const auto& range : activeComposition.attributes)
|
||||
{
|
||||
const auto len = range.len;
|
||||
auto attr = range.attr;
|
||||
|
||||
// Use the color at the cursor if TSF didn't specify any explicit color.
|
||||
if (attr.GetBackground().IsDefault())
|
||||
{
|
||||
attr.SetBackground(_compositionCache->baseAttribute.GetBackground());
|
||||
}
|
||||
if (attr.GetForeground().IsDefault())
|
||||
{
|
||||
attr.SetForeground(_compositionCache->baseAttribute.GetForeground());
|
||||
}
|
||||
|
||||
state.text = text.substr(off, len);
|
||||
state.columnBegin = state.columnEnd;
|
||||
const_cast<ROW&>(r).ReplaceText(state);
|
||||
const_cast<ROW&>(r).ReplaceAttributes(state.columnBegin, state.columnEnd, attr);
|
||||
off += len;
|
||||
}
|
||||
|
||||
compositionEnd = state.columnEnd;
|
||||
}
|
||||
|
||||
// The text we've overwritten may have been crucial to the user,
|
||||
// so copy it back by absorbing available whitespace to the right
|
||||
// and re-inserting the non-whitespace characters instead.
|
||||
const auto compositionWidth = compositionEnd - _compositionCache->absoluteOrigin.x;
|
||||
const auto colLimit = r.GetReadableColumnCount();
|
||||
if (compositionWidth > 0 && compositionEnd < colLimit)
|
||||
{
|
||||
const auto text = scratch.GetText();
|
||||
auto srcCol = _compositionCache->absoluteOrigin.x;
|
||||
auto dstCol = compositionEnd;
|
||||
auto remaining = compositionWidth;
|
||||
size_t i = scratch.GetCharOffset(srcCol);
|
||||
|
||||
while (i < text.size() && dstCol < colLimit)
|
||||
{
|
||||
// Treat whitespace we encounter as a credit towards our composition width.
|
||||
// This loop essentially absorbs the whitespace.
|
||||
while (i < text.size() && til::at(text, i) == L' ' && remaining > 0)
|
||||
{
|
||||
remaining--;
|
||||
srcCol++;
|
||||
i++;
|
||||
}
|
||||
if (remaining <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the end of the non-whitespace span: Our span of text to insert.
|
||||
auto spanEnd = i;
|
||||
while (spanEnd < text.size() && til::at(text, spanEnd) != L' ')
|
||||
{
|
||||
spanEnd++;
|
||||
}
|
||||
|
||||
// Copy the non-whitespace segment from the original text (scratch) back in.
|
||||
RowCopyTextFromState state{
|
||||
.source = scratch,
|
||||
.columnBegin = dstCol,
|
||||
.columnLimit = colLimit,
|
||||
.sourceColumnBegin = srcCol,
|
||||
.sourceColumnLimit = scratch.GetLeadingColumnAtCharOffset(spanEnd),
|
||||
};
|
||||
const_cast<ROW&>(r).CopyTextFrom(state);
|
||||
|
||||
const auto srcBeg = gsl::narrow_cast<uint16_t>(srcCol);
|
||||
const auto srcEnd = gsl::narrow_cast<uint16_t>(state.sourceColumnEnd);
|
||||
const auto attr = scratch.Attributes().slice(srcBeg, srcEnd);
|
||||
const auto dstBeg = gsl::narrow_cast<uint16_t>(dstCol);
|
||||
const auto dstEnd = gsl::narrow_cast<uint16_t>(dstCol + attr.size());
|
||||
const_cast<ROW&>(r).Attributes().replace(dstBeg, dstEnd, attr);
|
||||
|
||||
dstCol = state.columnEnd;
|
||||
srcCol = state.sourceColumnEnd;
|
||||
i = spanEnd;
|
||||
}
|
||||
}
|
||||
|
||||
return &scratch;
|
||||
}
|
||||
|
||||
static bool _IsAllSpaces(const std::wstring_view v)
|
||||
{
|
||||
// first non-space char is not found (is npos)
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace Microsoft::Console::Render
|
||||
void _scheduleRenditionBlink();
|
||||
[[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine);
|
||||
void _PaintBufferOutput(_In_ IRenderEngine* const pEngine);
|
||||
ROW* _PaintBufferOutputComposition(TextBuffer& buffer, const ROW& r, const Composition& activeComposition);
|
||||
void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, TextBufferCellIterator it, const til::point target);
|
||||
void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine, const TextAttribute textAttribute, const size_t cchLine, const til::point coordTarget);
|
||||
bool _isHoveredHyperlink(const TextAttribute& textAttribute) const noexcept;
|
||||
|
||||
Reference in New Issue
Block a user