diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt
index 8e83ab3e4..efd6edff8 100644
--- a/.github/actions/spelling/expect/expect.txt
+++ b/.github/actions/spelling/expect/expect.txt
@@ -557,6 +557,7 @@ DECSTR
DECSWL
DECTCEM
Dedupe
+deduplicate
deduplicated
DEFAPP
DEFAULTBACKGROUND
@@ -784,6 +785,7 @@ FINDSTRINGEXACT
FINDUP
FIter
FIXEDCONVERTED
+FIXEDFILEINFO
Flg
flyout
fmodern
@@ -1992,6 +1994,7 @@ resx
retval
rfa
rfc
+rfid
rftp
rgb
rgba
diff --git a/README.md b/README.md
index 553c5aaef..ca416b82c 100644
--- a/README.md
+++ b/README.md
@@ -289,6 +289,7 @@ If you would like to ask a question that you feel doesn't warrant an issue
* You must [enable Developer Mode in the Windows Settings
app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development)
to locally install and run Windows Terminal
+* You must have [PowerShell 7 or later](https://github.com/PowerShell/PowerShell/releases/latest) installed
* You must have the [Windows 10 1903
SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk)
installed
diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json
index 07e584c6b..319e0bae7 100644
--- a/doc/cascadia/profiles.schema.json
+++ b/doc/cascadia/profiles.schema.json
@@ -1401,7 +1401,8 @@
"properties": {
"acrylicOpacity": {
"default": 0.5,
- "description": "When useAcrylic is set to true, it sets the transparency of the window for the profile. Accepts floating point values from 0-1 (default 0.5).",
+ "description": "[deprecated] Please use `opacity` instead.",
+ "deprecated": true,
"maximum": 1,
"minimum": 0,
"type": "number"
@@ -1614,6 +1615,13 @@
"minLength": 1,
"type": "string"
},
+ "opacity": {
+ "default": 100,
+ "description": "Sets the opacity of the window for the profile. Accepts values from 0-100. Defaults to 50 when useAcrylic is set to true.",
+ "maximum": 100,
+ "minimum": 0,
+ "type": "number"
+ },
"padding": {
"default": "8, 8, 8, 8",
"description": "Sets the padding around the text within the window. Can have three different formats:\n -\"#\" sets the same padding for all sides \n -\"#, #\" sets the same padding for left-right and top-bottom\n -\"#, #, #, #\" sets the padding individually for left, top, right, and bottom.",
diff --git a/scratch/ScratchIslandApp/Package/Package.wapproj b/scratch/ScratchIslandApp/Package/Package.wapproj
index 262719541..f7458a902 100644
--- a/scratch/ScratchIslandApp/Package/Package.wapproj
+++ b/scratch/ScratchIslandApp/Package/Package.wapproj
@@ -140,12 +140,12 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
diff --git a/scratch/ScratchIslandApp/SampleApp/MySettings.h b/scratch/ScratchIslandApp/SampleApp/MySettings.h
index 34643f7fe..4d34b8624 100644
--- a/scratch/ScratchIslandApp/SampleApp/MySettings.h
+++ b/scratch/ScratchIslandApp/SampleApp/MySettings.h
@@ -45,7 +45,7 @@ namespace winrt::SampleApp::implementation
WINRT_PROPERTY(winrt::hstring, ProfileName);
WINRT_PROPERTY(bool, UseAcrylic, false);
- WINRT_PROPERTY(double, TintOpacity, 0.5);
+ WINRT_PROPERTY(double, Opacity, .5);
WINRT_PROPERTY(winrt::hstring, Padding, DEFAULT_PADDING);
WINRT_PROPERTY(winrt::hstring, FontFace, L"Consolas");
WINRT_PROPERTY(int32_t, FontSize, DEFAULT_FONT_SIZE);
diff --git a/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj
index 54a6c5706..04b9b0824 100644
--- a/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj
+++ b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj
@@ -147,13 +147,13 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
diff --git a/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj
index 23b157e9d..59d500ccc 100644
--- a/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj
+++ b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj
@@ -80,13 +80,13 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
diff --git a/scratch/ScratchIslandApp/SampleApp/packages.config b/scratch/ScratchIslandApp/SampleApp/packages.config
index 9de6f1b8d..78467467c 100644
--- a/scratch/ScratchIslandApp/SampleApp/packages.config
+++ b/scratch/ScratchIslandApp/SampleApp/packages.config
@@ -1,6 +1,6 @@
-
+
diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj
index e8cb725b7..4332ae5d0 100644
--- a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj
+++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj
@@ -120,14 +120,14 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
diff --git a/scratch/ScratchIslandApp/WindowExe/packages.config b/scratch/ScratchIslandApp/WindowExe/packages.config
index 3d9e08c4b..d55a833a5 100644
--- a/scratch/ScratchIslandApp/WindowExe/packages.config
+++ b/scratch/ScratchIslandApp/WindowExe/packages.config
@@ -2,6 +2,6 @@
-
+
diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj
index b18c6d57a..ba722f246 100644
--- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj
+++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj
@@ -146,12 +146,12 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
diff --git a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp
index b4545ed85..85a9dd34a 100644
--- a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp
@@ -5,9 +5,11 @@
#include "../TerminalSettingsModel/ColorScheme.h"
#include "../TerminalSettingsModel/CascadiaSettings.h"
+#include "../types/inc/colorTable.hpp"
#include "JsonTestClass.h"
using namespace Microsoft::Console;
+using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
@@ -32,339 +34,293 @@ namespace SettingsModelLocalTests
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
END_TEST_CLASS()
- TEST_METHOD(CanLayerColorScheme);
- TEST_METHOD(LayerColorSchemeProperties);
+ TEST_METHOD(ParseSimpleColorScheme);
TEST_METHOD(LayerColorSchemesOnArray);
TEST_METHOD(UpdateSchemeReferences);
- TEST_CLASS_SETUP(ClassSetup)
+ static Core::Color rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
{
- InitializeJsonReader();
- return true;
+ return Core::Color{ r, g, b, 255 };
}
};
- void ColorSchemeTests::CanLayerColorScheme()
+ void ColorSchemeTests::ParseSimpleColorScheme()
{
- const std::string scheme0String{ R"({
- "name": "scheme0",
- "foreground": "#000000",
- "background": "#010101"
- })" };
- const std::string scheme1String{ R"({
- "name": "scheme1",
- "foreground": "#020202",
- "background": "#030303"
- })" };
- const std::string scheme2String{ R"({
- "name": "scheme0",
- "foreground": "#040404",
- "background": "#050505"
- })" };
- const std::string scheme3String{ R"({
- // "name": "scheme3",
- "foreground": "#060606",
- "background": "#070707"
- })" };
+ const std::string campbellScheme{ "{"
+ "\"background\" : \"#0C0C0C\","
+ "\"black\" : \"#0C0C0C\","
+ "\"blue\" : \"#0037DA\","
+ "\"brightBlack\" : \"#767676\","
+ "\"brightBlue\" : \"#3B78FF\","
+ "\"brightCyan\" : \"#61D6D6\","
+ "\"brightGreen\" : \"#16C60C\","
+ "\"brightPurple\" : \"#B4009E\","
+ "\"brightRed\" : \"#E74856\","
+ "\"brightWhite\" : \"#F2F2F2\","
+ "\"brightYellow\" : \"#F9F1A5\","
+ "\"cursorColor\" : \"#FFFFFF\","
+ "\"cyan\" : \"#3A96DD\","
+ "\"foreground\" : \"#F2F2F2\","
+ "\"green\" : \"#13A10E\","
+ "\"name\" : \"Campbell\","
+ "\"purple\" : \"#881798\","
+ "\"red\" : \"#C50F1F\","
+ "\"selectionBackground\" : \"#131313\","
+ "\"white\" : \"#CCCCCC\","
+ "\"yellow\" : \"#C19C00\""
+ "}" };
- const auto scheme0Json = VerifyParseSucceeded(scheme0String);
- const auto scheme1Json = VerifyParseSucceeded(scheme1String);
- const auto scheme2Json = VerifyParseSucceeded(scheme2String);
- const auto scheme3Json = VerifyParseSucceeded(scheme3String);
+ const auto schemeObject = VerifyParseSucceeded(campbellScheme);
+ auto scheme = ColorScheme::FromJson(schemeObject);
+ VERIFY_ARE_EQUAL(L"Campbell", scheme->Name());
+ VERIFY_ARE_EQUAL(til::color(0xf2, 0xf2, 0xf2, 255), til::color{ scheme->Foreground() });
+ VERIFY_ARE_EQUAL(til::color(0x0c, 0x0c, 0x0c, 255), til::color{ scheme->Background() });
+ VERIFY_ARE_EQUAL(til::color(0x13, 0x13, 0x13, 255), til::color{ scheme->SelectionBackground() });
+ VERIFY_ARE_EQUAL(til::color(0xFF, 0xFF, 0xFF, 255), til::color{ scheme->CursorColor() });
- const auto scheme0 = ColorScheme::FromJson(scheme0Json);
+ std::array expectedCampbellTable;
+ const auto campbellSpan = gsl::make_span(expectedCampbellTable);
+ Utils::InitializeCampbellColorTable(campbellSpan);
+ Utils::SetColorTableAlpha(campbellSpan, 0);
- VERIFY_IS_TRUE(scheme0->ShouldBeLayered(scheme0Json));
- VERIFY_IS_FALSE(scheme0->ShouldBeLayered(scheme1Json));
- VERIFY_IS_TRUE(scheme0->ShouldBeLayered(scheme2Json));
- VERIFY_IS_FALSE(scheme0->ShouldBeLayered(scheme3Json));
+ for (size_t i = 0; i < expectedCampbellTable.size(); i++)
+ {
+ const auto& expected = expectedCampbellTable.at(i);
+ const til::color actual{ scheme->Table().at(static_cast(i)) };
+ VERIFY_ARE_EQUAL(expected, actual);
+ }
- const auto scheme1 = ColorScheme::FromJson(scheme1Json);
-
- VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme0Json));
- VERIFY_IS_TRUE(scheme1->ShouldBeLayered(scheme1Json));
- VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme2Json));
- VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme3Json));
-
- const auto scheme3 = ColorScheme::FromJson(scheme3Json);
-
- VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme0Json));
- VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme1Json));
- VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme2Json));
- VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme3Json));
- }
-
- void ColorSchemeTests::LayerColorSchemeProperties()
- {
- const std::string scheme0String{ R"({
- "name": "scheme0",
- "foreground": "#000000",
- "background": "#010101",
- "selectionBackground": "#010100",
- "cursorColor": "#010001",
- "red": "#010000",
- "green": "#000100",
- "blue": "#000001"
- })" };
- const std::string scheme1String{ R"({
- "name": "scheme1",
- "foreground": "#020202",
- "background": "#030303",
- "selectionBackground": "#020200",
- "cursorColor": "#040004",
- "red": "#020000",
-
- "blue": "#000002"
- })" };
- const std::string scheme2String{ R"({
- "name": "scheme0",
- "foreground": "#040404",
- "background": "#050505",
- "selectionBackground": "#030300",
- "cursorColor": "#060006",
- "red": "#030000",
- "green": "#000300"
- })" };
-
- const auto scheme0Json = VerifyParseSucceeded(scheme0String);
- const auto scheme1Json = VerifyParseSucceeded(scheme1String);
- const auto scheme2Json = VerifyParseSucceeded(scheme2String);
-
- auto scheme0 = ColorScheme::FromJson(scheme0Json);
- VERIFY_ARE_EQUAL(L"scheme0", scheme0->_Name);
- VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
- VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 0), scheme0->_SelectionBackground);
- VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 1), scheme0->_CursorColor);
- VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 0), scheme0->_table[XTERM_RED_ATTR]);
- VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0->_table[XTERM_GREEN_ATTR]);
- VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 1), scheme0->_table[XTERM_BLUE_ATTR]);
-
- Log::Comment(NoThrowString().Format(
- L"Layering scheme1 on top of scheme0"));
- scheme0->LayerJson(scheme1Json);
-
- VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme0->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme0->_Background);
- VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 0), scheme0->_SelectionBackground);
- VERIFY_ARE_EQUAL(ARGB(0, 4, 0, 4), scheme0->_CursorColor);
- VERIFY_ARE_EQUAL(ARGB(0, 2, 0, 0), scheme0->_table[XTERM_RED_ATTR]);
- VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0->_table[XTERM_GREEN_ATTR]);
- VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0->_table[XTERM_BLUE_ATTR]);
-
- Log::Comment(NoThrowString().Format(
- L"Layering scheme2Json on top of (scheme0+scheme1)"));
- scheme0->LayerJson(scheme2Json);
-
- VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
- VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 0), scheme0->_SelectionBackground);
- VERIFY_ARE_EQUAL(ARGB(0, 6, 0, 6), scheme0->_CursorColor);
- VERIFY_ARE_EQUAL(ARGB(0, 3, 0, 0), scheme0->_table[XTERM_RED_ATTR]);
- VERIFY_ARE_EQUAL(ARGB(0, 0, 3, 0), scheme0->_table[XTERM_GREEN_ATTR]);
- VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0->_table[XTERM_BLUE_ATTR]);
+ Log::Comment(L"Roundtrip Test for Color Scheme");
+ Json::Value outJson{ scheme->ToJson() };
+ VERIFY_ARE_EQUAL(schemeObject, outJson);
}
void ColorSchemeTests::LayerColorSchemesOnArray()
{
- const std::string scheme0String{ R"({
- "name": "scheme0",
- "foreground": "#000000",
- "background": "#010101"
+ static constexpr std::string_view inboxSettings{ R"({
+ "schemes": [
+ {
+ "background": "#0C0C0C",
+ "black": "#0C0C0C",
+ "blue": "#0037DA",
+ "brightBlack": "#767676",
+ "brightBlue": "#3B78FF",
+ "brightCyan": "#61D6D6",
+ "brightGreen": "#16C60C",
+ "brightPurple": "#B4009E",
+ "brightRed": "#E74856",
+ "brightWhite": "#F2F2F2",
+ "brightYellow": "#F9F1A5",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#3A96DD",
+ "foreground": "#CCCCCC",
+ "green": "#13A10E",
+ "name": "Campbell",
+ "purple": "#881798",
+ "red": "#C50F1F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#CCCCCC",
+ "yellow": "#C19C00"
+ }
+ ]
})" };
- const std::string scheme1String{ R"({
- "name": "scheme1",
- "foreground": "#020202",
- "background": "#030303"
- })" };
- const std::string scheme2String{ R"({
- "name": "scheme0",
- "foreground": "#040404",
- "background": "#050505"
- })" };
- const std::string scheme3String{ R"({
- // by not providing a name, the scheme will have the name ""
- "foreground": "#060606",
- "background": "#070707"
+ static constexpr std::string_view userSettings{ R"({
+ "profiles": [
+ {
+ "name" : "profile0"
+ }
+ ],
+ "schemes": [
+ {
+ "background": "#121314",
+ "black": "#121314",
+ "blue": "#121314",
+ "brightBlack": "#121314",
+ "brightBlue": "#121314",
+ "brightCyan": "#121314",
+ "brightGreen": "#121314",
+ "brightPurple": "#121314",
+ "brightRed": "#121314",
+ "brightWhite": "#121314",
+ "brightYellow": "#121314",
+ "cursorColor": "#121314",
+ "cyan": "#121314",
+ "foreground": "#121314",
+ "green": "#121314",
+ "name": "Campbell",
+ "purple": "#121314",
+ "red": "#121314",
+ "selectionBackground": "#121314",
+ "white": "#121314",
+ "yellow": "#121314"
+ },
+ {
+ "background": "#012456",
+ "black": "#0C0C0C",
+ "blue": "#0037DA",
+ "brightBlack": "#767676",
+ "brightBlue": "#3B78FF",
+ "brightCyan": "#61D6D6",
+ "brightGreen": "#16C60C",
+ "brightPurple": "#B4009E",
+ "brightRed": "#E74856",
+ "brightWhite": "#F2F2F2",
+ "brightYellow": "#F9F1A5",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#3A96DD",
+ "foreground": "#CCCCCC",
+ "green": "#13A10E",
+ "name": "Campbell Powershell",
+ "purple": "#881798",
+ "red": "#C50F1F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#CCCCCC",
+ "yellow": "#C19C00"
+ }
+ ]
})" };
- const auto scheme0Json = VerifyParseSucceeded(scheme0String);
- const auto scheme1Json = VerifyParseSucceeded(scheme1String);
- const auto scheme2Json = VerifyParseSucceeded(scheme2String);
- const auto scheme3Json = VerifyParseSucceeded(scheme3String);
+ const auto settings = winrt::make_self(userSettings, inboxSettings);
- auto settings = winrt::make_self();
+ const auto colorSchemes = settings->GlobalSettings().ColorSchemes();
+ VERIFY_ARE_EQUAL(2u, colorSchemes.Size());
- VERIFY_ARE_EQUAL(0u, settings->_globals->ColorSchemes().Size());
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme0Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme1Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme2Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
+ const auto scheme0 = winrt::get_self(colorSchemes.Lookup(L"Campbell"));
+ VERIFY_ARE_EQUAL(rgb(0x12, 0x13, 0x14), scheme0->Foreground());
+ VERIFY_ARE_EQUAL(rgb(0x12, 0x13, 0x14), scheme0->Background());
- settings->_LayerOrCreateColorScheme(scheme0Json);
- {
- for (auto kv : settings->_globals->ColorSchemes())
- {
- Log::Comment(NoThrowString().Format(
- L"kv:%s->%s", kv.Key().data(), kv.Value().Name().data()));
- }
- VERIFY_ARE_EQUAL(1u, settings->_globals->ColorSchemes().Size());
-
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
- auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
- auto scheme0 = winrt::get_self(scheme0Proj);
-
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme1Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
- VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
- }
-
- settings->_LayerOrCreateColorScheme(scheme1Json);
-
- {
- VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
-
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
- auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
- auto scheme0 = winrt::get_self(scheme0Proj);
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
- auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
- auto scheme1 = winrt::get_self(scheme1Proj);
-
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
- VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
- VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
- }
- settings->_LayerOrCreateColorScheme(scheme2Json);
-
- {
- VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
-
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
- auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
- auto scheme0 = winrt::get_self(scheme0Proj);
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
- auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
- auto scheme1 = winrt::get_self(scheme1Proj);
-
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
- VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
- VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
- }
- settings->_LayerOrCreateColorScheme(scheme3Json);
-
- {
- VERIFY_ARE_EQUAL(3u, settings->_globals->ColorSchemes().Size());
-
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
- auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
- auto scheme0 = winrt::get_self(scheme0Proj);
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
- auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
- auto scheme1 = winrt::get_self(scheme1Proj);
- VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L""));
- auto scheme2Proj = settings->_globals->ColorSchemes().Lookup(L"");
- auto scheme2 = winrt::get_self(scheme2Proj);
-
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
- VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
- VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
- VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
- VERIFY_ARE_EQUAL(ARGB(0, 6, 6, 6), scheme2->_Foreground);
- VERIFY_ARE_EQUAL(ARGB(0, 7, 7, 7), scheme2->_Background);
- }
+ const auto scheme1 = winrt::get_self(colorSchemes.Lookup(L"Campbell Powershell"));
+ VERIFY_ARE_EQUAL(rgb(0xCC, 0xCC, 0xCC), scheme1->Foreground());
+ VERIFY_ARE_EQUAL(rgb(0x01, 0x24, 0x56), scheme1->Background());
}
void ColorSchemeTests::UpdateSchemeReferences()
{
- const std::string settingsString{ R"json({
- "defaultProfile": "Inherited reference",
- "profiles": {
- "defaults": {
- "colorScheme": "Scheme 1"
- },
- "list": [
- {
- "name": "Explicit scheme reference",
- "colorScheme": "Scheme 1"
- },
- {
- "name": "Explicit reference; hidden",
- "colorScheme": "Scheme 1",
- "hidden": true
- },
- {
- "name": "Inherited reference"
- },
- {
- "name": "Different reference",
- "colorScheme": "Scheme 2"
- }
- ]
- },
- "schemes": [
- { "name": "Scheme 1" },
- { "name": "Scheme 2" },
- { "name": "Scheme 1 (renamed)" }
- ]
- })json" };
+ static constexpr std::string_view settingsString{ R"json({
+ "defaultProfile": "Inherited reference",
+ "profiles": {
+ "defaults": {
+ "colorScheme": "Campbell"
+ },
+ "list": [
+ {
+ "name": "Explicit scheme reference",
+ "colorScheme": "Campbell"
+ },
+ {
+ "name": "Explicit reference; hidden",
+ "colorScheme": "Campbell",
+ "hidden": true
+ },
+ {
+ "name": "Inherited reference"
+ },
+ {
+ "name": "Different reference",
+ "colorScheme": "One Half Dark"
+ }
+ ]
+ },
+ "schemes": [
+ {
+ "background": "#0C0C0C",
+ "black": "#0C0C0C",
+ "blue": "#0037DA",
+ "brightBlack": "#767676",
+ "brightBlue": "#3B78FF",
+ "brightCyan": "#61D6D6",
+ "brightGreen": "#16C60C",
+ "brightPurple": "#B4009E",
+ "brightRed": "#E74856",
+ "brightWhite": "#F2F2F2",
+ "brightYellow": "#F9F1A5",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#3A96DD",
+ "foreground": "#CCCCCC",
+ "green": "#13A10E",
+ "name": "Campbell",
+ "purple": "#881798",
+ "red": "#C50F1F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#CCCCCC",
+ "yellow": "#C19C00"
+ },
+ {
+ "background": "#0C0C0C",
+ "black": "#0C0C0C",
+ "blue": "#0037DA",
+ "brightBlack": "#767676",
+ "brightBlue": "#3B78FF",
+ "brightCyan": "#61D6D6",
+ "brightGreen": "#16C60C",
+ "brightPurple": "#B4009E",
+ "brightRed": "#E74856",
+ "brightWhite": "#F2F2F2",
+ "brightYellow": "#F9F1A5",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#3A96DD",
+ "foreground": "#CCCCCC",
+ "green": "#13A10E",
+ "name": "Campbell (renamed)",
+ "purple": "#881798",
+ "red": "#C50F1F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#CCCCCC",
+ "yellow": "#C19C00"
+ },
+ {
+ "background": "#282C34",
+ "black": "#282C34",
+ "blue": "#61AFEF",
+ "brightBlack": "#5A6374",
+ "brightBlue": "#61AFEF",
+ "brightCyan": "#56B6C2",
+ "brightGreen": "#98C379",
+ "brightPurple": "#C678DD",
+ "brightRed": "#E06C75",
+ "brightWhite": "#DCDFE4",
+ "brightYellow": "#E5C07B",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#56B6C2",
+ "foreground": "#DCDFE4",
+ "green": "#98C379",
+ "name": "One Half Dark",
+ "purple": "#C678DD",
+ "red": "#E06C75",
+ "selectionBackground": "#FFFFFF",
+ "white": "#DCDFE4",
+ "yellow": "#E5C07B"
+ }
+ ]
+ })json" };
- auto settings{ winrt::make_self(false) };
- settings->_ParseJsonString(settingsString, false);
- settings->_ApplyDefaultsFromUserSettings();
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
+ const auto settings{ winrt::make_self(settingsString) };
- // update all references to "Scheme 1"
- const auto newName{ L"Scheme 1 (renamed)" };
- settings->UpdateColorSchemeReferences(L"Scheme 1", newName);
+ const auto newName{ L"Campbell (renamed)" };
+ settings->UpdateColorSchemeReferences(L"Campbell", newName);
- // verify profile defaults
- Log::Comment(L"Profile Defaults");
VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasColorSchemeName());
- // verify all other profiles
const auto& profiles{ settings->AllProfiles() };
{
const auto& prof{ profiles.GetAt(0) };
- Log::Comment(prof.Name().c_str());
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
}
{
const auto& prof{ profiles.GetAt(1) };
- Log::Comment(prof.Name().c_str());
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
}
{
const auto& prof{ profiles.GetAt(2) };
- Log::Comment(prof.Name().c_str());
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_FALSE(prof.DefaultAppearance().HasColorSchemeName());
}
{
const auto& prof{ profiles.GetAt(3) };
- Log::Comment(prof.Name().c_str());
- VERIFY_ARE_EQUAL(L"Scheme 2", prof.DefaultAppearance().ColorSchemeName());
+ VERIFY_ARE_EQUAL(L"One Half Dark", prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
}
}
diff --git a/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp b/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp
index e8ab845b3..b25d476e7 100644
--- a/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp
@@ -43,12 +43,6 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestLayerOnAutogeneratedName);
TEST_METHOD(TestGenerateCommandline);
-
- TEST_CLASS_SETUP(ClassSetup)
- {
- InitializeJsonReader();
- return true;
- }
};
void CommandTests::ManyCommandsSameAction()
diff --git a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp
index df4fcb9a2..7f6638954 100644
--- a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp
@@ -8,7 +8,6 @@
#include "JsonTestClass.h"
#include "TestUtils.h"
#include
-#include "../ut_app/TestDynamicProfileGenerator.h"
using namespace Microsoft::Console;
using namespace WEX::Logging;
@@ -44,61 +43,68 @@ namespace SettingsModelLocalTests
TEST_METHOD(LayerGlobalProperties);
TEST_METHOD(ValidateProfileOrdering);
TEST_METHOD(ValidateHideProfiles);
- TEST_METHOD(ValidateProfilesGenerateGuids);
- TEST_METHOD(GeneratedGuidRoundtrips);
- TEST_METHOD(TestAllValidationsWithNullGuids);
TEST_METHOD(TestReorderWithNullGuids);
TEST_METHOD(TestReorderingWithoutGuid);
TEST_METHOD(TestLayeringNameOnlyProfiles);
- TEST_METHOD(TestExplodingNameOnlyProfiles);
TEST_METHOD(TestHideAllProfiles);
TEST_METHOD(TestInvalidColorSchemeName);
TEST_METHOD(TestHelperFunctions);
-
TEST_METHOD(TestProfileBackgroundImageWithEnvVar);
TEST_METHOD(TestProfileBackgroundImageWithDesktopWallpaper);
-
TEST_METHOD(TestCloseOnExitParsing);
TEST_METHOD(TestCloseOnExitCompatibilityShim);
-
TEST_METHOD(TestLayerUserDefaultsBeforeProfiles);
TEST_METHOD(TestDontLayerGuidFromUserDefaults);
TEST_METHOD(TestLayerUserDefaultsOnDynamics);
-
TEST_METHOD(FindMissingProfile);
-
TEST_METHOD(ValidateKeybindingsWarnings);
-
TEST_METHOD(ValidateColorSchemeInCommands);
-
TEST_METHOD(ValidateExecuteCommandlineWarning);
-
- TEST_METHOD(ValidateLegacyGlobalsWarning);
-
TEST_METHOD(TestTrailingCommas);
-
TEST_METHOD(TestCommandsAndKeybindings);
-
TEST_METHOD(TestNestedCommandWithoutName);
TEST_METHOD(TestNestedCommandWithBadSubCommands);
TEST_METHOD(TestUnbindNestedCommand);
TEST_METHOD(TestRebindNestedCommand);
-
TEST_METHOD(TestCopy);
TEST_METHOD(TestCloneInheritanceTree);
-
TEST_METHOD(TestValidDefaults);
-
TEST_METHOD(TestInheritedCommand);
- TEST_CLASS_SETUP(ClassSetup)
+ private:
+ static winrt::com_ptr createSettings(const std::string_view& userJSON)
{
- InitializeJsonReader();
- return true;
+ static constexpr std::string_view inboxJSON{ R"({
+ "schemes": [
+ {
+ "name": "Campbell",
+ "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"
+ }
+ ]
+ })" };
+
+ return winrt::make_self(userJSON, inboxJSON);
}
- private:
- void _logCommandNames(winrt::Windows::Foundation::Collections::IMapView commands, const int indentation = 1)
+ static void _logCommandNames(winrt::Windows::Foundation::Collections::IMapView commands, const int indentation = 1)
{
if (indentation == 1)
{
@@ -125,7 +131,7 @@ namespace SettingsModelLocalTests
void DeserializationTests::ValidateProfilesExist()
{
- const std::string settingsWithProfiles{ R"(
+ static constexpr std::string_view settingsWithProfiles{ R"(
{
"profiles": [
{
@@ -134,30 +140,26 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string settingsWithoutProfiles{ R"(
+ static constexpr std::string_view settingsWithoutProfiles{ R"(
{
"defaultProfile": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}"
})" };
- const std::string settingsWithEmptyProfiles{ R"(
+ static constexpr std::string_view settingsWithEmptyProfiles{ R"(
{
"profiles": []
})" };
{
// Case 1: Good settings
- const auto settingsObject = VerifyParseSucceeded(settingsWithProfiles);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
- settings->_ValidateProfilesExist();
+ auto settings = winrt::make_self(settingsWithProfiles);
}
{
// Case 2: Bad settings
- const auto settingsObject = VerifyParseSucceeded(settingsWithoutProfiles);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
bool caughtExpectedException = false;
try
{
- settings->_ValidateProfilesExist();
+ auto settings = winrt::make_self(settingsWithoutProfiles);
}
catch (const implementation::SettingsException& ex)
{
@@ -168,12 +170,10 @@ namespace SettingsModelLocalTests
}
{
// Case 3: Bad settings
- const auto settingsObject = VerifyParseSucceeded(settingsWithEmptyProfiles);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
bool caughtExpectedException = false;
try
{
- settings->_ValidateProfilesExist();
+ auto settings = winrt::make_self(settingsWithEmptyProfiles);
}
catch (const implementation::SettingsException& ex)
{
@@ -186,7 +186,7 @@ namespace SettingsModelLocalTests
void DeserializationTests::ValidateDefaultProfileExists()
{
- const std::string goodProfiles{ R"(
+ static constexpr std::string_view goodProfiles{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -201,7 +201,7 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string badProfiles{ R"(
+ static constexpr std::string_view badProfiles{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -216,22 +216,7 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string noDefaultAtAll{ R"(
- {
- "alwaysShowTabs": true,
- "profiles": [
- {
- "name" : "profile0",
- "guid": "{6239a42c-5555-49a3-80bd-e8fdd045185c}"
- },
- {
- "name" : "profile1",
- "guid": "{6239a42c-6666-49a3-80bd-e8fdd045185c}"
- }
- ]
- })" };
-
- const std::string goodProfilesSpecifiedByName{ R"(
+ static constexpr std::string_view goodProfilesSpecifiedByName{ R"(
{
"defaultProfile": "profile1",
"profiles": [
@@ -250,87 +235,47 @@ namespace SettingsModelLocalTests
// Case 1: Good settings
Log::Comment(NoThrowString().Format(
L"Testing a pair of profiles with unique guids, and the defaultProfile is one of those guids"));
- const auto settingsObject = VerifyParseSucceeded(goodProfiles);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
- settings->_ResolveDefaultProfile();
- settings->_ValidateDefaultProfileExists();
- VERIFY_ARE_EQUAL(static_cast(0), settings->_warnings.Size());
- VERIFY_ARE_EQUAL(static_cast(2), settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_allProfiles.GetAt(0).Guid());
+ const auto settings = createSettings(goodProfiles);
+ VERIFY_ARE_EQUAL(static_cast(0), settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(static_cast(2), settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
}
{
// Case 2: Bad settings
Log::Comment(NoThrowString().Format(
L"Testing a pair of profiles with unique guids, but the defaultProfile is NOT one of those guids"));
- const auto settingsObject = VerifyParseSucceeded(badProfiles);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
- settings->_ResolveDefaultProfile();
- settings->_ValidateDefaultProfileExists();
- VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.GetAt(0));
+ const auto settings = createSettings(badProfiles);
+ VERIFY_ARE_EQUAL(static_cast(1), settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->Warnings().GetAt(0));
- VERIFY_ARE_EQUAL(static_cast(2), settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_allProfiles.GetAt(0).Guid());
+ VERIFY_ARE_EQUAL(static_cast(2), settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
}
{
// Case 2: Bad settings
Log::Comment(NoThrowString().Format(
L"Testing a pair of profiles with unique guids, and no defaultProfile at all"));
- const auto settingsObject = VerifyParseSucceeded(badProfiles);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
- settings->_ResolveDefaultProfile();
- settings->_ValidateDefaultProfileExists();
- VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.GetAt(0));
+ const auto settings = createSettings(badProfiles);
+ VERIFY_ARE_EQUAL(static_cast(1), settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->Warnings().GetAt(0));
- VERIFY_ARE_EQUAL(static_cast(2), settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_allProfiles.GetAt(0).Guid());
+ VERIFY_ARE_EQUAL(static_cast(2), settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
}
{
// Case 4: Good settings, default profile is a string
Log::Comment(NoThrowString().Format(
L"Testing a pair of profiles with unique guids, and the defaultProfile is one of the profile names"));
- const auto settingsObject = VerifyParseSucceeded(goodProfilesSpecifiedByName);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
- settings->_ResolveDefaultProfile();
- settings->_ValidateDefaultProfileExists();
- VERIFY_ARE_EQUAL(static_cast(0), settings->_warnings.Size());
- VERIFY_ARE_EQUAL(static_cast(2), settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_allProfiles.GetAt(1).Guid());
+ const auto settings = createSettings(goodProfilesSpecifiedByName);
+ VERIFY_ARE_EQUAL(static_cast(0), settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(static_cast(2), settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(1).Guid());
}
}
void DeserializationTests::ValidateDuplicateProfiles()
{
- const std::string goodProfiles{ R"(
- {
- "profiles": [
- {
- "name" : "profile0",
- "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
- },
- {
- "name" : "profile0",
- "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
- }
- ]
- })" };
-
- const std::string badProfiles{ R"(
- {
- "profiles": [
- {
- "name" : "profile0",
- "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
- },
- {
- "name" : "profile1",
- "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
- }
- ]
- })" };
-
- const std::string veryBadProfiles{ R"(
+ static constexpr std::string_view veryBadProfiles{ R"(
{
"profiles": [
{
@@ -363,82 +308,22 @@ namespace SettingsModelLocalTests
}
]
})" };
- Profile profile0 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-4444-49a3-80bd-e8fdd045185c}"));
- profile0.Name(L"profile0");
- Profile profile1 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-5555-49a3-80bd-e8fdd045185c}"));
- profile1.Name(L"profile1");
- Profile profile2 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-4444-49a3-80bd-e8fdd045185c}"));
- profile2.Name(L"profile2");
- Profile profile3 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-4444-49a3-80bd-e8fdd045185c}"));
- profile3.Name(L"profile3");
- Profile profile4 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-6666-49a3-80bd-e8fdd045185c}"));
- profile4.Name(L"profile4");
- Profile profile5 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-5555-49a3-80bd-e8fdd045185c}"));
- profile5.Name(L"profile5");
- Profile profile6 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-7777-49a3-80bd-e8fdd045185c}"));
- profile6.Name(L"profile6");
- {
- // Case 1: Good settings
- Log::Comment(NoThrowString().Format(
- L"Testing a pair of profiles with unique guids"));
+ const auto settings = createSettings(veryBadProfiles);
- auto settings = winrt::make_self();
- settings->_allProfiles.Append(profile0);
- settings->_allProfiles.Append(profile1);
+ VERIFY_ARE_EQUAL(static_cast(1), settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::DuplicateProfile, settings->Warnings().GetAt(0));
- settings->_ValidateNoDuplicateProfiles();
-
- VERIFY_ARE_EQUAL(static_cast(0), settings->_warnings.Size());
- VERIFY_ARE_EQUAL(static_cast(2), settings->_allProfiles.Size());
- }
- {
- // Case 2: Bad settings
- Log::Comment(NoThrowString().Format(
- L"Testing a pair of profiles with the same guid"));
-
- auto settings = winrt::make_self();
- settings->_allProfiles.Append(profile2);
- settings->_allProfiles.Append(profile3);
-
- settings->_ValidateNoDuplicateProfiles();
-
- VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::DuplicateProfile, settings->_warnings.GetAt(0));
-
- VERIFY_ARE_EQUAL(static_cast(1), settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(0).Name());
- }
- {
- // Case 3: Very bad settings
- Log::Comment(NoThrowString().Format(
- L"Testing a set of profiles, many of which with duplicated guids"));
-
- auto settings = winrt::make_self();
- settings->_allProfiles.Append(profile0);
- settings->_allProfiles.Append(profile1);
- settings->_allProfiles.Append(profile2);
- settings->_allProfiles.Append(profile3);
- settings->_allProfiles.Append(profile4);
- settings->_allProfiles.Append(profile5);
- settings->_allProfiles.Append(profile6);
-
- settings->_ValidateNoDuplicateProfiles();
-
- VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::DuplicateProfile, settings->_warnings.GetAt(0));
-
- VERIFY_ARE_EQUAL(static_cast(4), settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"profile6", settings->_allProfiles.GetAt(3).Name());
- }
+ VERIFY_ARE_EQUAL(static_cast(4), settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"profile1", settings->AllProfiles().GetAt(1).Name());
+ VERIFY_ARE_EQUAL(L"profile4", settings->AllProfiles().GetAt(2).Name());
+ VERIFY_ARE_EQUAL(L"profile6", settings->AllProfiles().GetAt(3).Name());
}
void DeserializationTests::ValidateManyWarnings()
{
- const std::string badProfiles{ R"(
+ static constexpr std::string_view badProfiles{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -453,72 +338,56 @@ namespace SettingsModelLocalTests
{
"name" : "profile2",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
+ },
+ {
+ "name" : "profile3",
+ "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}"
+ },
+ {
+ "name" : "profile4",
+ "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}"
}
]
})" };
- Profile profile4 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-4444-49a3-80bd-e8fdd045185c}"));
- profile4.Name(L"profile4");
- Profile profile5 = winrt::make(::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-4444-49a3-80bd-e8fdd045185c}"));
- profile5.Name(L"profile5");
- // Case 2: Bad settings
- Log::Comment(NoThrowString().Format(
- L"Testing a pair of profiles with the same guid"));
- const auto settingsObject = VerifyParseSucceeded(badProfiles);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
+ const auto settings = createSettings(badProfiles);
- settings->_allProfiles.Append(profile4);
- settings->_allProfiles.Append(profile5);
+ VERIFY_ARE_EQUAL(2u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::DuplicateProfile, settings->Warnings().GetAt(0));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->Warnings().GetAt(1));
- settings->_ValidateSettings();
-
- VERIFY_ARE_EQUAL(3u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::DuplicateProfile, settings->_warnings.GetAt(0));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.GetAt(1));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::UnknownColorScheme, settings->_warnings.GetAt(2));
-
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_allProfiles.GetAt(0).Guid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(2).HasGuid());
+ VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Guid(), settings->GlobalSettings().DefaultProfile());
}
void DeserializationTests::LayerGlobalProperties()
{
- const std::string settings0String{ R"(
- {
+ static constexpr std::string_view inboxSettings{ R"({
"alwaysShowTabs": true,
"initialCols" : 120,
"initialRows" : 30
})" };
- const std::string settings1String{ R"(
- {
+ static constexpr std::string_view userSettings{ R"({
"showTabsInTitlebar": false,
"initialCols" : 240,
- "initialRows" : 60
+ "initialRows" : 60,
+ "profiles": [
+ {
+ "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
+ }
+ ]
})" };
- const auto settings0Json = VerifyParseSucceeded(settings0String);
- const auto settings1Json = VerifyParseSucceeded(settings1String);
- auto settings = winrt::make_self();
-
- settings->LayerJson(settings0Json);
- VERIFY_ARE_EQUAL(true, settings->_globals->AlwaysShowTabs());
- VERIFY_ARE_EQUAL(120, settings->_globals->InitialCols());
- VERIFY_ARE_EQUAL(30, settings->_globals->InitialRows());
- VERIFY_ARE_EQUAL(true, settings->_globals->ShowTabsInTitlebar());
-
- settings->LayerJson(settings1Json);
- VERIFY_ARE_EQUAL(true, settings->_globals->AlwaysShowTabs());
- VERIFY_ARE_EQUAL(240, settings->_globals->InitialCols());
- VERIFY_ARE_EQUAL(60, settings->_globals->InitialRows());
- VERIFY_ARE_EQUAL(false, settings->_globals->ShowTabsInTitlebar());
+ const auto settings = winrt::make_self(userSettings, inboxSettings);
+ VERIFY_ARE_EQUAL(true, settings->GlobalSettings().AlwaysShowTabs());
+ VERIFY_ARE_EQUAL(240, settings->GlobalSettings().InitialCols());
+ VERIFY_ARE_EQUAL(60, settings->GlobalSettings().InitialRows());
+ VERIFY_ARE_EQUAL(false, settings->GlobalSettings().ShowTabsInTitlebar());
}
void DeserializationTests::ValidateProfileOrdering()
{
- const std::string userProfiles0String{ R"(
+ static constexpr std::string_view userProfiles0String{ R"(
{
"profiles": [
{
@@ -532,7 +401,7 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string defaultProfilesString{ R"(
+ static constexpr std::string_view defaultProfilesString{ R"(
{
"profiles": [
{
@@ -546,7 +415,7 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string userProfiles1String{ R"(
+ static constexpr std::string_view userProfiles1String{ R"(
{
"profiles": [
{
@@ -560,63 +429,32 @@ namespace SettingsModelLocalTests
]
})" };
- const auto userProfiles0Json = VerifyParseSucceeded(userProfiles0String);
- const auto userProfiles1Json = VerifyParseSucceeded(userProfiles1String);
- const auto defaultProfilesJson = VerifyParseSucceeded(defaultProfilesString);
-
{
Log::Comment(NoThrowString().Format(
L"Case 1: Simple swapping of the ordering. The user has the "
L"default profiles in the opposite order of the default ordering."));
- auto settings = winrt::make_self();
- settings->_ParseJsonString(defaultProfilesString, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(1).Name());
-
- settings->_ParseJsonString(userProfiles0String, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(1).Name());
-
- settings->_ReorderProfilesToMatchUserSettingsOrder();
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(1).Name());
+ const auto settings = winrt::make_self(userProfiles0String, defaultProfilesString);
+ VERIFY_ARE_EQUAL(2u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"profile1", settings->AllProfiles().GetAt(1).Name());
}
{
Log::Comment(NoThrowString().Format(
L"Case 2: Make sure all the user's profiles appear before the defaults."));
- auto settings = winrt::make_self();
- settings->_ParseJsonString(defaultProfilesString, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(1).Name());
-
- settings->_ParseJsonString(userProfiles1String, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"profile5", settings->_allProfiles.GetAt(2).Name());
-
- settings->_ReorderProfilesToMatchUserSettingsOrder();
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile5", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(2).Name());
+ const auto settings = winrt::make_self(userProfiles1String, defaultProfilesString);
+ VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(L"profile4", settings->AllProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"profile5", settings->AllProfiles().GetAt(1).Name());
+ VERIFY_ARE_EQUAL(L"profile2", settings->AllProfiles().GetAt(2).Name());
}
}
void DeserializationTests::ValidateHideProfiles()
{
- const std::string defaultProfilesString{ R"(
+ static constexpr std::string_view defaultProfilesString{ R"(
{
"profiles": [
{
@@ -630,7 +468,7 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string userProfiles0String{ R"(
+ static constexpr std::string_view userProfiles0String{ R"(
{
"profiles": [
{
@@ -645,7 +483,7 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string userProfiles1String{ R"(
+ static constexpr std::string_view userProfiles1String{ R"(
{
"profiles": [
{
@@ -665,237 +503,28 @@ namespace SettingsModelLocalTests
]
})" };
- const auto userProfiles0Json = VerifyParseSucceeded(userProfiles0String);
- const auto userProfiles1Json = VerifyParseSucceeded(userProfiles1String);
- const auto defaultProfilesJson = VerifyParseSucceeded(defaultProfilesString);
-
{
- auto settings = winrt::make_self();
- settings->_ParseJsonString(defaultProfilesString, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(false, settings->_allProfiles.GetAt(0).Hidden());
- VERIFY_ARE_EQUAL(false, settings->_allProfiles.GetAt(1).Hidden());
-
- settings->_ParseJsonString(userProfiles0String, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(false, settings->_allProfiles.GetAt(0).Hidden());
- VERIFY_ARE_EQUAL(true, settings->_allProfiles.GetAt(1).Hidden());
-
- settings->_ReorderProfilesToMatchUserSettingsOrder();
- settings->_UpdateActiveProfiles();
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(1u, settings->_activeProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile1", settings->_activeProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(false, settings->_activeProfiles.GetAt(0).Hidden());
+ const auto settings = winrt::make_self(userProfiles0String, defaultProfilesString);
+ VERIFY_ARE_EQUAL(2u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(1u, settings->ActiveProfiles().Size());
+ VERIFY_ARE_EQUAL(L"profile1", settings->ActiveProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(false, settings->ActiveProfiles().GetAt(0).Hidden());
}
{
- auto settings = winrt::make_self();
- settings->_ParseJsonString(defaultProfilesString, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(false, settings->_allProfiles.GetAt(0).Hidden());
- VERIFY_ARE_EQUAL(false, settings->_allProfiles.GetAt(1).Hidden());
-
- settings->_ParseJsonString(userProfiles1String, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"profile5", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"profile6", settings->_allProfiles.GetAt(3).Name());
- VERIFY_ARE_EQUAL(false, settings->_allProfiles.GetAt(0).Hidden());
- VERIFY_ARE_EQUAL(true, settings->_allProfiles.GetAt(1).Hidden());
- VERIFY_ARE_EQUAL(false, settings->_allProfiles.GetAt(2).Hidden());
- VERIFY_ARE_EQUAL(true, settings->_allProfiles.GetAt(3).Hidden());
-
- settings->_ReorderProfilesToMatchUserSettingsOrder();
- settings->_UpdateActiveProfiles();
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(2u, settings->_activeProfiles.Size());
- VERIFY_ARE_EQUAL(L"profile5", settings->_activeProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile2", settings->_activeProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(false, settings->_activeProfiles.GetAt(0).Hidden());
- VERIFY_ARE_EQUAL(false, settings->_activeProfiles.GetAt(1).Hidden());
+ const auto settings = winrt::make_self(userProfiles1String, defaultProfilesString);
+ VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(2u, settings->ActiveProfiles().Size());
+ VERIFY_ARE_EQUAL(L"profile5", settings->ActiveProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"profile2", settings->ActiveProfiles().GetAt(1).Name());
+ VERIFY_ARE_EQUAL(false, settings->ActiveProfiles().GetAt(0).Hidden());
+ VERIFY_ARE_EQUAL(false, settings->ActiveProfiles().GetAt(1).Hidden());
}
}
- void DeserializationTests::ValidateProfilesGenerateGuids()
- {
- const std::string profile0String{ R"(
- {
- "name" : "profile0"
- })" };
- const std::string profile1String{ R"(
- {
- "name" : "profile1"
- })" };
- const std::string profile2String{ R"(
- {
- "name" : "profile2",
- "guid" : null
- })" };
- const std::string profile3String{ R"(
- {
- "name" : "profile3",
- "guid" : "{00000000-0000-0000-0000-000000000000}"
- })" };
- const std::string profile4String{ R"(
- {
- "name" : "profile4",
- "guid" : "{6239a42c-1de4-49a3-80bd-e8fdd045185c}"
- })" };
- const std::string profile5String{ R"(
- {
- "name" : "profile2"
- })" };
-
- const auto profile0Json = VerifyParseSucceeded(profile0String);
- const auto profile1Json = VerifyParseSucceeded(profile1String);
- const auto profile2Json = VerifyParseSucceeded(profile2String);
- const auto profile3Json = VerifyParseSucceeded(profile3String);
- const auto profile4Json = VerifyParseSucceeded(profile4String);
- const auto profile5Json = VerifyParseSucceeded(profile5String);
-
- const auto profile0 = implementation::Profile::FromJson(profile0Json);
- const auto profile1 = implementation::Profile::FromJson(profile1Json);
- const auto profile2 = implementation::Profile::FromJson(profile2Json);
- const auto profile3 = implementation::Profile::FromJson(profile3Json);
- const auto profile4 = implementation::Profile::FromJson(profile4Json);
- const auto profile5 = implementation::Profile::FromJson(profile5Json);
-
- const winrt::guid cmdGuid{ Utils::GuidFromString(L"{6239a42c-1de4-49a3-80bd-e8fdd045185c}") };
- const winrt::guid nullGuid{};
-
- VERIFY_IS_FALSE(profile0->HasGuid());
- VERIFY_IS_FALSE(profile1->HasGuid());
- VERIFY_IS_FALSE(profile2->HasGuid());
- VERIFY_IS_TRUE(profile3->HasGuid());
- VERIFY_IS_TRUE(profile4->HasGuid());
- VERIFY_IS_FALSE(profile5->HasGuid());
-
- VERIFY_ARE_EQUAL(profile3->Guid(), nullGuid);
- VERIFY_ARE_EQUAL(profile4->Guid(), cmdGuid);
-
- auto settings = winrt::make_self();
- settings->_allProfiles.Append(*profile0);
- settings->_allProfiles.Append(*profile1);
- settings->_allProfiles.Append(*profile2);
- settings->_allProfiles.Append(*profile3);
- settings->_allProfiles.Append(*profile4);
- settings->_allProfiles.Append(*profile5);
-
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(4).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(5).HasGuid());
-
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(0).Guid(), nullGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(1).Guid(), nullGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(2).Guid(), nullGuid);
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(3).Guid(), nullGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(4).Guid(), nullGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(5).Guid(), nullGuid);
-
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(0).Guid(), cmdGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(1).Guid(), cmdGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(2).Guid(), cmdGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(3).Guid(), cmdGuid);
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(4).Guid(), cmdGuid);
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(5).Guid(), cmdGuid);
-
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(0).Guid(), settings->_allProfiles.GetAt(2).Guid());
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(1).Guid(), settings->_allProfiles.GetAt(2).Guid());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(2).Guid(), settings->_allProfiles.GetAt(2).Guid());
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(3).Guid(), settings->_allProfiles.GetAt(2).Guid());
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(4).Guid(), settings->_allProfiles.GetAt(2).Guid());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(5).Guid(), settings->_allProfiles.GetAt(2).Guid());
- }
-
- void DeserializationTests::GeneratedGuidRoundtrips()
- {
- // Parse a profile without a guid.
- // We should automatically generate a GUID for that profile.
- // When that profile is serialized and deserialized again, the GUID we
- // generated for it should persist.
- const std::string profileWithoutGuid{ R"({
- "name" : "profile0"
- })" };
- const auto profile0Json = VerifyParseSucceeded(profileWithoutGuid);
-
- const auto profile0 = implementation::Profile::FromJson(profile0Json);
- const GUID nullGuid{ 0 };
-
- VERIFY_IS_FALSE(profile0->HasGuid());
-
- const auto serialized0Profile = profile0->GenerateStub();
- const auto profile1 = implementation::Profile::FromJson(serialized0Profile);
- VERIFY_IS_FALSE(profile0->HasGuid());
- VERIFY_IS_TRUE(profile1->HasGuid());
-
- auto settings = winrt::make_self();
- settings->_allProfiles.Append(*profile1);
-
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
-
- const auto profileImpl = winrt::get_self(settings->_allProfiles.GetAt(0));
- const auto serialized1Profile = profileImpl->GenerateStub();
-
- const auto profile2 = implementation::Profile::FromJson(serialized1Profile);
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(profile2->HasGuid());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(0).Guid(), profile2->Guid());
- }
-
- void DeserializationTests::TestAllValidationsWithNullGuids()
- {
- const std::string settings0String{ R"(
- {
- "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
- "profiles": [
- {
- "name" : "profile0",
- "guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
- },
- {
- "name" : "profile1"
- }
- ],
- "schemes": [
- { "name": "Campbell" }
- ]
- })" };
-
- const auto settings0Json = VerifyParseSucceeded(settings0String);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
-
- settings->_ValidateSettings();
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
- }
-
void DeserializationTests::TestReorderWithNullGuids()
{
- const std::string settings0String{ R"(
+ static constexpr std::string_view settings0String{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -913,41 +542,18 @@ namespace SettingsModelLocalTests
]
})" };
- const auto settings0Json = VerifyParseSucceeded(settings0String);
+ const auto settings = winrt::make_self(settings0String, DefaultJson);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(DefaultJson, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(1).Name());
-
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"cmdFromUserSettings", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(3).Name());
-
- settings->_ValidateSettings();
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"cmdFromUserSettings", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(3).Name());
+ VERIFY_ARE_EQUAL(0u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(0).HasGuid());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(1).HasGuid());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(2).HasGuid());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(3).HasGuid());
+ VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"profile1", settings->AllProfiles().GetAt(1).Name());
+ VERIFY_ARE_EQUAL(L"cmdFromUserSettings", settings->AllProfiles().GetAt(2).Name());
+ VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->AllProfiles().GetAt(3).Name());
}
void DeserializationTests::TestReorderingWithoutGuid()
@@ -961,7 +567,7 @@ namespace SettingsModelLocalTests
L" about this scenario specifically that causes a crash, when "
L" TestReorderWithNullGuids did _not_."));
- const std::string settings0String{ R"(
+ static constexpr std::string_view settings0String{ R"(
{
"defaultProfile" : "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"profiles": [
@@ -1014,41 +620,18 @@ namespace SettingsModelLocalTests
]
})" };
- const auto settings0Json = VerifyParseSucceeded(settings0String);
+ const auto settings = winrt::make_self(settings0String, DefaultJson);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(DefaultJson, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(1).Name());
-
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileShouldNotCrash", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"Ubuntu", settings->_allProfiles.GetAt(3).Name());
-
- settings->_ValidateSettings();
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileShouldNotCrash", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"Ubuntu", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(3).Name());
+ VERIFY_ARE_EQUAL(0u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(0).HasGuid());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(1).HasGuid());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(2).HasGuid());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(3).HasGuid());
+ VERIFY_ARE_EQUAL(L"Command Prompt", settings->AllProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"ThisProfileShouldNotCrash", settings->AllProfiles().GetAt(1).Name());
+ VERIFY_ARE_EQUAL(L"Ubuntu", settings->AllProfiles().GetAt(2).Name());
+ VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->AllProfiles().GetAt(3).Name());
}
void DeserializationTests::TestLayeringNameOnlyProfiles()
@@ -1057,14 +640,13 @@ namespace SettingsModelLocalTests
// profile, it should only layer with other name-only profiles with the
// _same name_
- const std::string settings0String{ R"(
+ static constexpr std::string_view settings0String{ R"(
{
"defaultProfile" : "{00000000-0000-5f56-a8ff-afceeeaa6101}",
"profiles": [
{
"guid" : "{00000000-0000-5f56-a8ff-afceeeaa6101}",
"name" : "ThisProfileIsGood"
-
},
{
"name" : "ThisProfileShouldNotLayer"
@@ -1075,141 +657,19 @@ namespace SettingsModelLocalTests
]
})" };
- const auto settings0Json = VerifyParseSucceeded(settings0String);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(DefaultJson, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(1).Name());
-
- Log::Comment(NoThrowString().Format(
- L"Parse the user settings"));
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(5u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(4).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileIsGood", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileShouldNotLayer", settings->_allProfiles.GetAt(3).Name());
- VERIFY_ARE_EQUAL(L"NeitherShouldThisOne", settings->_allProfiles.GetAt(4).Name());
- }
-
- void DeserializationTests::TestExplodingNameOnlyProfiles()
- {
- // This is a test for GH#2782. When we add a name-only profile, we'll
- // generate a GUID for it. We should make sure that we don't re-append
- // that profile to the list of profiles.
-
- const std::string settings0String{ R"(
- {
- "defaultProfile" : "{00000000-0000-5f56-a8ff-afceeeaa6101}",
- "profiles": [
- {
- "guid" : "{00000000-0000-5f56-a8ff-afceeeaa6101}",
- "name" : "ThisProfileIsGood"
-
- },
- {
- "name" : "ThisProfileShouldNotDuplicate"
- },
- {
- "name" : "NeitherShouldThisOne"
- }
- ]
- })" };
-
- const auto settings0Json = VerifyParseSucceeded(settings0String);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(DefaultJson, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(1).Name());
-
- Log::Comment(NoThrowString().Format(
- L"Parse the user settings"));
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(5u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(4).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileIsGood", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileShouldNotDuplicate", settings->_allProfiles.GetAt(3).Name());
- VERIFY_ARE_EQUAL(L"NeitherShouldThisOne", settings->_allProfiles.GetAt(4).Name());
-
- Log::Comment(NoThrowString().Format(
- L"Pretend like we're checking to append dynamic profiles to the "
- L"user's settings file. We absolutely _shouldn't_ be adding anything here."));
- bool const needToWriteFile = settings->_AppendDynamicProfilesToUserSettings();
- VERIFY_IS_FALSE(needToWriteFile);
- VERIFY_ARE_EQUAL(settings0String.size(), settings->_userSettingsString.size());
-
- Log::Comment(NoThrowString().Format(
- L"Re-parse the settings file. We should have the _same_ settings as before."));
- Log::Comment(NoThrowString().Format(
- L"Do this to a _new_ settings object, to make sure it turns out the same."));
- {
- auto settings2 = winrt::make_self();
- settings2->_ParseJsonString(DefaultJson, true);
- settings2->LayerJson(settings2->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings2->_allProfiles.Size());
- // Initialize the second settings object from the first settings
- // object's settings string, the one that we synthesized.
- const auto firstSettingsString = settings->_userSettingsString;
- settings2->_ParseJsonString(firstSettingsString, false);
- settings2->LayerJson(settings2->_userSettings);
- VERIFY_ARE_EQUAL(5u, settings2->_allProfiles.Size());
- VERIFY_IS_TRUE(settings2->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_TRUE(settings2->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_TRUE(settings2->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_FALSE(settings2->_allProfiles.GetAt(3).HasGuid());
- VERIFY_IS_FALSE(settings2->_allProfiles.GetAt(4).HasGuid());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings2->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings2->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileIsGood", settings2->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileShouldNotDuplicate", settings2->_allProfiles.GetAt(3).Name());
- VERIFY_ARE_EQUAL(L"NeitherShouldThisOne", settings2->_allProfiles.GetAt(4).Name());
- }
-
- Log::Comment(NoThrowString().Format(
- L"Validate the settings. All the profiles we have should be valid."));
- settings->_ValidateSettings();
-
- VERIFY_ARE_EQUAL(5u, settings->_allProfiles.Size());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(2).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(3).HasGuid());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(4).HasGuid());
- VERIFY_ARE_EQUAL(L"ThisProfileIsGood", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"ThisProfileShouldNotDuplicate", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"NeitherShouldThisOne", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"Windows PowerShell", settings->_allProfiles.GetAt(3).Name());
- VERIFY_ARE_EQUAL(L"Command Prompt", settings->_allProfiles.GetAt(4).Name());
+ const auto settings = winrt::make_self(settings0String, DefaultJson);
+ const auto profiles = settings->AllProfiles();
+ VERIFY_ARE_EQUAL(5u, profiles.Size());
+ VERIFY_ARE_EQUAL(L"ThisProfileIsGood", profiles.GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"ThisProfileShouldNotLayer", profiles.GetAt(1).Name());
+ VERIFY_ARE_EQUAL(L"NeitherShouldThisOne", profiles.GetAt(2).Name());
+ VERIFY_ARE_EQUAL(L"Windows PowerShell", profiles.GetAt(3).Name());
+ VERIFY_ARE_EQUAL(L"Command Prompt", profiles.GetAt(4).Name());
}
void DeserializationTests::TestHideAllProfiles()
{
- const std::string settingsWithProfiles{ R"(
+ static constexpr std::string_view settingsWithProfiles{ R"(
{
"profiles": [
{
@@ -1223,7 +683,7 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string settingsWithoutProfiles{ R"(
+ static constexpr std::string_view settingsWithoutProfiles{ R"(
{
"profiles": [
{
@@ -1237,38 +697,17 @@ namespace SettingsModelLocalTests
]
})" };
- VerifyParseSucceeded(settingsWithProfiles);
- VerifyParseSucceeded(settingsWithoutProfiles);
-
{
// Case 1: Good settings
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsWithProfiles, false);
- settings->LayerJson(settings->_userSettings);
-
- settings->_UpdateActiveProfiles();
- Log::Comment(NoThrowString().Format(
- L"settingsWithProfiles successfully parsed and validated"));
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(1u, settings->_activeProfiles.Size());
+ const auto settings = createSettings(settingsWithProfiles);
+ VERIFY_ARE_EQUAL(2u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(1u, settings->ActiveProfiles().Size());
}
{
// Case 2: Bad settings
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsWithoutProfiles, false);
- settings->LayerJson(settings->_userSettings);
-
- bool caughtExpectedException = false;
- try
- {
- settings->_UpdateActiveProfiles();
- }
- catch (const implementation::SettingsException& ex)
- {
- VERIFY_IS_TRUE(ex.Error() == SettingsLoadErrors::AllProfilesHidden);
- caughtExpectedException = true;
- }
- VERIFY_IS_TRUE(caughtExpectedException);
+ VERIFY_THROWS_SPECIFIC(winrt::make_self(settingsWithoutProfiles), const implementation::SettingsException, [](const auto& ex) {
+ return ex.Error() == SettingsLoadErrors::AllProfilesHidden;
+ });
}
}
@@ -1277,12 +716,11 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Ensure that setting a profile's scheme to a non-existent scheme causes a warning."));
- const std::string settings0String{ R"(
- {
+ static constexpr std::string_view settings0String{ R"({
"profiles": [
{
"name" : "profile0",
- "colorScheme": "schemeOne"
+ "colorScheme": "Campbell"
},
{
"name" : "profile1",
@@ -1292,43 +730,19 @@ namespace SettingsModelLocalTests
"name" : "profile2"
// Will use the Profile default value, "Campbell"
}
- ],
- "schemes": [
- {
- "name": "schemeOne",
- "foreground": "#111111"
- },
- {
- "name": "schemeTwo",
- "foreground": "#222222"
- }
]
})" };
- VerifyParseSucceeded(settings0String);
+ const auto settings = createSettings(settings0String);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
+ VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::UnknownColorScheme, settings->Warnings().GetAt(0));
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
-
- VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName());
- VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName());
- VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName());
-
- settings->_ValidateAllSchemesExist();
-
- VERIFY_ARE_EQUAL(1u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::UnknownColorScheme, settings->_warnings.GetAt(0));
-
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
-
- VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName());
- VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName());
- VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName());
+ VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
+ for (const auto& profile : settings->AllProfiles())
+ {
+ VERIFY_ARE_EQUAL(L"Campbell", profile.DefaultAppearance().ColorSchemeName());
+ }
}
void DeserializationTests::ValidateColorSchemeInCommands()
@@ -1336,23 +750,17 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Ensure that setting a command's color scheme to a non-existent scheme causes a warning."));
- const std::string settings0String{ R"(
+ static constexpr std::string_view settings0String{ R"(
{
"profiles": [
{
"name" : "profile0",
- "colorScheme": "schemeOne"
- }
- ],
- "schemes": [
- {
- "name": "schemeOne",
- "foreground": "#111111"
+ "colorScheme": "Campbell"
}
],
"actions": [
{
- "command": { "action": "setColorScheme", "colorScheme": "schemeOne" }
+ "command": { "action": "setColorScheme", "colorScheme": "Campbell" }
},
{
"command": { "action": "setColorScheme", "colorScheme": "invalidScheme" }
@@ -1360,23 +768,17 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string settings1String{ R"(
+ static constexpr std::string_view settings1String{ R"(
{
"profiles": [
{
"name" : "profile0",
- "colorScheme": "schemeOne"
- }
- ],
- "schemes": [
- {
- "name": "schemeOne",
- "foreground": "#111111"
+ "colorScheme": "Campbell"
}
],
"actions": [
{
- "command": { "action": "setColorScheme", "colorScheme": "schemeOne" }
+ "command": { "action": "setColorScheme", "colorScheme": "Campbell" }
},
{
"name": "parent",
@@ -1387,23 +789,17 @@ namespace SettingsModelLocalTests
]
})" };
- const std::string settings2String{ R"(
+ static constexpr std::string_view settings2String{ R"(
{
"profiles": [
{
"name" : "profile0",
- "colorScheme": "schemeOne"
- }
- ],
- "schemes": [
- {
- "name": "schemeOne",
- "foreground": "#111111"
+ "colorScheme": "Campbell"
}
],
"actions": [
{
- "command": { "action": "setColorScheme", "colorScheme": "schemeOne" }
+ "command": { "action": "setColorScheme", "colorScheme": "Campbell" }
},
{
"name": "grandparent",
@@ -1425,49 +821,37 @@ namespace SettingsModelLocalTests
// Case 1: setColorScheme command with invalid scheme
Log::Comment(NoThrowString().Format(
L"Testing a simple command with invalid scheme"));
- VerifyParseSucceeded(settings0String);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateColorSchemesInCommands();
+ const auto settings = createSettings(settings0String);
- VERIFY_ARE_EQUAL(1u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::InvalidColorSchemeInCmd, settings->_warnings.GetAt(0));
+ VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::InvalidColorSchemeInCmd, settings->Warnings().GetAt(0));
}
{
// Case 2: nested setColorScheme command with invalid scheme
Log::Comment(NoThrowString().Format(
L"Testing a nested command with invalid scheme"));
- VerifyParseSucceeded(settings1String);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settings1String, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateColorSchemesInCommands();
+ const auto settings = createSettings(settings1String);
- VERIFY_ARE_EQUAL(1u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::InvalidColorSchemeInCmd, settings->_warnings.GetAt(0));
+ VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::InvalidColorSchemeInCmd, settings->Warnings().GetAt(0));
}
{
// Case 3: nested-in-nested setColorScheme command with invalid scheme
Log::Comment(NoThrowString().Format(
L"Testing a nested-in-nested command with invalid scheme"));
- VerifyParseSucceeded(settings2String);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settings2String, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateColorSchemesInCommands();
+ const auto settings = createSettings(settings2String);
- VERIFY_ARE_EQUAL(1u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::InvalidColorSchemeInCmd, settings->_warnings.GetAt(0));
+ VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::InvalidColorSchemeInCmd, settings->Warnings().GetAt(0));
}
}
void DeserializationTests::TestHelperFunctions()
{
- const std::string settings0String{ R"(
+ static constexpr std::string_view settings0String{ R"(
{
"defaultProfile" : "{2C4DE342-38B7-51CF-B940-2309A097F518}",
"profiles": [
@@ -1489,48 +873,37 @@ namespace SettingsModelLocalTests
]
})" };
- auto name0{ L"profile0" };
- auto name1{ L"profile1" };
- auto name2{ L"Ubuntu" };
- auto name3{ L"ThisProfileShouldNotThrow" };
- auto badName{ L"DoesNotExist" };
+ const auto name0{ L"profile0" };
+ const auto name1{ L"profile1" };
+ const auto name2{ L"Ubuntu" };
+ const auto name3{ L"ThisProfileShouldNotThrow" };
+ const auto badName{ L"DoesNotExist" };
- const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-5555-49a3-80bd-e8fdd045185c}") };
- const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-6666-49a3-80bd-e8fdd045185c}") };
- const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{2C4DE342-38B7-51CF-B940-2309A097F518}") };
- const winrt::guid fakeGuid{ ::Microsoft::Console::Utils::GuidFromString(L"{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}") };
+ const winrt::guid guid0{ Utils::GuidFromString(L"{6239a42c-5555-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid1{ Utils::GuidFromString(L"{6239a42c-6666-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid2{ Utils::GuidFromString(L"{2C4DE342-38B7-51CF-B940-2309A097F518}") };
+ const winrt::guid fakeGuid{ Utils::GuidFromString(L"{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}") };
const winrt::guid autogeneratedGuid{ implementation::Profile::_GenerateGuidForProfile(name3, L"") };
- const std::optional badGuid{};
- VerifyParseSucceeded(settings0String);
+ const auto settings = createSettings(settings0String);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settings0String, false);
- settings->LayerJson(settings->_userSettings);
+ VERIFY_ARE_EQUAL(guid0, settings->GetProfileByName(name0).Guid());
+ VERIFY_ARE_EQUAL(guid1, settings->GetProfileByName(name1).Guid());
+ VERIFY_ARE_EQUAL(guid2, settings->GetProfileByName(name2).Guid());
+ VERIFY_ARE_EQUAL(autogeneratedGuid, settings->GetProfileByName(name3).Guid());
+ VERIFY_IS_NULL(settings->GetProfileByName(badName));
- VERIFY_ARE_EQUAL(guid0, settings->_GetProfileGuidByName(name0));
- VERIFY_ARE_EQUAL(guid1, settings->_GetProfileGuidByName(name1));
- VERIFY_ARE_EQUAL(guid2, settings->_GetProfileGuidByName(name2));
- VERIFY_ARE_EQUAL(autogeneratedGuid, settings->_GetProfileGuidByName(name3));
- VERIFY_ARE_EQUAL(badGuid, settings->_GetProfileGuidByName(badName));
-
- auto prof0{ settings->FindProfile(guid0) };
- auto prof1{ settings->FindProfile(guid1) };
- auto prof2{ settings->FindProfile(guid2) };
-
- auto badProf{ settings->FindProfile(fakeGuid) };
- VERIFY_ARE_EQUAL(badProf, nullptr);
-
- VERIFY_ARE_EQUAL(name0, prof0.Name());
- VERIFY_ARE_EQUAL(name1, prof1.Name());
- VERIFY_ARE_EQUAL(name2, prof2.Name());
+ VERIFY_ARE_EQUAL(name0, settings->FindProfile(guid0).Name());
+ VERIFY_ARE_EQUAL(name1, settings->FindProfile(guid1).Name());
+ VERIFY_ARE_EQUAL(name2, settings->FindProfile(guid2).Name());
+ VERIFY_IS_NULL(settings->FindProfile(fakeGuid));
}
void DeserializationTests::TestProfileBackgroundImageWithEnvVar()
{
const auto expectedPath = wil::ExpandEnvironmentStringsW(L"%WINDIR%\\System32\\x_80.png");
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"profiles": [
{
@@ -1540,19 +913,16 @@ namespace SettingsModelLocalTests
]
})" };
- VerifyParseSucceeded(settingsJson);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_NOT_EQUAL(0u, settings->_allProfiles.Size());
- VERIFY_ARE_EQUAL(expectedPath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
+ const auto settings = createSettings(settingsJson);
+ VERIFY_ARE_NOT_EQUAL(0u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(expectedPath, settings->AllProfiles().GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
}
+
void DeserializationTests::TestProfileBackgroundImageWithDesktopWallpaper()
{
const winrt::hstring expectedBackgroundImagePath{ L"desktopWallpaper" };
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"profiles": [
{
@@ -1562,17 +932,14 @@ namespace SettingsModelLocalTests
]
})" };
- VerifyParseSucceeded(settingsJson);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().BackgroundImagePath());
- VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
+ const auto settings = createSettings(settingsJson);
+ VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->AllProfiles().GetAt(0).DefaultAppearance().BackgroundImagePath());
+ VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->AllProfiles().GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
}
+
void DeserializationTests::TestCloseOnExitParsing()
{
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"profiles": [
{
@@ -1594,21 +961,18 @@ namespace SettingsModelLocalTests
]
})" };
- VerifyParseSucceeded(settingsJson);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->_allProfiles.GetAt(0).CloseOnExit());
- VERIFY_ARE_EQUAL(CloseOnExitMode::Always, settings->_allProfiles.GetAt(1).CloseOnExit());
- VERIFY_ARE_EQUAL(CloseOnExitMode::Never, settings->_allProfiles.GetAt(2).CloseOnExit());
+ const auto settings = createSettings(settingsJson);
+ VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->AllProfiles().GetAt(0).CloseOnExit());
+ VERIFY_ARE_EQUAL(CloseOnExitMode::Always, settings->AllProfiles().GetAt(1).CloseOnExit());
+ VERIFY_ARE_EQUAL(CloseOnExitMode::Never, settings->AllProfiles().GetAt(2).CloseOnExit());
// Unknown modes parse as "Graceful"
- VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->_allProfiles.GetAt(3).CloseOnExit());
+ VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->AllProfiles().GetAt(3).CloseOnExit());
}
+
void DeserializationTests::TestCloseOnExitCompatibilityShim()
{
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"profiles": [
{
@@ -1622,13 +986,9 @@ namespace SettingsModelLocalTests
]
})" };
- VerifyParseSucceeded(settingsJson);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->_allProfiles.GetAt(0).CloseOnExit());
- VERIFY_ARE_EQUAL(CloseOnExitMode::Never, settings->_allProfiles.GetAt(1).CloseOnExit());
+ const auto settings = createSettings(settingsJson);
+ VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->AllProfiles().GetAt(0).CloseOnExit());
+ VERIFY_ARE_EQUAL(CloseOnExitMode::Never, settings->AllProfiles().GetAt(1).CloseOnExit());
}
void DeserializationTests::TestLayerUserDefaultsBeforeProfiles()
@@ -1639,7 +999,7 @@ namespace SettingsModelLocalTests
// we'll override that value, and in the other, we'll leave it
// untouched.
- const std::string settings0String{ R"(
+ static constexpr std::string_view settings0String{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": {
@@ -1659,24 +1019,16 @@ namespace SettingsModelLocalTests
]
}
})" };
- VerifyParseSucceeded(settings0String);
- const auto guid1String = L"{6239a42c-1111-49a3-80bd-e8fdd045185c}";
+ const auto settings = createSettings(settings0String);
- {
- auto settings = winrt::make_self(false);
- settings->_ParseJsonString(settings0String, false);
- VERIFY_IS_NULL(settings->_userDefaultProfileSettings);
- settings->_ApplyDefaultsFromUserSettings();
- VERIFY_IS_NOT_NULL(settings->_userDefaultProfileSettings);
- settings->LayerJson(settings->_userSettings);
+ VERIFY_IS_NOT_NULL(settings->ProfileDefaults());
- VERIFY_ARE_EQUAL(guid1String, settings->_globals->UnparsedDefaultProfile());
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
+ VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", settings->GlobalSettings().UnparsedDefaultProfile());
+ VERIFY_ARE_EQUAL(2u, settings->AllProfiles().Size());
- VERIFY_ARE_EQUAL(2345, settings->_allProfiles.GetAt(0).HistorySize());
- VERIFY_ARE_EQUAL(1234, settings->_allProfiles.GetAt(1).HistorySize());
- }
+ VERIFY_ARE_EQUAL(2345, settings->AllProfiles().GetAt(0).HistorySize());
+ VERIFY_ARE_EQUAL(1234, settings->AllProfiles().GetAt(1).HistorySize());
}
void DeserializationTests::TestDontLayerGuidFromUserDefaults()
@@ -1685,8 +1037,7 @@ namespace SettingsModelLocalTests
// "guid" in the "defaultSettings", and have that apply to all the other
// profiles
- const std::string settings0String{ R"(
- {
+ static constexpr std::string_view settings0String{ R"({
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": {
"defaults": {
@@ -1705,36 +1056,16 @@ namespace SettingsModelLocalTests
]
}
})" };
- VerifyParseSucceeded(settings0String);
const auto guid1String = L"{6239a42c-1111-49a3-80bd-e8fdd045185c}";
- const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(guid1String) };
- const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid1{ Utils::GuidFromString(guid1String) };
- {
- auto settings = winrt::make_self(false);
- settings->_ParseJsonString(DefaultJson, true);
- settings->LayerJson(settings->_defaultSettings);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
+ const auto settings = winrt::make_self(settings0String, DefaultJson);
- settings->_ParseJsonString(settings0String, false);
- VERIFY_IS_NULL(settings->_userDefaultProfileSettings);
- settings->_ApplyDefaultsFromUserSettings();
- VERIFY_IS_NOT_NULL(settings->_userDefaultProfileSettings);
-
- Log::Comment(NoThrowString().Format(
- L"Ensure that cmd and powershell don't get their GUIDs overwritten"));
- VERIFY_ARE_NOT_EQUAL(guid2, settings->_allProfiles.GetAt(0).Guid());
- VERIFY_ARE_NOT_EQUAL(guid2, settings->_allProfiles.GetAt(1).Guid());
-
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(guid1String, settings->_globals->UnparsedDefaultProfile());
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
-
- VERIFY_ARE_EQUAL(guid1, settings->_allProfiles.GetAt(2).Guid());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(3).HasGuid());
- }
+ VERIFY_ARE_EQUAL(guid1String, settings->GlobalSettings().UnparsedDefaultProfile());
+ VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(guid1, settings->AllProfiles().GetAt(0).Guid());
+ VERIFY_ARE_NOT_EQUAL(guid1, settings->AllProfiles().GetAt(1).Guid());
}
void DeserializationTests::TestLayerUserDefaultsOnDynamics()
@@ -1746,11 +1077,35 @@ namespace SettingsModelLocalTests
// settings in defaultSettings should apply _on top_ of settings from
// dynamic profiles.
- const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}") };
- const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}") };
- const winrt::guid guid3{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid1{ Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid2{ Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid3{ Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid4{ Utils::GuidFromString(L"{6239a42c-4444-49a3-80bd-e8fdd045185c}") };
- const std::string userProfiles{ R"(
+ static constexpr std::string_view dynamicProfiles{ R"({
+ "profiles": [
+ {
+ "name": "profile0",
+ "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
+ "source": "Terminal.App.UnitTest.0",
+ "historySize": 1111
+ },
+ {
+ "name": "profile1",
+ "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
+ "source": "Terminal.App.UnitTest.1",
+ "historySize": 2222
+ },
+ {
+ "name": "profile2",
+ "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}",
+ "source": "Terminal.App.UnitTest.1",
+ "historySize": 4444
+ }
+ ]
+ })" };
+
+ static constexpr std::string_view userProfiles{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": {
@@ -1759,18 +1114,18 @@ namespace SettingsModelLocalTests
},
"list": [
{
- "name" : "profile0FromUserSettings", // this is _allProfiles.GetAt(0)
+ "name" : "profile0FromUserSettings",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.0"
},
{
- "name" : "profile1FromUserSettings", // this is _allProfiles.GetAt(2)
+ "name" : "profile1FromUserSettings",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.1",
"historySize": 4444
},
{
- "name" : "profile2FromUserSettings", // this is _allProfiles.GetAt(3)
+ "name" : "profile2FromUserSettings",
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
"historySize": 5555
}
@@ -1778,73 +1133,29 @@ namespace SettingsModelLocalTests
}
})" };
- auto gen0 = std::make_unique(L"Terminal.App.UnitTest.0");
- gen0->pfnGenerate = [guid1, guid2]() {
- std::vector profiles;
- Profile p0 = winrt::make(guid1);
- p0.Name(L"profile0"); // this is _allProfiles.GetAt(0)
- p0.HistorySize(1111);
- profiles.push_back(p0);
- return profiles;
- };
- auto gen1 = std::make_unique(L"Terminal.App.UnitTest.1");
- gen1->pfnGenerate = [guid1, guid2]() {
- std::vector profiles;
- Profile p0 = winrt::make(guid1);
- Profile p1 = winrt::make(guid2);
- p0.Name(L"profile0"); // this is _allProfiles.GetAt(1)
- p1.Name(L"profile1"); // this is _allProfiles.GetAt(2)
- p0.HistorySize(2222);
- profiles.push_back(p0);
- p1.HistorySize(3333);
- profiles.push_back(p1);
- return profiles;
- };
-
- auto settings = winrt::make_self(false);
- settings->_profileGenerators.emplace_back(std::move(gen0));
- settings->_profileGenerators.emplace_back(std::move(gen1));
+ const auto settings = winrt::make_self(userProfiles, dynamicProfiles);
+ const auto allProfiles = settings->AllProfiles();
Log::Comment(NoThrowString().Format(
L"All profiles with the same name have the same GUID. However, they"
L" will not be layered, because they have different source's"));
- // parse userProfiles as the user settings
- settings->_ParseJsonString(userProfiles, false);
- VERIFY_ARE_EQUAL(0u, settings->_allProfiles.Size(), L"Just parsing the user settings doesn't actually layer them");
- settings->_LoadDynamicProfiles();
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
+ VERIFY_ARE_EQUAL(4u, allProfiles.Size());
- VERIFY_ARE_EQUAL(1111, settings->_allProfiles.GetAt(0).HistorySize());
- VERIFY_ARE_EQUAL(2222, settings->_allProfiles.GetAt(1).HistorySize());
- VERIFY_ARE_EQUAL(3333, settings->_allProfiles.GetAt(2).HistorySize());
+ VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.0", allProfiles.GetAt(0).Source());
+ VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.1", allProfiles.GetAt(1).Source());
+ VERIFY_ARE_EQUAL(L"", allProfiles.GetAt(2).Source());
+ VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.1", allProfiles.GetAt(3).Source());
- settings->_ApplyDefaultsFromUserSettings();
+ VERIFY_ARE_EQUAL(guid1, allProfiles.GetAt(0).Guid());
+ VERIFY_ARE_EQUAL(guid2, allProfiles.GetAt(1).Guid());
+ VERIFY_ARE_EQUAL(guid3, allProfiles.GetAt(2).Guid());
+ VERIFY_ARE_EQUAL(guid4, allProfiles.GetAt(3).Guid());
- VERIFY_ARE_EQUAL(1234, settings->_allProfiles.GetAt(0).HistorySize());
- VERIFY_ARE_EQUAL(1234, settings->_allProfiles.GetAt(1).HistorySize());
- VERIFY_ARE_EQUAL(1234, settings->_allProfiles.GetAt(2).HistorySize());
-
- settings->LayerJson(settings->_userSettings);
- VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
-
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(0).Source().empty());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).Source().empty());
- VERIFY_IS_FALSE(settings->_allProfiles.GetAt(2).Source().empty());
- VERIFY_IS_TRUE(settings->_allProfiles.GetAt(3).Source().empty());
-
- VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.0", settings->_allProfiles.GetAt(0).Source());
- VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.1", settings->_allProfiles.GetAt(1).Source());
- VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.1", settings->_allProfiles.GetAt(2).Source());
-
- VERIFY_ARE_EQUAL(guid1, settings->_allProfiles.GetAt(0).Guid());
- VERIFY_ARE_EQUAL(guid1, settings->_allProfiles.GetAt(1).Guid());
- VERIFY_ARE_EQUAL(guid2, settings->_allProfiles.GetAt(2).Guid());
-
- VERIFY_ARE_EQUAL(L"profile0FromUserSettings", settings->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(L"profile1FromUserSettings", settings->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(L"profile2FromUserSettings", settings->_allProfiles.GetAt(3).Name());
+ VERIFY_ARE_EQUAL(L"profile0FromUserSettings", allProfiles.GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"profile1FromUserSettings", allProfiles.GetAt(1).Name());
+ VERIFY_ARE_EQUAL(L"profile2FromUserSettings", allProfiles.GetAt(2).Name());
+ VERIFY_ARE_EQUAL(L"profile2", allProfiles.GetAt(3).Name());
Log::Comment(NoThrowString().Format(
L"This is the real meat of the test: The two dynamic profiles that "
@@ -1852,17 +1163,17 @@ namespace SettingsModelLocalTests
L"1234 as their historySize(from the defaultSettings).The other two"
L" profiles should have their custom historySize value."));
- VERIFY_ARE_EQUAL(1234, settings->_allProfiles.GetAt(0).HistorySize());
- VERIFY_ARE_EQUAL(1234, settings->_allProfiles.GetAt(1).HistorySize());
- VERIFY_ARE_EQUAL(4444, settings->_allProfiles.GetAt(2).HistorySize());
- VERIFY_ARE_EQUAL(5555, settings->_allProfiles.GetAt(3).HistorySize());
+ VERIFY_ARE_EQUAL(1234, allProfiles.GetAt(0).HistorySize());
+ VERIFY_ARE_EQUAL(4444, allProfiles.GetAt(1).HistorySize());
+ VERIFY_ARE_EQUAL(5555, allProfiles.GetAt(2).HistorySize());
+ VERIFY_ARE_EQUAL(1234, allProfiles.GetAt(3).HistorySize());
}
void DeserializationTests::FindMissingProfile()
{
// Test that CascadiaSettings::FindProfile returns null for a GUID that
// doesn't exist
- const std::string settingsString{ R"(
+ static constexpr std::string_view settingsString{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -1876,12 +1187,11 @@ namespace SettingsModelLocalTests
}
]
})" };
- const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
- auto settings = implementation::CascadiaSettings::FromJson(settingsJsonObj);
+ const auto settings = createSettings(settingsString);
- const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
- const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
- const auto guid3 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
+ const auto guid1 = Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
+ const auto guid2 = Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
+ const auto guid3 = Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
const auto profile1 = settings->FindProfile(guid1);
const auto profile2 = settings->FindProfile(guid2);
@@ -1897,7 +1207,7 @@ namespace SettingsModelLocalTests
void DeserializationTests::ValidateKeybindingsWarnings()
{
- const std::string badSettings{ R"(
+ static constexpr std::string_view badSettings{ R"(
{
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -1918,35 +1228,35 @@ namespace SettingsModelLocalTests
]
})" };
- const auto settingsObject = VerifyParseSucceeded(badSettings);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
+ const auto settings = createSettings(badSettings);
// KeyMap: ctrl+a/b are mapped to "invalid"
// ActionMap: "splitPane" and "invalid" are the only deserialized actions
// NameMap: "splitPane" has no key binding, but it is still added to the name map
- VERIFY_ARE_EQUAL(2u, settings->_globals->_actionMap->_KeyMap.size());
- VERIFY_ARE_EQUAL(2u, settings->_globals->_actionMap->_ActionMap.size());
- VERIFY_ARE_EQUAL(1u, settings->_globals->_actionMap->NameMap().Size());
+ const auto actionMap = winrt::get_self(settings->GlobalSettings().ActionMap());
+ VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
+ VERIFY_ARE_EQUAL(2u, actionMap->_ActionMap.size());
+ VERIFY_ARE_EQUAL(1u, actionMap->NameMap().Size());
+ VERIFY_ARE_EQUAL(5u, settings->Warnings().Size());
- VERIFY_ARE_EQUAL(4u, settings->_globals->_keybindingsWarnings.size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, settings->_globals->_keybindingsWarnings.at(0));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(1));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(2));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_globals->_keybindingsWarnings.at(3));
+ const auto globalAppSettings = winrt::get_self(settings->GlobalSettings());
+ const auto& keybindingsWarnings = globalAppSettings->KeybindingsWarnings();
+ VERIFY_ARE_EQUAL(4u, keybindingsWarnings.size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, keybindingsWarnings.at(0));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, keybindingsWarnings.at(1));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, keybindingsWarnings.at(2));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, keybindingsWarnings.at(3));
- settings->_ValidateKeybindings();
-
- VERIFY_ARE_EQUAL(5u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, settings->_warnings.GetAt(1));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(2));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(3));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_warnings.GetAt(4));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->Warnings().GetAt(0));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, settings->Warnings().GetAt(1));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->Warnings().GetAt(2));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->Warnings().GetAt(3));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->Warnings().GetAt(4));
}
void DeserializationTests::ValidateExecuteCommandlineWarning()
{
- const std::string badSettings{ R"(
+ static constexpr std::string_view badSettings{ R"(
{
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -1966,98 +1276,37 @@ namespace SettingsModelLocalTests
]
})" };
- const auto settingsObject = VerifyParseSucceeded(badSettings);
+ const auto settings = createSettings(badSettings);
- auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
+ const auto actionMap = winrt::get_self(settings->GlobalSettings().ActionMap());
+ VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
+ VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('A'), 0 }));
+ VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('B'), 0 }));
+ VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('C'), 0 }));
- VERIFY_ARE_EQUAL(3u, settings->_globals->_actionMap->_KeyMap.size());
- VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('A'), 0 }));
- VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('B'), 0 }));
- VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('C'), 0 }));
+ const auto globalAppSettings = winrt::get_self(settings->GlobalSettings());
+ const auto& keybindingsWarnings = globalAppSettings->KeybindingsWarnings();
+ VERIFY_ARE_EQUAL(3u, keybindingsWarnings.size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, keybindingsWarnings.at(0));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, keybindingsWarnings.at(1));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, keybindingsWarnings.at(2));
- for (const auto& warning : settings->_globals->_keybindingsWarnings)
- {
- Log::Comment(NoThrowString().Format(
- L"warning:%d", warning));
- }
- VERIFY_ARE_EQUAL(3u, settings->_globals->_keybindingsWarnings.size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(0));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(1));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(2));
-
- settings->_ValidateKeybindings();
-
- VERIFY_ARE_EQUAL(4u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(1));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(2));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(3));
- }
-
- void DeserializationTests::ValidateLegacyGlobalsWarning()
- {
- const std::string badSettings{ R"(
- {
- "globals": {},
- "defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
- "profiles": [
- {
- "name" : "profile0",
- "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
- },
- {
- "name" : "profile1",
- "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
- }
- ],
- "keybindings": []
- })" };
-
- // Create the default settings
- auto settings = winrt::make_self();
- settings->_ParseJsonString(DefaultJson, true);
- settings->LayerJson(settings->_defaultSettings);
-
- settings->_ValidateNoGlobalsKey();
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
-
- // Now layer on the user's settings
- settings->_ParseJsonString(badSettings, false);
- settings->LayerJson(settings->_userSettings);
-
- settings->_ValidateNoGlobalsKey();
- VERIFY_ARE_EQUAL(1u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::LegacyGlobalsProperty, settings->_warnings.GetAt(0));
+ VERIFY_ARE_EQUAL(4u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->Warnings().GetAt(0));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->Warnings().GetAt(1));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->Warnings().GetAt(2));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->Warnings().GetAt(3));
}
void DeserializationTests::TestTrailingCommas()
{
- const std::string badSettings{ R"(
- {
- "defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
- "profiles": [
- {
- "name" : "profile0",
- "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
- },
- {
- "name" : "profile1",
- "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
- },
- ],
- "keybindings": [],
+ static constexpr std::string_view badSettings{ R"({
+ "profiles": [{ "name": "profile0" }],
})" };
- // Create the default settings
- auto settings = winrt::make_self();
- settings->_ParseJsonString(DefaultJson, true);
- settings->LayerJson(settings->_defaultSettings);
-
- // Now layer on the user's settings
try
{
- settings->_ParseJsonString(badSettings, false);
- settings->LayerJson(settings->_userSettings);
+ const auto settings = createSettings(badSettings);
}
catch (...)
{
@@ -2067,7 +1316,7 @@ namespace SettingsModelLocalTests
void DeserializationTests::TestCommandsAndKeybindings()
{
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -2099,23 +1348,15 @@ namespace SettingsModelLocalTests
]
})" };
- const auto guid0 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
- const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
+ const auto settings = createSettings(settingsJson);
- VerifyParseSucceeded(settingsJson);
+ VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
-
- const auto profile2Guid = settings->_allProfiles.GetAt(2).Guid();
+ const auto profile2Guid = settings->AllProfiles().GetAt(2).Guid();
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
- auto actionMap = winrt::get_self(settings->_globals->ActionMap());
- VERIFY_ARE_EQUAL(5u, actionMap->_KeyMap.size());
+ auto actionMap = winrt::get_self(settings->GlobalSettings().ActionMap());
+ VERIFY_ARE_EQUAL(5u, actionMap->KeyBindings().Size());
// A/D, B, C, E will be in the list of commands, for 4 total.
// * A and D share the same name, so they'll only generate a single action.
@@ -2124,8 +1365,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
- KeyChord kc{ true, false, false, false, static_cast('A'), 0 };
- auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
+ const KeyChord kc{ true, false, false, false, static_cast('A'), 0 };
+ const auto actionAndArgs = TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2141,8 +1382,8 @@ namespace SettingsModelLocalTests
Log::Comment(L"Note that we're skipping ctrl+B, since that doesn't have `keys` set.");
{
- KeyChord kc{ true, false, false, false, static_cast('C'), 0 };
- auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
+ const KeyChord kc{ true, false, false, false, static_cast('C'), 0 };
+ const auto actionAndArgs = TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2155,8 +1396,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
}
{
- KeyChord kc{ true, false, false, false, static_cast('D'), 0 };
- auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
+ const KeyChord kc{ true, false, false, false, static_cast('D'), 0 };
+ const auto actionAndArgs = TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2169,8 +1410,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
}
{
- KeyChord kc{ true, false, false, false, static_cast('E'), 0 };
- auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
+ const KeyChord kc{ true, false, false, false, static_cast('E'), 0 };
+ const auto actionAndArgs = TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2183,8 +1424,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
}
{
- KeyChord kc{ true, false, false, false, static_cast('F'), 0 };
- auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
+ const KeyChord kc{ true, false, false, false, static_cast('F'), 0 };
+ const auto actionAndArgs = TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2201,18 +1442,18 @@ namespace SettingsModelLocalTests
_logCommandNames(nameMap);
{
// This was renamed to "ctrl+c" in C. So this does not exist.
- auto command = nameMap.TryLookup(L"Split pane, split: vertical");
+ const auto command = nameMap.TryLookup(L"Split pane, split: vertical");
VERIFY_IS_NULL(command);
}
{
// This was renamed to "ctrl+c" in C. So this does not exist.
- auto command = nameMap.TryLookup(L"ctrl+b");
+ const auto command = nameMap.TryLookup(L"ctrl+b");
VERIFY_IS_NULL(command);
}
{
- auto command = nameMap.TryLookup(L"ctrl+c");
+ const auto command = nameMap.TryLookup(L"ctrl+c");
VERIFY_IS_NOT_NULL(command);
- auto actionAndArgs = command.ActionAndArgs();
+ const auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as();
@@ -2227,7 +1468,7 @@ namespace SettingsModelLocalTests
}
{
// This was renamed to null (aka removed from the name map) in F. So this does not exist.
- auto command = nameMap.TryLookup(L"Split pane, split: horizontal");
+ const auto command = nameMap.TryLookup(L"Split pane, split: horizontal");
VERIFY_IS_NULL(command);
}
}
@@ -2238,7 +1479,7 @@ namespace SettingsModelLocalTests
// of command should just be ignored, since we can't auto-generate names
// for nested commands, they _must_ have names specified.
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -2273,29 +1514,16 @@ namespace SettingsModelLocalTests
}
]
},
- ],
- "schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
+ ]
})" };
- VerifyParseSucceeded(settingsJson);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
-
- settings->_ValidateSettings();
- const auto& nameMap{ settings->ActionMap().NameMap() };
- _logCommandNames(nameMap);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
-
+ const auto settings = createSettings(settingsJson);
+ VERIFY_ARE_EQUAL(0u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
// Because the "parent" command didn't have a name, it couldn't be
// placed into the list of commands. It and it's children are just
// ignored.
- VERIFY_ARE_EQUAL(0u, nameMap.Size());
+ VERIFY_ARE_EQUAL(0u, settings->ActionMap().NameMap().Size());
}
void DeserializationTests::TestNestedCommandWithBadSubCommands()
@@ -2304,7 +1532,7 @@ namespace SettingsModelLocalTests
// of command should just be ignored, since we can't auto-generate names
// for nested commands, they _must_ have names specified.
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -2327,20 +1555,14 @@ namespace SettingsModelLocalTests
}
]
},
- ],
- "schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
+ ]
})" };
- VerifyParseSucceeded(settingsJson);
+ const auto settings = createSettings(settingsJson);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
- VERIFY_ARE_EQUAL(2u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
- VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_warnings.GetAt(1));
+ VERIFY_ARE_EQUAL(2u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->Warnings().GetAt(0));
+ VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->Warnings().GetAt(1));
const auto& nameMap{ settings->ActionMap().NameMap() };
VERIFY_ARE_EQUAL(0u, nameMap.Size());
}
@@ -2349,7 +1571,7 @@ namespace SettingsModelLocalTests
{
// Test that layering a command with `"commands": null` set will unbind a command that already exists.
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -2385,11 +1607,10 @@ namespace SettingsModelLocalTests
}
]
},
- ],
- "schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
+ ]
})" };
- const std::string settings1Json{ R"(
+ static constexpr std::string_view settings1Json{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"actions": [
@@ -2400,33 +1621,9 @@ namespace SettingsModelLocalTests
],
})" };
- VerifyParseSucceeded(settingsJson);
- VerifyParseSucceeded(settings1Json);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
-
- settings->_ValidateSettings();
- auto nameMap{ settings->ActionMap().NameMap() };
- _logCommandNames(nameMap);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(1u, nameMap.Size());
-
- Log::Comment(L"Layer second bit of json, to unbind the original command.");
-
- settings->_ParseJsonString(settings1Json, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
- nameMap = settings->ActionMap().NameMap();
- _logCommandNames(nameMap);
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(0u, nameMap.Size());
+ const auto settings = winrt::make_self(settings1Json, settingsJson);
+ VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(0u, settings->ActionMap().NameMap().Size());
}
void DeserializationTests::TestRebindNestedCommand()
@@ -2434,7 +1631,7 @@ namespace SettingsModelLocalTests
// Test that layering a command with an action set on top of a command
// with nested commands replaces the nested commands with an action.
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -2470,11 +1667,10 @@ namespace SettingsModelLocalTests
}
]
},
- ],
- "schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
+ ]
})" };
- const std::string settings1Json{ R"(
+ static constexpr std::string_view settings1Json{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"actions": [
@@ -2485,52 +1681,17 @@ namespace SettingsModelLocalTests
],
})" };
- VerifyParseSucceeded(settingsJson);
- VerifyParseSucceeded(settings1Json);
+ const auto settings = winrt::make_self(settings1Json, settingsJson);
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
-
- const auto& actionMap{ settings->ActionMap() };
- settings->_ValidateSettings();
- auto nameMap{ actionMap.NameMap() };
- _logCommandNames(nameMap);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
+ const auto nameMap = settings->ActionMap().NameMap();
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
- winrt::hstring commandName{ L"parent" };
- auto commandProj = nameMap.TryLookup(commandName);
- VERIFY_IS_NOT_NULL(commandProj);
-
- winrt::com_ptr commandImpl;
- commandImpl.copy_from(winrt::get_self(commandProj));
-
- VERIFY_IS_TRUE(commandImpl->HasNestedCommands());
- VERIFY_ARE_EQUAL(2u, commandImpl->_subcommands.Size());
- }
-
- Log::Comment(L"Layer second bit of json, to unbind the original command.");
- settings->_ParseJsonString(settings1Json, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
- nameMap = settings->ActionMap().NameMap();
- _logCommandNames(nameMap);
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(1u, nameMap.Size());
-
- {
- winrt::hstring commandName{ L"parent" };
- auto commandProj = nameMap.TryLookup(commandName);
+ const winrt::hstring commandName{ L"parent" };
+ const auto commandProj = nameMap.TryLookup(commandName);
VERIFY_IS_NOT_NULL(commandProj);
- auto actionAndArgs = commandProj.ActionAndArgs();
+ const auto actionAndArgs = commandProj.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as();
@@ -2545,7 +1706,7 @@ namespace SettingsModelLocalTests
void DeserializationTests::TestCopy()
{
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialCols": 50,
@@ -2600,43 +1761,37 @@ namespace SettingsModelLocalTests
]
})" };
- VerifyParseSucceeded(settingsJson);
-
- auto settings{ winrt::make_self() };
- settings->_ParseJsonString(settingsJson, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
+ const auto settings{ winrt::make_self(settingsJson) };
const auto copy{ settings->Copy() };
const auto copyImpl{ winrt::get_self(copy) };
// test globals
- VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), copyImpl->_globals->DefaultProfile());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), copyImpl->GlobalSettings().DefaultProfile());
// test profiles
- VERIFY_ARE_EQUAL(settings->_allProfiles.Size(), copyImpl->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(0).Name(), copyImpl->_allProfiles.GetAt(0).Name());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().Size(), copyImpl->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Name(), copyImpl->AllProfiles().GetAt(0).Name());
// test schemes
const auto schemeName{ L"Campbell, but for a test" };
- VERIFY_ARE_EQUAL(settings->_globals->_colorSchemes.Size(), copyImpl->_globals->_colorSchemes.Size());
- VERIFY_ARE_EQUAL(settings->_globals->_colorSchemes.HasKey(schemeName), copyImpl->_globals->_colorSchemes.HasKey(schemeName));
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().ColorSchemes().Size(), copyImpl->GlobalSettings().ColorSchemes().Size());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().ColorSchemes().HasKey(schemeName), copyImpl->GlobalSettings().ColorSchemes().HasKey(schemeName));
// test actions
- VERIFY_ARE_EQUAL(settings->_globals->_actionMap->_KeyMap.size(), copyImpl->_globals->_actionMap->_KeyMap.size());
- const auto& nameMapOriginal{ settings->_globals->_actionMap->NameMap() };
- const auto& nameMapCopy{ copyImpl->_globals->_actionMap->NameMap() };
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().ActionMap().KeyBindings().Size(), copyImpl->GlobalSettings().ActionMap().KeyBindings().Size());
+ const auto& nameMapOriginal{ settings->GlobalSettings().ActionMap().NameMap() };
+ const auto& nameMapCopy{ copyImpl->GlobalSettings().ActionMap().NameMap() };
VERIFY_ARE_EQUAL(nameMapOriginal.Size(), nameMapCopy.Size());
// Test that changing the copy should not change the original
- VERIFY_ARE_EQUAL(settings->_globals->WordDelimiters(), copyImpl->_globals->WordDelimiters());
- copyImpl->_globals->WordDelimiters(L"changed value");
- VERIFY_ARE_NOT_EQUAL(settings->_globals->WordDelimiters(), copyImpl->_globals->WordDelimiters());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().WordDelimiters(), copyImpl->GlobalSettings().WordDelimiters());
+ copyImpl->GlobalSettings().WordDelimiters(L"changed value");
+ VERIFY_ARE_NOT_EQUAL(settings->GlobalSettings().WordDelimiters(), copyImpl->GlobalSettings().WordDelimiters());
}
void DeserializationTests::TestCloneInheritanceTree()
{
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"profiles":
@@ -2660,45 +1815,38 @@ namespace SettingsModelLocalTests
}
})" };
- VerifyParseSucceeded(settingsJson);
-
- auto settings{ winrt::make_self() };
- settings->_ParseJsonString(settingsJson, false);
- settings->_ApplyDefaultsFromUserSettings();
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
+ const auto settings{ winrt::make_self(settingsJson) };
const auto copy{ settings->Copy() };
const auto copyImpl{ winrt::get_self(copy) };
// test globals
- VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), copyImpl->_globals->DefaultProfile());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), copyImpl->GlobalSettings().DefaultProfile());
// test profiles
- VERIFY_ARE_EQUAL(settings->_allProfiles.Size(), copyImpl->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(0).Name(), copyImpl->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(1).Name(), copyImpl->_allProfiles.GetAt(1).Name());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(2).Name(), copyImpl->_allProfiles.GetAt(2).Name());
- VERIFY_ARE_EQUAL(settings->_userDefaultProfileSettings->Name(), copyImpl->_userDefaultProfileSettings->Name());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().Size(), copyImpl->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Name(), copyImpl->AllProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(1).Name(), copyImpl->AllProfiles().GetAt(1).Name());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(2).Name(), copyImpl->AllProfiles().GetAt(2).Name());
+ VERIFY_ARE_EQUAL(settings->ProfileDefaults().Name(), copyImpl->ProfileDefaults().Name());
// Modifying profile.defaults should...
- VERIFY_ARE_EQUAL(settings->_userDefaultProfileSettings->HasName(), copyImpl->_userDefaultProfileSettings->HasName());
- copyImpl->_userDefaultProfileSettings->Name(L"changed value");
+ VERIFY_ARE_EQUAL(settings->ProfileDefaults().HasName(), copyImpl->ProfileDefaults().HasName());
+ copyImpl->ProfileDefaults().Name(L"changed value");
// ...keep the same name for the first two profiles
- VERIFY_ARE_EQUAL(settings->_allProfiles.Size(), copyImpl->_allProfiles.Size());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(0).Name(), copyImpl->_allProfiles.GetAt(0).Name());
- VERIFY_ARE_EQUAL(settings->_allProfiles.GetAt(1).Name(), copyImpl->_allProfiles.GetAt(1).Name());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().Size(), copyImpl->AllProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Name(), copyImpl->AllProfiles().GetAt(0).Name());
+ VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(1).Name(), copyImpl->AllProfiles().GetAt(1).Name());
// ...but change the name for the one that inherited it from profile.defaults
- VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(2).Name(), copyImpl->_allProfiles.GetAt(2).Name());
+ VERIFY_ARE_NOT_EQUAL(settings->AllProfiles().GetAt(2).Name(), copyImpl->AllProfiles().GetAt(2).Name());
// profile.defaults should be different between the two graphs
- VERIFY_ARE_EQUAL(settings->_userDefaultProfileSettings->HasName(), copyImpl->_userDefaultProfileSettings->HasName());
- VERIFY_ARE_NOT_EQUAL(settings->_userDefaultProfileSettings->Name(), copyImpl->_userDefaultProfileSettings->Name());
+ VERIFY_ARE_EQUAL(settings->ProfileDefaults().HasName(), copyImpl->ProfileDefaults().HasName());
+ VERIFY_ARE_NOT_EQUAL(settings->ProfileDefaults().Name(), copyImpl->ProfileDefaults().Name());
Log::Comment(L"Test empty profiles.defaults");
- const std::string emptyPDJson{ R"(
+ static constexpr std::string_view emptyPDJson{ R"(
{
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"profiles":
@@ -2714,7 +1862,7 @@ namespace SettingsModelLocalTests
}
})" };
- const std::string missingPDJson{ R"(
+ static constexpr std::string_view missingPDJson{ R"(
{
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"profiles":
@@ -2726,28 +1874,21 @@ namespace SettingsModelLocalTests
]
})" };
- auto verifyEmptyPD = [this](const std::string json) {
- VerifyParseSucceeded(json);
-
- auto settings{ winrt::make_self() };
- settings->_ParseJsonString(json, false);
- settings->_ApplyDefaultsFromUserSettings();
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
+ auto verifyEmptyPD = [this](const auto json) {
+ const auto settings{ winrt::make_self(json) };
const auto copy{ settings->Copy() };
const auto copyImpl{ winrt::get_self(copy) };
// if we don't have profiles.defaults, it should still be in the tree
- VERIFY_IS_NOT_NULL(settings->_userDefaultProfileSettings);
- VERIFY_IS_NOT_NULL(copyImpl->_userDefaultProfileSettings);
+ VERIFY_IS_NOT_NULL(settings->ProfileDefaults());
+ VERIFY_IS_NOT_NULL(copyImpl->ProfileDefaults());
VERIFY_ARE_EQUAL(settings->ActiveProfiles().Size(), 1u);
VERIFY_ARE_EQUAL(settings->ActiveProfiles().Size(), copyImpl->ActiveProfiles().Size());
// so we should only have one parent, instead of two
- auto srcProfile{ winrt::get_self(settings->ActiveProfiles().GetAt(0)) };
- auto copyProfile{ winrt::get_self(copyImpl->ActiveProfiles().GetAt(0)) };
+ const auto srcProfile{ winrt::get_self(settings->ActiveProfiles().GetAt(0)) };
+ const auto copyProfile{ winrt::get_self(copyImpl->ActiveProfiles().GetAt(0)) };
VERIFY_ARE_EQUAL(srcProfile->Parents().size(), 1u);
VERIFY_ARE_EQUAL(srcProfile->Parents().size(), copyProfile->Parents().size());
};
@@ -2769,7 +1910,7 @@ namespace SettingsModelLocalTests
{
// Test unbinding a command's key chord or name that originated in another layer.
- const std::string settings1Json{ R"(
+ static constexpr std::string_view settings1Json{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -2797,11 +1938,10 @@ namespace SettingsModelLocalTests
"command": "closePane",
"keys": "ctrl+shift+w"
}
- ],
- "schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
+ ]
})" };
- const std::string settings2Json{ R"(
+ static constexpr std::string_view settings2Json{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"actions": [
@@ -2809,13 +1949,6 @@ namespace SettingsModelLocalTests
"command": null,
"keys": "ctrl+shift+w"
},
- ],
- })" };
-
- const std::string settings3Json{ R"(
- {
- "defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
- "actions": [
{
"name": "bar",
"command": "closePane"
@@ -2823,82 +1956,10 @@ namespace SettingsModelLocalTests
],
})" };
- VerifyParseSucceeded(settings1Json);
- VerifyParseSucceeded(settings2Json);
- VerifyParseSucceeded(settings3Json);
-
- auto settings = winrt::make_self();
- settings->_ParseJsonString(settings1Json, false);
- settings->LayerJson(settings->_userSettings);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
-
- settings->_ValidateSettings();
- auto nameMap{ settings->ActionMap().NameMap() };
- _logCommandNames(nameMap);
-
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(1u, nameMap.Size());
-
+ const auto settings = winrt::make_self(settings2Json, settings1Json);
const KeyChord expectedKeyChord{ true, false, true, false, static_cast('W'), 0 };
- {
- // Verify NameMap returns correct value
- const auto& cmd{ nameMap.TryLookup(L"foo") };
- VERIFY_IS_NOT_NULL(cmd);
- VERIFY_IS_NOT_NULL(cmd.Keys());
- VERIFY_IS_TRUE(cmd.Keys().Modifiers() == expectedKeyChord.Modifiers() && cmd.Keys().Vkey() == expectedKeyChord.Vkey());
- }
- {
- // Verify ActionMap::GetActionByKeyChord API
- const auto& cmd{ settings->ActionMap().GetActionByKeyChord(expectedKeyChord) };
- VERIFY_IS_NOT_NULL(cmd);
- VERIFY_IS_NOT_NULL(cmd.Keys());
- VERIFY_IS_TRUE(cmd.Keys().Modifiers() == expectedKeyChord.Modifiers() && cmd.Keys().Vkey() == expectedKeyChord.Vkey());
- }
- {
- // Verify ActionMap::GetKeyBindingForAction API
- const auto& actualKeyChord{ settings->ActionMap().GetKeyBindingForAction(ShortcutAction::ClosePane) };
- VERIFY_IS_NOT_NULL(actualKeyChord);
- VERIFY_IS_TRUE(actualKeyChord.Modifiers() == expectedKeyChord.Modifiers() && actualKeyChord.Vkey() == expectedKeyChord.Vkey());
- }
- Log::Comment(L"Layer second bit of json, to unbind the key chord of the original command.");
-
- settings->_ParseJsonString(settings2Json, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
- nameMap = settings->ActionMap().NameMap();
- _logCommandNames(nameMap);
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
- VERIFY_ARE_EQUAL(1u, nameMap.Size());
- {
- // Verify NameMap returns correct value
- const auto& cmd{ nameMap.TryLookup(L"foo") };
- VERIFY_IS_NOT_NULL(cmd);
- VERIFY_IS_NULL(cmd.Keys());
- }
- {
- // Verify ActionMap::GetActionByKeyChord API
- const auto& cmd{ settings->ActionMap().GetActionByKeyChord(expectedKeyChord) };
- VERIFY_IS_NULL(cmd);
- }
- {
- // Verify ActionMap::GetKeyBindingForAction API
- const auto& actualKeyChord{ settings->ActionMap().GetKeyBindingForAction(ShortcutAction::ClosePane) };
- VERIFY_IS_NULL(actualKeyChord);
- }
-
- Log::Comment(L"Layer third bit of json, to unbind name of the original command.");
-
- settings->_ParseJsonString(settings3Json, false);
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
-
- nameMap = settings->ActionMap().NameMap();
- _logCommandNames(nameMap);
- VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
+ const auto nameMap = settings->ActionMap().NameMap();
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
// Verify NameMap returns correct value
diff --git a/src/cascadia/LocalTests_SettingsModel/JsonTestClass.h b/src/cascadia/LocalTests_SettingsModel/JsonTestClass.h
index 5cb0bf3f5..c39c6f0d2 100644
--- a/src/cascadia/LocalTests_SettingsModel/JsonTestClass.h
+++ b/src/cascadia/LocalTests_SettingsModel/JsonTestClass.h
@@ -7,44 +7,34 @@ Module Name:
Abstract:
- This class is a helper that can be used to quickly create tests that need to
- read & parse json data. Test classes that need to read JSON should make sure
- to derive from this class, and also make sure to call InitializeJsonReader()
- in the TEST_CLASS_SETUP().
+ read & parse json data.
Author(s):
Mike Griese (migrie) August-2019
--*/
+#pragma once
+
class JsonTestClass
{
public:
- void InitializeJsonReader()
+ static Json::Value VerifyParseSucceeded(const std::string_view& content)
{
- _reader = std::unique_ptr(Json::CharReaderBuilder::CharReaderBuilder().newCharReader());
- };
+ static const std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
- void InitializeJsonWriter()
- {
- _writer = std::unique_ptr(Json::StreamWriterBuilder::StreamWriterBuilder().newStreamWriter());
- }
-
- Json::Value VerifyParseSucceeded(std::string content)
- {
Json::Value root;
std::string errs;
- const bool parseResult = _reader->parse(content.c_str(), content.c_str() + content.size(), &root, &errs);
+ const bool parseResult = reader->parse(content.data(), content.data() + content.size(), &root, &errs);
VERIFY_IS_TRUE(parseResult, winrt::to_hstring(errs).c_str());
return root;
};
- std::string toString(const Json::Value& json)
+ static std::string toString(const Json::Value& json)
{
+ static const std::unique_ptr writer{ Json::StreamWriterBuilder::StreamWriterBuilder().newStreamWriter() };
+
std::stringstream s;
- _writer->write(json, &s);
+ writer->write(json, &s);
return s.str();
}
-
-protected:
- std::unique_ptr _reader;
- std::unique_ptr _writer;
};
diff --git a/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp
index 211567e49..ad7a20113 100644
--- a/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp
@@ -59,12 +59,6 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestGetKeyBindingForAction);
TEST_METHOD(KeybindingsWithoutVkey);
-
- TEST_CLASS_SETUP(ClassSetup)
- {
- InitializeJsonReader();
- return true;
- }
};
void KeyBindingsTests::KeyChords()
diff --git a/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp b/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp
index 582a52c82..54c405592 100644
--- a/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp
@@ -7,6 +7,8 @@
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "JsonTestClass.h"
+#include
+
using namespace Microsoft::Console;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace WEX::Logging;
@@ -32,81 +34,86 @@ namespace SettingsModelLocalTests
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
END_TEST_CLASS()
- TEST_METHOD(CanLayerProfile);
+ TEST_METHOD(ProfileGeneratesGuid);
TEST_METHOD(LayerProfileProperties);
TEST_METHOD(LayerProfileIcon);
TEST_METHOD(LayerProfilesOnArray);
TEST_METHOD(DuplicateProfileTest);
-
- TEST_CLASS_SETUP(ClassSetup)
- {
- InitializeJsonReader();
- return true;
- }
+ TEST_METHOD(TestGenGuidsForProfiles);
};
- void ProfileTests::CanLayerProfile()
+ void ProfileTests::ProfileGeneratesGuid()
{
- const std::string profile0String{ R"({
- "name" : "profile0",
- "guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
- })" };
- const std::string profile1String{ R"({
- "name" : "profile1",
- "guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
- })" };
- const std::string profile2String{ R"({
- "name" : "profile2",
- "guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
- })" };
- const std::string profile3String{ R"({
- "name" : "profile3"
- })" };
+ // Parse some profiles without guids. We should NOT generate new guids
+ // for them. If a profile doesn't have a GUID, we'll leave its _guid
+ // set to nullopt. The Profile::Guid() getter will
+ // ensure all profiles have a GUID that's actually set.
+ // The null guid _is_ a valid guid, so we won't re-generate that
+ // guid. null is _not_ a valid guid, so we'll leave that nullopt
- const auto profile0Json = VerifyParseSucceeded(profile0String);
- const auto profile1Json = VerifyParseSucceeded(profile1String);
- const auto profile2Json = VerifyParseSucceeded(profile2String);
- const auto profile3Json = VerifyParseSucceeded(profile3String);
+ // See SettingsTests::ValidateProfilesGenerateGuids for a version of
+ // this test that includes synthesizing GUIDS for profiles without GUIDs
+ // set
+
+ const std::string profileWithoutGuid{ R"({
+ "name" : "profile0"
+ })" };
+ const std::string secondProfileWithoutGuid{ R"({
+ "name" : "profile1"
+ })" };
+ const std::string profileWithNullForGuid{ R"({
+ "name" : "profile2",
+ "guid" : null
+ })" };
+ const std::string profileWithNullGuid{ R"({
+ "name" : "profile3",
+ "guid" : "{00000000-0000-0000-0000-000000000000}"
+ })" };
+ const std::string profileWithGuid{ R"({
+ "name" : "profile4",
+ "guid" : "{6239a42c-1de4-49a3-80bd-e8fdd045185c}"
+ })" };
+
+ const auto profile0Json = VerifyParseSucceeded(profileWithoutGuid);
+ const auto profile1Json = VerifyParseSucceeded(secondProfileWithoutGuid);
+ const auto profile2Json = VerifyParseSucceeded(profileWithNullForGuid);
+ const auto profile3Json = VerifyParseSucceeded(profileWithNullGuid);
+ const auto profile4Json = VerifyParseSucceeded(profileWithGuid);
const auto profile0 = implementation::Profile::FromJson(profile0Json);
-
- VERIFY_IS_FALSE(profile0->ShouldBeLayered(profile1Json));
- VERIFY_IS_TRUE(profile0->ShouldBeLayered(profile2Json));
- VERIFY_IS_FALSE(profile0->ShouldBeLayered(profile3Json));
-
const auto profile1 = implementation::Profile::FromJson(profile1Json);
-
- VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile0Json));
- // A profile _can_ be layered with itself, though what's the point?
- VERIFY_IS_TRUE(profile1->ShouldBeLayered(profile1Json));
- VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile2Json));
- VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile3Json));
-
+ const auto profile2 = implementation::Profile::FromJson(profile2Json);
const auto profile3 = implementation::Profile::FromJson(profile3Json);
+ const auto profile4 = implementation::Profile::FromJson(profile4Json);
+ const winrt::guid cmdGuid = Utils::GuidFromString(L"{6239a42c-1de4-49a3-80bd-e8fdd045185c}");
+ const winrt::guid nullGuid{};
- VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile0Json));
- // A profile _can_ be layered with itself, though what's the point?
- VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile1Json));
- VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile2Json));
- VERIFY_IS_TRUE(profile3->ShouldBeLayered(profile3Json));
+ VERIFY_IS_FALSE(profile0->HasGuid());
+ VERIFY_IS_FALSE(profile1->HasGuid());
+ VERIFY_IS_FALSE(profile2->HasGuid());
+ VERIFY_IS_TRUE(profile3->HasGuid());
+ VERIFY_IS_TRUE(profile4->HasGuid());
+
+ VERIFY_ARE_EQUAL(profile3->Guid(), nullGuid);
+ VERIFY_ARE_EQUAL(profile4->Guid(), cmdGuid);
}
void ProfileTests::LayerProfileProperties()
{
- const std::string profile0String{ R"({
+ static constexpr std::string_view profile0String{ R"({
"name": "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"foreground": "#000000",
"background": "#010101",
"selectionBackground": "#010101"
})" };
- const std::string profile1String{ R"({
+ static constexpr std::string_view profile1String{ R"({
"name": "profile1",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"foreground": "#020202",
"startingDirectory": "C:/"
})" };
- const std::string profile2String{ R"({
+ static constexpr std::string_view profile2String{ R"({
"name": "profile2",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"foreground": "#030303",
@@ -172,21 +179,21 @@ namespace SettingsModelLocalTests
void ProfileTests::LayerProfileIcon()
{
- const std::string profile0String{ R"({
+ static constexpr std::string_view profile0String{ R"({
"name": "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"icon": "not-null.png"
})" };
- const std::string profile1String{ R"({
+ static constexpr std::string_view profile1String{ R"({
"name": "profile1",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"icon": null
})" };
- const std::string profile2String{ R"({
+ static constexpr std::string_view profile2String{ R"({
"name": "profile2",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
})" };
- const std::string profile3String{ R"({
+ static constexpr std::string_view profile3String{ R"({
"name": "profile3",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"icon": "another-real.png"
@@ -228,102 +235,95 @@ namespace SettingsModelLocalTests
void ProfileTests::LayerProfilesOnArray()
{
- const std::string profile0String{ R"({
- "name" : "profile0",
- "guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
+ static constexpr std::string_view inboxProfiles{ R"({
+ "profiles": [
+ {
+ "name" : "profile0",
+ "guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
+ }, {
+ "name" : "profile1",
+ "guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
+ }, {
+ "name" : "profile2",
+ "guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
+ }
+ ]
})" };
- const std::string profile1String{ R"({
- "name" : "profile1",
- "guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
- })" };
- const std::string profile2String{ R"({
- "name" : "profile2",
- "guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
- })" };
- const std::string profile3String{ R"({
- "name" : "profile3",
- "guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
- })" };
- const std::string profile4String{ R"({
- "name" : "profile4",
- "guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
+ static constexpr std::string_view userProfiles{ R"({
+ "profiles": [
+ {
+ "name" : "profile3",
+ "guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
+ }, {
+ "name" : "profile4",
+ "guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
+ }
+ ]
})" };
- const auto profile0Json = VerifyParseSucceeded(profile0String);
- const auto profile1Json = VerifyParseSucceeded(profile1String);
- const auto profile2Json = VerifyParseSucceeded(profile2String);
- const auto profile3Json = VerifyParseSucceeded(profile3String);
- const auto profile4Json = VerifyParseSucceeded(profile4String);
-
- auto settings = winrt::make_self();
-
- VERIFY_ARE_EQUAL(0u, settings->_allProfiles.Size());
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile0Json));
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile1Json));
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile3Json));
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile4Json));
-
- settings->_LayerOrCreateProfile(profile0Json);
- VERIFY_ARE_EQUAL(1u, settings->_allProfiles.Size());
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile1Json));
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
-
- settings->_LayerOrCreateProfile(profile1Json);
- VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
- VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
-
- settings->_LayerOrCreateProfile(profile2Json);
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
- VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
-
- settings->_LayerOrCreateProfile(profile3Json);
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
- VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(0).Name());
-
- settings->_LayerOrCreateProfile(profile4Json);
- VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
- VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
- VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(0).Name());
+ const auto settings = winrt::make_self(userProfiles, inboxProfiles);
+ const auto allProfiles = settings->AllProfiles();
+ VERIFY_ARE_EQUAL(3u, allProfiles.Size());
+ VERIFY_ARE_EQUAL(L"profile3", allProfiles.GetAt(0).Name());
+ VERIFY_ARE_EQUAL(L"profile4", allProfiles.GetAt(1).Name());
+ VERIFY_ARE_EQUAL(L"profile2", allProfiles.GetAt(2).Name());
}
void ProfileTests::DuplicateProfileTest()
{
- const std::string profile0String{ R"({
- "name" : "profile0",
- "backgroundImage" : "some//path"
+ static constexpr std::string_view userProfiles{ R"({
+ "profiles": [
+ {
+ "name": "profile0",
+ "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
+ "backgroundImage": "file:///some/path",
+ "hidden": false,
+ }
+ ]
})" };
- const auto profile0Json = VerifyParseSucceeded(profile0String);
+ const auto settings = winrt::make_self(userProfiles);
+ const auto profile = settings->AllProfiles().GetAt(0);
+ const auto duplicatedProfile = settings->DuplicateProfile(profile);
- auto settings = winrt::make_self();
-
- settings->_LayerOrCreateProfile(profile0Json);
- auto duplicatedProfile = settings->DuplicateProfile(*settings->_FindMatchingProfile(profile0Json));
- duplicatedProfile.Name(L"profile0");
+ duplicatedProfile.Guid(profile.Guid());
+ duplicatedProfile.Name(profile.Name());
+ const auto json = winrt::get_self(profile)->ToJson();
const auto duplicatedJson = winrt::get_self(duplicatedProfile)->ToJson();
- VERIFY_ARE_EQUAL(profile0Json, duplicatedJson);
+ VERIFY_ARE_EQUAL(json, duplicatedJson, til::u8u16(toString(duplicatedJson)).c_str());
+ }
+
+ void ProfileTests::TestGenGuidsForProfiles()
+ {
+ // We'll generate GUIDs in the Profile::Guid getter. We should make sure that
+ // the GUID generated for a dynamic profile (with a source) is different
+ // than that of a profile without a source.
+
+ static constexpr std::string_view userSettings{ R"({
+ "profiles": [
+ {
+ "name": "profile0",
+ "source": "Terminal.App.UnitTest.0",
+ },
+ {
+ "name": "profile0"
+ }
+ ]
+ })" };
+
+ const auto settings = winrt::make_self(userSettings, DefaultJson);
+
+ VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size());
+
+ VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(0).HasGuid());
+ VERIFY_IS_FALSE(settings->AllProfiles().GetAt(0).Source().empty());
+
+ VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(1).Name());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(1).HasGuid());
+ VERIFY_IS_TRUE(settings->AllProfiles().GetAt(1).Source().empty());
+
+ VERIFY_ARE_NOT_EQUAL(settings->AllProfiles().GetAt(0).Guid(), settings->AllProfiles().GetAt(1).Guid());
}
}
diff --git a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp
index 54d5863e0..e538967f9 100644
--- a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp
@@ -8,7 +8,6 @@
#include "JsonTestClass.h"
#include "TestUtils.h"
#include
-#include "../ut_app/TestDynamicProfileGenerator.h"
using namespace Microsoft::Console;
using namespace WEX::Logging;
@@ -43,13 +42,6 @@ namespace SettingsModelLocalTests
TEST_METHOD(CascadiaSettings);
TEST_METHOD(LegacyFontSettings);
- TEST_CLASS_SETUP(ClassSetup)
- {
- InitializeJsonReader();
- InitializeJsonWriter();
- return true;
- }
-
private:
// Method Description:
// - deserializes and reserializes a json string representing a settings object model of type T
@@ -309,10 +301,10 @@ namespace SettingsModelLocalTests
])" };
const std::string actionsString9B{ R"([
{
- "commands":
+ "commands":
[
{
- "command":
+ "command":
{
"action": "sendInput",
"input": "${profile.name}"
@@ -325,13 +317,13 @@ namespace SettingsModelLocalTests
])" };
const std::string actionsString9C{ R""([
{
- "commands":
+ "commands":
[
{
- "commands":
+ "commands":
[
{
- "command":
+ "command":
{
"action": "sendInput",
"input": "${profile.name} ${scheme.name}"
@@ -348,7 +340,7 @@ namespace SettingsModelLocalTests
])"" };
const std::string actionsString9D{ R""([
{
- "command":
+ "command":
{
"action": "newTab",
"profile": "${profile.name}"
@@ -404,75 +396,71 @@ namespace SettingsModelLocalTests
void SerializationTests::CascadiaSettings()
{
const std::string settingsString{ R"({
- "$schema": "https://aka.ms/terminal-profiles-schema",
- "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
- "disabledProfileSources": [ "Windows.Terminal.Wsl" ],
+ "$help" : "https://aka.ms/terminal-documentation",
+ "$schema" : "https://aka.ms/terminal-profiles-schema",
+ "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
+ "disabledProfileSources": [ "Windows.Terminal.Wsl" ],
+ "profiles": {
+ "defaults": {
+ "font": {
+ "face": "Zamora Code"
+ }
+ },
+ "list": [
+ {
+ "font": { "face": "Cascadia Code" },
+ "guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
+ "name": "HowettShell"
+ },
+ {
+ "hidden": true,
+ "guid": "{c08b0496-e71c-5503-b84e-3af7a7a6d2a7}",
+ "name": "BhojwaniShell"
+ },
+ {
+ "antialiasingMode": "aliased",
+ "guid": "{fe9df758-ac22-5c20-922d-c7766cdd13af}",
+ "name": "NiksaShell"
+ }
+ ]
+ },
+ "schemes": [
+ {
+ "name": "Cinnamon Roll",
- "profiles": {
- "defaults": {
- "font": {
- "face": "Zamora Code"
- }
- },
- "list": [
- {
- "font": { "face": "Cascadia Code" },
- "guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
- "name": "HowettShell"
- },
- {
- "hidden": true,
- "name": "BhojwaniShell"
- },
- {
- "antialiasingMode": "aliased",
- "name": "NiksaShell"
- }
- ]
- },
- "schemes": [
- {
- "name": "Cinnamon Roll",
+ "cursorColor": "#FFFFFD",
+ "selectionBackground": "#FFFFFF",
- "cursorColor": "#FFFFFD",
- "selectionBackground": "#FFFFFF",
+ "background": "#3C0315",
+ "foreground": "#FFFFFD",
- "background": "#3C0315",
- "foreground": "#FFFFFD",
+ "black": "#282A2E",
+ "blue": "#0170C5",
+ "cyan": "#3F8D83",
+ "green": "#76AB23",
+ "purple": "#7D498F",
+ "red": "#BD0940",
+ "white": "#FFFFFD",
+ "yellow": "#E0DE48",
+ "brightBlack": "#676E7A",
+ "brightBlue": "#5C98C5",
+ "brightCyan": "#8ABEB7",
+ "brightGreen": "#B5D680",
+ "brightPurple": "#AC79BB",
+ "brightRed": "#BD6D85",
+ "brightWhite": "#FFFFFD",
+ "brightYellow": "#FFFD76"
+ }
+ ],
+ "actions": [
+ { "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" }
+ ]
+ })" };
- "black": "#282A2E",
- "blue": "#0170C5",
- "cyan": "#3F8D83",
- "green": "#76AB23",
- "purple": "#7D498F",
- "red": "#BD0940",
- "white": "#FFFFFD",
- "yellow": "#E0DE48",
- "brightBlack": "#676E7A",
- "brightBlue": "#5C98C5",
- "brightCyan": "#8ABEB7",
- "brightGreen": "#B5D680",
- "brightPurple": "#AC79BB",
- "brightRed": "#BD6D85",
- "brightWhite": "#FFFFFD",
- "brightYellow": "#FFFD76"
- }
- ],
- "actions": [
- { "command": { "action": "renameTab", "title": "Liang Tab" }, "keys": "ctrl+t" },
- { "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" },
- { "command": { "action": "renameWindow", "name": "Hecker Window" }, "keys": "ctrl+l" }
- ]
- })" };
-
- auto settings{ winrt::make_self(false) };
- settings->_ParseJsonString(settingsString, false);
- settings->_ApplyDefaultsFromUserSettings();
- settings->LayerJson(settings->_userSettings);
- settings->_ValidateSettings();
+ const auto settings{ winrt::make_self(settingsString) };
const auto result{ settings->ToJson() };
- VERIFY_ARE_EQUAL(toString(settings->_userSettings), toString(result));
+ VERIFY_ARE_EQUAL(toString(VerifyParseSucceeded(settingsString)), toString(result));
}
void SerializationTests::LegacyFontSettings()
diff --git a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj
index ac1af0396..576c89e7d 100644
--- a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj
+++ b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj
@@ -98,10 +98,10 @@
x86
$(Platform)
- <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\"
+ <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\runtimes\win10-$(Native-Platform)\native\"
-
+
diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
index 498f6822e..0fa507a9d 100644
--- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
@@ -62,7 +62,7 @@ namespace SettingsModelLocalTests
void TerminalSettingsTests::TestTerminalArgsForBinding()
{
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": { "list": [
@@ -106,12 +106,12 @@ namespace SettingsModelLocalTests
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}") };
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ const auto settings = winrt::make_self(settingsJson);
- auto actionMap = settings.GlobalSettings().ActionMap();
- VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
+ auto actionMap = settings->GlobalSettings().ActionMap();
+ VERIFY_ARE_EQUAL(3u, settings->ActiveProfiles().Size());
- const auto profile2Guid = settings.ActiveProfiles().GetAt(2).Guid();
+ const auto profile2Guid = settings->ActiveProfiles().GetAt(2).Guid();
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
const auto& actionMapImpl{ winrt::get_self(actionMap) };
@@ -131,8 +131,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
@@ -153,8 +153,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
@@ -175,8 +175,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
@@ -197,8 +197,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
@@ -219,13 +219,13 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
{
// This action specified a command but no profile; it gets reassigned to the base profile
- VERIFY_ARE_EQUAL(settings.ProfileDefaults(), profile);
+ VERIFY_ARE_EQUAL(settings->ProfileDefaults(), profile);
VERIFY_ARE_EQUAL(29, termSettings.HistorySize());
}
else
@@ -251,8 +251,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
@@ -271,8 +271,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
@@ -292,8 +292,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
@@ -315,8 +315,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
@@ -337,8 +337,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
@@ -360,8 +360,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
@@ -385,8 +385,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
- const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
+ const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
@@ -399,7 +399,7 @@ namespace SettingsModelLocalTests
void TerminalSettingsTests::MakeSettingsForProfile()
{
// Test that making settings generally works.
- const std::string settingsString{ R"(
+ static constexpr std::string_view settingsString{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -415,17 +415,17 @@ namespace SettingsModelLocalTests
}
]
})" };
- CascadiaSettings settings{ til::u8u16(settingsString) };
+ const auto settings = winrt::make_self(settingsString);
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
- const auto profile1 = settings.FindProfile(guid1);
- const auto profile2 = settings.FindProfile(guid2);
+ const auto profile1 = settings->FindProfile(guid1);
+ const auto profile2 = settings->FindProfile(guid2);
try
{
- auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile1, nullptr) };
+ auto terminalSettings{ TerminalSettings::CreateWithProfile(*settings, profile1, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
}
@@ -436,7 +436,7 @@ namespace SettingsModelLocalTests
try
{
- auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile2, nullptr) };
+ auto terminalSettings{ TerminalSettings::CreateWithProfile(*settings, profile2, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
}
@@ -447,7 +447,7 @@ namespace SettingsModelLocalTests
try
{
- const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
+ const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
}
@@ -463,7 +463,7 @@ namespace SettingsModelLocalTests
// defaultProfile that's not in the list, we validate the settings, and
// then call MakeSettings(nullopt). The validation should ensure that
// the default profile is something reasonable
- const std::string settingsString{ R"(
+ static constexpr std::string_view settingsString{ R"(
{
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -479,14 +479,14 @@ namespace SettingsModelLocalTests
}
]
})" };
- CascadiaSettings settings{ til::u8u16(settingsString) };
+ const auto settings = winrt::make_self(settingsString);
- VERIFY_ARE_EQUAL(2u, settings.Warnings().Size());
- VERIFY_ARE_EQUAL(2u, settings.ActiveProfiles().Size());
- VERIFY_ARE_EQUAL(settings.GlobalSettings().DefaultProfile(), settings.ActiveProfiles().GetAt(0).Guid());
+ VERIFY_ARE_EQUAL(2u, settings->Warnings().Size());
+ VERIFY_ARE_EQUAL(2u, settings->ActiveProfiles().Size());
+ VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->ActiveProfiles().GetAt(0).Guid());
try
{
- const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
+ const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
}
@@ -501,7 +501,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Ensure that setting (or not) a property in the profile that should override a property of the color scheme works correctly."));
- const std::string settings0String{ R"(
+ static constexpr std::string_view settings0String{ R"(
{
"defaultProfile": "profile5",
"profiles": [
@@ -534,18 +534,50 @@ namespace SettingsModelLocalTests
"schemes": [
{
"name": "schemeWithCursorColor",
- "cursorColor": "#123456"
+ "cursorColor": "#123456",
+ "black": "#121314",
+ "red": "#121314",
+ "green": "#121314",
+ "yellow": "#121314",
+ "blue": "#121314",
+ "purple": "#121314",
+ "cyan": "#121314",
+ "white": "#121314",
+ "brightBlack": "#121314",
+ "brightRed": "#121314",
+ "brightGreen": "#121314",
+ "brightYellow": "#121314",
+ "brightBlue": "#121314",
+ "brightPurple": "#121314",
+ "brightCyan": "#121314",
+ "brightWhite": "#121314"
},
{
- "name": "schemeWithoutCursorColor"
+ "name": "schemeWithoutCursorColor",
+ "black": "#121314",
+ "red": "#121314",
+ "green": "#121314",
+ "yellow": "#121314",
+ "blue": "#121314",
+ "purple": "#121314",
+ "cyan": "#121314",
+ "white": "#121314",
+ "brightBlack": "#121314",
+ "brightRed": "#121314",
+ "brightGreen": "#121314",
+ "brightYellow": "#121314",
+ "brightBlue": "#121314",
+ "brightPurple": "#121314",
+ "brightCyan": "#121314",
+ "brightWhite": "#121314"
}
]
})" };
- CascadiaSettings settings{ til::u8u16(settings0String) };
+ const auto settings = winrt::make_self(settings0String);
- VERIFY_ARE_EQUAL(6u, settings.ActiveProfiles().Size());
- VERIFY_ARE_EQUAL(2u, settings.GlobalSettings().ColorSchemes().Size());
+ VERIFY_ARE_EQUAL(6u, settings->ActiveProfiles().Size());
+ VERIFY_ARE_EQUAL(2u, settings->GlobalSettings().ColorSchemes().Size());
auto createTerminalSettings = [&](const auto& profile, const auto& schemes) {
auto terminalSettings{ winrt::make_self() };
@@ -554,12 +586,14 @@ namespace SettingsModelLocalTests
return terminalSettings;
};
- auto terminalSettings0 = createTerminalSettings(settings.ActiveProfiles().GetAt(0), settings.GlobalSettings().ColorSchemes());
- auto terminalSettings1 = createTerminalSettings(settings.ActiveProfiles().GetAt(1), settings.GlobalSettings().ColorSchemes());
- auto terminalSettings2 = createTerminalSettings(settings.ActiveProfiles().GetAt(2), settings.GlobalSettings().ColorSchemes());
- auto terminalSettings3 = createTerminalSettings(settings.ActiveProfiles().GetAt(3), settings.GlobalSettings().ColorSchemes());
- auto terminalSettings4 = createTerminalSettings(settings.ActiveProfiles().GetAt(4), settings.GlobalSettings().ColorSchemes());
- auto terminalSettings5 = createTerminalSettings(settings.ActiveProfiles().GetAt(5), settings.GlobalSettings().ColorSchemes());
+ const auto activeProfiles = settings->ActiveProfiles();
+ const auto colorSchemes = settings->GlobalSettings().ColorSchemes();
+ const auto terminalSettings0 = createTerminalSettings(activeProfiles.GetAt(0), colorSchemes);
+ const auto terminalSettings1 = createTerminalSettings(activeProfiles.GetAt(1), colorSchemes);
+ const auto terminalSettings2 = createTerminalSettings(activeProfiles.GetAt(2), colorSchemes);
+ const auto terminalSettings3 = createTerminalSettings(activeProfiles.GetAt(3), colorSchemes);
+ const auto terminalSettings4 = createTerminalSettings(activeProfiles.GetAt(4), colorSchemes);
+ const auto terminalSettings5 = createTerminalSettings(activeProfiles.GetAt(5), colorSchemes);
VERIFY_ARE_EQUAL(ARGB(0, 0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1->CursorColor()); // default
@@ -571,7 +605,7 @@ namespace SettingsModelLocalTests
void TerminalSettingsTests::TestCommandlineToTitlePromotion()
{
- const std::string settingsJson{ R"(
+ static constexpr std::string_view settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": { "list": [
@@ -587,65 +621,63 @@ namespace SettingsModelLocalTests
} }
})" };
- const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
-
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ const auto settings = winrt::make_self(settingsJson);
{ // just a profile (profile wins)
NewTerminalArgs args{};
args.Profile(L"profile0");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
}
{ // profile and command line -> no promotion (profile wins)
NewTerminalArgs args{};
args.Profile(L"profile0");
args.Commandline(L"foo.exe");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
}
{ // just a title -> it is propagated
NewTerminalArgs args{};
args.TabTitle(L"Analog Kid");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings().StartingTitle());
}
{ // title and command line -> no promotion
NewTerminalArgs args{};
args.TabTitle(L"Digital Man");
args.Commandline(L"foo.exe");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings().StartingTitle());
}
{ // just a commandline -> promotion
NewTerminalArgs args{};
args.Commandline(L"foo.exe");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
}
// various typesof commandline follow
{
NewTerminalArgs args{};
args.Commandline(L"foo.exe bar");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L"\"foo exe.exe\" bar");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings().StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L"\"\" grand designs");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L" imagine a man");
- const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
+ const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
}
}
diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp
index ff83a2ae5..fd7a8e446 100644
--- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp
+++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp
@@ -79,7 +79,7 @@ namespace TerminalAppLocalTests
// containing a ${profile.name} to replace. When we expand it, it should
// have created one command for each profile.
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -111,10 +111,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- const auto guid0 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
- const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
-
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -207,7 +204,7 @@ namespace TerminalAppLocalTests
// For this test, put an iterable command without a given `name` to
// replace. When we expand it, it should still work.
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -238,10 +235,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- const auto guid0 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
- const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
-
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -335,7 +329,7 @@ namespace TerminalAppLocalTests
// cause bad json to be filled in. Something like a profile with a name
// of "Foo\"", so the trailing '"' might break the json parsing.
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -367,10 +361,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- const auto guid0 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
- const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
-
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -468,7 +459,7 @@ namespace TerminalAppLocalTests
// ├─ first.com
// └─ second.com
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -508,7 +499,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@@ -558,7 +549,7 @@ namespace TerminalAppLocalTests
// ├─ child1
// └─ child2
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -603,7 +594,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@@ -691,7 +682,7 @@ namespace TerminalAppLocalTests
// ├─ Split pane, direction: right, profile: profile2
// └─ Split pane, direction: down, profile: profile2
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -727,7 +718,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@@ -828,7 +819,7 @@ namespace TerminalAppLocalTests
// ├─ Profile 2
// └─ Profile 3
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -864,7 +855,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@@ -926,7 +917,7 @@ namespace TerminalAppLocalTests
// ├─ Split right
// └─ Split down
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -967,7 +958,7 @@ namespace TerminalAppLocalTests
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@@ -1071,7 +1062,7 @@ namespace TerminalAppLocalTests
// containing a ${profile.name} to replace. When we expand it, it should
// have created one command for each profile.
- const std::string settingsJson{ R"(
+ static constexpr std::wstring_view settingsJson{ LR"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -1107,7 +1098,7 @@ namespace TerminalAppLocalTests
]
})" };
- CascadiaSettings settings{ til::u8u16(settingsJson) };
+ CascadiaSettings settings{ settingsJson, {} };
// Since at least one profile does not reference a color scheme,
// we add a warning saying "the color scheme is unknown"
diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp
index 782ae265f..4e3da70aa 100644
--- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp
+++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp
@@ -311,7 +311,7 @@ namespace TerminalAppLocalTests
// TerminalPage and not only create them successfully, but also create a
// tab using those settings successfully.
- const std::string settingsJson0{ R"(
+ static constexpr std::wstring_view settingsJson0{ LR"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -328,7 +328,7 @@ namespace TerminalAppLocalTests
]
})" };
- CascadiaSettings settings0{ til::u8u16(settingsJson0) };
+ CascadiaSettings settings0{ settingsJson0, {} };
VERIFY_IS_NOT_NULL(settings0);
// This is super wacky, but we can't just initialize the
@@ -357,7 +357,7 @@ namespace TerminalAppLocalTests
//
// Created to test GH#2455
- const std::string settingsJson0{ R"(
+ static constexpr std::wstring_view settingsJson0{ LR"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -374,7 +374,7 @@ namespace TerminalAppLocalTests
]
})" };
- const std::string settingsJson1{ R"(
+ static constexpr std::wstring_view settingsJson1{ LR"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -386,10 +386,10 @@ namespace TerminalAppLocalTests
]
})" };
- CascadiaSettings settings0{ til::u8u16(settingsJson0) };
+ CascadiaSettings settings0{ settingsJson0, {} };
VERIFY_IS_NOT_NULL(settings0);
- CascadiaSettings settings1{ til::u8u16(settingsJson1) };
+ CascadiaSettings settings1{ settingsJson1, {} };
VERIFY_IS_NOT_NULL(settings1);
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
@@ -444,7 +444,7 @@ namespace TerminalAppLocalTests
//
// Created to test GH#2455
- const std::string settingsJson0{ R"(
+ static constexpr std::wstring_view settingsJson0{ LR"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -461,7 +461,7 @@ namespace TerminalAppLocalTests
]
})" };
- const std::string settingsJson1{ R"(
+ static constexpr std::wstring_view settingsJson1{ LR"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
@@ -473,10 +473,10 @@ namespace TerminalAppLocalTests
]
})" };
- CascadiaSettings settings0{ til::u8u16(settingsJson0) };
+ CascadiaSettings settings0{ settingsJson0, {} };
VERIFY_IS_NOT_NULL(settings0);
- CascadiaSettings settings1{ til::u8u16(settingsJson1) };
+ CascadiaSettings settings1{ settingsJson1, {} };
VERIFY_IS_NOT_NULL(settings1);
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
@@ -558,7 +558,7 @@ namespace TerminalAppLocalTests
// - The initialized TerminalPage, ready to use.
winrt::com_ptr TabTests::_commonSetup()
{
- const std::string settingsJson0{ R"(
+ static constexpr std::wstring_view settingsJson0{ LR"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"showTabsInTitlebar": false,
@@ -659,7 +659,7 @@ namespace TerminalAppLocalTests
]
})" };
- CascadiaSettings settings0{ til::u8u16(settingsJson0) };
+ CascadiaSettings settings0{ settingsJson0, {} };
VERIFY_IS_NOT_NULL(settings0);
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj
index a6340764b..95d8ea17f 100644
--- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj
+++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj
@@ -92,11 +92,11 @@
x86
$(Platform)
- <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\"
+ <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\runtimes\win10-$(Native-Platform)\native\"
-
+
diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj
index cef96d52f..722ce9f5d 100644
--- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj
+++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj
@@ -123,7 +123,7 @@
-
+
diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp
index 9bf60fc8b..c16eb52e9 100644
--- a/src/cascadia/Remoting/Monarch.cpp
+++ b/src/cascadia/Remoting/Monarch.cpp
@@ -50,6 +50,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - Add the given peasant to the list of peasants we're tracking. This
// Peasant may have already been assigned an ID. If it hasn't, then give
// it an ID.
+ // - NB: this takes a unique_lock on _peasantsMutex.
// Arguments:
// - peasant: the new Peasant to track.
// Return Value:
@@ -71,10 +72,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
// Peasant already had an ID (from an older monarch). Leave that one
// be. Make sure that the next peasant's ID is higher than it.
- _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID;
+ // If multiple peasants are added concurrently we keep trying to update
+ // until we get to set the new id.
+ uint64_t current;
+ do
+ {
+ current = _nextPeasantID.load(std::memory_order_relaxed);
+ } while (current <= providedID && !_nextPeasantID.compare_exchange_weak(current, providedID + 1, std::memory_order_relaxed));
}
auto newPeasantsId = peasant.GetID();
+
+ // Keep track of which peasant we are
+ // SAFETY: this is only true for one peasant, and each peasant
+ // is only added to a monarch once, so we do not need synchronization here.
+ if (peasant.GetPID() == _ourPID)
+ {
+ _ourPeasantId = newPeasantsId;
+ }
// Add an event listener to the peasant's WindowActivated event.
peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated });
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
@@ -84,7 +99,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
peasant.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll });
- _peasants[newPeasantsId] = peasant;
+ {
+ std::unique_lock lock{ _peasantsMutex };
+ _peasants[newPeasantsId] = peasant;
+ }
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_AddPeasant",
@@ -124,9 +142,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// closing all windows.
_QuitAllRequestedHandlers(*this, nullptr);
+ _quitting.store(true);
// Tell all peasants to exit.
- const auto callback = [&](const auto& /*id*/, const auto& p) {
- p.Quit();
+ const auto callback = [&](const auto& id, const auto& p) {
+ // We want to tell our peasant to quit last, so that we don't try
+ // to perform a bunch of elections on quit.
+ if (id != _ourPeasantId)
+ {
+ p.Quit();
+ }
};
const auto onError = [&](const auto& id) {
TraceLoggingWrite(g_hRemotingProvider,
@@ -137,18 +161,46 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
};
_forEachPeasant(callback, onError);
+
+ {
+ std::shared_lock lock{ _peasantsMutex };
+ const auto peasantSearch = _peasants.find(_ourPeasantId);
+ if (peasantSearch != _peasants.end())
+ {
+ peasantSearch->second.Quit();
+ }
+ else
+ {
+ // Somehow we don't have our own peasant, this should never happen.
+ // We are trying to quit anyways so just fail here.
+ assert(peasantSearch != _peasants.end());
+ }
+ }
}
// Method Description:
// - Tells the monarch that a peasant is being closed.
+ // - NB: this (separately) takes unique locks on _peasantsMutex and
+ // _mruPeasantsMutex.
// Arguments:
// - peasantId: the id of the peasant
// Return Value:
// -
void Monarch::SignalClose(const uint64_t peasantId)
{
- _clearOldMruEntries(peasantId);
- _peasants.erase(peasantId);
+ // If we are quitting we don't care about maintaining our list of
+ // peasants anymore, and don't need to notify the host that something
+ // changed.
+ if (_quitting.load(std::memory_order_acquire))
+ {
+ return;
+ }
+
+ _clearOldMruEntries({ peasantId });
+ {
+ std::unique_lock lock{ _peasantsMutex };
+ _peasants.erase(peasantId);
+ }
_WindowClosedHandlers(nullptr, nullptr);
}
@@ -160,23 +212,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - the number of active peasants.
uint64_t Monarch::GetNumberOfPeasants()
{
- auto num = 0;
- auto callback = [&](const auto& /*id*/, const auto& p) {
- // Check that the peasant is alive, and if so increment the count
- p.GetID();
- num += 1;
- };
- auto onError = [](const auto& id) {
- TraceLoggingWrite(g_hRemotingProvider,
- "Monarch_GetNumberOfPeasants_Failed",
- TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not enumerate"),
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
- TraceLoggingKeyword(TIL_KEYWORD_TRACE));
- };
-
- _forEachPeasant(callback, onError);
-
- return num;
+ std::shared_lock lock{ _peasantsMutex };
+ return _peasants.size();
}
// Method Description:
@@ -197,16 +234,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// Method Description:
// - Lookup a peasant by its ID. If the peasant has died, this will also
// remove the peasant from our list of peasants.
+ // - NB: this (separately) takes unique locks on _peasantsMutex and
+ // _mruPeasantsMutex.
// Arguments:
// - peasantID: The ID Of the peasant to find
+ // - clearMruPeasantOnFailure: When true this function will handle clearing
+ // from _mruPeasants if a peasant was not found, otherwise the caller is
+ // expected to handle that cleanup themselves.
// Return Value:
// - the peasant if it exists in our map, otherwise null
- Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID)
+ Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID, bool clearMruPeasantOnFailure)
{
try
{
- const auto peasantSearch = _peasants.find(peasantID);
- auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second;
+ IPeasant maybeThePeasant = nullptr;
+ {
+ std::shared_lock lock{ _peasantsMutex };
+ const auto peasantSearch = _peasants.find(peasantID);
+ maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second;
+ }
// Ask the peasant for their PID. This will validate that they're
// actually still alive.
if (maybeThePeasant)
@@ -218,12 +264,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
catch (...)
{
LOG_CAUGHT_EXCEPTION();
- // Remove the peasant from the list of peasants
- _peasants.erase(peasantID);
- // Remove the peasant from the list of MRU windows. They're dead.
- // They can't be the MRU anymore.
- _clearOldMruEntries(peasantID);
+ // Remove the peasant from the list of peasants
+ {
+ std::unique_lock lock{ _peasantsMutex };
+ _peasants.erase(peasantID);
+ }
+
+ if (clearMruPeasantOnFailure)
+ {
+ // Remove the peasant from the list of MRU windows. They're dead.
+ // They can't be the MRU anymore.
+ _clearOldMruEntries({ peasantID });
+ }
return nullptr;
}
}
@@ -244,39 +297,27 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return 0;
}
- std::vector peasantsToErase{};
uint64_t result = 0;
- for (const auto& [id, p] : _peasants)
- {
- try
- {
- auto otherName = p.WindowName();
- if (otherName == name)
- {
- result = id;
- break;
- }
- }
- catch (...)
- {
- LOG_CAUGHT_EXCEPTION();
- // Normally, we'd just erase the peasant here. However, we can't
- // erase from the map while we're iterating over it like this.
- // Instead, pull a good ole Java and collect this id for removal
- // later.
- peasantsToErase.push_back(id);
- }
- }
- // Remove the dead peasants we came across while iterating.
- for (const auto& id : peasantsToErase)
- {
- // Remove the peasant from the list of peasants
- _peasants.erase(id);
- // Remove the peasant from the list of MRU windows. They're dead.
- // They can't be the MRU anymore.
- _clearOldMruEntries(id);
- }
+ const auto callback = [&](const auto& id, const auto& p) {
+ auto otherName = p.WindowName();
+ if (otherName == name)
+ {
+ result = id;
+ return false;
+ }
+ return true;
+ };
+
+ const auto onError = [&](const auto& id) {
+ TraceLoggingWrite(g_hRemotingProvider,
+ "Monarch_lookupPeasantIdForName_Failed",
+ TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not get the name of"),
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
+ TraceLoggingKeyword(TIL_KEYWORD_TRACE));
+ };
+
+ _forEachPeasant(callback, onError);
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_lookupPeasantIdForName",
@@ -328,33 +369,42 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - Helper for removing a peasant from the list of MRU peasants. We want to
// do this both when the peasant dies, and also when the peasant is newly
// activated (so that we don't leave an old entry for it in the list).
+ // - NB: This takes a unique lock on _mruPeasantsMutex.
// Arguments:
- // - peasantID: The ID of the peasant to remove from the MRU list
+ // - peasantIds: The list of peasant IDs to remove from the MRU list
// Return Value:
// -
- void Monarch::_clearOldMruEntries(const uint64_t peasantID)
+ void Monarch::_clearOldMruEntries(const std::unordered_set& peasantIds)
{
- auto result = std::find_if(_mruPeasants.begin(),
- _mruPeasants.end(),
- [peasantID](auto&& other) {
- return peasantID == other.PeasantID();
- });
-
- if (result != std::end(_mruPeasants))
+ if (peasantIds.size() == 0)
{
- TraceLoggingWrite(g_hRemotingProvider,
- "Monarch_RemovedPeasantFromDesktop",
- TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"),
- TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"),
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
- TraceLoggingKeyword(TIL_KEYWORD_TRACE));
-
- _mruPeasants.erase(result);
+ return;
}
+
+ std::unique_lock lock{ _mruPeasantsMutex };
+ auto partition = std::remove_if(_mruPeasants.begin(), _mruPeasants.end(), [&](const auto& p) {
+ const auto id = p.PeasantID();
+ // remove the element if it was found in the list to erase.
+ if (peasantIds.count(id) == 1)
+ {
+ TraceLoggingWrite(g_hRemotingProvider,
+ "Monarch_RemovedPeasantFromDesktop",
+ TraceLoggingUInt64(id, "peasantID", "The ID of the peasant"),
+ TraceLoggingGuid(p.DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"),
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
+ TraceLoggingKeyword(TIL_KEYWORD_TRACE));
+ return true;
+ }
+ return false;
+ });
+
+ // Remove everything that was in the list
+ _mruPeasants.erase(partition, _mruPeasants.end());
}
// Method Description:
// - Actually handle inserting the WindowActivatedArgs into our list of MRU windows.
+ // - NB: this takes a unique_lock on _mruPeasantsMutex.
// Arguments:
// - localArgs: an in-proc WindowActivatedArgs that we should add to our list of MRU windows.
// Return Value:
@@ -365,19 +415,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// * Check all the current lists to look for this peasant.
// remove it from any where it exists.
- _clearOldMruEntries(localArgs->PeasantID());
+ _clearOldMruEntries({ localArgs->PeasantID() });
// * If the current desktop doesn't have a vector, add one.
const auto desktopGuid{ localArgs->DesktopID() };
- // * Add this args list. By using lower_bound with insert, we can get it
- // into exactly the right spot, without having to re-sort the whole
- // array.
- _mruPeasants.insert(std::lower_bound(_mruPeasants.begin(),
- _mruPeasants.end(),
- *localArgs,
- [](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }),
- *localArgs);
+ {
+ std::unique_lock lock{ _mruPeasantsMutex };
+ // * Add this args list. By using lower_bound with insert, we can get it
+ // into exactly the right spot, without having to re-sort the whole
+ // array.
+ _mruPeasants.insert(std::lower_bound(_mruPeasants.begin(),
+ _mruPeasants.end(),
+ *localArgs,
+ [](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }),
+ *localArgs);
+ }
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SetMostRecentPeasant",
@@ -391,6 +444,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// Method Description:
// - Retrieves the ID of the MRU peasant window. If requested, will limit
// the search to windows that are on the current desktop.
+ // - NB: This method will hold a shared lock on _mruPeasantsMutex and
+ // potentially a unique_lock on _peasantsMutex at the same time.
+ // Separately it might hold a unique_lock on _mruPeasantsMutex.
// Arguments:
// - limitToCurrentDesktop: if true, only return the MRU peasant that's
// actually on the current desktop.
@@ -403,8 +459,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - the ID of the most recent peasant, otherwise 0 if we could not find one.
uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop, const bool ignoreQuakeWindow)
{
+ std::shared_lock lock{ _mruPeasantsMutex };
if (_mruPeasants.empty())
{
+ // unlock the mruPeasants mutex to make sure we can't deadlock here.
+ lock.unlock();
+ // Only need a shared lock for read
+ std::shared_lock peasantsLock{ _peasantsMutex };
// We haven't yet been told the MRU peasant. Just use the first one.
// This is just gonna be a random one, but really shouldn't happen
// in practice. The WindowManager should set the MRU peasant
@@ -445,15 +506,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - If it isn't on the current desktop, we'll loop again, on the
// following peasant.
// * If we don't care, then we'll just return that one.
- //
- // We're not just using an iterator because the contents of the list
- // might change while we're iterating here (if the peasant is dead we'll
- // remove it from the list).
- int positionInList = 0;
- while (_mruPeasants.cbegin() + positionInList < _mruPeasants.cend())
+ uint64_t result = 0;
+ std::unordered_set peasantsToErase{};
+ for (const auto& mruWindowArgs : _mruPeasants)
{
- const auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) };
- const auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) };
+ // Try to get the peasant, but do not have _getPeasant clean up old
+ // _mruPeasants because we are iterating here.
+ // SAFETY: _getPeasant can take a unique_lock on _peasantsMutex if
+ // it detects a peasant is dead. Currently _getMostRecentPeasantId
+ // is the only method that holds a lock on both _mruPeasantsMutex and
+ // _peasantsMutex at the same time so there cannot be a deadlock here.
+ const auto peasant{ _getPeasant(mruWindowArgs.PeasantID(), false) };
if (!peasant)
{
TraceLoggingWrite(g_hRemotingProvider,
@@ -467,6 +530,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// We'll go through the loop again. We removed the current one
// at positionInList, so the next one in positionInList will be
// a new, different peasant.
+ peasantsToErase.emplace(mruWindowArgs.PeasantID());
continue;
}
@@ -504,7 +568,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
"true if this window was in fact on the current desktop"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
- return mruWindowArgs.PeasantID();
+ result = mruWindowArgs.PeasantID();
+ break;
}
// If this window wasn't on the current desktop, another one
// might be. We'll increment positionInList below, and try
@@ -518,20 +583,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
- return mruWindowArgs.PeasantID();
+ result = mruWindowArgs.PeasantID();
+ break;
}
- positionInList++;
}
- // Here, we've checked all the windows, and none of them was both alive
- // and the most recent (on this desktop). Just return 0 - the caller
- // will use this to create a new window.
- TraceLoggingWrite(g_hRemotingProvider,
- "Monarch_getMostRecentPeasantID_NotFound",
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
- TraceLoggingKeyword(TIL_KEYWORD_TRACE));
+ lock.unlock();
- return 0;
+ if (peasantsToErase.size() > 0)
+ {
+ _clearOldMruEntries(peasantsToErase);
+ }
+
+ if (result == 0)
+ {
+ // Here, we've checked all the windows, and none of them was both alive
+ // and the most recent (on this desktop). Just return 0 - the caller
+ // will use this to create a new window.
+ TraceLoggingWrite(g_hRemotingProvider,
+ "Monarch_getMostRecentPeasantID_NotFound",
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
+ TraceLoggingKeyword(TIL_KEYWORD_TRACE));
+ }
+
+ return result;
}
// Method Description:
@@ -855,7 +930,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Windows::Foundation::Collections::IVectorView Monarch::GetPeasantInfos()
{
std::vector names;
- names.reserve(_peasants.size());
+ {
+ std::shared_lock lock{ _peasantsMutex };
+ names.reserve(_peasants.size());
+ }
const auto func = [&](const auto& id, const auto& p) -> void {
names.push_back({ id, p.WindowName(), p.ActiveTabTitle() });
diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h
index 2e8e40aa1..7ec904499 100644
--- a/src/cascadia/Remoting/Monarch.h
+++ b/src/cascadia/Remoting/Monarch.h
@@ -7,6 +7,7 @@
#include "Peasant.h"
#include "../cascadia/inc/cppwinrt_utils.h"
#include "WindowActivatedArgs.h"
+#include
// We sure different GUIDs here depending on whether we're running a Release,
// Preview, or Dev build. This ensures that different installs don't
@@ -69,23 +70,29 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
private:
uint64_t _ourPID;
- uint64_t _nextPeasantID{ 1 };
- uint64_t _thisPeasantID{ 0 };
+ std::atomic _nextPeasantID{ 1 };
+ uint64_t _ourPeasantId{ 0 };
+
+ // When we're quitting we do not care as much about handling some events that we know will be triggered
+ std::atomic _quitting{ false };
winrt::com_ptr _desktopManager{ nullptr };
std::unordered_map _peasants;
-
std::vector _mruPeasants;
+ // These should not be locked at the same time to prevent deadlocks
+ // unless they are both shared_locks.
+ std::shared_mutex _peasantsMutex{};
+ std::shared_mutex _mruPeasantsMutex{};
- winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID);
+ winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID, bool clearMruPeasantOnFailure = true);
uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop, const bool ignoreQuakeWindow);
uint64_t _lookupPeasantIdForName(std::wstring_view name);
void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
void _doHandleActivatePeasant(const winrt::com_ptr& args);
- void _clearOldMruEntries(const uint64_t peasantID);
+ void _clearOldMruEntries(const std::unordered_set& peasantIds);
void _forAllPeasantsIgnoringTheDead(std::function callback,
std::function errorCallback);
@@ -107,6 +114,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// returns false.
// - If any single peasant is dead, then we'll call onError and then add it to a
// list of peasants to clean up once the loop ends.
+ // - NB: this (separately) takes unique locks on _peasantsMutex and
+ // _mruPeasantsMutex.
// Arguments:
// - func: The function to call on each peasant
// - onError: The function to call if a peasant is dead.
@@ -119,44 +128,55 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
using R = std::invoke_result_t;
static constexpr auto IsVoid = std::is_void_v;
- std::vector peasantsToErase;
-
- for (const auto& [id, p] : _peasants)
+ std::unordered_set peasantsToErase;
{
- try
+ std::shared_lock lock{ _peasantsMutex };
+
+ for (const auto& [id, p] : _peasants)
{
- if constexpr (IsVoid)
+ try
{
- func(id, p);
- }
- else
- {
- if (!func(id, p))
+ if constexpr (IsVoid)
{
- break;
+ func(id, p);
+ }
+ else
+ {
+ if (!func(id, p))
+ {
+ break;
+ }
}
}
- }
- catch (const winrt::hresult_error& exception)
- {
- onError(id);
+ catch (const winrt::hresult_error& exception)
+ {
+ onError(id);
- if (exception.code() == 0x800706ba) // The RPC server is unavailable.
- {
- peasantsToErase.emplace_back(id);
- }
- else
- {
- LOG_CAUGHT_EXCEPTION();
- throw;
+ if (exception.code() == 0x800706ba) // The RPC server is unavailable.
+ {
+ peasantsToErase.emplace(id);
+ }
+ else
+ {
+ LOG_CAUGHT_EXCEPTION();
+ throw;
+ }
}
}
}
- for (const auto& id : peasantsToErase)
+ if (peasantsToErase.size() > 0)
{
- _peasants.erase(id);
- _clearOldMruEntries(id);
+ // Don't hold a lock on _peasants and _mruPeasants at the same
+ // time to avoid deadlocks.
+ {
+ std::unique_lock lock{ _peasantsMutex };
+ for (const auto& id : peasantsToErase)
+ {
+ _peasants.erase(id);
+ }
+ }
+ _clearOldMruEntries(peasantsToErase);
}
}
diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp
index e3b85e0a2..9a71fcef0 100644
--- a/src/cascadia/Remoting/WindowManager.cpp
+++ b/src/cascadia/Remoting/WindowManager.cpp
@@ -324,6 +324,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
+ // If the peasant asks us to quit we should not try to act in future elections.
+ _peasant.QuitRequested([weakThis{ get_weak() }](auto&&, auto&&) {
+ if (auto wm = weakThis.get())
+ {
+ wm->_monarchWaitInterrupt.SetEvent();
+ }
+ });
+
return _peasant;
}
diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml
index bc77872e3..251028dee 100644
--- a/src/cascadia/TerminalApp/App.xaml
+++ b/src/cascadia/TerminalApp/App.xaml
@@ -20,7 +20,8 @@
-
+
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml
index eea2039f6..b6a4676dd 100644
--- a/src/cascadia/TerminalApp/TerminalPage.xaml
+++ b/src/cascadia/TerminalApp/TerminalPage.xaml
@@ -9,10 +9,11 @@
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
+ Background="Transparent"
mc:Ignorable="d">
+ Background="Transparent">
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index f5e2ca206..53c75eb6f 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -1400,11 +1400,22 @@ namespace winrt::TerminalApp::implementation
deselectedTabBrush.Color(deselectedTabColor);
// currently if a tab has a custom color, a deselected state is
- // signified by using the same color with a bit ot transparency
+ // signified by using the same color with a bit of transparency
+ //
+ // Prior to MUX 2.7, we set TabViewItemHeaderBackground, but now we can
+ // use TabViewItem().Background() for that. HOWEVER,
+ // TabViewItem().Background() only sets the color of the tab background
+ // when the TabViewItem is unselected. So we still need to set the other
+ // properties ourselves.
+ TabViewItem().Background(deselectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
- TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
+
+ // TabViewItem().Foreground() unfortunately does not work for us. It
+ // sets the color for the text when the TabViewItem isn't selected, but
+ // not when it is hovered, pressed, dragged, or selected, so we'll need
+ // to just set them all anyways.
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
@@ -1442,7 +1453,6 @@ namespace winrt::TerminalApp::implementation
void TerminalTab::_ClearTabBackgroundColor()
{
winrt::hstring keys[] = {
- L"TabViewItemHeaderBackground",
L"TabViewItemHeaderBackgroundSelected",
L"TabViewItemHeaderBackgroundPointerOver",
L"TabViewItemHeaderForeground",
@@ -1463,6 +1473,9 @@ namespace winrt::TerminalApp::implementation
}
}
+ // Clear out the Background.
+ TabViewItem().Background(nullptr);
+
_RefreshVisualState();
_colorCleared();
}
@@ -1487,7 +1500,7 @@ namespace winrt::TerminalApp::implementation
// -
void TerminalTab::_RefreshVisualState()
{
- if (_focusState != FocusState::Unfocused)
+ if (TabViewItem().IsSelected())
{
VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
index 5f9d3f7c7..a5d865776 100644
--- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
+++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
@@ -89,13 +89,13 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
diff --git a/src/cascadia/TerminalApp/packages.config b/src/cascadia/TerminalApp/packages.config
index 9de6f1b8d..78467467c 100644
--- a/src/cascadia/TerminalApp/packages.config
+++ b/src/cascadia/TerminalApp/packages.config
@@ -1,6 +1,6 @@
-
+
diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp
index 5c2373fd2..b6ad2ce7f 100644
--- a/src/cascadia/TerminalConnection/ConptyConnection.cpp
+++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp
@@ -16,6 +16,10 @@
#include "LibraryResources.h"
using namespace ::Microsoft::Console;
+using namespace std::string_view_literals;
+
+// Format is: "DecimalResult (HexadecimalForm)"
+static constexpr auto _errorFormat = L"{0} ({0:#010x})"sv;
// Notes:
// There is a number of ways that the Conpty connection can be terminated (voluntarily or not):
@@ -417,7 +421,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto hr = wil::ResultFromCaughtException();
winrt::hstring failureText{ fmt::format(std::wstring_view{ RS_(L"ProcessFailedToLaunch") },
- gsl::narrow_cast(hr),
+ fmt::format(_errorFormat, hr),
_commandline) };
_TerminalOutputHandlers(failureText);
@@ -444,7 +448,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
try
{
- winrt::hstring exitText{ fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, status) };
+ winrt::hstring exitText{ fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, fmt::format(_errorFormat, status)) };
_TerminalOutputHandlers(L"\r\n");
_TerminalOutputHandlers(exitText);
}
diff --git a/src/cascadia/TerminalConnection/Resources/en-US/Resources.resw b/src/cascadia/TerminalConnection/Resources/en-US/Resources.resw
index d32f59269..7af3cf590 100644
--- a/src/cascadia/TerminalConnection/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalConnection/Resources/en-US/Resources.resw
@@ -205,15 +205,15 @@
[process exited with code {0}]
- The first argument {0} is the (positive) error code of the process. When there is no error, the number ZERO will be displayed.
+ The first argument {0} is the error code of the process. When there is no error, the number ZERO will be displayed.
- [error {0:#08x} when launching `{1}']
- The first argument {0...} is the hexadecimal error code. The second argument {1} is the user-specified path to a program.
+ [error {0} when launching `{1}']
+ The first argument {0} is the error code. The second argument {1} is the user-specified path to a program.
If this string is broken to multiple lines, it will not be displayed properly.
Could not access starting directory "{0}"
The first argument {0} is a path to a directory on the filesystem, as provided by the user.
-
\ No newline at end of file
+
diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
index 5cfd57c40..f594bc46f 100644
--- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
+++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
@@ -56,7 +56,9 @@
-
+
+ Designer
+
@@ -88,11 +90,11 @@
- $(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories)
+ $(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories)
$(OpenConsoleCommonOutDir)\conptylib.lib;%(AdditionalDependencies)
-
+
\ No newline at end of file
diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj.filters b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj.filters
index 12a9cc6c4..a7d2f9092 100644
--- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj.filters
+++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj.filters
@@ -32,6 +32,7 @@
+
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index eac350c2a..e09eb93ae 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -276,7 +276,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// GH#5098: Inform the engine of the opacity of the default text background.
if (_settings.UseAcrylic())
{
- _renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast(_settings.TintOpacity()));
+ _renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast(_settings.Opacity()));
}
THROW_IF_FAILED(_renderEngine->Enable());
@@ -425,41 +425,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
- auto newOpacity = std::clamp(_settings.TintOpacity() + adjustment,
+ auto newOpacity = std::clamp(_settings.Opacity() + adjustment,
0.0,
1.0);
- if (_settings.UseAcrylic())
- {
- try
- {
- _settings.TintOpacity(newOpacity);
- if (newOpacity >= 1.0)
- {
- _settings.UseAcrylic(false);
- }
- else
- {
- // GH#5098: Inform the engine of the new opacity of the default text background.
- SetBackgroundOpacity(::base::saturated_cast(newOpacity));
- }
+ // GH#5098: Inform the engine of the new opacity of the default text background.
+ SetBackgroundOpacity(::base::saturated_cast(newOpacity));
- auto eventArgs = winrt::make_self(newOpacity);
- _TransparencyChangedHandlers(*this, *eventArgs);
- }
- CATCH_LOG();
- }
- else if (adjustment < 0)
- {
- _settings.UseAcrylic(true);
+ _settings.Opacity(newOpacity);
- //Setting initial opacity set to 1 to ensure smooth transition to acrylic during mouse scroll
- newOpacity = std::clamp(1.0 + adjustment, 0.0, 1.0);
- _settings.TintOpacity(newOpacity);
-
- auto eventArgs = winrt::make_self(newOpacity);
- _TransparencyChangedHandlers(*this, *eventArgs);
- }
+ auto eventArgs = winrt::make_self(newOpacity);
+ _TransparencyChangedHandlers(*this, *eventArgs);
}
void ControlCore::ToggleShaderEffects()
@@ -1003,6 +979,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_terminal->WritePastedText(hstr);
_terminal->ClearSelection();
+ _renderer->TriggerSelection();
_terminal->TrySnapOnInput();
}
diff --git a/src/cascadia/TerminalControl/IControlAppearance.idl b/src/cascadia/TerminalControl/IControlAppearance.idl
index a0fa0a0ad..fd06bc853 100644
--- a/src/cascadia/TerminalControl/IControlAppearance.idl
+++ b/src/cascadia/TerminalControl/IControlAppearance.idl
@@ -13,6 +13,7 @@ namespace Microsoft.Terminal.Control
Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment;
Boolean IntenseIsBold;
// IntenseIsBright is in Core Appearance
+ Double Opacity;
// Experimental settings
Boolean RetroTerminalEffect;
diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl
index 56aab7395..1b67df91f 100644
--- a/src/cascadia/TerminalControl/IControlSettings.idl
+++ b/src/cascadia/TerminalControl/IControlSettings.idl
@@ -29,7 +29,6 @@ namespace Microsoft.Terminal.Control
String ProfileName;
Boolean UseAcrylic;
- Double TintOpacity;
ScrollbarState ScrollState;
String FontFace;
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 115f9afc5..fc74dd0a9 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -428,6 +428,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// -
void TermControl::_InitializeBackgroundBrush()
{
+ auto appearance = _settings.try_as();
if (_settings.UseAcrylic())
{
// See if we've already got an acrylic background brush
@@ -449,7 +450,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
acrylic.TintColor(bgColor);
// Apply brush settings
- acrylic.TintOpacity(_settings.TintOpacity());
+ acrylic.TintOpacity(appearance.Opacity());
// Apply brush to control if it's not already there
if (RootGrid().Background() != acrylic)
@@ -458,15 +459,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
// GH#5098: Inform the engine of the new opacity of the default text background.
- _core.SetBackgroundOpacity(::base::saturated_cast(_settings.TintOpacity()));
+ _core.SetBackgroundOpacity(::base::saturated_cast(appearance.Opacity()));
}
else
{
Media::SolidColorBrush solidColor{};
+ solidColor.Opacity(_settings.Opacity());
RootGrid().Background(solidColor);
// GH#5098: Inform the engine of the new opacity of the default text background.
- _core.SetBackgroundOpacity(1.0f);
+ _core.SetBackgroundOpacity(appearance.Opacity());
}
}
@@ -497,7 +499,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else if (auto solidColor = RootGrid().Background().try_as())
{
+ const auto originalOpacity = solidColor.Opacity();
solidColor.Color(bg);
+ solidColor.Opacity(originalOpacity);
}
}
}
diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml
index 94aa92c5f..663ccf7ce 100644
--- a/src/cascadia/TerminalControl/TermControl.xaml
+++ b/src/cascadia/TerminalControl/TermControl.xaml
@@ -14,6 +14,7 @@
d:DesignWidth="1024"
AllowDrop="True"
AllowFocusOnInteraction="True"
+ Background="Transparent"
CharacterReceived="_CharacterHandler"
DragOver="_DragOverHandler"
Drop="_DragDropHandler"
diff --git a/src/cascadia/TerminalSettingsEditor/Launch.cpp b/src/cascadia/TerminalSettingsEditor/Launch.cpp
index 09215561d..4dee9ac5f 100644
--- a/src/cascadia/TerminalSettingsEditor/Launch.cpp
+++ b/src/cascadia/TerminalSettingsEditor/Launch.cpp
@@ -34,7 +34,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Launch::OnNavigatedTo(const NavigationEventArgs& e)
{
_State = e.Parameter().as();
- _State.Settings().RefreshDefaultTerminals();
}
IInspectable Launch::CurrentDefaultProfile()
diff --git a/src/cascadia/TerminalSettingsEditor/Launch.xaml b/src/cascadia/TerminalSettingsEditor/Launch.xaml
index ece569aff..ce6e35ef2 100644
--- a/src/cascadia/TerminalSettingsEditor/Launch.xaml
+++ b/src/cascadia/TerminalSettingsEditor/Launch.xaml
@@ -74,7 +74,7 @@
x:Uid="Globals_DefaultTerminal"
x:Load="false">
diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.xaml b/src/cascadia/TerminalSettingsEditor/MainPage.xaml
index 9576102a1..9d24ac109 100644
--- a/src/cascadia/TerminalSettingsEditor/MainPage.xaml
+++ b/src/cascadia/TerminalSettingsEditor/MainPage.xaml
@@ -8,6 +8,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
+ Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj
index e60fccb32..e4335126c 100644
--- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj
+++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj
@@ -312,12 +312,12 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.h b/src/cascadia/TerminalSettingsEditor/Profiles.h
index e1ceba5d0..54d7d4108 100644
--- a/src/cascadia/TerminalSettingsEditor/Profiles.h
+++ b/src/cascadia/TerminalSettingsEditor/Profiles.h
@@ -22,7 +22,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void SetAcrylicOpacityPercentageValue(double value)
{
- AcrylicOpacity(winrt::Microsoft::Terminal::Settings::Editor::Converters::PercentageValueToPercentage(value));
+ _profile.DefaultAppearance().Opacity(winrt::Microsoft::Terminal::Settings::Editor::Converters::PercentageValueToPercentage(value));
};
void SetPadding(double value)
@@ -66,7 +66,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_profile, TabColor);
OBSERVABLE_PROJECTED_SETTING(_profile, SuppressApplicationTitle);
OBSERVABLE_PROJECTED_SETTING(_profile, UseAcrylic);
- OBSERVABLE_PROJECTED_SETTING(_profile, AcrylicOpacity);
OBSERVABLE_PROJECTED_SETTING(_profile, ScrollState);
OBSERVABLE_PROJECTED_SETTING(_profile, Padding);
OBSERVABLE_PROJECTED_SETTING(_profile, Commandline);
@@ -78,6 +77,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Background);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), SelectionBackground);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), CursorColor);
+ OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Opacity);
OBSERVABLE_PROJECTED_SETTING(_profile, HistorySize);
OBSERVABLE_PROJECTED_SETTING(_profile, SnapOnInput);
OBSERVABLE_PROJECTED_SETTING(_profile, AltGrAliasing);
diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.idl b/src/cascadia/TerminalSettingsEditor/Profiles.idl
index 4ecc48899..482a19671 100644
--- a/src/cascadia/TerminalSettingsEditor/Profiles.idl
+++ b/src/cascadia/TerminalSettingsEditor/Profiles.idl
@@ -47,7 +47,7 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference, TabColor);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SuppressApplicationTitle);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, UseAcrylic);
- OBSERVABLE_PROJECTED_PROFILE_SETTING(Double, AcrylicOpacity);
+ OBSERVABLE_PROJECTED_PROFILE_SETTING(Double, Opacity);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.ScrollbarState, ScrollState);
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Padding);
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Commandline);
@@ -109,6 +109,6 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentScrollState;
Windows.Foundation.Collections.IObservableVector ScrollStateList { get; };
- Windows.UI.Xaml.Controls.Slider AcrylicOpacitySlider { get; };
+ Windows.UI.Xaml.Controls.Slider OpacitySlider { get; };
}
}
diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.xaml b/src/cascadia/TerminalSettingsEditor/Profiles.xaml
index 5e11e2450..24e6a7fe1 100644
--- a/src/cascadia/TerminalSettingsEditor/Profiles.xaml
+++ b/src/cascadia/TerminalSettingsEditor/Profiles.xaml
@@ -238,11 +238,33 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
index 4c39ba4d1..a21ae08e3 100644
--- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
@@ -495,6 +495,14 @@
Sets the transparency of the window.
A description for what the "acrylic opacity" setting does. Presented near "Profile_AcrylicOpacity.Header".
+
+ Background Opacity
+ Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque.
+
+
+ Sets the transparency of the window.
+ A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header".
+
Advanced
Header for a sub-page of profile settings focused on more advanced scenarios.
@@ -986,6 +994,10 @@
Acrylic
Header for a group of settings related to the acrylic texture rendering on the background of the app.
+
+ Transparency
+ Header for a group of settings related to transparency, including the acrylic texture rendering on the background of the app.
+
Background image
Header for a group of settings that control the image presented on the background of the app. Presented near "Profile_BackgroundImage" and other keys starting with "Profile_BackgroundImage".
diff --git a/src/cascadia/TerminalSettingsEditor/packages.config b/src/cascadia/TerminalSettingsEditor/packages.config
index ecf244764..bd1e02e27 100644
--- a/src/cascadia/TerminalSettingsEditor/packages.config
+++ b/src/cascadia/TerminalSettingsEditor/packages.config
@@ -1,5 +1,5 @@
-
+
diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp
index 67eb279ea..2384de2ff 100644
--- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp
+++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp
@@ -26,30 +26,32 @@ static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageA
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
static constexpr std::string_view PixelShaderPathKey{ "experimental.pixelShaderPath" };
static constexpr std::string_view IntenseTextStyleKey{ "intenseTextStyle" };
+static constexpr std::string_view LegacyAcrylicTransparencyKey{ "acrylicOpacity" };
+static constexpr std::string_view OpacityKey{ "opacity" };
-winrt::Microsoft::Terminal::Settings::Model::implementation::AppearanceConfig::AppearanceConfig(const winrt::weak_ref sourceProfile) :
- _sourceProfile(sourceProfile)
+AppearanceConfig::AppearanceConfig(winrt::weak_ref sourceProfile) :
+ _sourceProfile(std::move(sourceProfile))
{
}
-winrt::com_ptr AppearanceConfig::CopyAppearance(const winrt::com_ptr source, const winrt::weak_ref sourceProfile)
+winrt::com_ptr AppearanceConfig::CopyAppearance(const AppearanceConfig* source, winrt::weak_ref sourceProfile)
{
- auto appearance{ winrt::make_self(sourceProfile) };
- auto const sourceAppearance = source.try_as();
- appearance->_BackgroundImagePath = sourceAppearance->_BackgroundImagePath;
- appearance->_BackgroundImageOpacity = sourceAppearance->_BackgroundImageOpacity;
- appearance->_BackgroundImageStretchMode = sourceAppearance->_BackgroundImageStretchMode;
- appearance->_ColorSchemeName = sourceAppearance->_ColorSchemeName;
- appearance->_Foreground = sourceAppearance->_Foreground;
- appearance->_Background = sourceAppearance->_Background;
- appearance->_SelectionBackground = sourceAppearance->_SelectionBackground;
- appearance->_CursorColor = sourceAppearance->_CursorColor;
- appearance->_CursorShape = sourceAppearance->_CursorShape;
- appearance->_CursorHeight = sourceAppearance->_CursorHeight;
- appearance->_BackgroundImageAlignment = sourceAppearance->_BackgroundImageAlignment;
- appearance->_RetroTerminalEffect = sourceAppearance->_RetroTerminalEffect;
- appearance->_PixelShaderPath = sourceAppearance->_PixelShaderPath;
- appearance->_IntenseTextStyle = sourceAppearance->_IntenseTextStyle;
+ auto appearance{ winrt::make_self(std::move(sourceProfile)) };
+ appearance->_BackgroundImagePath = source->_BackgroundImagePath;
+ appearance->_BackgroundImageOpacity = source->_BackgroundImageOpacity;
+ appearance->_BackgroundImageStretchMode = source->_BackgroundImageStretchMode;
+ appearance->_ColorSchemeName = source->_ColorSchemeName;
+ appearance->_Foreground = source->_Foreground;
+ appearance->_Background = source->_Background;
+ appearance->_SelectionBackground = source->_SelectionBackground;
+ appearance->_CursorColor = source->_CursorColor;
+ appearance->_CursorShape = source->_CursorShape;
+ appearance->_CursorHeight = source->_CursorHeight;
+ appearance->_BackgroundImageAlignment = source->_BackgroundImageAlignment;
+ appearance->_RetroTerminalEffect = source->_RetroTerminalEffect;
+ appearance->_PixelShaderPath = source->_PixelShaderPath;
+ appearance->_IntenseTextStyle = source->_IntenseTextStyle;
+ appearance->_Opacity = source->_Opacity;
return appearance;
}
@@ -71,6 +73,7 @@ Json::Value AppearanceConfig::ToJson() const
JsonUtils::SetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect);
JsonUtils::SetValueForKey(json, PixelShaderPathKey, _PixelShaderPath);
JsonUtils::SetValueForKey(json, IntenseTextStyleKey, _IntenseTextStyle);
+ JsonUtils::SetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{});
return json;
}
@@ -102,6 +105,8 @@ void AppearanceConfig::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect);
JsonUtils::GetValueForKey(json, PixelShaderPathKey, _PixelShaderPath);
JsonUtils::GetValueForKey(json, IntenseTextStyleKey, _IntenseTextStyle);
+ JsonUtils::GetValueForKey(json, LegacyAcrylicTransparencyKey, _Opacity);
+ JsonUtils::GetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{});
}
winrt::Microsoft::Terminal::Settings::Model::Profile AppearanceConfig::SourceProfile()
diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h
index 7a0c47985..e15f5ef82 100644
--- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h
+++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h
@@ -18,7 +18,6 @@ Author(s):
#include "AppearanceConfig.g.h"
#include "JsonUtils.h"
-#include "../inc/cppwinrt_utils.h"
#include "IInheritable.h"
#include
@@ -27,8 +26,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct AppearanceConfig : AppearanceConfigT, IInheritable
{
public:
- AppearanceConfig(const winrt::weak_ref sourceProfile);
- static winrt::com_ptr CopyAppearance(const winrt::com_ptr source, const winrt::weak_ref sourceProfile);
+ AppearanceConfig(winrt::weak_ref sourceProfile);
+ static winrt::com_ptr CopyAppearance(const AppearanceConfig* source, winrt::weak_ref sourceProfile);
Json::Value ToJson() const;
void LayerJson(const Json::Value& json);
@@ -53,6 +52,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::IAppearanceConfig, bool, RetroTerminalEffect, false);
INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, PixelShaderPath, L"");
INHERITABLE_SETTING(Model::IAppearanceConfig, Model::IntenseStyle, IntenseTextStyle, Model::IntenseStyle::Bright);
+ INHERITABLE_SETTING(Model::IAppearanceConfig, double, Opacity, 1.0);
private:
winrt::weak_ref _sourceProfile;
diff --git a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp
index 41b26163d..a69f44eae 100644
--- a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp
+++ b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp
@@ -6,16 +6,14 @@
#include "AzureCloudShellGenerator.h"
#include "LegacyProfileGeneratorNamespaces.h"
-#include "../../types/inc/utils.hpp"
#include "../../inc/DefaultSettings.h"
-#include "Utils.h"
-#include "DefaultProfileUtils.h"
+#include "DynamicProfileUtils.h"
using namespace ::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
-std::wstring_view AzureCloudShellGenerator::GetNamespace()
+std::wstring_view AzureCloudShellGenerator::GetNamespace() const noexcept
{
return AzureGeneratorNamespace;
}
@@ -27,19 +25,14 @@ std::wstring_view AzureCloudShellGenerator::GetNamespace()
// -
// Return Value:
// - a vector with the Azure Cloud Shell connection profile, if available.
-std::vector AzureCloudShellGenerator::GenerateProfiles()
+void AzureCloudShellGenerator::GenerateProfiles(std::vector>& profiles) const
{
- std::vector profiles;
-
if (AzureConnection::IsAzureConnectionAvailable())
{
- auto azureCloudShellProfile{ CreateDefaultProfile(L"Azure Cloud Shell") };
- azureCloudShellProfile.Commandline(L"Azure");
- azureCloudShellProfile.StartingDirectory(DEFAULT_STARTING_DIRECTORY);
- azureCloudShellProfile.DefaultAppearance().ColorSchemeName(L"Vintage");
- azureCloudShellProfile.ConnectionType(AzureConnection::ConnectionType());
- profiles.emplace_back(azureCloudShellProfile);
+ auto azureCloudShellProfile{ CreateDynamicProfile(L"Azure Cloud Shell") };
+ azureCloudShellProfile->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY });
+ azureCloudShellProfile->DefaultAppearance().ColorSchemeName(L"Vintage");
+ azureCloudShellProfile->ConnectionType(AzureConnection::ConnectionType());
+ profiles.emplace_back(std::move(azureCloudShellProfile));
}
-
- return profiles;
}
diff --git a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h
index 79ad05a31..f62b66fce 100644
--- a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h
+++ b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h
@@ -16,17 +16,15 @@ Author(s):
--*/
#pragma once
+
#include "IDynamicProfileGenerator.h"
-namespace Microsoft::Terminal::Settings::Model
+namespace winrt::Microsoft::Terminal::Settings::Model
{
- class AzureCloudShellGenerator : public IDynamicProfileGenerator
+ class AzureCloudShellGenerator final : public IDynamicProfileGenerator
{
public:
- AzureCloudShellGenerator() = default;
- ~AzureCloudShellGenerator() = default;
- std::wstring_view GetNamespace() override;
-
- std::vector GenerateProfiles() override;
+ std::wstring_view GetNamespace() const noexcept override;
+ void GenerateProfiles(std::vector>& profiles) const override;
};
};
diff --git a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp b/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp
index 14a61f33b..cb351935f 100644
--- a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp
+++ b/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp
@@ -3,15 +3,12 @@
#include "pch.h"
#include "BaseVisualStudioGenerator.h"
-#include "DefaultProfileUtils.h"
+#include "DynamicProfileUtils.h"
-using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model;
-std::vector BaseVisualStudioGenerator::GenerateProfiles()
+void BaseVisualStudioGenerator::GenerateProfiles(std::vector>& profiles) const
{
- std::vector profiles;
-
// There's no point in enumerating valid Visual Studio instances more than once,
// so cache them for use by both Visual Studio profile generators.
static const auto instances = VsSetupConfiguration::QueryInstances();
@@ -25,27 +22,15 @@ std::vector BaseVisualStudioGenerator::GenerateProfiles()
continue;
}
- auto DevShell{ CreateProfile(GetProfileGuidSeed(instance)) };
- DevShell.Name(GetProfileName(instance));
- DevShell.Commandline(GetProfileCommandLine(instance));
- DevShell.StartingDirectory(instance.GetInstallationPath());
- DevShell.Icon(GetProfileIconPath());
-
- profiles.emplace_back(DevShell);
+ const auto seed = GetProfileGuidSeed(instance);
+ const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(seed))) };
+ auto profile = winrt::make_self(profileGuid);
+ profile->Name(winrt::hstring{ GetProfileName(instance) });
+ profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance) });
+ profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() });
+ profile->Icon(winrt::hstring{ GetProfileIconPath() });
+ profiles.emplace_back(std::move(profile));
}
CATCH_LOG();
}
-
- return profiles;
-}
-
-Profile BaseVisualStudioGenerator::CreateProfile(const std::wstring_view seed)
-{
- const winrt::guid profileGuid{ Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID,
- gsl::as_bytes(gsl::make_span(seed))) };
-
- auto newProfile = winrt::make_self(profileGuid);
- newProfile->Origin(OriginTag::Generated);
-
- return *newProfile;
}
diff --git a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h b/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h
index eba2ea598..e2ea620b8 100644
--- a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h
+++ b/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h
@@ -18,14 +18,12 @@ Author(s):
#include "IDynamicProfileGenerator.h"
#include "VsSetupConfiguration.h"
-namespace Microsoft::Terminal::Settings::Model
+namespace winrt::Microsoft::Terminal::Settings::Model
{
class BaseVisualStudioGenerator : public IDynamicProfileGenerator
{
public:
- // Inherited via IDynamicProfileGenerator
- std::wstring_view GetNamespace() override = 0;
- std::vector GenerateProfiles() override;
+ void GenerateProfiles(std::vector>& profiles) const override;
protected:
virtual bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance& instance) const = 0;
@@ -33,8 +31,5 @@ namespace Microsoft::Terminal::Settings::Model
virtual std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const = 0;
virtual std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const = 0;
virtual std::wstring GetProfileIconPath() const = 0;
-
- private:
- winrt::Microsoft::Terminal::Settings::Model::Profile CreateProfile(const std::wstring_view instanceId);
};
};
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
index e98592ce4..61521ade2 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
@@ -6,137 +6,99 @@
#include "CascadiaSettings.g.cpp"
#include
+#include
-#include "AzureCloudShellGenerator.h"
-#include "PowershellCoreProfileGenerator.h"
-#include "VsDevCmdGenerator.h"
-#include "VsDevShellGenerator.h"
-#include "WslDistroGenerator.h"
-
-using namespace ::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal;
-using namespace winrt::Microsoft::Terminal::Control;
+using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
+using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Windows::Foundation::Collections;
using namespace Microsoft::Console;
-static constexpr std::wstring_view PACKAGED_PROFILE_ICON_PATH{ L"ms-appx:///ProfileIcons/" };
-
-static constexpr std::wstring_view PACKAGED_PROFILE_ICON_EXTENSION{ L".png" };
-static constexpr std::wstring_view DEFAULT_LINUX_ICON_GUID{ L"{9acb9455-ca41-5af7-950f-6bca1bc9722f}" };
-
-// make sure this matches defaults.json.
-static constexpr std::wstring_view DEFAULT_WINDOWS_POWERSHELL_GUID{ L"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" };
-
-CascadiaSettings::CascadiaSettings() :
- CascadiaSettings(true)
+winrt::com_ptr Model::implementation::CreateChild(const winrt::com_ptr& parent)
{
+ auto profile = winrt::make_self();
+ profile->Origin(OriginTag::User);
+ profile->Name(parent->Name());
+ profile->Guid(parent->Guid());
+ profile->Hidden(parent->Hidden());
+ profile->InsertParent(parent);
+ return profile;
}
-// Constructor Description:
-// - Creates a new settings object. If addDynamicProfiles is true, we'll
-// automatically add the built-in profile generators to our list of profile
-// generators. Set this to `false` for unit testing.
-// Arguments:
-// - addDynamicProfiles: if true, we'll add the built-in DPGs.
-CascadiaSettings::CascadiaSettings(const bool addDynamicProfiles) :
- _globals{ winrt::make_self() },
- _allProfiles{ winrt::single_threaded_observable_vector() },
- _activeProfiles{ winrt::single_threaded_observable_vector() },
- _warnings{ winrt::single_threaded_vector() },
- _deserializationErrorMessage{ L"" },
- _defaultTerminals{ winrt::single_threaded_observable_vector() },
- _currentDefaultTerminal{ nullptr }
+Model::CascadiaSettings CascadiaSettings::Copy() const
{
- if (addDynamicProfiles)
+ const auto settings{ winrt::make_self() };
+
+ // user settings
{
- _profileGenerators.emplace_back(std::make_unique());
- _profileGenerators.emplace_back(std::make_unique());
- _profileGenerators.emplace_back(std::make_unique());
- _profileGenerators.emplace_back(std::make_unique());
- _profileGenerators.emplace_back(std::make_unique());
+ std::vector allProfiles;
+ std::vector activeProfiles;
+ allProfiles.reserve(_allProfiles.Size());
+ activeProfiles.reserve(_activeProfiles.Size());
+
+ // Clone the graph of profiles.
+ // _baseLayerProfile is part of the graph
+ // and thus needs to be handled here as well.
+ {
+ std::vector> sourceProfiles;
+ std::vector> targetProfiles;
+ sourceProfiles.reserve(allProfiles.size());
+ targetProfiles.reserve(allProfiles.size());
+
+ for (const auto& profile : _allProfiles)
+ {
+ winrt::com_ptr profileImpl;
+ profileImpl.copy_from(winrt::get_self(profile));
+ sourceProfiles.emplace_back(std::move(profileImpl));
+ }
+
+ // Profiles are basically a directed acyclic graph. Cloning it without creating duplicated nodes,
+ // requires us to "intern" visited profiles. Thus the "visited" map contains a cache of
+ // previously cloned profiles/sub-graphs. It maps from source-profile-pointer to cloned-profile.
+ std::unordered_map> visited;
+ // I'm just gonna estimate that each profile has 3 parents at most on average:
+ // * base layer
+ // * fragment
+ // * inbox defaults
+ visited.reserve(sourceProfiles.size() * 3);
+
+ // _baseLayerProfile is part of the profile graph.
+ // In order to get a reference to the clone, we need to copy it explicitly.
+ settings->_baseLayerProfile = _baseLayerProfile->CopyInheritanceGraph(visited);
+ Profile::CopyInheritanceGraphs(visited, sourceProfiles, targetProfiles);
+
+ for (const auto& profile : targetProfiles)
+ {
+ allProfiles.emplace_back(*profile);
+ if (!profile->Hidden())
+ {
+ activeProfiles.emplace_back(*profile);
+ }
+ }
+ }
+
+ settings->_globals = _globals->Copy();
+ settings->_allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles));
+ settings->_activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles));
}
-}
-CascadiaSettings::CascadiaSettings(winrt::hstring json) :
- CascadiaSettings(false)
-{
- const auto jsonString{ til::u16u8(json) };
- _ParseJsonString(jsonString, false);
- _ApplyDefaultsFromUserSettings();
- LayerJson(_userSettings);
- _ValidateSettings();
-}
-
-winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::Copy() const
-{
- // dynamic profile generators added by default
- auto settings{ winrt::make_self() };
- settings->_globals = _globals->Copy();
- for (auto warning : _warnings)
+ // load errors
{
- settings->_warnings.Append(warning);
- }
- settings->_loadError = _loadError;
- settings->_deserializationErrorMessage = _deserializationErrorMessage;
- settings->_userSettingsString = _userSettingsString;
- settings->_userSettings = _userSettings;
- settings->_defaultSettings = _defaultSettings;
+ std::vector warnings{ _warnings.Size() };
+ _warnings.GetMany(0, warnings);
- settings->_defaultTerminals = _defaultTerminals;
+ settings->_warnings = winrt::single_threaded_vector(std::move(warnings));
+ settings->_loadError = _loadError;
+ settings->_deserializationErrorMessage = _deserializationErrorMessage;
+ }
+
+ // defterm
settings->_currentDefaultTerminal = _currentDefaultTerminal;
- _CopyProfileInheritanceTree(settings);
-
return *settings;
}
-// Method Description:
-// - Copies the inheritance tree for profiles and hooks them up to a clone CascadiaSettings
-// Arguments:
-// - cloneSettings: the CascadiaSettings we're copying the inheritance tree to
-// Return Value:
-// -
-void CascadiaSettings::_CopyProfileInheritanceTree(winrt::com_ptr& cloneSettings) const
-{
- // Our profiles inheritance graph doesn't have a formal root.
- // However, if we create a dummy Profile, and set _profiles as the parent,
- // we now have a root. So we'll do just that, then copy the inheritance graph
- // from the dummyRoot.
- auto dummyRootSource{ winrt::make_self() };
- for (const auto& profile : _allProfiles)
- {
- winrt::com_ptr profileImpl;
- profileImpl.copy_from(winrt::get_self(profile));
- Profile::InsertParentHelper(dummyRootSource, profileImpl);
- }
-
- auto dummyRootClone{ winrt::make_self() };
- std::unordered_map> visited{};
-
- if (_userDefaultProfileSettings)
- {
- // profile.defaults must be saved to CascadiaSettings
- // So let's do that manually first, and add that to visited
- cloneSettings->_userDefaultProfileSettings = Profile::CopySettings(_userDefaultProfileSettings);
- visited[_userDefaultProfileSettings.get()] = cloneSettings->_userDefaultProfileSettings;
- }
-
- Profile::CloneInheritanceGraph(dummyRootSource, dummyRootClone, visited);
-
- // All of the parents of the dummy root clone are _profiles.
- // Get the parents and add them to the settings clone.
- const auto cloneParents{ dummyRootClone->Parents() };
- for (const auto& profile : cloneParents)
- {
- cloneSettings->_allProfiles.Append(*profile);
- if (!profile->Hidden())
- {
- cloneSettings->_activeProfiles.Append(*profile);
- }
- }
-}
-
// Method Description:
// - Finds a profile that matches the given GUID. If there is no profile in this
// settings object that matches, returns nullptr.
@@ -145,18 +107,14 @@ void CascadiaSettings::_CopyProfileInheritanceTree(winrt::com_ptr
// Return Value:
// - an iterable collection of all of our Profiles.
-IObservableVector CascadiaSettings::AllProfiles() const noexcept
+IObservableVector CascadiaSettings::AllProfiles() const noexcept
{
return _allProfiles;
}
@@ -178,7 +136,7 @@ IObservableVector Cascadia
// -
// Return Value:
// - an iterable collection of all of our Profiles.
-IObservableVector CascadiaSettings::ActiveProfiles() const noexcept
+IObservableVector CascadiaSettings::ActiveProfiles() const noexcept
{
return _activeProfiles;
}
@@ -189,7 +147,7 @@ IObservableVector Cascadia
// -
// Return Value:
// - the globally configured keybindings
-winrt::Microsoft::Terminal::Settings::Model::ActionMap CascadiaSettings::ActionMap() const noexcept
+Model::ActionMap CascadiaSettings::ActionMap() const noexcept
{
return _globals->ActionMap();
}
@@ -200,7 +158,7 @@ winrt::Microsoft::Terminal::Settings::Model::ActionMap CascadiaSettings::ActionM
// -
// Return Value:
// - a reference to our global settings
-winrt::Microsoft::Terminal::Settings::Model::GlobalAppSettings CascadiaSettings::GlobalSettings() const
+Model::GlobalAppSettings CascadiaSettings::GlobalSettings() const
{
return *_globals;
}
@@ -211,9 +169,9 @@ winrt::Microsoft::Terminal::Settings::Model::GlobalAppSettings CascadiaSettings:
// -
// Return Value:
// - a reference to our profile.defaults object
-winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::ProfileDefaults() const
+Model::Profile CascadiaSettings::ProfileDefaults() const
{
- return *_userDefaultProfileSettings;
+ return *_baseLayerProfile;
}
// Method Description:
@@ -222,7 +180,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::ProfileDe
// -
// Return Value:
// - a reference to the new profile
-winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::CreateNewProfile()
+Model::Profile CascadiaSettings::CreateNewProfile()
{
if (_allProfiles.Size() == std::numeric_limits::max())
{
@@ -241,7 +199,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::CreateNew
}
}
- const auto newProfile = _CreateNewProfile(newName);
+ const auto newProfile = _createNewProfile(newName);
_allProfiles.Append(*newProfile);
_activeProfiles.Append(*newProfile);
return *newProfile;
@@ -259,7 +217,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::CreateNew
// - source: the Profile object we are duplicating (must not be null)
// Return Value:
// - a reference to the new profile
-winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::DuplicateProfile(const Model::Profile& source)
+Model::Profile CascadiaSettings::DuplicateProfile(const Model::Profile& source)
{
THROW_HR_IF_NULL(E_INVALIDARG, source);
@@ -276,7 +234,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
newName = fmt::format(L"{} ({} {})", source.Name(), RS_(L"CopySuffix"), candidateIndex + 2);
}
- const auto duplicated = _CreateNewProfile(newName);
+ const auto duplicated = _createNewProfile(newName);
static constexpr auto isProfilesDefaultsOrigin = [](const auto& profile) -> bool {
return profile && profile.Origin() != OriginTag::ProfilesDefaults;
@@ -286,16 +244,19 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
return sub && isProfilesDefaultsOrigin(sub.SourceProfile());
};
-#define DUPLICATE_SETTING_MACRO(settingName) \
- if (source.Has##settingName() || isProfilesDefaultsOrigin(source.settingName##OverrideSource())) \
- { \
- duplicated->settingName(source.settingName()); \
+#define NEEDS_DUPLICATION(settingName) source.Has##settingName() || isProfilesDefaultsOrigin(source.settingName##OverrideSource())
+#define NEEDS_DUPLICATION_SUB(source, settingName) source.Has##settingName() || isProfilesDefaultsOriginSub(source.settingName##OverrideSource())
+
+#define DUPLICATE_SETTING_MACRO(settingName) \
+ if (NEEDS_DUPLICATION(settingName)) \
+ { \
+ duplicated->settingName(source.settingName()); \
}
-#define DUPLICATE_SETTING_MACRO_SUB(source, target, settingName) \
- if (source.Has##settingName() || isProfilesDefaultsOriginSub(source.settingName##OverrideSource())) \
- { \
- target.settingName(source.settingName()); \
+#define DUPLICATE_SETTING_MACRO_SUB(source, target, settingName) \
+ if (NEEDS_DUPLICATION_SUB(source, settingName)) \
+ { \
+ target.settingName(source.settingName()); \
}
// If the source is hidden and the Settings UI creates a
@@ -307,7 +268,6 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
DUPLICATE_SETTING_MACRO(TabColor);
DUPLICATE_SETTING_MACRO(SuppressApplicationTitle);
DUPLICATE_SETTING_MACRO(UseAcrylic);
- DUPLICATE_SETTING_MACRO(AcrylicOpacity);
DUPLICATE_SETTING_MACRO(ScrollState);
DUPLICATE_SETTING_MACRO(Padding);
DUPLICATE_SETTING_MACRO(Commandline);
@@ -322,7 +282,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
{
const auto font = source.FontInfo();
- auto target = duplicated->FontInfo();
+ const auto target = duplicated->FontInfo();
DUPLICATE_SETTING_MACRO_SUB(font, target, FontFace);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontSize);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontWeight);
@@ -332,7 +292,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
{
const auto appearance = source.DefaultAppearance();
- auto target = duplicated->DefaultAppearance();
+ const auto target = duplicated->DefaultAppearance();
DUPLICATE_SETTING_MACRO_SUB(appearance, target, ColorSchemeName);
DUPLICATE_SETTING_MACRO_SUB(appearance, target, Foreground);
DUPLICATE_SETTING_MACRO_SUB(appearance, target, Background);
@@ -347,32 +307,25 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
DUPLICATE_SETTING_MACRO_SUB(appearance, target, RetroTerminalEffect);
DUPLICATE_SETTING_MACRO_SUB(appearance, target, CursorShape);
DUPLICATE_SETTING_MACRO_SUB(appearance, target, CursorHeight);
+ DUPLICATE_SETTING_MACRO_SUB(appearance, target, Opacity);
}
// UnfocusedAppearance is treated as a single setting,
// but requires a little more legwork to duplicate properly
- if (source.HasUnfocusedAppearance() ||
- (source.UnfocusedAppearanceOverrideSource() != nullptr && source.UnfocusedAppearanceOverrideSource().Origin() != OriginTag::ProfilesDefaults))
+ if (NEEDS_DUPLICATION(UnfocusedAppearance))
{
- // First, get a com_ptr to the source's unfocused appearance
- // We need this to be able to call CopyAppearance (it is alright to simply call CopyAppearance here
- // instead of needing a separate function like DuplicateAppearance since UnfocusedAppearance is treated
- // as a single setting)
- winrt::com_ptr sourceUnfocusedAppearanceImpl;
- sourceUnfocusedAppearanceImpl.copy_from(winrt::get_self(source.UnfocusedAppearance()));
-
- // Get a weak ref to the duplicate profile so we can provide a source profile to the new UnfocusedAppearance
- // we are about to create
- const auto weakRefToDuplicated = weak_ref(*duplicated);
- auto duplicatedUnfocusedAppearanceImpl = AppearanceConfig::CopyAppearance(sourceUnfocusedAppearanceImpl, weakRefToDuplicated);
+ // It is alright to simply call CopyAppearance here instead of needing a separate function
+ // like DuplicateAppearance since UnfocusedAppearance is treated as a single setting.
+ const auto unfocusedAppearance = AppearanceConfig::CopyAppearance(
+ winrt::get_self(source.UnfocusedAppearance()),
+ winrt::weak_ref(*duplicated));
// Make sure to add the default appearance of the duplicated profile as a parent to the duplicate's UnfocusedAppearance
- winrt::com_ptr duplicatedDefaultAppearanceImpl;
- duplicatedDefaultAppearanceImpl.copy_from(winrt::get_self(duplicated->DefaultAppearance()));
- duplicatedUnfocusedAppearanceImpl->InsertParent(duplicatedDefaultAppearanceImpl);
+ winrt::com_ptr defaultAppearance;
+ defaultAppearance.copy_from(winrt::get_self(duplicated->DefaultAppearance()));
+ unfocusedAppearance->InsertParent(defaultAppearance);
- // Finally, set the duplicate's UnfocusedAppearance
- duplicated->UnfocusedAppearance(*duplicatedUnfocusedAppearanceImpl);
+ duplicated->UnfocusedAppearance(*unfocusedAppearance);
}
if (source.HasConnectionType())
@@ -390,54 +343,33 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
// knew were bad when we called `_ValidateSettings` last.
// Return Value:
// - a reference to our list of warnings.
-IVectorView CascadiaSettings::Warnings()
+IVectorView CascadiaSettings::Warnings() const
{
return _warnings.GetView();
}
-void CascadiaSettings::ClearWarnings()
-{
- _warnings.Clear();
-}
-
-void CascadiaSettings::AppendWarning(SettingsLoadWarnings warning)
-{
- _warnings.Append(warning);
-}
-
-winrt::Windows::Foundation::IReference CascadiaSettings::GetLoadingError()
+winrt::Windows::Foundation::IReference CascadiaSettings::GetLoadingError() const
{
return _loadError;
}
-winrt::hstring CascadiaSettings::GetSerializationErrorMessage()
+winrt::hstring CascadiaSettings::GetSerializationErrorMessage() const
{
return _deserializationErrorMessage;
}
// As used by CreateNewProfile and DuplicateProfile this function
// creates a new Profile instance with a random UUID and a given name.
-winrt::com_ptr CascadiaSettings::_CreateNewProfile(const std::wstring_view& name) const
+winrt::com_ptr CascadiaSettings::_createNewProfile(const std::wstring_view& name) const
{
- winrt::com_ptr profile;
-
- if (_userDefaultProfileSettings)
- {
- profile = _userDefaultProfileSettings->CreateChild();
- }
- else
- {
- profile = winrt::make_self();
- }
-
// Technically there's Utils::CreateV5Uuid which we could use, but I wanted
// truly globally unique UUIDs for profiles created through the settings UI.
GUID guid{};
LOG_IF_FAILED(CoCreateGuid(&guid));
+ auto profile = CreateChild(_baseLayerProfile);
profile->Guid(guid);
profile->Name(winrt::hstring{ name });
-
return profile;
}
@@ -451,237 +383,12 @@ winrt::com_ptr CascadiaSettings::_CreateNewProfile(const std::wstring_v
// -
// Return Value:
// -
-void CascadiaSettings::_ValidateSettings()
+void CascadiaSettings::_validateSettings()
{
- // Make sure to check that profiles exists at all first and foremost:
- _ValidateProfilesExist();
-
- // Re-order profiles so that all profiles from the user's settings appear
- // before profiles that _weren't_ in the user profiles.
- _ReorderProfilesToMatchUserSettingsOrder();
-
- // Remove hidden profiles _after_ re-ordering. The re-ordering uses the raw
- // json, and will get confused if the profile isn't in the list.
- _UpdateActiveProfiles();
-
- // Then do some validation on the profiles. The order of these does not
- // terribly matter.
- _ValidateNoDuplicateProfiles();
-
- // Resolve the default profile before we validate that it exists.
- _ResolveDefaultProfile();
- _ValidateDefaultProfileExists();
-
- // Ensure that all the profile's color scheme names are
- // actually the names of schemes we've parsed. If the scheme doesn't exist,
- // just use the hardcoded defaults
- _ValidateAllSchemesExist();
-
- // Ensure all profile's with specified images resources have valid file path.
- // This validates icons and background images.
- _ValidateMediaResources();
-
- // TODO:GH#2548 ensure there's at least one key bound. Display a warning if
- // there's _NO_ keys bound to any actions. That's highly irregular, and
- // likely an indication of an error somehow.
-
- // GH#3522 - With variable args to keybindings, it's possible that a user
- // set a keybinding without all the required args for an action. Display a
- // warning if an action didn't have a required arg.
- // This will also catch other keybinding warnings, like from GH#4239
- _ValidateKeybindings();
-
- _ValidateColorSchemesInCommands();
-
- _ValidateNoGlobalsKey();
-}
-
-// Method Description:
-// - Checks if the settings contain profiles at all. As we'll need to have some
-// profiles at all, we'll throw an error if there aren't any profiles.
-void CascadiaSettings::_ValidateProfilesExist()
-{
- const bool hasProfiles = _allProfiles.Size() > 0;
- if (!hasProfiles)
- {
- // Throw an exception. This is an invalid state, and we want the app to
- // be able to gracefully use the default settings.
-
- // We can't add the warning to the list of warnings here, because this
- // object is not going to be returned at any point.
-
- throw SettingsException(Microsoft::Terminal::Settings::Model::SettingsLoadErrors::NoProfiles);
- }
-}
-
-// Method Description:
-// - Resolves the "defaultProfile", which can be a profile name, to a GUID
-// and stores it back to the globals.
-void CascadiaSettings::_ResolveDefaultProfile()
-{
- const auto unparsedDefaultProfile{ GlobalSettings().UnparsedDefaultProfile() };
- if (!unparsedDefaultProfile.empty())
- {
- auto maybeParsedDefaultProfile{ _GetProfileGuidByName(unparsedDefaultProfile) };
- auto defaultProfileGuid{ til::coalesce_value(maybeParsedDefaultProfile, winrt::guid{}) };
- GlobalSettings().DefaultProfile(defaultProfileGuid);
- }
-}
-
-// Method Description:
-// - Checks if the "defaultProfile" is set to one of the profiles we
-// actually have. If the value is unset, or the value is set to something that
-// doesn't exist in the list of profiles, we'll arbitrarily pick the first
-// profile to use temporarily as the default.
-// - Appends a SettingsLoadWarnings::MissingDefaultProfile to our list of
-// warnings if we failed to find the default.
-void CascadiaSettings::_ValidateDefaultProfileExists()
-{
- const winrt::guid defaultProfileGuid{ GlobalSettings().DefaultProfile() };
- const bool nullDefaultProfile = defaultProfileGuid == winrt::guid{};
- bool defaultProfileNotInProfiles = true;
- for (const auto& profile : _allProfiles)
- {
- if (profile.Guid() == defaultProfileGuid)
- {
- defaultProfileNotInProfiles = false;
- break;
- }
- }
-
- if (nullDefaultProfile || defaultProfileNotInProfiles)
- {
- _warnings.Append(Microsoft::Terminal::Settings::Model::SettingsLoadWarnings::MissingDefaultProfile);
- // Use the first profile as the new default
-
- // _temporarily_ set the default profile to the first profile. Because
- // we're adding a warning, this settings change won't be re-serialized.
- GlobalSettings().DefaultProfile(_allProfiles.GetAt(0).Guid());
- }
-}
-
-// Method Description:
-// - Checks to make sure there aren't any duplicate profiles in the list of
-// profiles. If so, we'll remove the subsequent entries (temporarily), as they
-// won't be accessible anyways.
-// - Appends a SettingsLoadWarnings::DuplicateProfile to our list of warnings if
-// we find any such duplicate.
-void CascadiaSettings::_ValidateNoDuplicateProfiles()
-{
- bool foundDupe = false;
-
- std::vector indicesToDelete;
-
- std::set uniqueGuids;
-
- // Try collecting all the unique guids. If we ever encounter a guid that's
- // already in the set, then we need to delete that profile.
- for (uint32_t i = 0; i < _allProfiles.Size(); i++)
- {
- if (!uniqueGuids.insert(_allProfiles.GetAt(i).Guid()).second)
- {
- foundDupe = true;
- indicesToDelete.push_back(i);
- }
- }
-
- // Remove all the duplicates we've marked
- // Walk backwards, so we don't accidentally shift any of the elements
- for (auto iter = indicesToDelete.rbegin(); iter != indicesToDelete.rend(); iter++)
- {
- _allProfiles.RemoveAt(*iter);
- }
-
- if (foundDupe)
- {
- _warnings.Append(Microsoft::Terminal::Settings::Model::SettingsLoadWarnings::DuplicateProfile);
- }
-}
-
-// Method Description:
-// - Re-orders the list of profiles to match what the user would expect them to
-// be. Orders profiles to be in the ordering { [profiles from user settings],
-// [default profiles that weren't in the user profiles]}.
-// - Does not set any warnings.
-// Arguments:
-// -
-// Return Value:
-// -
-void CascadiaSettings::_ReorderProfilesToMatchUserSettingsOrder()
-{
- std::set uniqueGuids;
- std::deque guidOrder;
-
- auto collectGuids = [&](const auto& json) {
- for (auto profileJson : _GetProfilesJsonObject(json))
- {
- if (profileJson.isObject())
- {
- auto guid = implementation::Profile::GetGuidOrGenerateForJson(profileJson);
- if (uniqueGuids.insert(guid).second)
- {
- guidOrder.push_back(guid);
- }
- }
- }
- };
-
- // Push all the userSettings profiles' GUIDS into the set
- collectGuids(_userSettings);
-
- // Push all the defaultSettings profiles' GUIDS into the set
- collectGuids(_defaultSettings);
- std::equal_to equals;
- // Re-order the list of profiles to match that ordering
- // for (gIndex=0 -> uniqueGuids.size)
- // pIndex = the pIndex of the profile with guid==guids[gIndex]
- // profiles.swap(pIndex <-> gIndex)
- // This is O(N^2), which is kinda rough. I'm sure there's a better way
- for (uint32_t gIndex = 0; gIndex < guidOrder.size(); gIndex++)
- {
- const auto guid = guidOrder.at(gIndex);
- for (uint32_t pIndex = gIndex; pIndex < _allProfiles.Size(); pIndex++)
- {
- auto profileGuid = _allProfiles.GetAt(pIndex).Guid();
- if (equals(profileGuid, guid))
- {
- auto prof1 = _allProfiles.GetAt(pIndex);
- _allProfiles.SetAt(pIndex, _allProfiles.GetAt(gIndex));
- _allProfiles.SetAt(gIndex, prof1);
- break;
- }
- }
- }
-}
-
-// Method Description:
-// - Updates the list of active profiles from the list of all profiles
-// - If there are no active profiles (all profiles are hidden), throw a SettingsException
-// - Does not set any warnings.
-// Arguments:
-// -
-// Return Value:
-// -
-void CascadiaSettings::_UpdateActiveProfiles()
-{
- _activeProfiles.Clear();
- for (auto const& profile : _allProfiles)
- {
- if (!profile.Hidden())
- {
- _activeProfiles.Append(profile);
- }
- }
-
- // Ensure that we still have some profiles here. If we don't, then throw an
- // exception, so the app can use the defaults.
- const bool hasProfiles = _activeProfiles.Size() > 0;
- if (!hasProfiles)
- {
- // Throw an exception. This is an invalid state, and we want the app to
- // be able to gracefully use the default settings.
- throw SettingsException(SettingsLoadErrors::AllProfilesHidden);
- }
+ _validateAllSchemesExist();
+ _validateMediaResources();
+ _validateKeybindings();
+ _validateColorSchemesInCommands();
}
// Method Description:
@@ -694,24 +401,19 @@ void CascadiaSettings::_UpdateActiveProfiles()
// -
// - Appends a SettingsLoadWarnings::UnknownColorScheme to our list of warnings if
// we find any such duplicate.
-void CascadiaSettings::_ValidateAllSchemesExist()
+void CascadiaSettings::_validateAllSchemesExist()
{
+ const auto colorSchemes = _globals->ColorSchemes();
bool foundInvalidScheme = false;
- for (auto profile : _allProfiles)
+
+ for (const auto& profile : _allProfiles)
{
- const auto schemeName = profile.DefaultAppearance().ColorSchemeName();
- if (!_globals->ColorSchemes().HasKey(schemeName))
+ for (const auto& appearance : std::array{ profile.DefaultAppearance(), profile.UnfocusedAppearance() })
{
- // Clear the user set color scheme. We'll just fallback instead.
- profile.DefaultAppearance().ClearColorSchemeName();
- foundInvalidScheme = true;
- }
- if (profile.UnfocusedAppearance())
- {
- const auto unfocusedSchemeName = profile.UnfocusedAppearance().ColorSchemeName();
- if (!_globals->ColorSchemes().HasKey(unfocusedSchemeName))
+ if (appearance && !colorSchemes.HasKey(appearance.ColorSchemeName()))
{
- profile.UnfocusedAppearance().ClearColorSchemeName();
+ // Clear the user set color scheme. We'll just fallback instead.
+ appearance.ClearColorSchemeName();
foundInvalidScheme = true;
}
}
@@ -734,65 +436,61 @@ void CascadiaSettings::_ValidateAllSchemesExist()
// we find any invalid background images.
// - Appends a SettingsLoadWarnings::InvalidIconImage to our list of warnings if
// we find any invalid icon images.
-void CascadiaSettings::_ValidateMediaResources()
+void CascadiaSettings::_validateMediaResources()
{
bool invalidBackground{ false };
bool invalidIcon{ false };
for (auto profile : _allProfiles)
{
- if (!profile.DefaultAppearance().BackgroundImagePath().empty())
+ if (const auto path = profile.DefaultAppearance().ExpandedBackgroundImagePath(); !path.empty())
{
// Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable.
// This covers file paths on the machine, app data, URLs, and other resource paths.
try
{
- winrt::Windows::Foundation::Uri imagePath{ profile.DefaultAppearance().ExpandedBackgroundImagePath() };
+ winrt::Windows::Foundation::Uri imagePath{ path };
}
catch (...)
{
// reset background image path
- profile.DefaultAppearance().BackgroundImagePath(L"");
+ profile.DefaultAppearance().ClearBackgroundImagePath();
invalidBackground = true;
}
}
if (profile.UnfocusedAppearance())
{
- if (!profile.UnfocusedAppearance().BackgroundImagePath().empty())
+ if (const auto path = profile.UnfocusedAppearance().ExpandedBackgroundImagePath(); !path.empty())
{
// Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable.
// This covers file paths on the machine, app data, URLs, and other resource paths.
try
{
- winrt::Windows::Foundation::Uri imagePath{ profile.UnfocusedAppearance().ExpandedBackgroundImagePath() };
+ winrt::Windows::Foundation::Uri imagePath{ path };
}
catch (...)
{
// reset background image path
- profile.UnfocusedAppearance().BackgroundImagePath(L"");
+ profile.UnfocusedAppearance().ClearBackgroundImagePath();
invalidBackground = true;
}
}
}
- if (!profile.Icon().empty())
+ // Anything longer than 2 wchar_t's _isn't_ an emoji or symbol,
+ // so treat it as an invalid path.
+ if (const auto icon = profile.Icon(); icon.size() > 2)
{
- const auto iconPath{ wil::ExpandEnvironmentStringsW(profile.Icon().c_str()) };
+ const auto iconPath{ wil::ExpandEnvironmentStringsW(icon.c_str()) };
try
{
winrt::Windows::Foundation::Uri imagePath{ iconPath };
}
catch (...)
{
- // Anything longer than 2 wchar_t's _isn't_ an emoji or symbol,
- // so treat it as an invalid path.
- if (iconPath.size() > 2)
- {
- // reset icon path
- profile.Icon(L"");
- invalidIcon = true;
- }
+ profile.ClearIcon();
+ invalidIcon = true;
}
}
}
@@ -824,27 +522,22 @@ void CascadiaSettings::_ValidateMediaResources()
// and attempt to look the profile up by name instead.
// Return Value:
// - the GUID of the profile corresponding to this combination of index and NewTerminalArgs
-winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const
+Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const
{
- std::optional profileByIndex, profileByName;
if (newTerminalArgs)
{
- if (newTerminalArgs.ProfileIndex() != nullptr)
+ if (auto profile = GetProfileByName(newTerminalArgs.Profile()))
{
- profileByIndex = _GetProfileGuidByIndex(newTerminalArgs.ProfileIndex().Value());
+ return profile;
}
- profileByName = _GetProfileGuidByName(newTerminalArgs.Profile());
- }
-
- if (profileByName)
- {
- return FindProfile(*profileByName);
- }
-
- if (profileByIndex)
- {
- return FindProfile(*profileByIndex);
+ if (const auto index = newTerminalArgs.ProfileIndex())
+ {
+ if (auto profile = GetProfileByIndex(gsl::narrow(index.Value())))
+ {
+ return profile;
+ }
+ }
}
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
@@ -866,13 +559,12 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::GetProfil
}
// Method Description:
-// - Helper to get the GUID of a profile given a name that could be a guid or an actual name.
+// - Helper to get a profile given a name that could be a guid or an actual name.
// Arguments:
// - name: a guid string _or_ the name of a profile
// Return Value:
// - the GUID of the profile corresponding to this name
-std::optional CascadiaSettings::_GetProfileGuidByName(const winrt::hstring name) const
-try
+Model::Profile CascadiaSettings::GetProfileByName(const winrt::hstring& name) const
{
// First, try and parse the "name" as a GUID. If it's a
// GUID, and the GUID of one of our profiles, then use that as the
@@ -885,10 +577,10 @@ try
// it doesn't, it's _definitely_ not a GUID.
if (name.size() == 38 && name[0] == L'{')
{
- const auto newGUID{ Utils::GuidFromString(static_cast(name)) };
- if (FindProfile(newGUID))
+ const auto newGUID{ Utils::GuidFromString(name.c_str()) };
+ if (auto profile = FindProfile(newGUID))
{
- return newGUID;
+ return profile;
}
}
@@ -899,43 +591,24 @@ try
{
if (profile.Name() == name)
{
- return profile.Guid();
+ return profile;
}
}
}
- return std::nullopt;
-}
-catch (...)
-{
- LOG_CAUGHT_EXCEPTION();
- return std::nullopt;
+ return nullptr;
}
// Method Description:
-// - Helper to find the profile GUID for a the profile at the given index in the
-// list of profiles. If no index is provided, this instead returns the default
-// profile's guid. This is used by the NewTabProfile ShortcutActions to
-// create a tab for the Nth profile in the list of profiles.
+// - Helper to get the profile at the given index in the list of profiles.
+// - Returns a nullptr if the index is out of bounds.
// Arguments:
-// - index: if provided, the index in the list of profiles to get the GUID for.
-// If omitted, instead return the default profile's GUID
+// - index: The profile index in ActiveProfiles()
// Return Value:
-// - the Nth profile's GUID, or the default profile's GUID
-std::optional CascadiaSettings::_GetProfileGuidByIndex(std::optional index) const
+// - the Nth profile
+Model::Profile CascadiaSettings::GetProfileByIndex(uint32_t index) const
{
- if (index)
- {
- const auto realIndex{ index.value() };
- // If we don't have that many profiles, then do nothing.
- if (realIndex >= 0 &&
- realIndex < gsl::narrow_cast(_activeProfiles.Size()))
- {
- const auto& selectedProfile = _activeProfiles.GetAt(realIndex);
- return selectedProfile.Guid();
- }
- }
- return std::nullopt;
+ return index < _activeProfiles.Size() ? _activeProfiles.GetAt(index) : nullptr;
}
// Method Description:
@@ -943,13 +616,18 @@ std::optional CascadiaSettings::_GetProfileGuidByIndex(std::optiona
// keybindings, add them to the list of warnings here. If there were warnings
// generated in this way, we'll add a AtLeastOneKeybindingWarning, which will
// act as a header for the other warnings
+// - GH#3522
+// With variable args to keybindings, it's possible that a user
+// set a keybinding without all the required args for an action.
+// Display a warning if an action didn't have a required arg.
+// This will also catch other keybinding warnings, like from GH#4239.
// Arguments:
// -
// Return Value:
// -
-void CascadiaSettings::_ValidateKeybindings()
+void CascadiaSettings::_validateKeybindings() const
{
- auto keybindingWarnings = _globals->KeybindingsWarnings();
+ const auto keybindingWarnings = _globals->KeybindingsWarnings();
if (!keybindingWarnings.empty())
{
@@ -969,12 +647,12 @@ void CascadiaSettings::_ValidateKeybindings()
// -
// - Appends a SettingsLoadWarnings::InvalidColorSchemeInCmd to our list of warnings if
// we find any command with an invalid color scheme.
-void CascadiaSettings::_ValidateColorSchemesInCommands()
+void CascadiaSettings::_validateColorSchemesInCommands() const
{
bool foundInvalidScheme{ false };
for (const auto& nameAndCmd : _globals->ActionMap().NameMap())
{
- if (_HasInvalidColorScheme(nameAndCmd.Value()))
+ if (_hasInvalidColorScheme(nameAndCmd.Value()))
{
foundInvalidScheme = true;
break;
@@ -987,14 +665,14 @@ void CascadiaSettings::_ValidateColorSchemesInCommands()
}
}
-bool CascadiaSettings::_HasInvalidColorScheme(const Model::Command& command)
+bool CascadiaSettings::_hasInvalidColorScheme(const Model::Command& command) const
{
bool invalid{ false };
if (command.HasNestedCommands())
{
for (const auto& nested : command.NestedCommands())
{
- if (_HasInvalidColorScheme(nested.Value()))
+ if (_hasInvalidColorScheme(nested.Value()))
{
invalid = true;
break;
@@ -1005,7 +683,7 @@ bool CascadiaSettings::_HasInvalidColorScheme(const Model::Command& command)
{
if (const auto& realArgs = actionAndArgs.Args().try_as())
{
- auto cmdImpl{ winrt::get_self(command) };
+ const auto cmdImpl{ winrt::get_self(command) };
// no need to validate iterable commands on color schemes
// they will be expanded to commands with a valid scheme name
if (cmdImpl->IterateOn() != ExpandCommandType::ColorSchemes &&
@@ -1019,66 +697,6 @@ bool CascadiaSettings::_HasInvalidColorScheme(const Model::Command& command)
return invalid;
}
-// Method Description:
-// - Checks for the presence of the legacy "globals" key in the user's
-// settings.json. If this key is present, then they've probably got a pre-0.11
-// settings file that won't work as expected anymore. We should warn them
-// about that.
-// Arguments:
-// -
-// Return Value:
-// -
-// - Appends a SettingsLoadWarnings::LegacyGlobalsProperty to our list of warnings if
-// we find any invalid background images.
-void CascadiaSettings::_ValidateNoGlobalsKey()
-{
- // use isMember here. If you use [], you're actually injecting "globals": null.
- if (_userSettings.isMember("globals"))
- {
- _warnings.Append(SettingsLoadWarnings::LegacyGlobalsProperty);
- }
-}
-
-// Method Description
-// - Replaces known tokens DEFAULT_PROFILE, PRODUCT and VERSION in the settings template
-// with their expected values. DEFAULT_PROFILE is updated to match PowerShell Core's GUID
-// if such a profile is detected. If it isn't, it'll be set to Windows PowerShell's GUID.
-// Arguments:
-// - settingsTemplate: a settings template
-// Return value:
-// - The new settings string.
-std::string CascadiaSettings::_ApplyFirstRunChangesToSettingsTemplate(std::string_view settingsTemplate) const
-{
- // We're using replace_needle_in_haystack_inplace here, because it's more
- // efficient to iteratively modify a single string in-place than it is to
- // keep copying over the contents and modifying a copy (which
- // replace_needle_in_haystack would do).
- std::string finalSettings{ settingsTemplate };
-
- std::wstring defaultProfileGuid{ DEFAULT_WINDOWS_POWERSHELL_GUID };
- if (const auto psCoreProfileGuid{ _GetProfileGuidByName(hstring{ PowershellCoreProfileGenerator::GetPreferredPowershellProfileName() }) })
- {
- defaultProfileGuid = Utils::GuidToString(*psCoreProfileGuid);
- }
-
- til::replace_needle_in_haystack_inplace(finalSettings,
- "%DEFAULT_PROFILE%",
- til::u16u8(defaultProfileGuid));
-
- til::replace_needle_in_haystack_inplace(finalSettings,
- "%VERSION%",
- til::u16u8(ApplicationVersion()));
- til::replace_needle_in_haystack_inplace(finalSettings,
- "%PRODUCT%",
- til::u16u8(ApplicationDisplayName()));
-
- til::replace_needle_in_haystack_inplace(finalSettings,
- "%COMMAND_PROMPT_LOCALIZED_NAME%",
- RS_A(L"CommandPromptDisplayName"));
-
- return finalSettings;
-}
-
// Method Description:
// - Lookup the color scheme for a given profile. If the profile doesn't exist,
// or the scheme name listed in the profile doesn't correspond to a scheme,
@@ -1087,7 +705,7 @@ std::string CascadiaSettings::_ApplyFirstRunChangesToSettingsTemplate(std::strin
// - profileGuid: the GUID of the profile to find the scheme for.
// Return Value:
// - a non-owning pointer to the scheme.
-winrt::Microsoft::Terminal::Settings::Model::ColorScheme CascadiaSettings::GetColorSchemeForProfile(const Model::Profile& profile) const
+Model::ColorScheme CascadiaSettings::GetColorSchemeForProfile(const Model::Profile& profile) const
{
if (!profile)
{
@@ -1104,22 +722,23 @@ winrt::Microsoft::Terminal::Settings::Model::ColorScheme CascadiaSettings::GetCo
// - newName: the new name for the color scheme
// Return Value:
// -
-void CascadiaSettings::UpdateColorSchemeReferences(const hstring oldName, const hstring newName)
+void CascadiaSettings::UpdateColorSchemeReferences(const winrt::hstring& oldName, const winrt::hstring& newName)
{
// update profiles.defaults, if necessary
- if (_userDefaultProfileSettings &&
- _userDefaultProfileSettings->DefaultAppearance().HasColorSchemeName() &&
- _userDefaultProfileSettings->DefaultAppearance().ColorSchemeName() == oldName)
+ if (_baseLayerProfile &&
+ _baseLayerProfile->DefaultAppearance().HasColorSchemeName() &&
+ _baseLayerProfile->DefaultAppearance().ColorSchemeName() == oldName)
{
- _userDefaultProfileSettings->DefaultAppearance().ColorSchemeName(newName);
+ _baseLayerProfile->DefaultAppearance().ColorSchemeName(newName);
}
// update all profiles referencing this color scheme
for (const auto& profile : _allProfiles)
{
- if (profile.DefaultAppearance().HasColorSchemeName() && profile.DefaultAppearance().ColorSchemeName() == oldName)
+ const auto defaultAppearance = profile.DefaultAppearance();
+ if (defaultAppearance.HasColorSchemeName() && defaultAppearance.ColorSchemeName() == oldName)
{
- profile.DefaultAppearance().ColorSchemeName(newName);
+ defaultAppearance.ColorSchemeName(newName);
}
if (profile.UnfocusedAppearance())
@@ -1155,7 +774,12 @@ winrt::hstring CascadiaSettings::ApplicationVersion()
}
CATCH_LOG();
- // Try to get the version the old-fashioned way
+ // Get the product version the old-fashioned way from the localized version compartment.
+ //
+ // We explicitly aren't using VS_FIXEDFILEINFO here, because our build pipeline puts
+ // a non-standard version number into the localized version field.
+ // For instance the fixed file info might contain "1.12.2109.13002",
+ // while the localized field might contain "1.11.210830001-release1.11".
try
{
struct LocalizationInfo
@@ -1191,38 +815,6 @@ winrt::hstring CascadiaSettings::ApplicationVersion()
return RS_(L"ApplicationVersionUnknown");
}
-// Method Description:
-// - Forces a refresh of all default terminal state. This hits the registry to
-// read off the disk, so best to not do it on the UI thread.
-// Arguments:
-// -
-// Return Value:
-// - - Updates internal state
-void CascadiaSettings::RefreshDefaultTerminals()
-{
- _defaultTerminals.Clear();
-
- for (const auto& term : Model::DefaultTerminal::Available())
- {
- _defaultTerminals.Append(term);
- }
-
- _currentDefaultTerminal = Model::DefaultTerminal::Current();
-}
-
-// Helper to do the version check
-static bool _isOnBuildWithDefTerm() noexcept
-{
- OSVERSIONINFOEXW osver{ 0 };
- osver.dwOSVersionInfoSize = sizeof(osver);
- osver.dwBuildNumber = 21359;
-
- DWORDLONG dwlConditionMask = 0;
- VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
-
- return VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask);
-}
-
// Method Description:
// - Determines if we're on an OS platform that supports
// the default terminal handoff functionality.
@@ -1232,9 +824,14 @@ static bool _isOnBuildWithDefTerm() noexcept
// - True if OS supports default terminal. False otherwise.
bool CascadiaSettings::IsDefaultTerminalAvailable() noexcept
{
- // Cached on first use since the OS version shouldn't change while we're running.
- static bool isAvailable = _isOnBuildWithDefTerm();
- return isAvailable;
+ OSVERSIONINFOEXW osver{};
+ osver.dwOSVersionInfoSize = sizeof(osver);
+ osver.dwBuildNumber = 22000;
+
+ DWORDLONG dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
+
+ return VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE;
}
// Method Description:
@@ -1243,9 +840,12 @@ bool CascadiaSettings::IsDefaultTerminalAvailable() noexcept
// -
// Return Value:
// - an iterable collection of all available terminals that could be the default.
-IObservableVector CascadiaSettings::DefaultTerminals() const noexcept
+IObservableVector CascadiaSettings::DefaultTerminals() const noexcept
{
- return _defaultTerminals;
+ const auto available = DefaultTerminal::Available();
+ std::vector terminals{ available.Size(), nullptr };
+ available.GetMany(0, terminals);
+ return winrt::single_threaded_observable_vector(std::move(terminals));
}
// Method Description:
@@ -1259,8 +859,12 @@ IObservableVector CascadiaSettings::DefaultTer
// -
// Return Value:
// - the selected default terminal application
-Settings::Model::DefaultTerminal CascadiaSettings::CurrentDefaultTerminal() const noexcept
+Settings::Model::DefaultTerminal CascadiaSettings::CurrentDefaultTerminal() noexcept
{
+ if (!_currentDefaultTerminal)
+ {
+ _currentDefaultTerminal = DefaultTerminal::Current();
+ }
return _currentDefaultTerminal;
}
@@ -1270,7 +874,7 @@ Settings::Model::DefaultTerminal CascadiaSettings::CurrentDefaultTerminal() cons
// - terminal - Terminal from `DefaultTerminals` list to set as default
// Return Value:
// -
-void CascadiaSettings::CurrentDefaultTerminal(Settings::Model::DefaultTerminal terminal)
+void CascadiaSettings::CurrentDefaultTerminal(const Model::DefaultTerminal& terminal)
{
_currentDefaultTerminal = terminal;
}
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
index ee5782277..e690f7208 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
@@ -20,163 +20,137 @@ Author(s):
#include "CascadiaSettings.g.h"
#include "GlobalAppSettings.h"
-#include "TerminalWarnings.h"
-#include "IDynamicProfileGenerator.h"
-
#include "Profile.h"
-#include "ColorScheme.h"
-// fwdecl unittest classes
-namespace SettingsModelLocalTests
+namespace winrt::Microsoft::Terminal::Settings::Model
{
- class SerializationTests;
- class DeserializationTests;
- class ProfileTests;
- class ColorSchemeTests;
- class KeyBindingsTests;
-};
-namespace TerminalAppUnitTests
-{
- class DynamicProfileTests;
- class JsonTests;
-};
-
-namespace Microsoft::Terminal::Settings::Model
-{
- class SettingsTypedDeserializationException;
-};
-
-class Microsoft::Terminal::Settings::Model::SettingsTypedDeserializationException final : public std::runtime_error
-{
-public:
- SettingsTypedDeserializationException(const std::string_view description) :
- runtime_error(description.data()) {}
-};
+ class IDynamicProfileGenerator;
+}
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
+ winrt::com_ptr CreateChild(const winrt::com_ptr& parent);
+
+ class SettingsTypedDeserializationException final : public std::runtime_error
+ {
+ public:
+ SettingsTypedDeserializationException(const char* message) noexcept :
+ std::runtime_error(message) {}
+ };
+
+ struct ParsedSettings
+ {
+ winrt::com_ptr globals;
+ winrt::com_ptr baseLayerProfile;
+ std::vector> profiles;
+ std::unordered_map> profilesByGuid;
+ };
+
+ struct SettingsLoader
+ {
+ static SettingsLoader Default(const std::string_view& userJSON, const std::string_view& inboxJSON);
+ SettingsLoader(const std::string_view& userJSON, const std::string_view& inboxJSON);
+
+ void GenerateProfiles();
+ void ApplyRuntimeInitialSettings();
+ void MergeInboxIntoUserSettings();
+ void FindFragmentsAndMergeIntoUserSettings();
+ void FinalizeLayering();
+ bool DisableDeletedProfiles();
+
+ ParsedSettings inboxSettings;
+ ParsedSettings userSettings;
+ bool duplicateProfile = false;
+
+ private:
+ static std::pair _lineAndColumnFromPosition(const std::string_view& string, const size_t position);
+ static void _rethrowSerializationExceptionWithLocationInfo(const JsonUtils::DeserializationError& e, const std::string_view& settingsString);
+ static Json::Value _parseJSON(const std::string_view& content);
+ static const Json::Value& _getJSONValue(const Json::Value& json, const std::string_view& key) noexcept;
+ static bool _isValidProfileObject(const Json::Value& profileJson);
+ gsl::span> _getNonUserOriginProfiles() const;
+ void _parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
+ void _appendProfile(winrt::com_ptr&& profile, ParsedSettings& settings);
+ void _executeGenerator(const IDynamicProfileGenerator& generator);
+
+ std::unordered_set _ignoredNamespaces;
+ // See _getNonUserOriginProfiles().
+ size_t _userProfileCount = 0;
+ };
+
struct CascadiaSettings : CascadiaSettingsT
{
public:
- CascadiaSettings();
- explicit CascadiaSettings(const bool addDynamicProfiles);
- CascadiaSettings(hstring json);
- Model::CascadiaSettings Copy() const;
-
static Model::CascadiaSettings LoadDefaults();
static Model::CascadiaSettings LoadAll();
static Model::CascadiaSettings LoadUniversal();
- Model::GlobalAppSettings GlobalSettings() const;
- Windows::Foundation::Collections::IObservableVector AllProfiles() const noexcept;
- Windows::Foundation::Collections::IObservableVector ActiveProfiles() const noexcept;
- Model::ActionMap ActionMap() const noexcept;
-
- static com_ptr FromJson(const Json::Value& json);
- void LayerJson(const Json::Value& json);
-
- void WriteSettingsToDisk() const;
- Json::Value ToJson() const;
-
- static hstring SettingsPath();
- static hstring DefaultSettingsPath();
- Model::Profile ProfileDefaults() const;
-
+ static winrt::hstring SettingsPath();
+ static winrt::hstring DefaultSettingsPath();
static winrt::hstring ApplicationDisplayName();
static winrt::hstring ApplicationVersion();
+ CascadiaSettings() noexcept = default;
+ CascadiaSettings(const winrt::hstring& userJSON, const winrt::hstring& inboxJSON);
+ CascadiaSettings(const std::string_view& userJSON, const std::string_view& inboxJSON = {});
+ explicit CascadiaSettings(SettingsLoader&& loader);
+
+ // user settings
+ Model::CascadiaSettings Copy() const;
+ Model::GlobalAppSettings GlobalSettings() const;
+ winrt::Windows::Foundation::Collections::IObservableVector AllProfiles() const noexcept;
+ winrt::Windows::Foundation::Collections::IObservableVector ActiveProfiles() const noexcept;
+ Model::ActionMap ActionMap() const noexcept;
+ void WriteSettingsToDisk() const;
+ Json::Value ToJson() const;
+ Model::Profile ProfileDefaults() const;
Model::Profile CreateNewProfile();
- Model::Profile FindProfile(const guid& profileGuid) const noexcept;
+ Model::Profile FindProfile(const winrt::guid& guid) const noexcept;
Model::ColorScheme GetColorSchemeForProfile(const Model::Profile& profile) const;
- void UpdateColorSchemeReferences(const hstring oldName, const hstring newName);
-
- Windows::Foundation::Collections::IVectorView Warnings();
- void ClearWarnings();
- void AppendWarning(SettingsLoadWarnings warning);
- Windows::Foundation::IReference GetLoadingError();
- hstring GetSerializationErrorMessage();
-
+ void UpdateColorSchemeReferences(const winrt::hstring& oldName, const winrt::hstring& newName);
Model::Profile GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const;
-
+ Model::Profile GetProfileByName(const winrt::hstring& name) const;
+ Model::Profile GetProfileByIndex(uint32_t index) const;
Model::Profile DuplicateProfile(const Model::Profile& source);
- void RefreshDefaultTerminals();
+ // load errors
+ winrt::Windows::Foundation::Collections::IVectorView Warnings() const;
+ winrt::Windows::Foundation::IReference GetLoadingError() const;
+ winrt::hstring GetSerializationErrorMessage() const;
+
+ // defterm
static bool IsDefaultTerminalAvailable() noexcept;
- Windows::Foundation::Collections::IObservableVector DefaultTerminals() const noexcept;
- Model::DefaultTerminal CurrentDefaultTerminal() const noexcept;
- void CurrentDefaultTerminal(Model::DefaultTerminal terminal);
+ winrt::Windows::Foundation::Collections::IObservableVector DefaultTerminals() const noexcept;
+ Model::DefaultTerminal CurrentDefaultTerminal() noexcept;
+ void CurrentDefaultTerminal(const Model::DefaultTerminal& terminal);
private:
- com_ptr _globals;
- Windows::Foundation::Collections::IObservableVector _allProfiles;
- Windows::Foundation::Collections::IObservableVector _activeProfiles;
- Windows::Foundation::Collections::IVector _warnings;
- Windows::Foundation::IReference _loadError;
- hstring _deserializationErrorMessage;
+ static const std::filesystem::path& _settingsPath();
- Windows::Foundation::Collections::IObservableVector _defaultTerminals;
- Model::DefaultTerminal _currentDefaultTerminal;
+ winrt::com_ptr _createNewProfile(const std::wstring_view& name) const;
- std::vector> _profileGenerators;
+ void _resolveDefaultProfile() const;
- std::string _userSettingsString;
- Json::Value _userSettings;
- Json::Value _defaultSettings;
- winrt::com_ptr _userDefaultProfileSettings{ nullptr };
+ void _validateSettings();
+ void _validateAllSchemesExist();
+ void _validateMediaResources();
+ void _validateKeybindings() const;
+ void _validateColorSchemesInCommands() const;
+ bool _hasInvalidColorScheme(const Model::Command& command) const;
- winrt::com_ptr _CreateNewProfile(const std::wstring_view& name) const;
+ // user settings
+ winrt::com_ptr _globals;
+ winrt::com_ptr _baseLayerProfile;
+ winrt::Windows::Foundation::Collections::IObservableVector _allProfiles;
+ winrt::Windows::Foundation::Collections::IObservableVector _activeProfiles;
- void _LayerOrCreateProfile(const Json::Value& profileJson);
- winrt::com_ptr _FindMatchingProfile(const Json::Value& profileJson);
- std::optional _FindMatchingProfileIndex(const Json::Value& profileJson);
- void _LayerOrCreateColorScheme(const Json::Value& schemeJson);
- Json::Value _ParseUtf8JsonString(std::string_view fileData);
+ // load errors
+ winrt::Windows::Foundation::Collections::IVector _warnings;
+ winrt::Windows::Foundation::IReference _loadError;
+ winrt::hstring _deserializationErrorMessage;
- winrt::com_ptr _FindMatchingColorScheme(const Json::Value& schemeJson);
- void _ParseJsonString(std::string_view fileData, const bool isDefaultSettings);
- static const Json::Value& _GetProfilesJsonObject(const Json::Value& json);
- static const Json::Value& _GetDisabledProfileSourcesJsonObject(const Json::Value& json);
- bool _PrependSchemaDirective();
- bool _AppendDynamicProfilesToUserSettings();
- std::string _ApplyFirstRunChangesToSettingsTemplate(std::string_view settingsTemplate) const;
- void _CopyProfileInheritanceTree(com_ptr& cloneSettings) const;
-
- void _ApplyDefaultsFromUserSettings();
-
- void _LoadDynamicProfiles();
- void _LoadFragmentExtensions();
- void _ApplyJsonStubsHelper(const std::wstring_view directory, const std::unordered_set& ignoredNamespaces);
- std::unordered_set _AccumulateJsonFilesInDirectory(const std::wstring_view directory);
- void _ParseAndLayerFragmentFiles(const std::unordered_set files, const winrt::hstring source);
-
- static const std::filesystem::path& _SettingsPath();
- static std::optional _ReadUserSettings();
-
- std::optional _GetProfileGuidByName(const hstring) const;
- std::optional _GetProfileGuidByIndex(std::optional index) const;
-
- void _ValidateSettings();
- void _ValidateProfilesExist();
- void _ValidateDefaultProfileExists();
- void _ValidateNoDuplicateProfiles();
- void _ResolveDefaultProfile();
- void _ReorderProfilesToMatchUserSettingsOrder();
- void _UpdateActiveProfiles();
- void _ValidateAllSchemesExist();
- void _ValidateMediaResources();
- void _ValidateKeybindings();
- void _ValidateColorSchemesInCommands();
- void _ValidateNoGlobalsKey();
-
- bool _HasInvalidColorScheme(const Model::Command& command);
-
- friend class SettingsModelLocalTests::SerializationTests;
- friend class SettingsModelLocalTests::DeserializationTests;
- friend class SettingsModelLocalTests::ProfileTests;
- friend class SettingsModelLocalTests::ColorSchemeTests;
- friend class SettingsModelLocalTests::KeyBindingsTests;
- friend class TerminalAppUnitTests::DynamicProfileTests;
- friend class TerminalAppUnitTests::JsonTests;
+ // defterm
+ Model::DefaultTerminal _currentDefaultTerminal{ nullptr };
};
}
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl
index 2f3248fbc..092401ebc 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl
@@ -9,11 +9,6 @@ import "DefaultTerminal.idl";
namespace Microsoft.Terminal.Settings.Model
{
[default_interface] runtimeclass CascadiaSettings {
- CascadiaSettings(String json);
- CascadiaSettings Copy();
-
- void WriteSettingsToDisk();
-
static CascadiaSettings LoadDefaults();
static CascadiaSettings LoadAll();
static CascadiaSettings LoadUniversal();
@@ -23,19 +18,24 @@ namespace Microsoft.Terminal.Settings.Model
static String ApplicationDisplayName { get; };
static String ApplicationVersion { get; };
+
+ CascadiaSettings(String userJSON, String inboxJSON);
+
+ CascadiaSettings Copy();
+ void WriteSettingsToDisk();
GlobalAppSettings GlobalSettings { get; };
Profile ProfileDefaults { get; };
- Windows.Foundation.Collections.IObservableVector AllProfiles { get; };
- Windows.Foundation.Collections.IObservableVector ActiveProfiles { get; };
+ IObservableVector AllProfiles { get; };
+ IObservableVector ActiveProfiles { get; };
Profile DuplicateProfile(Profile sourceProfile);
ActionMap ActionMap { get; };
- Windows.Foundation.Collections.IVectorView Warnings { get; };
+ IVectorView Warnings { get; };
Windows.Foundation.IReference GetLoadingError { get; };
String GetSerializationErrorMessage { get; };
@@ -46,9 +46,8 @@ namespace Microsoft.Terminal.Settings.Model
Profile GetProfileForArgs(NewTerminalArgs newTerminalArgs);
- void RefreshDefaultTerminals();
static Boolean IsDefaultTerminalAvailable { get; };
- Windows.Foundation.Collections.IObservableVector DefaultTerminals { get; };
+ IObservableVector DefaultTerminals { get; };
DefaultTerminal CurrentDefaultTerminal;
}
}
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
index 36ca2a316..b704be14d 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
@@ -4,128 +4,580 @@
#include "pch.h"
#include "CascadiaSettings.h"
+#include
#include
#include
+#include
-// defaults.h is a file containing the default json settings in a std::string_view
+#include "AzureCloudShellGenerator.h"
+#include "PowershellCoreProfileGenerator.h"
+#include "VsDevCmdGenerator.h"
+#include "VsDevShellGenerator.h"
+#include "WslDistroGenerator.h"
+
+// The following files are generated at build time into the "Generated Files" directory.
+// defaults(-universal).h is a file containing the default json settings in a std::string_view.
#include "defaults.h"
#include "defaults-universal.h"
// userDefault.h is like the above, but with a default template for the user's settings.json.
+#include
+
#include "userDefaults.h"
-// Both defaults.h and userDefaults.h are generated at build time into the
-// "Generated Files" directory.
#include "ApplicationState.h"
#include "FileUtils.h"
+using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
-using namespace ::Microsoft::Console;
-using namespace ::Microsoft::Terminal::Settings::Model;
static constexpr std::wstring_view SettingsFilename{ L"settings.json" };
-static constexpr std::wstring_view LegacySettingsFilename{ L"profiles.json" };
-
static constexpr std::wstring_view DefaultsFilename{ L"defaults.json" };
-static constexpr std::string_view SchemaKey{ "$schema" };
-static constexpr std::string_view SchemaValue{ "https://aka.ms/terminal-profiles-schema" };
static constexpr std::string_view ProfilesKey{ "profiles" };
static constexpr std::string_view DefaultSettingsKey{ "defaults" };
static constexpr std::string_view ProfilesListKey{ "list" };
-static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" };
-static constexpr std::string_view ActionsKey{ "actions" };
static constexpr std::string_view SchemesKey{ "schemes" };
static constexpr std::string_view NameKey{ "name" };
-static constexpr std::string_view UpdatesKey{ "updates" };
static constexpr std::string_view GuidKey{ "guid" };
-static constexpr std::string_view DisabledProfileSourcesKey{ "disabledProfileSources" };
-
-static constexpr std::string_view SettingsSchemaFragment{ "\n"
- R"( "$schema": "https://aka.ms/terminal-profiles-schema")" };
-
-static constexpr std::string_view jsonExtension{ ".json" };
-static constexpr std::string_view FragmentsSubDirectory{ "\\Fragments" };
+static constexpr std::wstring_view jsonExtension{ L".json" };
+static constexpr std::wstring_view FragmentsSubDirectory{ L"\\Fragments" };
static constexpr std::wstring_view FragmentsPath{ L"\\Microsoft\\Windows Terminal\\Fragments" };
-static constexpr std::string_view AppExtensionHostName{ "com.microsoft.windows.terminal.settings" };
+static constexpr std::wstring_view AppExtensionHostName{ L"com.microsoft.windows.terminal.settings" };
+
+// make sure this matches defaults.json.
+static constexpr winrt::guid DEFAULT_WINDOWS_POWERSHELL_GUID{ 0x61c54bbd, 0xc2c6, 0x5271, { 0x96, 0xe7, 0x00, 0x9a, 0x87, 0xff, 0x44, 0xbf } };
+static constexpr winrt::guid DEFAULT_COMMAND_PROMPT_GUID{ 0x0caa0dad, 0x35be, 0x5f56, { 0xa8, 0xff, 0xaf, 0xce, 0xee, 0xaa, 0x61, 0x01 } };
// Function Description:
// - Extracting the value from an async task (like talking to the app catalog) when we are on the
// UI thread causes C++/WinRT to complain quite loudly (and halt execution!)
// This templated function extracts the result from a task with chicanery.
template
-static auto _extractValueFromTaskWithoutMainThreadAwait(TTask&& task) -> decltype(task.get())
+static auto extractValueFromTaskWithoutMainThreadAwait(TTask&& task) -> decltype(task.get())
{
- using TVal = decltype(task.get());
- std::optional finalVal{};
- std::condition_variable cv;
- std::mutex mtx;
+ std::optional finalVal;
+ til::latch latch{ 1 };
- auto waitOnBackground = [&]() -> winrt::fire_and_forget {
+ const auto _ = [&]() -> winrt::fire_and_forget {
co_await winrt::resume_background();
- auto v{ co_await task };
+ finalVal.emplace(co_await task);
+ latch.count_down();
+ }();
- std::unique_lock lock{ mtx };
- finalVal.emplace(std::move(v));
- cv.notify_all();
- };
-
- std::unique_lock lock{ mtx };
- waitOnBackground();
- cv.wait(lock, [&]() { return finalVal.has_value(); });
- return *finalVal;
+ latch.wait();
+ return finalVal.value();
}
-static std::tuple _LineAndColumnFromPosition(const std::string_view string, ptrdiff_t position)
+// Concatenates the two given strings (!) and returns them as a path.
+// You better make sure there's a path separator at the end of lhs or at the start of rhs.
+static std::filesystem::path buildPath(const std::wstring_view& lhs, const std::wstring_view& rhs)
{
- size_t line = 1, column = position + 1;
- auto lastNL = string.find_last_of('\n', position);
- if (lastNL != std::string::npos)
- {
- column = (position - lastNL);
- line = std::count(string.cbegin(), string.cbegin() + lastNL + 1, '\n') + 1;
- }
-
- return { line, column };
+ std::wstring buffer;
+ buffer.reserve(lhs.size() + rhs.size());
+ buffer.append(lhs);
+ buffer.append(rhs);
+ return { std::move(buffer) };
}
-static void _CatchRethrowSerializationExceptionWithLocationInfo(std::string_view settingsString)
+// This is a convenience method used by the CascadiaSettings constructor.
+// It runs some basic settings layering without relying on external programs or files.
+// This makes it suitable for most unit tests.
+SettingsLoader SettingsLoader::Default(const std::string_view& userJSON, const std::string_view& inboxJSON)
{
- std::string msg;
+ SettingsLoader loader{ userJSON, inboxJSON };
+ loader.MergeInboxIntoUserSettings();
+ loader.FinalizeLayering();
+ return loader;
+}
+
+// The SettingsLoader class is an internal implementation detail of CascadiaSettings.
+// Member methods aren't safe against misuse and you need to ensure to call them in a specific order.
+// See CascadiaSettings::LoadAll() for a specific usage example.
+//
+// This constructor only handles parsing the two given JSON strings.
+// At a minimum you should do at least everything that SettingsLoader::Default does.
+SettingsLoader::SettingsLoader(const std::string_view& userJSON, const std::string_view& inboxJSON)
+{
+ _parse(OriginTag::InBox, {}, inboxJSON, inboxSettings);
try
{
- throw;
+ _parse(OriginTag::User, {}, userJSON, userSettings);
}
catch (const JsonUtils::DeserializationError& e)
{
- static constexpr std::string_view basicHeader{ "* Line {line}, Column {column}\n{message}" };
- static constexpr std::string_view keyedHeader{ "* Line {line}, Column {column} ({key})\n{message}" };
+ _rethrowSerializationExceptionWithLocationInfo(e, userJSON);
+ }
- std::string jsonValueAsString{ "array or object" };
- try
+ if (const auto sources = userSettings.globals->DisabledProfileSources())
+ {
+ _ignoredNamespaces.reserve(sources.Size());
+ for (const auto& id : sources)
{
- jsonValueAsString = e.jsonValue.asString();
- if (e.jsonValue.isString())
+ _ignoredNamespaces.emplace(id);
+ }
+ }
+
+ // See member description of _userProfileCount.
+ _userProfileCount = userSettings.profiles.size();
+}
+
+// Generate dynamic profiles and add them to the list of "inbox" profiles
+// (meaning profiles specified by the application rather by the user).
+void SettingsLoader::GenerateProfiles()
+{
+ _executeGenerator(PowershellCoreProfileGenerator{});
+ _executeGenerator(WslDistroGenerator{});
+ _executeGenerator(AzureCloudShellGenerator{});
+ _executeGenerator(VsDevCmdGenerator{});
+ _executeGenerator(VsDevShellGenerator{});
+}
+
+// A new settings.json gets a special treatment:
+// 1. The default profile is a PowerShell 7+ one, if one was generated,
+// and falls back to the standard PowerShell 5 profile otherwise.
+// 2. cmd.exe gets a localized name.
+void SettingsLoader::ApplyRuntimeInitialSettings()
+{
+ // 1.
+ {
+ const auto preferredPowershellProfile = PowershellCoreProfileGenerator::GetPreferredPowershellProfileName();
+ auto guid = DEFAULT_WINDOWS_POWERSHELL_GUID;
+
+ for (const auto& profile : inboxSettings.profiles)
+ {
+ if (profile->Name() == preferredPowershellProfile)
{
- jsonValueAsString = fmt::format("\"{}\"", jsonValueAsString);
+ guid = profile->Guid();
+ break;
}
}
- catch (...)
+
+ userSettings.globals->DefaultProfile(guid);
+ }
+
+ // 2.
+ {
+ for (const auto& profile : userSettings.profiles)
{
- // discard: we're in the middle of error handling
+ if (profile->Guid() == DEFAULT_COMMAND_PROMPT_GUID)
+ {
+ profile->Name(RS_(L"CommandPromptDisplayName"));
+ break;
+ }
+ }
+ }
+}
+
+// Adds profiles from .inboxSettings as parents of matching profiles in .userSettings.
+// That way the user profiles will get appropriate defaults from the generators (like icons and such).
+// If a matching profile doesn't exist yet in .userSettings, one will be created.
+void SettingsLoader::MergeInboxIntoUserSettings()
+{
+ for (const auto& profile : inboxSettings.profiles)
+ {
+ if (const auto [it, inserted] = userSettings.profilesByGuid.emplace(profile->Guid(), profile); !inserted)
+ {
+ // If inserted is false, we got a matching user profile with identical GUID.
+ // --> The generated profile is a parent of the existing user profile.
+ it->second->InsertParent(profile);
+ }
+ else
+ {
+ // If inserted is true, then this is a generated profile that doesn't exist in the user's settings.
+ // While emplace() has already created an appropriate entry in .profilesByGuid, we still need to
+ // add it to .profiles (which is basically a sorted list of .profilesByGuid's values).
+ //
+ // When a user modifies a profile they shouldn't modify the (static/constant)
+ // inbox profile of course. That's why we need to call CreateChild here.
+ userSettings.profiles.emplace_back(CreateChild(profile));
+ }
+ }
+}
+
+// Searches AppData/ProgramData and app extension directories for settings JSON files.
+// If such JSON files are found, they're read and their contents added to .userSettings.
+//
+// Of course it would be more elegant to add fragments to .inboxSettings first and then have MergeInboxIntoUserSettings
+// merge them. Unfortunately however the "updates" key in fragment profiles make this impossible:
+// The targeted profile might be one that got created as part of SettingsLoader::MergeInboxIntoUserSettings.
+// Additionally the GUID in "updates" will conflict with existing GUIDs in .inboxSettings.
+void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
+{
+ ParsedSettings fragmentSettings;
+
+ const auto parseAndLayerFragmentFiles = [&](const std::filesystem::path& path, const winrt::hstring& source) {
+ for (const auto& fragmentExt : std::filesystem::directory_iterator{ path })
+ {
+ if (fragmentExt.path().extension() == jsonExtension)
+ {
+ try
+ {
+ const auto content = ReadUTF8File(fragmentExt.path());
+ _parse(OriginTag::Fragment, source, content, fragmentSettings);
+
+ for (const auto& fragmentProfile : fragmentSettings.profiles)
+ {
+ if (const auto updates = fragmentProfile->Updates(); updates != winrt::guid{})
+ {
+ if (const auto it = userSettings.profilesByGuid.find(updates); it != userSettings.profilesByGuid.end())
+ {
+ it->second->InsertParent(0, fragmentProfile);
+ }
+ }
+ else
+ {
+ _appendProfile(CreateChild(fragmentProfile), userSettings);
+ }
+ }
+
+ for (const auto& kv : fragmentSettings.globals->ColorSchemes())
+ {
+ userSettings.globals->AddColorScheme(kv.Value());
+ }
+ }
+ CATCH_LOG();
+ }
+ }
+ };
+
+ for (const auto& rfid : std::array{ FOLDERID_LocalAppData, FOLDERID_ProgramData })
+ {
+ wil::unique_cotaskmem_string folder;
+ THROW_IF_FAILED(SHGetKnownFolderPath(rfid, 0, nullptr, &folder));
+
+ const auto fragmentPath = buildPath(folder.get(), FragmentsPath);
+
+ if (std::filesystem::is_directory(fragmentPath))
+ {
+ for (const auto& fragmentExtFolder : std::filesystem::directory_iterator{ fragmentPath })
+ {
+ const auto filename = fragmentExtFolder.path().filename();
+ const auto& source = filename.native();
+
+ if (!_ignoredNamespaces.count(std::wstring_view{ source }) && fragmentExtFolder.is_directory())
+ {
+ parseAndLayerFragmentFiles(fragmentExtFolder.path(), winrt::hstring{ source });
+ }
+ }
+ }
+ }
+
+ // Search through app extensions
+ // Gets the catalog of extensions with the name "com.microsoft.windows.terminal.settings"
+ const auto catalog = winrt::Windows::ApplicationModel::AppExtensions::AppExtensionCatalog::Open(AppExtensionHostName);
+ const auto extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync());
+
+ for (const auto& ext : extensions)
+ {
+ const auto packageName = ext.Package().Id().FamilyName();
+ if (_ignoredNamespaces.count(std::wstring_view{ packageName }))
+ {
+ continue;
}
- msg = fmt::format(" Have: {}\n Expected: {}", jsonValueAsString, e.expectedType);
+ // Likewise, getting the public folder from an extension is an async operation.
+ auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync());
+ if (!foundFolder)
+ {
+ continue;
+ }
- auto [l, c] = _LineAndColumnFromPosition(settingsString, e.jsonValue.getOffsetStart());
- msg = fmt::format((e.key ? keyedHeader : basicHeader),
- fmt::arg("line", l),
- fmt::arg("column", c),
- fmt::arg("key", e.key.value_or("")),
- fmt::arg("message", msg));
- throw SettingsTypedDeserializationException{ msg };
+ // the StorageFolder class has its own methods for obtaining the files within the folder
+ // however, all those methods are Async methods
+ // you may have noticed that we need to resort to clunky implementations for async operations
+ // (they are in extractValueFromTaskWithoutMainThreadAwait)
+ // so for now we will just take the folder path and access the files that way
+ const auto path = buildPath(foundFolder.Path(), FragmentsSubDirectory);
+
+ if (std::filesystem::is_directory(path))
+ {
+ parseAndLayerFragmentFiles(path, packageName);
+ }
+ }
+}
+
+// Call this method before passing SettingsLoader to the CascadiaSettings constructor.
+// It layers all remaining objects onto each other (those that aren't covered
+// by MergeInboxIntoUserSettings/FindFragmentsAndMergeIntoUserSettings).
+void SettingsLoader::FinalizeLayering()
+{
+ // Layer default globals -> user globals
+ userSettings.globals->InsertParent(inboxSettings.globals);
+ userSettings.globals->_FinalizeInheritance();
+ // Layer default profile defaults -> user profile defaults
+ userSettings.baseLayerProfile->InsertParent(inboxSettings.baseLayerProfile);
+ userSettings.baseLayerProfile->_FinalizeInheritance();
+ // Layer user profile defaults -> user profiles
+ for (const auto& profile : userSettings.profiles)
+ {
+ profile->InsertParent(0, userSettings.baseLayerProfile);
+ profile->_FinalizeInheritance();
+ }
+}
+
+// Let's say a user doesn't know that they need to write `"hidden": true` in
+// order to prevent a profile from showing up (and a settings UI doesn't exist).
+// Naturally they would open settings.json and try to remove the profile object.
+// This section of code recognizes if a profile was seen before and marks it as
+// `"hidden": true` by default and thus ensures the behavior the user expects:
+// Profiles won't show up again after they've been removed from settings.json.
+bool SettingsLoader::DisableDeletedProfiles()
+{
+ const auto& state = winrt::get_self(ApplicationState::SharedInstance());
+ auto generatedProfileIds = state->GeneratedProfiles();
+ bool newGeneratedProfiles = false;
+
+ for (const auto& profile : _getNonUserOriginProfiles())
+ {
+ if (generatedProfileIds.emplace(profile->Guid()).second)
+ {
+ newGeneratedProfiles = true;
+ }
+ else
+ {
+ profile->Deleted(true);
+ profile->Hidden(true);
+ }
+ }
+
+ if (newGeneratedProfiles)
+ {
+ state->GeneratedProfiles(generatedProfileIds);
+ }
+
+ return newGeneratedProfiles;
+}
+
+// Give a string of length N and a position of [0,N) this function returns
+// the line/column within the string, similar to how text editors do it.
+// Newlines are considered part of the current line (as per POSIX).
+std::pair SettingsLoader::_lineAndColumnFromPosition(const std::string_view& string, const size_t position)
+{
+ size_t line = 1;
+ size_t column = 0;
+
+ for (;;)
+ {
+ const auto p = string.find('\n', column);
+ if (p >= position)
+ {
+ break;
+ }
+
+ column = p + 1;
+ line++;
+ }
+
+ return { line, position - column + 1 };
+}
+
+// Formats a JSON exception for humans to read and throws that.
+void SettingsLoader::_rethrowSerializationExceptionWithLocationInfo(const JsonUtils::DeserializationError& e, const std::string_view& settingsString)
+{
+ std::string jsonValueAsString;
+ try
+ {
+ jsonValueAsString = e.jsonValue.asString();
+ if (e.jsonValue.isString())
+ {
+ jsonValueAsString = fmt::format("\"{}\"", jsonValueAsString);
+ }
+ }
+ catch (...)
+ {
+ jsonValueAsString = "array or object";
+ }
+
+ const auto [line, column] = _lineAndColumnFromPosition(settingsString, static_cast(e.jsonValue.getOffsetStart()));
+
+ fmt::memory_buffer msg;
+ fmt::format_to(msg, "* Line {}, Column {}", line, column);
+ if (e.key)
+ {
+ fmt::format_to(msg, " ({})", *e.key);
+ }
+ fmt::format_to(msg, "\n Have: {}\n Expected: {}\0", jsonValueAsString, e.expectedType);
+
+ throw SettingsTypedDeserializationException{ msg.data() };
+}
+
+// Simply parses the given content to a Json::Value.
+Json::Value SettingsLoader::_parseJSON(const std::string_view& content)
+{
+ Json::Value json;
+ std::string errs;
+ const std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
+
+ if (!reader->parse(content.data(), content.data() + content.size(), &json, &errs))
+ {
+ throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
+ }
+
+ return json;
+}
+
+// A helper method similar to Json::Value::operator[], but compatible with std::string_view.
+const Json::Value& SettingsLoader::_getJSONValue(const Json::Value& json, const std::string_view& key) noexcept
+{
+ if (json.isObject())
+ {
+ if (const auto val = json.find(key.data(), key.data() + key.size()))
+ {
+ return *val;
+ }
+ }
+
+ return Json::Value::nullSingleton();
+}
+
+// Returns true if the given Json::Value looks like a profile.
+// We introduced a bug (GH#9962, fixed in GH#9964) that would result in one or
+// more nameless, guid-less profiles being emitted into the user's settings file.
+// Those profiles would show up in the list as "Default" later.
+bool SettingsLoader::_isValidProfileObject(const Json::Value& profileJson)
+{
+ return profileJson.isObject() &&
+ (profileJson.isMember(NameKey.data(), NameKey.data() + NameKey.size()) || // has a name (can generate a guid)
+ profileJson.isMember(GuidKey.data(), GuidKey.data() + GuidKey.size())); // or has a guid
+}
+
+// We treat userSettings.profiles as an append-only array and will
+// append profiles into the userSettings as necessary in this function.
+// _userProfileCount stores the number of profiles that were in userJSON during construction.
+//
+// Thus no matter how many profiles are added later on, the following condition holds true:
+// The userSettings.profiles in the range [0, _userProfileCount) contain all profiles specified by the user.
+// In turn all profiles in the range [_userProfileCount, ∞) contain newly generated/added profiles.
+// gsl::make_span(userSettings.profiles).subspan(_userProfileCount) gets us the latter range.
+gsl::span> SettingsLoader::_getNonUserOriginProfiles() const
+{
+ return gsl::make_span(userSettings.profiles).subspan(_userProfileCount);
+}
+
+// Parses the given JSON string ("content") and fills a ParsedSettings instance with it.
+void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings)
+{
+ const auto json = content.empty() ? Json::Value{ Json::ValueType::objectValue } : _parseJSON(content);
+ const auto& profilesObject = _getJSONValue(json, ProfilesKey);
+ const auto& defaultsObject = _getJSONValue(profilesObject, DefaultSettingsKey);
+ const auto& profilesArray = profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);
+
+ // globals
+ {
+ settings.globals = GlobalAppSettings::FromJson(json);
+
+ if (const auto& schemes = _getJSONValue(json, SchemesKey))
+ {
+ for (const auto& schemeJson : schemes)
+ {
+ if (schemeJson.isObject())
+ {
+ if (const auto scheme = ColorScheme::FromJson(schemeJson))
+ {
+ settings.globals->AddColorScheme(*scheme);
+ }
+ }
+ }
+ }
+ }
+
+ // profiles.defaults
+ {
+ settings.baseLayerProfile = Profile::FromJson(defaultsObject);
+ // Remove the `guid` member from the default settings.
+ // That will hyper-explode, so just don't let them do that.
+ settings.baseLayerProfile->ClearGuid();
+ settings.baseLayerProfile->Origin(OriginTag::ProfilesDefaults);
+ }
+
+ // profiles.list
+ {
+ const auto size = profilesArray.size();
+
+ // NOTE: This function is supposed to *replace* the contents of ParsedSettings. Don't break this promise.
+ // SettingsLoader::FindFragmentsAndMergeIntoUserSettings relies on this.
+ settings.profiles.clear();
+ settings.profiles.reserve(size);
+
+ settings.profilesByGuid.clear();
+ settings.profilesByGuid.reserve(size);
+
+ for (const auto& profileJson : profilesArray)
+ {
+ if (_isValidProfileObject(profileJson))
+ {
+ auto profile = Profile::FromJson(profileJson);
+ profile->Origin(origin);
+
+ // The Guid() generation below depends on the value of Source().
+ // --> Provide one if we got one.
+ if (!source.empty())
+ {
+ profile->Source(source);
+ }
+
+ // The Guid() getter generates one from Name() and Source() if none exists otherwise.
+ // We want to ensure that every profile has a GUID no matter what, not just to
+ // cache the value, but also to make them consistently identifiable later on.
+ if (!profile->HasGuid())
+ {
+ profile->Guid(profile->Guid());
+ }
+
+ _appendProfile(std::move(profile), settings);
+ }
+ }
+ }
+}
+
+// Adds a profile to the ParsedSettings instance. Takes ownership of the profile.
+// It ensures no duplicate GUIDs are added to the ParsedSettings instance.
+void SettingsLoader::_appendProfile(winrt::com_ptr&& profile, ParsedSettings& settings)
+{
+ // FYI: The static_cast ensures we don't move the profile into
+ // `profilesByGuid`, even though we still need it later for `profiles`.
+ if (settings.profilesByGuid.emplace(profile->Guid(), static_cast&>(profile)).second)
+ {
+ settings.profiles.emplace_back(profile);
+ }
+ else
+ {
+ duplicateProfile = true;
+ }
+}
+
+// As the name implies it executes a generator.
+// Generated profiles are added to .inboxSettings. Used by GenerateProfiles().
+void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator)
+{
+ const auto generatorNamespace = generator.GetNamespace();
+ if (_ignoredNamespaces.count(generatorNamespace))
+ {
+ return;
+ }
+
+ const auto previousSize = inboxSettings.profiles.size();
+
+ try
+ {
+ generator.GenerateProfiles(inboxSettings.profiles);
+ }
+ CATCH_LOG_MSG("Dynamic Profile Namespace: \"%.*s\"", gsl::narrow(generatorNamespace.size()), generatorNamespace.data())
+
+ // If the generator produced some profiles we're going to give them default attributes.
+ // By setting the Origin/Source/etc. here, we deduplicate some code and ensure they aren't missing accidentally.
+ if (inboxSettings.profiles.size() > previousSize)
+ {
+ const winrt::hstring source{ generatorNamespace };
+
+ for (const auto& profile : gsl::span(inboxSettings.profiles).subspan(previousSize))
+ {
+ profile->Origin(OriginTag::Generated);
+ profile->Source(source);
+ }
}
}
@@ -141,156 +593,69 @@ static void _CatchRethrowSerializationExceptionWithLocationInfo(std::string_view
// profiles inserted into their list of profiles.
// Return Value:
// - a unique_ptr containing a new CascadiaSettings object.
-winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::LoadAll()
+Model::CascadiaSettings CascadiaSettings::LoadAll()
+try
{
- try
+ const auto settingsString = ReadUTF8FileIfExists(_settingsPath()).value_or(std::string{});
+ const auto firstTimeSetup = settingsString.empty();
+ const auto settingsStringView = firstTimeSetup ? UserSettingsJson : settingsString;
+ auto mustWriteToDisk = firstTimeSetup;
+
+ SettingsLoader loader{ settingsStringView, DefaultJson };
+
+ // Generate dynamic profiles and add them as parents of user profiles.
+ // That way the user profiles will get appropriate defaults from the generators (like icons and such).
+ loader.GenerateProfiles();
+
+ // ApplyRuntimeInitialSettings depends on generated profiles.
+ // --> ApplyRuntimeInitialSettings must be called after GenerateProfiles.
+ if (firstTimeSetup)
{
- auto settings = LoadDefaults();
- auto resultPtr = winrt::get_self(settings);
- resultPtr->ClearWarnings();
+ loader.ApplyRuntimeInitialSettings();
+ }
- // GH 3588, we need this below to know if the user chose something that wasn't our default.
- // Collect it up here in case it gets modified by any of the other layers between now and when
- // the user's preferences are loaded and layered.
- const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
+ loader.MergeInboxIntoUserSettings();
+ // Fragments might reference user profiles created by a generator.
+ // --> FindFragmentsAndMergeIntoUserSettings must be called after MergeInboxIntoUserSettings.
+ loader.FindFragmentsAndMergeIntoUserSettings();
+ loader.FinalizeLayering();
- std::optional fileData = _ReadUserSettings();
+ // DisableDeletedProfiles returns true whenever we encountered any new generated/dynamic profiles.
+ // Coincidentally this is also the time we should write the new settings.json
+ // to disk (so that it contains the new profiles for manual editing by the user).
+ mustWriteToDisk |= loader.DisableDeletedProfiles();
- // Make sure the file isn't totally empty. If it is, we'll treat the file
- // like it doesn't exist at all.
- const bool fileHasData = fileData && !fileData->empty();
- bool needToWriteFile = false;
- if (fileHasData)
- {
- resultPtr->_ParseJsonString(*fileData, false);
- }
+ // If this throws, the app will catch it and use the default settings.
+ const auto settings = winrt::make_self(std::move(loader));
- // Load profiles from dynamic profile generators. _userSettings should be
- // created by now, because we're going to check in there for any generators
- // that should be disabled (if the user had any settings.)
- resultPtr->_LoadDynamicProfiles();
+ // If we created the file, or found new dynamic profiles, write the user
+ // settings string back to the file.
+ if (mustWriteToDisk)
+ {
try
{
- resultPtr->_LoadFragmentExtensions();
- }
- CATCH_LOG();
-
- if (!fileHasData)
- {
- // We didn't find the user settings. We'll need to create a file
- // to use as the user defaults.
- // For now, just parse our user settings template as their user settings.
- auto userSettings{ resultPtr->_ApplyFirstRunChangesToSettingsTemplate(UserSettingsJson) };
- resultPtr->_ParseJsonString(userSettings, false);
- needToWriteFile = true;
- }
-
- try
- {
- // See microsoft/terminal#2325: find the defaultSettings from the user's
- // settings. Layer those settings upon all the existing profiles we have
- // (defaults and dynamic profiles). We'll also set
- // _userDefaultProfileSettings here. When we LayerJson below to apply the
- // user settings, we'll make sure to use these defaultSettings _before_ any
- // profiles the user might have.
- resultPtr->_ApplyDefaultsFromUserSettings();
-
- // Apply the user's settings
- resultPtr->LayerJson(resultPtr->_userSettings);
+ settings->WriteSettingsToDisk();
}
catch (...)
{
- _CatchRethrowSerializationExceptionWithLocationInfo(resultPtr->_userSettingsString);
+ LOG_CAUGHT_EXCEPTION();
+ settings->_warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings);
}
-
- // Let's say a user doesn't know that they need to write `"hidden": true` in
- // order to prevent a profile from showing up (and a settings UI doesn't exist).
- // Naturally they would open settings.json and try to remove the profile object.
- // This section of code recognizes if a profile was seen before and marks it as
- // `"hidden": true` by default and thus ensures the behavior the user expects:
- // Profiles won't show up again after they've been removed from settings.json.
- {
- const auto state = winrt::get_self(ApplicationState::SharedInstance());
- auto generatedProfiles = state->GeneratedProfiles();
- bool generatedProfilesChanged = false;
-
- for (const auto& profile : resultPtr->_allProfiles)
- {
- const auto profileImpl = winrt::get_self(profile);
-
- if (generatedProfiles.emplace(profileImpl->Guid()).second)
- {
- generatedProfilesChanged = true;
- }
- else if (profileImpl->Origin() != OriginTag::User)
- {
- profileImpl->Deleted(true);
- profileImpl->Hidden(true);
- }
- }
-
- if (generatedProfilesChanged)
- {
- state->GeneratedProfiles(generatedProfiles);
- }
- }
-
- // After layering the user settings, check if there are any new profiles
- // that need to be inserted into their user settings file.
- needToWriteFile = resultPtr->_AppendDynamicProfilesToUserSettings() || needToWriteFile;
-
- if (needToWriteFile)
- {
- // For safety's sake, we need to re-parse the JSON document to ensure that
- // all future patches are applied with updated object offsets.
- resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
- }
-
- // Make sure there's a $schema at the top of the file.
- needToWriteFile = resultPtr->_PrependSchemaDirective() || needToWriteFile;
-
- // TODO:GH#2721 If powershell core is installed, we need to set that to the
- // default profile, but only when the settings file was newly created. We'll
- // re-write the segment of the user settings for "default profile" to have
- // the powershell core GUID instead.
-
- // If we created the file, or found new dynamic profiles, write the user
- // settings string back to the file.
- if (needToWriteFile)
- {
- // If AppendDynamicProfilesToUserSettings (or the pwsh check above)
- // changed the file, then our local settings JSON is no longer accurate.
- // We should re-parse, but not re-layer
- resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
-
- try
- {
- WriteUTF8FileAtomic(_SettingsPath(), resultPtr->_userSettingsString);
- }
- catch (...)
- {
- resultPtr->AppendWarning(SettingsLoadWarnings::FailedToWriteToSettings);
- }
- }
-
- // If this throws, the app will catch it and use the default settings
- resultPtr->_ValidateSettings();
-
- return *resultPtr;
- }
- catch (const SettingsException& ex)
- {
- auto settings{ winrt::make_self() };
- settings->_loadError = ex.Error();
- return *settings;
- }
- catch (const SettingsTypedDeserializationException& e)
- {
- auto settings{ winrt::make_self() };
- std::string_view what{ e.what() };
- settings->_deserializationErrorMessage = til::u8u16(what);
- return *settings;
}
+
+ return *settings;
+}
+catch (const SettingsException& ex)
+{
+ const auto settings{ winrt::make_self() };
+ settings->_loadError = ex.Error();
+ return *settings;
+}
+catch (const SettingsTypedDeserializationException& e)
+{
+ const auto settings{ winrt::make_self() };
+ settings->_deserializationErrorMessage = til::u8u16(e.what());
+ return *settings;
}
// Function Description:
@@ -299,37 +664,9 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
// -
// Return Value:
// - a unique_ptr to a CascadiaSettings with the connection types and settings for Universal terminal
-winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::LoadUniversal()
+Model::CascadiaSettings CascadiaSettings::LoadUniversal()
{
- // We're going to do this ourselves because we want to exclude almost everything
- // from the special Universal-for-developers configuration
-
- try
- {
- // Create settings and get the universal defaults loaded up.
- auto resultPtr = winrt::make_self();
- resultPtr->_ParseJsonString(DefaultUniversalJson, true);
- resultPtr->LayerJson(resultPtr->_defaultSettings);
-
- // Now validate.
- // If this throws, the app will catch it and use the default settings
- resultPtr->_ValidateSettings();
-
- return *resultPtr;
- }
- catch (const SettingsException& ex)
- {
- auto settings{ winrt::make_self() };
- settings->_loadError = ex.Error();
- return *settings;
- }
- catch (const SettingsTypedDeserializationException& e)
- {
- auto settings{ winrt::make_self() };
- std::string_view what{ e.what() };
- settings->_deserializationErrorMessage = til::u8u16(what);
- return *settings;
- }
+ return *winrt::make_self(std::string_view{}, DefaultUniversalJson);
}
// Function Description:
@@ -339,761 +676,80 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
// -
// Return Value:
// - a unique_ptr to a CascadiaSettings with the settings from defaults.json
-winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::LoadDefaults()
+Model::CascadiaSettings CascadiaSettings::LoadDefaults()
{
- auto resultPtr{ winrt::make_self() };
-
- // We already have the defaults in memory, because we stamp them into a
- // header as part of the build process. We don't need to bother with reading
- // them from a file (and the potential that could fail)
- resultPtr->_ParseJsonString(DefaultJson, true);
- resultPtr->LayerJson(resultPtr->_defaultSettings);
- resultPtr->_ResolveDefaultProfile();
- resultPtr->_UpdateActiveProfiles();
-
- // tag these profiles as in-box
- for (const auto& profile : resultPtr->AllProfiles())
- {
- const auto profileImpl{ winrt::get_self