Merge branch 'dev/migrie/f/just-elevated-state-2' into dev/migrie/f/non-terminal-content-elevation-warning

This commit is contained in:
Mike Griese 2021-09-22 14:48:29 -05:00
commit 7e2b371dae
75 changed files with 2475 additions and 5624 deletions

View file

@ -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

View file

@ -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

View file

@ -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<COLORREF, COLOR_TABLE_SIZE> 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<uint32_t>(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<CascadiaSettings>(userSettings, inboxSettings);
auto settings = winrt::make_self<CascadiaSettings>();
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<ColorScheme>(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<ColorScheme>(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<ColorScheme>(scheme0Proj);
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
auto scheme1 = winrt::get_self<ColorScheme>(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<ColorScheme>(scheme0Proj);
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
auto scheme1 = winrt::get_self<ColorScheme>(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<ColorScheme>(scheme0Proj);
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
auto scheme1 = winrt::get_self<ColorScheme>(scheme1Proj);
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L""));
auto scheme2Proj = settings->_globals->ColorSchemes().Lookup(L"");
auto scheme2 = winrt::get_self<ColorScheme>(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<ColorScheme>(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<CascadiaSettings>(false) };
settings->_ParseJsonString(settingsString, false);
settings->_ApplyDefaultsFromUserSettings();
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
const auto settings{ winrt::make_self<CascadiaSettings>(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());
}
}

View file

@ -43,12 +43,6 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestLayerOnAutogeneratedName);
TEST_METHOD(TestGenerateCommandline);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
return true;
}
};
void CommandTests::ManyCommandsSameAction()

File diff suppressed because it is too large Load diff

View file

@ -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::CharReader>(Json::CharReaderBuilder::CharReaderBuilder().newCharReader());
};
static const std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
void InitializeJsonWriter()
{
_writer = std::unique_ptr<Json::StreamWriter>(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<Json::StreamWriter> writer{ Json::StreamWriterBuilder::StreamWriterBuilder().newStreamWriter() };
std::stringstream s;
_writer->write(json, &s);
writer->write(json, &s);
return s.str();
}
protected:
std::unique_ptr<Json::CharReader> _reader;
std::unique_ptr<Json::StreamWriter> _writer;
};

View file

@ -59,12 +59,6 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestGetKeyBindingForAction);
TEST_METHOD(KeybindingsWithoutVkey);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
return true;
}
};
void KeyBindingsTests::KeyChords()

View file

@ -7,6 +7,8 @@
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "JsonTestClass.h"
#include <defaults.h>
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<implementation::CascadiaSettings>();
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<implementation::CascadiaSettings>(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<implementation::CascadiaSettings>(userProfiles);
const auto profile = settings->AllProfiles().GetAt(0);
const auto duplicatedProfile = settings->DuplicateProfile(profile);
auto settings = winrt::make_self<implementation::CascadiaSettings>();
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<implementation::Profile>(profile)->ToJson();
const auto duplicatedJson = winrt::get_self<implementation::Profile>(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<implementation::CascadiaSettings>(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());
}
}

View file

@ -8,7 +8,6 @@
#include "JsonTestClass.h"
#include "TestUtils.h"
#include <defaults.h>
#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<implementation::CascadiaSettings>(false) };
settings->_ParseJsonString(settingsString, false);
settings->_ApplyDefaultsFromUserSettings();
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(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()

View file

@ -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<implementation::CascadiaSettings>(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<implementation::ActionMap>(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<implementation::CascadiaSettings>(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<implementation::CascadiaSettings>(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<implementation::CascadiaSettings>(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<implementation::TerminalSettings>() };
@ -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<implementation::CascadiaSettings>(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());
}
}

View file

@ -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"

View file

@ -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<winrt::TerminalApp::implementation::TerminalPage> 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}");

View file

@ -31,12 +31,12 @@ namespace winrt
using IInspectable = Windows::Foundation::IInspectable;
}
static const winrt::hstring StartupTaskName = L"StartTerminalOnLoginTask";
static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask";
// clang-format off
// !!! IMPORTANT !!!
// Make sure that these keys are in the same order as the
// SettingsLoadWarnings/Errors enum is!
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWarnings::WARNINGS_SIZE)> settingsLoadWarningsLabels {
static const std::array settingsLoadWarningsLabels {
USES_RESOURCE(L"MissingDefaultProfileText"),
USES_RESOURCE(L"DuplicateProfileText"),
USES_RESOURCE(L"UnknownColorSchemeText"),
@ -45,7 +45,6 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWar
USES_RESOURCE(L"AtLeastOneKeybindingWarning"),
USES_RESOURCE(L"TooManyKeysForChord"),
USES_RESOURCE(L"MissingRequiredParameter"),
USES_RESOURCE(L"LegacyGlobalsProperty"),
USES_RESOURCE(L"FailedToParseCommandJson"),
USES_RESOURCE(L"FailedToWriteToSettings"),
USES_RESOURCE(L"InvalidColorSchemeInCmd"),
@ -53,12 +52,15 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWar
USES_RESOURCE(L"FailedToParseStartupActions"),
USES_RESOURCE(L"FailedToParseSubCommands"),
};
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels {
static const std::array settingsLoadErrorsLabels {
USES_RESOURCE(L"NoProfilesText"),
USES_RESOURCE(L"AllProfilesHiddenText")
};
// clang-format on
static_assert(settingsLoadWarningsLabels.size() == static_cast<size_t>(SettingsLoadWarnings::WARNINGS_SIZE));
static_assert(settingsLoadErrorsLabels.size() == static_cast<size_t>(SettingsLoadErrors::ERRORS_SIZE));
// Function Description:
// - General-purpose helper for looking up a localized string for a
// warning/error. First will look for the given key in the provided map of
@ -70,12 +72,12 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadErr
// - map: A map of keys->Resource keys.
// Return Value:
// - the localized string for the given type, if it exists.
template<std::size_t N>
static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_view, N> keys)
template<typename T>
winrt::hstring _GetMessageText(uint32_t index, const T& keys)
{
if (index < keys.size())
{
return GetLibraryResourceString(keys.at(index));
return GetLibraryResourceString(til::at(keys, index));
}
return {};
}
@ -470,27 +472,6 @@ namespace winrt::TerminalApp::implementation
if (!warningText.empty())
{
warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources()));
// The "LegacyGlobalsProperty" warning is special - it has a URL
// that goes with it. So we need to manually construct a
// Hyperlink and insert it along with the warning text.
if (warning == SettingsLoadWarnings::LegacyGlobalsProperty)
{
// Add the URL here too
const auto legacyGlobalsLinkLabel = RS_(L"LegacyGlobalsPropertyHrefLabel");
const auto legacyGlobalsLinkUriValue = RS_(L"LegacyGlobalsPropertyHrefUrl");
winrt::Windows::UI::Xaml::Documents::Run legacyGlobalsLinkText;
winrt::Windows::UI::Xaml::Documents::Hyperlink legacyGlobalsLink;
winrt::Windows::Foundation::Uri legacyGlobalsLinkUri{ legacyGlobalsLinkUriValue };
legacyGlobalsLinkText.Text(legacyGlobalsLinkLabel);
legacyGlobalsLink.NavigateUri(legacyGlobalsLinkUri);
legacyGlobalsLink.Inlines().Append(legacyGlobalsLinkText);
warningsTextBlock.Inlines().Append(legacyGlobalsLink);
}
warningsTextBlock.Inlines().Append(Documents::LineBreak{});
}
}

View file

@ -214,12 +214,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else if (_canSendVTMouseInput(modifiers))
{
const auto adjustment = _core->ScrollOffset() > 0 ? _core->BufferHeight() - _core->ScrollOffset() - _core->ViewHeight() : 0;
// If the click happened outside the active region, just don't send any mouse event
if (const auto adjustedY = terminalPosition.y() - adjustment; adjustedY >= 0)
{
_core->SendMouseEvent({ terminalPosition.x(), adjustedY }, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
}
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
}
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
{
@ -287,7 +282,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Short-circuit isReadOnly check to avoid warning dialog
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{
_core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
}
// GH#4603 - don't modify the selection if the pointer press didn't
// actually start _in_ the control bounds. Case in point - someone drags
@ -370,7 +365,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Short-circuit isReadOnly check to avoid warning dialog
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{
_core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
return;
}
@ -420,11 +415,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// here with a PointerPoint. However, as of #979, we don't have a
// PointerPoint to work with. So, we're just going to do a
// mousewheel event manually
return _core->SendMouseEvent(terminalPosition,
return _sendMouseEventHelper(terminalPosition,
WM_MOUSEWHEEL,
modifiers,
::base::saturated_cast<short>(delta),
toInternalMouseState(buttonState));
buttonState);
}
const auto ctrlPressed = modifiers.IsCtrlPressed();
@ -600,6 +595,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return til::point{ pixelPosition / fontSize };
}
bool ControlInteractivity::_sendMouseEventHelper(const til::point terminalPosition,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const SHORT wheelDelta,
Control::MouseButtonState buttonState)
{
const auto adjustment = _core->ScrollOffset() > 0 ? _core->BufferHeight() - _core->ScrollOffset() - _core->ViewHeight() : 0;
// If the click happened outside the active region, just don't send any mouse event
if (const auto adjustedY = terminalPosition.y() - adjustment; adjustedY >= 0)
{
return _core->SendMouseEvent({ terminalPosition.x(), adjustedY }, pointerUpdateKind, modifiers, wheelDelta, toInternalMouseState(buttonState));
}
return false;
}
// Method Description:
// - Creates an automation peer for the Terminal Control, enabling
// accessibility on our control.

View file

@ -142,6 +142,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _sendPastedTextToConnection(std::wstring_view wstr);
til::point _getTerminalPosition(const til::point& pixelPosition);
bool _sendMouseEventHelper(const til::point terminalPosition,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const SHORT wheelDelta,
Control::MouseButtonState buttonState);
friend class ControlUnitTests::ControlCoreTests;
friend class ControlUnitTests::ControlInteractivityTests;
};

View file

@ -848,6 +848,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
const auto keyStatus = e.KeyStatus();
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
const auto scanCode = gsl::narrow_cast<WORD>(keyStatus.ScanCode);
auto modifiers = _GetPressedModifierKeys();
// GH#11076:
// For some weird reason we sometimes receive a WM_KEYDOWN
// message without vkey or scanCode if a user drags a tab.
// The KeyChord constructor has a debug assertion ensuring that all KeyChord
// either have a valid vkey/scanCode. This is important, because this prevents
// accidential insertion of invalid KeyChords into classes like ActionMap.
if (!vkey && !scanCode)
{
e.Handled(true);
return;
}
// Mark the event as handled and do nothing if we're closing, or the key
// was the Windows key.
//
@ -856,19 +873,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// win32-input-mode, then we'll send all these keystrokes to the
// terminal - it's smart enough to ignore the keys it doesn't care
// about.
if (_IsClosing() ||
e.OriginalKey() == VirtualKey::LeftWindows ||
e.OriginalKey() == VirtualKey::RightWindows)
if (_IsClosing() || vkey == VK_LWIN || vkey == VK_RWIN)
{
e.Handled(true);
return;
}
auto modifiers = _GetPressedModifierKeys();
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
// Short-circuit isReadOnly check to avoid warning dialog
if (_core.IsInReadOnlyMode())
{
@ -876,7 +886,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
if (e.KeyStatus().IsExtendedKey)
if (keyStatus.IsExtendedKey)
{
modifiers |= ControlKeyStates::EnhancedKey;
}
@ -886,8 +896,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// will be sent through the TSFInputControl. See GH#1401 for more
// details
if (modifiers.IsAltPressed() &&
(e.OriginalKey() >= VirtualKey::NumberPad0 && e.OriginalKey() <= VirtualKey::NumberPad9))
(vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9))
{
e.Handled(true);
return;
@ -917,7 +926,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Manually prevent keyboard navigation with tab. We want to send tab to
// the terminal, and we don't want to be able to escape focus of the
// control with tab.
e.Handled(e.OriginalKey() == VirtualKey::Tab);
e.Handled(vkey == VK_TAB);
}
// Method Description:

View file

@ -34,7 +34,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Launch::OnNavigatedTo(const NavigationEventArgs& e)
{
_State = e.Parameter().as<Editor::LaunchPageNavigationState>();
_State.Settings().RefreshDefaultTerminals();
}
IInspectable Launch::CurrentDefaultProfile()

View file

@ -74,7 +74,7 @@
x:Uid="Globals_DefaultTerminal"
x:Load="false">
<ComboBox x:Name="DefaultTerminal"
ItemsSource="{x:Bind State.Settings.DefaultTerminals, Mode=OneWay}"
ItemsSource="{x:Bind State.Settings.DefaultTerminals}"
SelectedItem="{x:Bind State.Settings.CurrentDefaultTerminal, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>

View file

@ -29,30 +29,29 @@ 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<Profile> sourceProfile) :
_sourceProfile(sourceProfile)
AppearanceConfig::AppearanceConfig(winrt::weak_ref<Profile> sourceProfile) :
_sourceProfile(std::move(sourceProfile))
{
}
winrt::com_ptr<AppearanceConfig> AppearanceConfig::CopyAppearance(const winrt::com_ptr<AppearanceConfig> source, const winrt::weak_ref<Profile> sourceProfile)
winrt::com_ptr<AppearanceConfig> AppearanceConfig::CopyAppearance(const AppearanceConfig* source, winrt::weak_ref<Profile> sourceProfile)
{
auto appearance{ winrt::make_self<AppearanceConfig>(sourceProfile) };
auto const sourceAppearance = source.try_as<AppearanceConfig>();
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;
appearance->_Opacity = sourceAppearance->_Opacity;
auto appearance{ winrt::make_self<AppearanceConfig>(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;
}

View file

@ -18,7 +18,6 @@ Author(s):
#include "AppearanceConfig.g.h"
#include "JsonUtils.h"
#include "../inc/cppwinrt_utils.h"
#include "IInheritable.h"
#include <DefaultSettings.h>
@ -27,8 +26,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct AppearanceConfig : AppearanceConfigT<AppearanceConfig>, IInheritable<AppearanceConfig>
{
public:
AppearanceConfig(const winrt::weak_ref<Profile> sourceProfile);
static winrt::com_ptr<AppearanceConfig> CopyAppearance(const winrt::com_ptr<AppearanceConfig> source, const winrt::weak_ref<Profile> sourceProfile);
AppearanceConfig(winrt::weak_ref<Profile> sourceProfile);
static winrt::com_ptr<AppearanceConfig> CopyAppearance(const AppearanceConfig* source, winrt::weak_ref<Profile> sourceProfile);
Json::Value ToJson() const;
void LayerJson(const Json::Value& json);

View file

@ -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()
// - <none>
// Return Value:
// - a vector with the Azure Cloud Shell connection profile, if available.
std::vector<Profile> AzureCloudShellGenerator::GenerateProfiles()
void AzureCloudShellGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
{
std::vector<Profile> 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;
}

View file

@ -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<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() override;
std::wstring_view GetNamespace() const noexcept override;
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
};
};

View file

@ -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<Profile> BaseVisualStudioGenerator::GenerateProfiles()
void BaseVisualStudioGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
{
std::vector<Profile> 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<Profile> 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<implementation::Profile>(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<implementation::Profile>(profileGuid);
newProfile->Origin(OriginTag::Generated);
return *newProfile;
}

View file

@ -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<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() override;
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& 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);
};
};

File diff suppressed because it is too large Load diff

View file

@ -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<Profile> CreateChild(const winrt::com_ptr<Profile>& parent);
class SettingsTypedDeserializationException final : public std::runtime_error
{
public:
SettingsTypedDeserializationException(const char* message) noexcept :
std::runtime_error(message) {}
};
struct ParsedSettings
{
winrt::com_ptr<implementation::GlobalAppSettings> globals;
winrt::com_ptr<implementation::Profile> baseLayerProfile;
std::vector<winrt::com_ptr<implementation::Profile>> profiles;
std::unordered_map<winrt::guid, winrt::com_ptr<implementation::Profile>> 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<size_t, size_t> _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<const winrt::com_ptr<implementation::Profile>> _getNonUserOriginProfiles() const;
void _parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
void _appendProfile(winrt::com_ptr<implementation::Profile>&& profile, ParsedSettings& settings);
void _executeGenerator(const IDynamicProfileGenerator& generator);
std::unordered_set<std::wstring_view> _ignoredNamespaces;
// See _getNonUserOriginProfiles().
size_t _userProfileCount = 0;
};
struct CascadiaSettings : CascadiaSettingsT<CascadiaSettings>
{
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<Model::Profile> AllProfiles() const noexcept;
Windows::Foundation::Collections::IObservableVector<Model::Profile> ActiveProfiles() const noexcept;
Model::ActionMap ActionMap() const noexcept;
static com_ptr<CascadiaSettings> 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<Model::Profile> AllProfiles() const noexcept;
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> 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<SettingsLoadWarnings> Warnings();
void ClearWarnings();
void AppendWarning(SettingsLoadWarnings warning);
Windows::Foundation::IReference<SettingsLoadErrors> 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<Model::SettingsLoadWarnings> Warnings() const;
winrt::Windows::Foundation::IReference<Model::SettingsLoadErrors> GetLoadingError() const;
winrt::hstring GetSerializationErrorMessage() const;
// defterm
static bool IsDefaultTerminalAvailable() noexcept;
Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> DefaultTerminals() const noexcept;
Model::DefaultTerminal CurrentDefaultTerminal() const noexcept;
void CurrentDefaultTerminal(Model::DefaultTerminal terminal);
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> DefaultTerminals() const noexcept;
Model::DefaultTerminal CurrentDefaultTerminal() noexcept;
void CurrentDefaultTerminal(const Model::DefaultTerminal& terminal);
private:
com_ptr<GlobalAppSettings> _globals;
Windows::Foundation::Collections::IObservableVector<Model::Profile> _allProfiles;
Windows::Foundation::Collections::IObservableVector<Model::Profile> _activeProfiles;
Windows::Foundation::Collections::IVector<Model::SettingsLoadWarnings> _warnings;
Windows::Foundation::IReference<SettingsLoadErrors> _loadError;
hstring _deserializationErrorMessage;
static const std::filesystem::path& _settingsPath();
Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> _defaultTerminals;
Model::DefaultTerminal _currentDefaultTerminal;
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
std::vector<std::unique_ptr<::Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator>> _profileGenerators;
void _resolveDefaultProfile() const;
std::string _userSettingsString;
Json::Value _userSettings;
Json::Value _defaultSettings;
winrt::com_ptr<Profile> _userDefaultProfileSettings{ nullptr };
void _validateSettings();
void _validateAllSchemesExist();
void _validateMediaResources();
void _validateKeybindings() const;
void _validateColorSchemesInCommands() const;
bool _hasInvalidColorScheme(const Model::Command& command) const;
winrt::com_ptr<Profile> _CreateNewProfile(const std::wstring_view& name) const;
// user settings
winrt::com_ptr<implementation::GlobalAppSettings> _globals;
winrt::com_ptr<implementation::Profile> _baseLayerProfile;
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _allProfiles;
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _activeProfiles;
void _LayerOrCreateProfile(const Json::Value& profileJson);
winrt::com_ptr<implementation::Profile> _FindMatchingProfile(const Json::Value& profileJson);
std::optional<uint32_t> _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<Model::SettingsLoadWarnings> _warnings;
winrt::Windows::Foundation::IReference<Model::SettingsLoadErrors> _loadError;
winrt::hstring _deserializationErrorMessage;
winrt::com_ptr<implementation::ColorScheme> _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<CascadiaSettings>& cloneSettings) const;
void _ApplyDefaultsFromUserSettings();
void _LoadDynamicProfiles();
void _LoadFragmentExtensions();
void _ApplyJsonStubsHelper(const std::wstring_view directory, const std::unordered_set<std::wstring>& ignoredNamespaces);
std::unordered_set<std::string> _AccumulateJsonFilesInDirectory(const std::wstring_view directory);
void _ParseAndLayerFragmentFiles(const std::unordered_set<std::string> files, const winrt::hstring source);
static const std::filesystem::path& _SettingsPath();
static std::optional<std::string> _ReadUserSettings();
std::optional<guid> _GetProfileGuidByName(const hstring) const;
std::optional<guid> _GetProfileGuidByIndex(std::optional<int> 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 };
};
}

View file

@ -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<Profile> AllProfiles { get; };
Windows.Foundation.Collections.IObservableVector<Profile> ActiveProfiles { get; };
IObservableVector<Profile> AllProfiles { get; };
IObservableVector<Profile> ActiveProfiles { get; };
Profile DuplicateProfile(Profile sourceProfile);
ActionMap ActionMap { get; };
Windows.Foundation.Collections.IVectorView<SettingsLoadWarnings> Warnings { get; };
IVectorView<SettingsLoadWarnings> Warnings { get; };
Windows.Foundation.IReference<SettingsLoadErrors> 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<DefaultTerminal> DefaultTerminals { get; };
IObservableVector<DefaultTerminal> DefaultTerminals { get; };
DefaultTerminal CurrentDefaultTerminal;
}
}

View file

@ -3,7 +3,6 @@
#include "pch.h"
#include "ColorScheme.h"
#include "DefaultSettings.h"
#include "../../types/inc/Utils.hpp"
#include "../../types/inc/colorTable.hpp"
#include "Utils.h"
@ -41,25 +40,16 @@ static constexpr std::array<std::string_view, 16> TableColors = {
"brightWhite"
};
ColorScheme::ColorScheme() :
ColorScheme(L"", DEFAULT_FOREGROUND, DEFAULT_BACKGROUND, DEFAULT_CURSOR_COLOR)
ColorScheme::ColorScheme() noexcept :
ColorScheme{ winrt::hstring{} }
{
Utils::InitializeCampbellColorTable(_table);
}
ColorScheme::ColorScheme(winrt::hstring name) :
ColorScheme(name, DEFAULT_FOREGROUND, DEFAULT_BACKGROUND, DEFAULT_CURSOR_COLOR)
{
Utils::InitializeCampbellColorTable(_table);
}
ColorScheme::ColorScheme(winrt::hstring name, til::color defaultFg, til::color defaultBg, til::color cursorColor) :
_Name{ name },
_Foreground{ defaultFg },
_Background{ defaultBg },
_SelectionBackground{ DEFAULT_FOREGROUND },
_CursorColor{ cursorColor }
ColorScheme::ColorScheme(const winrt::hstring& name) noexcept :
_Name{ name }
{
const auto table = Utils::CampbellColorTable();
std::copy_n(table.data(), table.size(), _table.data());
}
winrt::com_ptr<ColorScheme> ColorScheme::Copy() const
@ -79,30 +69,11 @@ winrt::com_ptr<ColorScheme> ColorScheme::Copy() const
// Arguments:
// - json: an object which should be a serialization of a ColorScheme object.
// Return Value:
// - a new ColorScheme instance created from the values in `json`
// - Returns nullptr for invalid JSON.
winrt::com_ptr<ColorScheme> ColorScheme::FromJson(const Json::Value& json)
{
auto result = winrt::make_self<ColorScheme>();
result->LayerJson(json);
return result;
}
// Method Description:
// - Returns true if we think the provided json object represents an instance of
// the same object as this object. If true, we should layer that json object
// on us, instead of creating a new object.
// Arguments:
// - json: The json object to query to see if it's the same
// Return Value:
// - true iff the json object has the same `name` as we do.
bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
{
std::wstring nameFromJson{};
if (JsonUtils::GetValueForKey(json, NameKey, nameFromJson))
{
return nameFromJson == _Name;
}
return false;
auto result = winrt::make_self<ColorScheme>(uninitialized_t{});
return result->_layerJson(json) ? result : nullptr;
}
// Method Description:
@ -112,21 +83,27 @@ bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
// the new json object. Properties that _aren't_ in the json object will _not_
// be replaced.
// Arguments:
// - json: an object which should be a partial serialization of a ColorScheme object.
// - json: an object which should be a full serialization of a ColorScheme object.
// Return Value:
// <none>
void ColorScheme::LayerJson(const Json::Value& json)
// - Returns true if the given JSON was valid.
bool ColorScheme::_layerJson(const Json::Value& json)
{
JsonUtils::GetValueForKey(json, NameKey, _Name);
// Required fields
auto isValid = JsonUtils::GetValueForKey(json, NameKey, _Name);
// Optional fields (they have defaults in ColorScheme.h)
JsonUtils::GetValueForKey(json, ForegroundKey, _Foreground);
JsonUtils::GetValueForKey(json, BackgroundKey, _Background);
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _SelectionBackground);
JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor);
// Required fields
for (unsigned int i = 0; i < TableColors.size(); ++i)
{
JsonUtils::GetValueForKey(json, til::at(TableColors, i), _table.at(i));
isValid &= JsonUtils::GetValueForKey(json, til::at(TableColors, i), til::at(_table, i));
}
return isValid;
}
// Method Description:
@ -147,7 +124,7 @@ Json::Value ColorScheme::ToJson() const
for (unsigned int i = 0; i < TableColors.size(); ++i)
{
JsonUtils::SetValueForKey(json, til::at(TableColors, i), _table.at(i));
JsonUtils::SetValueForKey(json, til::at(TableColors, i), til::at(_table, i));
}
return json;
@ -155,9 +132,7 @@ Json::Value ColorScheme::ToJson() const
winrt::com_array<winrt::Microsoft::Terminal::Core::Color> ColorScheme::Table() const noexcept
{
winrt::com_array<winrt::Microsoft::Terminal::Core::Color> result{ base::checked_cast<uint32_t>(_table.size()) };
std::transform(_table.begin(), _table.end(), result.begin(), [](til::color c) -> winrt::Microsoft::Terminal::Core::Color { return c; });
return result;
return winrt::com_array<Core::Color>{ _table };
}
// Method Description:
@ -167,44 +142,8 @@ winrt::com_array<winrt::Microsoft::Terminal::Core::Color> ColorScheme::Table() c
// - value: the color value we are setting the color table color to
// Return Value:
// - none
void ColorScheme::SetColorTableEntry(uint8_t index, const winrt::Microsoft::Terminal::Core::Color& value) noexcept
void ColorScheme::SetColorTableEntry(uint8_t index, const Core::Color& value) noexcept
{
THROW_HR_IF(E_INVALIDARG, index > _table.size() - 1);
THROW_HR_IF(E_INVALIDARG, index >= _table.size());
_table[index] = value;
}
// Method Description:
// - Validates a given color scheme
// - A color scheme is valid if it has a name and defines all the colors
// Arguments:
// - The color scheme to validate
// Return Value:
// - true if the scheme is valid, false otherwise
bool ColorScheme::ValidateColorScheme(const Json::Value& scheme)
{
for (const auto& key : TableColors)
{
if (!scheme.isMember(JsonKey(key)))
{
return false;
}
}
if (!scheme.isMember(JsonKey(NameKey)))
{
return false;
}
return true;
}
// Method Description:
// - Parse the name from the JSON representation of a ColorScheme.
// Arguments:
// - json: an object which should be a serialization of a ColorScheme object.
// Return Value:
// - the name of the color scheme represented by `json` as a std::wstring optional
// i.e. the value of the `name` property.
// - returns std::nullopt if `json` doesn't have the `name` property
std::optional<std::wstring> ColorScheme::GetNameFromJson(const Json::Value& json)
{
return JsonUtils::GetValueForKey<std::optional<std::wstring>>(json, NameKey);
}

View file

@ -15,38 +15,28 @@ Author(s):
--*/
#pragma once
#include "../../inc/conattrs.hpp"
#include "inc/cppwinrt_utils.h"
#include "DefaultSettings.h"
#include "ColorScheme.g.h"
// fwdecl unittest classes
namespace SettingsModelLocalTests
{
class SettingsTests;
class ColorSchemeTests;
};
// Use this macro to quick implement both the getter and setter for a color property.
// This should only be used for color types where there's no logic in the
// getter/setter beyond just accessing/updating the value.
// This takes advantage of til::color
#define WINRT_TERMINAL_COLOR_PROPERTY(name, ...) \
public: \
winrt::Microsoft::Terminal::Core::Color name() const noexcept { return _##name; } \
void name(const winrt::Microsoft::Terminal::Core::Color& value) noexcept { _##name = value; } \
\
private: \
til::color _##name{ __VA_ARGS__ };
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct ColorScheme : ColorSchemeT<ColorScheme>
{
// A ColorScheme constructed with uninitialized_t
// leaves _table uninitialized.
struct uninitialized_t
{
};
public:
ColorScheme();
ColorScheme(hstring name);
ColorScheme(hstring name, til::color defaultFg, til::color defaultBg, til::color cursorColor);
ColorScheme() noexcept;
explicit ColorScheme(uninitialized_t) noexcept {}
explicit ColorScheme(const winrt::hstring& name) noexcept;
com_ptr<ColorScheme> Copy() const;
hstring ToString()
@ -55,29 +45,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
static com_ptr<ColorScheme> FromJson(const Json::Value& json);
bool ShouldBeLayered(const Json::Value& json) const;
void LayerJson(const Json::Value& json);
Json::Value ToJson() const;
static std::optional<std::wstring> GetNameFromJson(const Json::Value& json);
com_array<winrt::Microsoft::Terminal::Core::Color> Table() const noexcept;
void SetColorTableEntry(uint8_t index, const winrt::Microsoft::Terminal::Core::Color& value) noexcept;
static bool ValidateColorScheme(const Json::Value& scheme);
com_array<Core::Color> Table() const noexcept;
void SetColorTableEntry(uint8_t index, const Core::Color& value) noexcept;
WINRT_PROPERTY(winrt::hstring, Name);
WINRT_TERMINAL_COLOR_PROPERTY(Foreground); // defined in constructor
WINRT_TERMINAL_COLOR_PROPERTY(Background); // defined in constructor
WINRT_TERMINAL_COLOR_PROPERTY(SelectionBackground); // defined in constructor
WINRT_TERMINAL_COLOR_PROPERTY(CursorColor); // defined in constructor
WINRT_PROPERTY(Core::Color, Foreground, static_cast<Core::Color>(DEFAULT_FOREGROUND));
WINRT_PROPERTY(Core::Color, Background, static_cast<Core::Color>(DEFAULT_BACKGROUND));
WINRT_PROPERTY(Core::Color, SelectionBackground, static_cast<Core::Color>(DEFAULT_FOREGROUND));
WINRT_PROPERTY(Core::Color, CursorColor, static_cast<Core::Color>(DEFAULT_CURSOR_COLOR));
private:
std::array<til::color, COLOR_TABLE_SIZE> _table;
bool _layerJson(const Json::Value& json);
friend class SettingsModelLocalTests::SettingsTests;
friend class SettingsModelLocalTests::ColorSchemeTests;
std::array<Core::Color, COLOR_TABLE_SIZE> _table;
};
}

View file

@ -2,7 +2,7 @@
// Licensed under the MIT license.
#include "pch.h"
#include "DefaultProfileUtils.h"
#include "DynamicProfileUtils.h"
#include "../../types/inc/utils.hpp"
static constexpr std::wstring_view PACKAGED_PROFILE_ICON_PATH{ L"ms-appx:///ProfileIcons/" };
@ -15,20 +15,16 @@ static constexpr std::wstring_view PACKAGED_PROFILE_ICON_EXTENSION{ L".png" };
// - name: the name of the new profile.
// Return Value:
// - A Profile, ready to be filled in
winrt::Microsoft::Terminal::Settings::Model::Profile CreateDefaultProfile(const std::wstring_view name)
winrt::com_ptr<winrt::Microsoft::Terminal::Settings::Model::implementation::Profile> CreateDynamicProfile(const std::wstring_view& name)
{
const winrt::guid profileGuid{ Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID,
gsl::as_bytes(gsl::make_span(name))) };
auto newProfile = winrt::make_self<winrt::Microsoft::Terminal::Settings::Model::implementation::Profile>(profileGuid);
newProfile->Name(winrt::hstring{ name });
const auto profileGuid = Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(name)));
std::wstring iconPath{ PACKAGED_PROFILE_ICON_PATH };
iconPath.append(Microsoft::Console::Utils::GuidToString(profileGuid));
iconPath.append(PACKAGED_PROFILE_ICON_EXTENSION);
newProfile->Icon(winrt::hstring{ iconPath });
newProfile->Origin(winrt::Microsoft::Terminal::Settings::Model::OriginTag::Generated);
return *newProfile;
auto profile = winrt::make_self<winrt::Microsoft::Terminal::Settings::Model::implementation::Profile>(profileGuid);
profile->Name(winrt::hstring{ name });
profile->Icon(winrt::hstring{ iconPath });
return profile;
}

View file

@ -23,4 +23,4 @@ Author(s):
// uuidv5 properties: name format is UTF-16LE bytes
static constexpr GUID TERMINAL_PROFILE_NAMESPACE_GUID = { 0x2bde4a90, 0xd05f, 0x401c, { 0x94, 0x92, 0xe4, 0x8, 0x84, 0xea, 0xd1, 0xd8 } };
winrt::Microsoft::Terminal::Settings::Model::Profile CreateDefaultProfile(const std::wstring_view name);
winrt::com_ptr<winrt::Microsoft::Terminal::Settings::Model::implementation::Profile> CreateDynamicProfile(const std::wstring_view& name);

View file

@ -13,7 +13,7 @@
static constexpr std::string_view Utf8Bom{ u8"\uFEFF" };
static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" };
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
// Returns a path like C:\Users\<username>\AppData\Local\Packages\<packagename>\LocalState
// You can put your settings.json or state.json in this directory.
@ -188,7 +188,7 @@ namespace Microsoft::Terminal::Settings::Model
}
void WriteUTF8File(const std::filesystem::path& path,
const std::string_view content,
const std::string_view& content,
const bool elevatedOnly)
{
SECURITY_ATTRIBUTES sa;
@ -269,7 +269,7 @@ namespace Microsoft::Terminal::Settings::Model
}
void WriteUTF8FileAtomic(const std::filesystem::path& path,
const std::string_view content)
const std::string_view& content)
{
// GH#10787: rename() will replace symbolic links themselves and not the path they point at.
// It's thus important that we first resolve them before generating temporary path.

View file

@ -1,11 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
std::filesystem::path GetBaseSettingsPath();
std::string ReadUTF8File(const std::filesystem::path& path, const bool elevatedOnly = false);
std::optional<std::string> ReadUTF8FileIfExists(const std::filesystem::path& path, const bool elevatedOnly = false);
void WriteUTF8File(const std::filesystem::path& path, const std::string_view content, const bool elevatedOnly = false);
void WriteUTF8FileAtomic(const std::filesystem::path& path, const std::string_view content);
void WriteUTF8File(const std::filesystem::path& path, const std::string_view& content, const bool elevatedOnly = false);
void WriteUTF8FileAtomic(const std::filesystem::path& path, const std::string_view& content);
}

View file

@ -4,6 +4,7 @@
#include "pch.h"
#include "FontConfig.h"
#include "FontConfig.g.cpp"
#include "TerminalSettingsSerializationHelpers.h"
#include "JsonUtils.h"
@ -25,7 +26,7 @@ winrt::Microsoft::Terminal::Settings::Model::implementation::FontConfig::FontCon
{
}
winrt::com_ptr<FontConfig> FontConfig::CopyFontInfo(const winrt::com_ptr<FontConfig> source, winrt::weak_ref<Profile> sourceProfile)
winrt::com_ptr<FontConfig> FontConfig::CopyFontInfo(const FontConfig* source, winrt::weak_ref<Profile> sourceProfile)
{
auto fontInfo{ winrt::make_self<FontConfig>(std::move(sourceProfile)) };
fontInfo->_FontFace = source->_FontFace;

View file

@ -32,7 +32,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
public:
FontConfig(winrt::weak_ref<Profile> sourceProfile);
static winrt::com_ptr<FontConfig> CopyFontInfo(const winrt::com_ptr<FontConfig> source, winrt::weak_ref<Profile> sourceProfile);
static winrt::com_ptr<FontConfig> CopyFontInfo(const FontConfig* source, winrt::weak_ref<Profile> sourceProfile);
Json::Value ToJson() const;
void LayerJson(const Json::Value& json);
bool HasAnyOptionSet() const;

View file

@ -4,9 +4,7 @@
#include "pch.h"
#include "GlobalAppSettings.h"
#include "../../types/inc/Utils.hpp"
#include "../../inc/DefaultSettings.h"
#include "JsonUtils.h"
#include "TerminalSettingsSerializationHelpers.h"
#include "KeyChordSerialization.h"
#include "GlobalAppSettings.g.cpp"
@ -52,6 +50,7 @@ static constexpr std::string_view WindowingBehaviorKey{ "windowingBehavior" };
static constexpr std::string_view TrimBlockSelectionKey{ "trimBlockSelection" };
static constexpr std::string_view AlwaysShowNotificationIconKey{ "alwaysShowNotificationIcon" };
static constexpr std::string_view MinimizeToNotificationAreaKey{ "minimizeToNotificationArea" };
static constexpr std::string_view DisabledProfileSourcesKey{ "disabledProfileSources" };
static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };
@ -60,26 +59,6 @@ static constexpr std::string_view SoftwareRenderingKey{ "experimental.rendering.
static constexpr std::string_view ForceVTInputKey{ "experimental.input.forceVT" };
static constexpr std::string_view DetectURLsKey{ "experimental.detectURLs" };
#ifdef _DEBUG
static constexpr bool debugFeaturesDefault{ true };
#else
static constexpr bool debugFeaturesDefault{ false };
#endif
bool GlobalAppSettings::_getDefaultDebugFeaturesValue()
{
return debugFeaturesDefault;
}
GlobalAppSettings::GlobalAppSettings() :
_actionMap{ winrt::make_self<implementation::ActionMap>() },
_keybindingsWarnings{},
_validDefaultProfile{ false },
_defaultProfile{}
{
_colorSchemes = winrt::single_threaded_map<winrt::hstring, Model::ColorScheme>();
}
// Method Description:
// - Copies any extraneous data from the parent before completing a CreateChild call
// Arguments:
@ -88,13 +67,17 @@ GlobalAppSettings::GlobalAppSettings() :
// - <none>
void GlobalAppSettings::_FinalizeInheritance()
{
// Globals only ever has 1 parent
FAIL_FAST_IF(_parents.size() > 1);
for (auto parent : _parents)
for (const auto& parent : _parents)
{
_actionMap->InsertParent(parent->_actionMap);
_keybindingsWarnings = std::move(parent->_keybindingsWarnings);
_colorSchemes = std::move(parent->_colorSchemes);
_keybindingsWarnings.insert(_keybindingsWarnings.end(), parent->_keybindingsWarnings.begin(), parent->_keybindingsWarnings.end());
for (const auto& [k, v] : parent->_colorSchemes)
{
if (!_colorSchemes.HasKey(k))
{
_colorSchemes.Insert(k, v);
}
}
}
}
@ -137,12 +120,12 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_DetectURLs = _DetectURLs;
globals->_MinimizeToNotificationArea = _MinimizeToNotificationArea;
globals->_AlwaysShowNotificationIcon = _AlwaysShowNotificationIcon;
globals->_DisabledProfileSources = _DisabledProfileSources;
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
globals->_validDefaultProfile = _validDefaultProfile;
globals->_defaultProfile = _defaultProfile;
globals->_actionMap = _actionMap->Copy();
std::copy(_keybindingsWarnings.begin(), _keybindingsWarnings.end(), std::back_inserter(globals->_keybindingsWarnings));
globals->_keybindingsWarnings = _keybindingsWarnings;
if (_colorSchemes)
{
@ -153,9 +136,7 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
}
}
// Globals only ever has 1 parent
FAIL_FAST_IF(_parents.size() > 1);
for (auto parent : _parents)
for (const auto& parent : _parents)
{
globals->InsertParent(parent->Copy());
}
@ -168,69 +149,18 @@ winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microso
}
#pragma region DefaultProfile
void GlobalAppSettings::DefaultProfile(const winrt::guid& defaultProfile) noexcept
{
_validDefaultProfile = true;
_defaultProfile = defaultProfile;
_UnparsedDefaultProfile = Utils::GuidToString(defaultProfile);
}
winrt::guid GlobalAppSettings::DefaultProfile() const
{
// If we have an unresolved default profile, we should likely explode.
THROW_HR_IF(E_INVALIDARG, !_validDefaultProfile);
return _defaultProfile;
}
bool GlobalAppSettings::HasUnparsedDefaultProfile() const
{
return _UnparsedDefaultProfile.has_value();
}
winrt::hstring GlobalAppSettings::UnparsedDefaultProfile() const
{
const auto val{ _getUnparsedDefaultProfileImpl() };
return val ? *val : hstring{ L"" };
}
void GlobalAppSettings::UnparsedDefaultProfile(const hstring& value)
{
if (_UnparsedDefaultProfile != value)
{
_UnparsedDefaultProfile = value;
_validDefaultProfile = false;
}
}
void GlobalAppSettings::ClearUnparsedDefaultProfile()
{
if (HasUnparsedDefaultProfile())
{
_UnparsedDefaultProfile = std::nullopt;
}
}
std::optional<winrt::hstring> GlobalAppSettings::_getUnparsedDefaultProfileImpl() const
{
/*return user set value*/
if (_UnparsedDefaultProfile)
{
return _UnparsedDefaultProfile;
}
/*user set value was not set*/
/*iterate through parents to find a value*/
for (auto parent : _parents)
{
if (auto val{ parent->_getUnparsedDefaultProfileImpl() })
{
return val;
}
}
/*no value was found*/
return std::nullopt;
}
#pragma endregion
winrt::Microsoft::Terminal::Settings::Model::ActionMap GlobalAppSettings::ActionMap() const noexcept
@ -253,92 +183,53 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::FromJson(const Json::Value&
void GlobalAppSettings::LayerJson(const Json::Value& json)
{
// _validDefaultProfile keeps track of whether we've verified that DefaultProfile points to something
// CascadiaSettings::_ResolveDefaultProfile performs a validation and updates DefaultProfile() with the
// resolved value, then making it valid.
_validDefaultProfile = false;
JsonUtils::GetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
JsonUtils::GetValueForKey(json, AlwaysShowTabsKey, _AlwaysShowTabs);
JsonUtils::GetValueForKey(json, ConfirmCloseAllKey, _ConfirmCloseAllTabs);
JsonUtils::GetValueForKey(json, InitialRowsKey, _InitialRows);
JsonUtils::GetValueForKey(json, InitialColsKey, _InitialCols);
JsonUtils::GetValueForKey(json, InitialPositionKey, _InitialPosition);
JsonUtils::GetValueForKey(json, CenterOnLaunchKey, _CenterOnLaunch);
JsonUtils::GetValueForKey(json, ShowTitleInTitlebarKey, _ShowTitleInTitlebar);
JsonUtils::GetValueForKey(json, ShowTabsInTitlebarKey, _ShowTabsInTitlebar);
JsonUtils::GetValueForKey(json, WordDelimitersKey, _WordDelimiters);
JsonUtils::GetValueForKey(json, CopyOnSelectKey, _CopyOnSelect);
JsonUtils::GetValueForKey(json, InputServiceWarningKey, _InputServiceWarning);
JsonUtils::GetValueForKey(json, CopyFormattingKey, _CopyFormatting);
JsonUtils::GetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
JsonUtils::GetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
JsonUtils::GetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference);
JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode);
JsonUtils::GetValueForKey(json, LanguageKey, _Language);
JsonUtils::GetValueForKey(json, ThemeKey, _Theme);
JsonUtils::GetValueForKey(json, TabWidthModeKey, _TabWidthMode);
JsonUtils::GetValueForKey(json, UseAcrylicInTabRowKey, _UseAcrylicInTabRow);
JsonUtils::GetValueForKey(json, SnapToGridOnResizeKey, _SnapToGridOnResize);
// GetValueForKey will only override the current value if the key exists
JsonUtils::GetValueForKey(json, DebugFeaturesKey, _DebugFeaturesEnabled);
JsonUtils::GetValueForKey(json, ForceFullRepaintRenderingKey, _ForceFullRepaintRendering);
JsonUtils::GetValueForKey(json, SoftwareRenderingKey, _SoftwareRendering);
JsonUtils::GetValueForKey(json, ForceVTInputKey, _ForceVTInput);
JsonUtils::GetValueForKey(json, EnableStartupTaskKey, _StartOnUserLogin);
JsonUtils::GetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop);
// GH#8076 - when adding enum values to this key, we also changed it from
// "useTabSwitcher" to "tabSwitcherMode". Continue supporting
// "useTabSwitcher", but prefer "tabSwitcherMode"
JsonUtils::GetValueForKey(json, LegacyUseTabSwitcherModeKey, _TabSwitcherMode);
JsonUtils::GetValueForKey(json, TabSwitcherModeKey, _TabSwitcherMode);
JsonUtils::GetValueForKey(json, DisableAnimationsKey, _DisableAnimations);
JsonUtils::GetValueForKey(json, StartupActionsKey, _StartupActions);
JsonUtils::GetValueForKey(json, FocusFollowMouseKey, _FocusFollowMouse);
JsonUtils::GetValueForKey(json, WindowingBehaviorKey, _WindowingBehavior);
JsonUtils::GetValueForKey(json, TrimBlockSelectionKey, _TrimBlockSelection);
JsonUtils::GetValueForKey(json, DetectURLsKey, _DetectURLs);
JsonUtils::GetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea);
JsonUtils::GetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon);
JsonUtils::GetValueForKey(json, DisabledProfileSourcesKey, _DisabledProfileSources);
// This is a helper lambda to get the keybindings and commands out of both
// and array of objects. We'll use this twice, once on the legacy
// `keybindings` key, and again on the newer `bindings` key.
auto parseBindings = [this, &json](auto jsonKey) {
static constexpr std::array bindingsKeys{ LegacyKeybindingsKey, ActionsKey };
for (const auto& jsonKey : bindingsKeys)
{
if (auto bindings{ json[JsonKey(jsonKey)] })
{
auto warnings = _actionMap->LayerJson(bindings);
@ -351,9 +242,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
// list of warnings.
_keybindingsWarnings.insert(_keybindingsWarnings.end(), warnings.begin(), warnings.end());
}
};
parseBindings(LegacyKeybindingsKey);
parseBindings(ActionsKey);
}
}
// Method Description:
@ -381,7 +270,7 @@ void GlobalAppSettings::RemoveColorScheme(hstring schemeName)
// - <none>
// Return Value:
// - <none>
std::vector<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> GlobalAppSettings::KeybindingsWarnings() const
const std::vector<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& GlobalAppSettings::KeybindingsWarnings() const
{
return _keybindingsWarnings;
}
@ -433,7 +322,8 @@ Json::Value GlobalAppSettings::ToJson() const
JsonUtils::SetValueForKey(json, TrimBlockSelectionKey, _TrimBlockSelection);
JsonUtils::SetValueForKey(json, DetectURLsKey, _DetectURLs);
JsonUtils::SetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea);
JsonUtils::SetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon);
JsonUtils::SetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon);
JsonUtils::SetValueForKey(json, DisabledProfileSourcesKey, _DisabledProfileSources);
// clang-format on
json[JsonKey(ActionsKey)] = _actionMap->ToJson();

View file

@ -34,7 +34,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct GlobalAppSettings : GlobalAppSettingsT<GlobalAppSettings>, IInheritable<GlobalAppSettings>
{
public:
GlobalAppSettings();
void _FinalizeInheritance() override;
com_ptr<GlobalAppSettings> Copy() const;
@ -49,16 +48,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Json::Value ToJson() const;
std::vector<SettingsLoadWarnings> KeybindingsWarnings() const;
const std::vector<SettingsLoadWarnings>& KeybindingsWarnings() const;
// These are implemented manually to handle the string/GUID exchange
// by higher layers in the app.
// This DefaultProfile() setter is called by CascadiaSettings,
// when it parses UnparsedDefaultProfile in _finalizeSettings().
void DefaultProfile(const guid& defaultProfile) noexcept;
guid DefaultProfile() const;
bool HasUnparsedDefaultProfile() const;
winrt::hstring UnparsedDefaultProfile() const;
void UnparsedDefaultProfile(const hstring& value);
void ClearUnparsedDefaultProfile();
// TODO GH#9207: Remove this once we have a GlobalAppSettingsViewModel in TerminalSettingsEditor
void SetInvertedDisableAnimationsValue(bool invertedDisableAnimationsValue)
@ -90,7 +85,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ForceFullRepaintRendering, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, SoftwareRendering, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ForceVTInput, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DebugFeaturesEnabled, _getDefaultDebugFeaturesValue());
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DebugFeaturesEnabled, debugFeaturesDefault);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, StartOnUserLogin, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysOnTop, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::TabSwitcherMode, TabSwitcherMode, Model::TabSwitcherMode::InOrder);
@ -102,21 +97,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DetectURLs, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, MinimizeToNotificationArea, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowNotificationIcon, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, DisabledProfileSources, nullptr);
INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L"");
private:
guid _defaultProfile;
std::optional<hstring> _UnparsedDefaultProfile{ std::nullopt };
bool _validDefaultProfile;
#ifdef NDEBUG
static constexpr bool debugFeaturesDefault{ false };
#else
static constexpr bool debugFeaturesDefault{ true };
#endif
com_ptr<implementation::ActionMap> _actionMap;
winrt::guid _defaultProfile;
winrt::com_ptr<implementation::ActionMap> _actionMap{ winrt::make_self<implementation::ActionMap>() };
std::vector<SettingsLoadWarnings> _keybindingsWarnings;
Windows::Foundation::Collections::IMap<hstring, Model::ColorScheme> _colorSchemes;
std::optional<hstring> _getUnparsedDefaultProfileImpl() const;
static bool _getDefaultDebugFeaturesValue();
friend class SettingsModelLocalTests::DeserializationTests;
friend class SettingsModelLocalTests::ColorSchemeTests;
Windows::Foundation::Collections::IMap<winrt::hstring, Model::ColorScheme> _colorSchemes{ winrt::single_threaded_map<winrt::hstring, Model::ColorScheme>() };
};
}

View file

@ -83,6 +83,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, DetectURLs);
INHERITABLE_SETTING(Boolean, MinimizeToNotificationArea);
INHERITABLE_SETTING(Boolean, AlwaysShowNotificationIcon);
INHERITABLE_SETTING(IVector<String>, DisabledProfileSources);
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);

View file

@ -20,18 +20,16 @@ Author(s):
--*/
#pragma once
#include "Profile.h"
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
class IDynamicProfileGenerator;
class IDynamicProfileGenerator
{
public:
virtual ~IDynamicProfileGenerator(){};
virtual std::wstring_view GetNamespace() const noexcept = 0;
virtual void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const = 0;
};
};
class Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator
{
public:
virtual ~IDynamicProfileGenerator() = 0;
virtual std::wstring_view GetNamespace() = 0;
virtual std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() = 0;
};
inline Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator::~IDynamicProfileGenerator() {}

View file

@ -48,13 +48,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void InsertParent(com_ptr<T> parent)
{
_parents.push_back(parent);
_parents.emplace_back(std::move(parent));
}
void InsertParent(size_t index, com_ptr<T> parent)
{
auto pos{ _parents.begin() + index };
_parents.insert(pos, parent);
_parents.emplace(pos, std::move(parent));
}
const std::vector<com_ptr<T>>& Parents()

View file

@ -677,7 +677,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
GUID FromJson(const Json::Value& json)
{
return ::Microsoft::Console::Utils::GuidFromString(til::u8u16(Detail::GetStringView(json)));
return ::Microsoft::Console::Utils::GuidFromString(til::u8u16(Detail::GetStringView(json)).c_str());
}
bool CanConvert(const Json::Value& json)

View file

@ -44,7 +44,7 @@
<ClInclude Include="Command.h">
<DependentUpon>Command.idl</DependentUpon>
</ClInclude>
<ClInclude Include="DefaultProfileUtils.h" />
<ClInclude Include="DynamicProfileUtils.h" />
<ClInclude Include="FileUtils.h" />
<ClInclude Include="GlobalAppSettings.h">
<DependentUpon>GlobalAppSettings.idl</DependentUpon>
@ -123,7 +123,7 @@
<ClCompile Include="Command.cpp">
<DependentUpon>Command.idl</DependentUpon>
</ClCompile>
<ClCompile Include="DefaultProfileUtils.cpp" />
<ClCompile Include="DynamicProfileUtils.cpp" />
<ClCompile Include="FileUtils.cpp" />
<ClCompile Include="GlobalAppSettings.cpp">
<DependentUpon>GlobalAppSettings.idl</DependentUpon>
@ -254,15 +254,15 @@
we can include in the code directly. This way, we don't need to worry about
failing to load the default settings at runtime. -->
<Target Name="_TerminalAppGenerateDefaultsH" Inputs="defaults.json" Outputs="Generated Files\defaults.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile defaults.json -OutPath '&quot;Generated Files\defaults.h&quot;' -VariableName DefaultJson" />
<Exec Command="pwsh.exe -NoProfile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile defaults.json -OutPath &quot;Generated Files\defaults.h&quot; -VariableName DefaultJson" />
</Target>
<!-- A different set of defaults for Universal variant -->
<Target Name="_TerminalAppGenerateDefaultsUniversalH" Inputs="defaults-universal.json" Outputs="Generated Files\defaults-universal.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile defaults-universal.json -OutPath '&quot;Generated Files\defaults-universal.h&quot;' -VariableName DefaultUniversalJson" />
<Exec Command="pwsh.exe -NoProfile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile defaults-universal.json -OutPath &quot;Generated Files\defaults-universal.h&quot; -VariableName DefaultUniversalJson" />
</Target>
<!-- Same as above, but for the default settings.json template -->
<Target Name="_TerminalAppGenerateUserSettingsH" Inputs="userDefaults.json" Outputs="Generated Files\userDefaults.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile userDefaults.json -OutPath '&quot;Generated Files\userDefaults.h&quot;' -VariableName UserSettingsJson" />
<Exec Command="pwsh.exe -NoProfile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile userDefaults.json -OutPath &quot;Generated Files\userDefaults.h&quot; -VariableName UserSettingsJson" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
<Import Project="..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="Exists('..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />

View file

@ -24,7 +24,7 @@
<ClCompile Include="KeyChordSerialization.cpp" />
<ClCompile Include="Profile.cpp" />
<ClCompile Include="ColorScheme.cpp" />
<ClCompile Include="DefaultProfileUtils.cpp">
<ClCompile Include="DynamicProfileUtils.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
<ClCompile Include="$(OpenConsoleDir)\dep\jsoncpp\jsoncpp.cpp">
@ -69,7 +69,7 @@
<ClInclude Include="SettingsTypes.h" />
<ClInclude Include="TerminalWarnings.h" />
<ClInclude Include="ColorScheme.h" />
<ClInclude Include="DefaultProfileUtils.h">
<ClInclude Include="DynamicProfileUtils.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="JsonUtils.h">
@ -123,4 +123,4 @@
<UniqueIdentifier>{81a6314f-aa5b-4533-a499-13bc3a5c4af0}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>

View file

@ -8,7 +8,7 @@
#include "../../types/inc/utils.hpp"
#include "../../inc/DefaultSettings.h"
#include "Utils.h"
#include "DefaultProfileUtils.h"
#include "DynamicProfileUtils.h"
// These four are headers we do not want proliferating, so they're not in the PCH.
#include <winrt/Windows.ApplicationModel.h>
@ -289,7 +289,7 @@ static std::vector<PowerShellInstance> _collectPowerShellInstances()
// - PowerShell Core 574e775e-4f2a-5b96-ac1e-a2962a402336
static constexpr winrt::guid PowershellCoreGuid{ 0x574e775e, 0x4f2a, 0x5b96, { 0xac, 0x1e, 0xa2, 0x96, 0x2a, 0x40, 0x23, 0x36 } };
std::wstring_view PowershellCoreProfileGenerator::GetNamespace()
std::wstring_view PowershellCoreProfileGenerator::GetNamespace() const noexcept
{
return PowershellCoreGeneratorNamespace;
}
@ -300,34 +300,33 @@ std::wstring_view PowershellCoreProfileGenerator::GetNamespace()
// - <none>
// Return Value:
// - a vector with the PowerShell Core profile, if available.
std::vector<Profile> PowershellCoreProfileGenerator::GenerateProfiles()
void PowershellCoreProfileGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
{
std::vector<Profile> profiles;
const auto psInstances = _collectPowerShellInstances();
bool first = true;
auto psInstances = _collectPowerShellInstances();
for (const auto& psI : psInstances)
{
const auto name = psI.Name();
auto profile{ CreateDefaultProfile(name) };
profile.Commandline(psI.executablePath.wstring());
profile.StartingDirectory(DEFAULT_STARTING_DIRECTORY);
profile.DefaultAppearance().ColorSchemeName(L"Campbell");
auto profile{ CreateDynamicProfile(name) };
profile->Commandline(winrt::hstring{ psI.executablePath.native() });
profile->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY });
profile->DefaultAppearance().ColorSchemeName(L"Campbell");
profile->Icon(winrt::hstring{ WI_IsFlagSet(psI.flags, PowerShellFlags::Preview) ? POWERSHELL_PREVIEW_ICON : POWERSHELL_ICON });
if (first)
{
// Give the first ("algorithmically best") profile the official, and original, "PowerShell Core" GUID.
// This will turn the anchored default profile into "PowerShell Core Latest for Native Architecture through Store"
// (or the closest approximation thereof). It may choose a preview instance as the "best" if it is a higher version.
profile->Guid(PowershellCoreGuid);
profile->Name(winrt::hstring{ POWERSHELL_PREFERRED_PROFILE_NAME });
first = false;
}
profile.Icon(WI_IsFlagSet(psI.flags, PowerShellFlags::Preview) ? POWERSHELL_PREVIEW_ICON : POWERSHELL_ICON);
profiles.emplace_back(std::move(profile));
}
if (profiles.size() > 0)
{
// Give the first ("algorithmically best") profile the official, and original, "PowerShell Core" GUID.
// This will turn the anchored default profile into "PowerShell Core Latest for Native Architecture through Store"
// (or the closest approximation thereof). It may choose a preview instance as the "best" if it is a higher version.
auto firstProfile = profiles.begin();
firstProfile->Guid(PowershellCoreGuid);
firstProfile->Name(POWERSHELL_PREFERRED_PROFILE_NAME);
}
return profiles;
}
// Function Description:

View file

@ -18,17 +18,14 @@ Author(s):
#include "IDynamicProfileGenerator.h"
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
class PowershellCoreProfileGenerator : public Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator
class PowershellCoreProfileGenerator final : public IDynamicProfileGenerator
{
public:
static const std::wstring_view GetPreferredPowershellProfileName();
PowershellCoreProfileGenerator() = default;
~PowershellCoreProfileGenerator() = default;
std::wstring_view GetNamespace() override;
std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() override;
std::wstring_view GetNamespace() const noexcept override;
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
};
};

View file

@ -5,9 +5,7 @@
#include "Profile.h"
#include "JsonUtils.h"
#include "../../types/inc/Utils.hpp"
#include <DefaultSettings.h>
#include "LegacyProfileGeneratorNamespaces.h"
#include "TerminalSettingsSerializationHelpers.h"
#include "AppearanceConfig.h"
#include "FontConfig.h"
@ -22,6 +20,7 @@ using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::Foundation;
using namespace ::Microsoft::Console;
static constexpr std::string_view UpdatesKey{ "updates" };
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view GuidKey{ "guid" };
static constexpr std::string_view SourceKey{ "source" };
@ -47,13 +46,7 @@ static constexpr std::string_view TabColorKey{ "tabColor" };
static constexpr std::string_view BellStyleKey{ "bellStyle" };
static constexpr std::string_view UnfocusedAppearanceKey{ "unfocusedAppearance" };
static constexpr std::wstring_view DesktopWallpaperEnum{ L"desktopWallpaper" };
Profile::Profile()
{
}
Profile::Profile(guid guid) :
Profile::Profile(guid guid) noexcept :
_Guid(guid)
{
}
@ -79,61 +72,81 @@ void Profile::DeleteUnfocusedAppearance()
_UnfocusedAppearance = std::nullopt;
}
winrt::com_ptr<Profile> Profile::CopySettings(winrt::com_ptr<Profile> source)
// See CopyInheritanceGraph (singular) for more information.
// This does the same, but runs it on a list of graph nodes and clones each sub-graph.
void Profile::CopyInheritanceGraphs(std::unordered_map<const Profile*, winrt::com_ptr<Profile>>& visited, const std::vector<winrt::com_ptr<Profile>>& source, std::vector<winrt::com_ptr<Profile>>& target)
{
auto profile{ winrt::make_self<Profile>() };
for (const auto& sourceProfile : source)
{
target.emplace_back(sourceProfile->CopyInheritanceGraph(visited));
}
}
profile->_Deleted = source->_Deleted;
profile->_Guid = source->_Guid;
profile->_Name = source->_Name;
profile->_Source = source->_Source;
profile->_Hidden = source->_Hidden;
profile->_Icon = source->_Icon;
profile->_CloseOnExit = source->_CloseOnExit;
profile->_TabTitle = source->_TabTitle;
profile->_TabColor = source->_TabColor;
profile->_SuppressApplicationTitle = source->_SuppressApplicationTitle;
profile->_UseAcrylic = source->_UseAcrylic;
profile->_ScrollState = source->_ScrollState;
profile->_Padding = source->_Padding;
profile->_Commandline = source->_Commandline;
profile->_StartingDirectory = source->_StartingDirectory;
profile->_AntialiasingMode = source->_AntialiasingMode;
profile->_ForceFullRepaintRendering = source->_ForceFullRepaintRendering;
profile->_SoftwareRendering = source->_SoftwareRendering;
profile->_HistorySize = source->_HistorySize;
profile->_SnapOnInput = source->_SnapOnInput;
profile->_AltGrAliasing = source->_AltGrAliasing;
profile->_BellStyle = source->_BellStyle;
profile->_ConnectionType = source->_ConnectionType;
profile->_Origin = source->_Origin;
// A profile and its IInheritable parents basically behave like a directed acyclic graph (DAG).
// Cloning a DAG requires us to prevent the duplication of already cloned nodes (or profiles).
// This is where "visited" comes into play: It contains previously cloned sub-graphs of profiles and "interns" them.
winrt::com_ptr<Profile>& Profile::CopyInheritanceGraph(std::unordered_map<const Profile*, winrt::com_ptr<Profile>>& visited) const
{
// The operator[] is usually considered to suck, because it implicitly creates entries
// in maps/sets if the entry doesn't exist yet, which is often an unwanted behavior.
// But in this case it's just perfect. We want to return a reference to the profile if it's
// been created before and create a cloned profile if it doesn't. With the operator[]
// we can just assign the returned reference allowing us to write some lean code.
auto& clone = visited[this];
// Copy over the font info
const auto weakRefToProfile = weak_ref<Model::Profile>(*profile);
winrt::com_ptr<FontConfig> sourceFontInfoImpl;
sourceFontInfoImpl.copy_from(winrt::get_self<FontConfig>(source->_FontInfo));
auto copiedFontInfo = FontConfig::CopyFontInfo(sourceFontInfoImpl, weakRefToProfile);
profile->_FontInfo = *copiedFontInfo;
if (!clone)
{
clone = CopySettings();
CopyInheritanceGraphs(visited, _parents, clone->_parents);
clone->_FinalizeInheritance();
}
// Copy over the appearance
winrt::com_ptr<AppearanceConfig> sourceDefaultAppearanceImpl;
sourceDefaultAppearanceImpl.copy_from(winrt::get_self<AppearanceConfig>(source->_DefaultAppearance));
auto copiedDefaultAppearance = AppearanceConfig::CopyAppearance(sourceDefaultAppearanceImpl, weakRefToProfile);
profile->_DefaultAppearance = *copiedDefaultAppearance;
return clone;
}
if (source->_UnfocusedAppearance.has_value())
winrt::com_ptr<Profile> Profile::CopySettings() const
{
const auto profile = winrt::make_self<Profile>();
const auto weakProfile = winrt::make_weak<Model::Profile>(*profile);
const auto fontInfo = FontConfig::CopyFontInfo(winrt::get_self<FontConfig>(_FontInfo), weakProfile);
const auto defaultAppearance = AppearanceConfig::CopyAppearance(winrt::get_self<AppearanceConfig>(_DefaultAppearance), weakProfile);
profile->_Deleted = _Deleted;
profile->_Updates = _Updates;
profile->_Guid = _Guid;
profile->_Name = _Name;
profile->_Source = _Source;
profile->_Hidden = _Hidden;
profile->_Icon = _Icon;
profile->_CloseOnExit = _CloseOnExit;
profile->_TabTitle = _TabTitle;
profile->_TabColor = _TabColor;
profile->_SuppressApplicationTitle = _SuppressApplicationTitle;
profile->_UseAcrylic = _UseAcrylic;
profile->_ScrollState = _ScrollState;
profile->_Padding = _Padding;
profile->_Commandline = _Commandline;
profile->_StartingDirectory = _StartingDirectory;
profile->_AntialiasingMode = _AntialiasingMode;
profile->_ForceFullRepaintRendering = _ForceFullRepaintRendering;
profile->_SoftwareRendering = _SoftwareRendering;
profile->_HistorySize = _HistorySize;
profile->_SnapOnInput = _SnapOnInput;
profile->_AltGrAliasing = _AltGrAliasing;
profile->_BellStyle = _BellStyle;
profile->_ConnectionType = _ConnectionType;
profile->_Origin = _Origin;
profile->_FontInfo = *fontInfo;
profile->_DefaultAppearance = *defaultAppearance;
if (_UnfocusedAppearance)
{
Model::AppearanceConfig unfocused{ nullptr };
if (source->_UnfocusedAppearance.value() != nullptr)
if (*_UnfocusedAppearance)
{
// Copy over the unfocused appearance
winrt::com_ptr<AppearanceConfig> sourceUnfocusedAppearanceImpl;
sourceUnfocusedAppearanceImpl.copy_from(winrt::get_self<AppearanceConfig>(source->_UnfocusedAppearance.value()));
auto copiedUnfocusedAppearance = AppearanceConfig::CopyAppearance(sourceUnfocusedAppearanceImpl, weakRefToProfile);
// Make sure to add the default appearance as a parent
copiedUnfocusedAppearance->InsertParent(copiedDefaultAppearance);
unfocused = *copiedUnfocusedAppearance;
const auto appearance = AppearanceConfig::CopyAppearance(winrt::get_self<AppearanceConfig>(*_UnfocusedAppearance), weakProfile);
appearance->InsertParent(defaultAppearance);
unfocused = *appearance;
}
profile->_UnfocusedAppearance = unfocused;
}
@ -141,104 +154,6 @@ winrt::com_ptr<Profile> Profile::CopySettings(winrt::com_ptr<Profile> source)
return profile;
}
// Method Description:
// - Creates a copy of the inheritance graph by performing a depth-first traversal recursively.
// Profiles are recorded as visited via the "visited" parameter.
// Unvisited Profiles are copied into the "cloneGraph" parameter, then marked as visited.
// Arguments:
// - sourceGraph - the graph of Profile's we're cloning
// - cloneGraph - the clone of sourceGraph that is being constructed
// - visited - a map of which Profiles have been visited, and, if so, a reference to the Profile's clone
// Return Value:
// - a clone in both inheritance structure and Profile values of sourceGraph
winrt::com_ptr<Profile> Profile::CloneInheritanceGraph(winrt::com_ptr<Profile> sourceGraph, winrt::com_ptr<Profile> cloneGraph, std::unordered_map<void*, winrt::com_ptr<Profile>>& visited)
{
// If this is an unexplored Profile
// and we have parents...
if (visited.find(sourceGraph.get()) == visited.end() && !sourceGraph->_parents.empty())
{
// iterate through all of our parents to copy them
for (const auto& sourceParent : sourceGraph->_parents)
{
// If we visited this Profile already...
auto kv{ visited.find(sourceParent.get()) };
if (kv != visited.end())
{
// add this Profile's clone as a parent
InsertParentHelper(cloneGraph, kv->second);
}
else
{
// We have not visited this Profile yet,
// copy contents of sourceParent to clone
winrt::com_ptr<Profile> clone{ CopySettings(sourceParent) };
// add the new copy to the cloneGraph
InsertParentHelper(cloneGraph, clone);
// copy the sub-graph at "clone"
CloneInheritanceGraph(sourceParent, clone, visited);
// mark clone as "visited"
// save it to the map in case somebody else references it
visited[sourceParent.get()] = clone;
}
}
}
// we have no more to explore down this path.
return cloneGraph;
}
// Method Description:
// - Inserts a parent profile into a child profile, at the specified index if one was provided
// - Makes sure to call _FinalizeInheritance after inserting the parent
// Arguments:
// - child: the child profile to insert the parent into
// - parent: the parent profile to insert into the child
// - index: an optional index value to insert the parent into
void Profile::InsertParentHelper(winrt::com_ptr<Profile> child, winrt::com_ptr<Profile> parent, std::optional<size_t> index)
{
if (index)
{
child->InsertParent(index.value(), parent);
}
else
{
child->InsertParent(parent);
}
child->_FinalizeInheritance();
}
// Method Description:
// - Generates a Json::Value which is a "stub" of this profile. This stub will
// have enough information that it could be layered with this profile.
// - This method is used during dynamic profile generation - if a profile is
// ever generated that didn't already exist in the user's settings, we'll add
// this stub to the user's settings file, so the user has an easy point to
// modify the generated profile.
// Arguments:
// - <none>
// Return Value:
// - A json::Value with a guid, name and source (if applicable).
Json::Value Profile::GenerateStub() const
{
Json::Value stub;
///// Profile-specific settings /////
stub[JsonKey(GuidKey)] = winrt::to_string(Utils::GuidToString(Guid()));
stub[JsonKey(NameKey)] = winrt::to_string(Name());
const auto source{ Source() };
if (!source.empty())
{
stub[JsonKey(SourceKey)] = winrt::to_string(source);
}
return stub;
}
// Method Description:
// - Create a new instance of this class from a serialized JsonObject.
// Arguments:
@ -252,71 +167,6 @@ winrt::com_ptr<winrt::Microsoft::Terminal::Settings::Model::implementation::Prof
return result;
}
// Method Description:
// - Returns true if we think the provided json object represents an instance of
// the same object as this object. If true, we should layer that json object
// on us, instead of creating a new object.
// Arguments:
// - json: The json object to query to see if it's the same
// Return Value:
// - true iff the json object has the same `GUID` as we do.
bool Profile::ShouldBeLayered(const Json::Value& json) const
{
// First, check that GUIDs match. This is easy. If they don't match, they
// should _definitely_ not layer.
const auto otherGuid{ JsonUtils::GetValueForKey<std::optional<winrt::guid>>(json, GuidKey) };
const auto otherSource{ JsonUtils::GetValueForKey<std::optional<winrt::hstring>>(json, SourceKey) };
if (otherGuid)
{
if (otherGuid.value() != Guid())
{
return false;
}
}
else
{
// If the other json object didn't have a GUID,
// check if we auto-generate the same guid using the name and source.
const auto otherName{ JsonUtils::GetValueForKey<std::optional<winrt::hstring>>(json, NameKey) };
if (Guid() != _GenerateGuidForProfile(otherName ? *otherName : L"Default", otherSource ? *otherSource : L""))
{
return false;
}
}
// For profiles with a `source`, also check the `source` property.
bool sourceMatches = false;
const auto mySource{ Source() };
if (!mySource.empty())
{
if (otherSource.has_value())
{
// If we have a source and the other has a source, compare them!
sourceMatches = *otherSource == mySource;
}
else
{
// Special case the legacy dynamic profiles here. In this case,
// `this` is a dynamic profile with a source, and our _source is one
// of the legacy DPG namespaces. We're looking to see if the other
// json object has the same guid, but _no_ "source"
if (mySource == WslGeneratorNamespace ||
mySource == AzureGeneratorNamespace ||
mySource == PowershellCoreGeneratorNamespace)
{
sourceMatches = true;
}
}
}
else
{
// We do not have a source. The only way we match is if source is unset or set to "".
sourceMatches = (!otherSource.has_value() || otherSource.value() == L"");
}
return sourceMatches;
}
// Method Description:
// - Layer values from the given json object on top of the existing properties
// of this object. For any keys we're expecting to be able to parse in the
@ -341,6 +191,7 @@ void Profile::LayerJson(const Json::Value& json)
// Profile-specific Settings
JsonUtils::GetValueForKey(json, NameKey, _Name);
JsonUtils::GetValueForKey(json, UpdatesKey, _Updates);
JsonUtils::GetValueForKey(json, GuidKey, _Guid);
JsonUtils::GetValueForKey(json, HiddenKey, _Hidden);
JsonUtils::GetValueForKey(json, SourceKey, _Source);
@ -459,27 +310,13 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory)
return wil::ExpandEnvironmentStringsW<std::wstring>(directory.c_str());
}
// Function Description:
// - Returns true if the given JSON object represents a dynamic profile object.
// If it is a dynamic profile object, we should make sure to only layer the
// object on a matching profile from a dynamic source.
// Arguments:
// - json: the partial serialization of a profile object to check
// Return Value:
// - true iff the object has a non-null `source` property
bool Profile::IsDynamicProfileObject(const Json::Value& json)
{
const auto& source = json.isMember(JsonKey(SourceKey)) ? json[JsonKey(SourceKey)] : Json::Value::null;
return !source.isNull();
}
// Function Description:
// - Generates a unique guid for a profile, given the name. For an given name, will always return the same GUID.
// Arguments:
// - name: The name to generate a unique GUID from
// Return Value:
// - a uuidv5 GUID generated from the given name.
winrt::guid Profile::_GenerateGuidForProfile(const hstring& name, const hstring& source) noexcept
winrt::guid Profile::_GenerateGuidForProfile(const std::wstring_view& name, const std::wstring_view& source) noexcept
{
// If we have a _source, then we can from a dynamic profile generator. Use
// our source to build the namespace guid, instead of using the default GUID.
@ -493,27 +330,6 @@ winrt::guid Profile::_GenerateGuidForProfile(const hstring& name, const hstring&
return { Utils::CreateV5Uuid(namespaceGuid, gsl::as_bytes(gsl::make_span(name))) };
}
// Function Description:
// - Parses the given JSON object to get its GUID. If the json object does not
// have a `guid` set, we'll generate one, using the `name` field.
// Arguments:
// - json: the JSON object to get a GUID from, or generate a unique GUID for
// (given the `name`)
// Return Value:
// - The json's `guid`, or a guid synthesized for it.
winrt::guid Profile::GetGuidOrGenerateForJson(const Json::Value& json) noexcept
{
if (const auto guid{ JsonUtils::GetValueForKey<std::optional<GUID>>(json, GuidKey) })
{
return { guid.value() };
}
const auto name{ JsonUtils::GetValueForKey<hstring>(json, NameKey) };
const auto source{ JsonUtils::GetValueForKey<hstring>(json, SourceKey) };
return Profile::_GenerateGuidForProfile(name, source);
}
// Method Description:
// - Create a new serialized JsonObject from an instance of this class
// Arguments:

View file

@ -76,8 +76,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct Profile : ProfileT<Profile>, IInheritable<Profile>
{
public:
Profile();
Profile(guid guid);
Profile() noexcept = default;
Profile(guid guid) noexcept;
void CreateUnfocusedAppearance();
void DeleteUnfocusedAppearance();
@ -87,19 +87,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return Name();
}
static com_ptr<Profile> CloneInheritanceGraph(com_ptr<Profile> oldProfile, com_ptr<Profile> newProfile, std::unordered_map<void*, com_ptr<Profile>>& visited);
static com_ptr<Profile> CopySettings(com_ptr<Profile> source);
static void InsertParentHelper(com_ptr<Profile> child, com_ptr<Profile> parent, std::optional<size_t> index = std::nullopt);
static void CopyInheritanceGraphs(std::unordered_map<const Profile*, winrt::com_ptr<Profile>>& visited, const std::vector<winrt::com_ptr<Profile>>& source, std::vector<winrt::com_ptr<Profile>>& target);
winrt::com_ptr<Profile>& CopyInheritanceGraph(std::unordered_map<const Profile*, winrt::com_ptr<Profile>>& visited) const;
winrt::com_ptr<Profile> CopySettings() const;
Json::Value GenerateStub() const;
static com_ptr<Profile> FromJson(const Json::Value& json);
bool ShouldBeLayered(const Json::Value& json) const;
void LayerJson(const Json::Value& json);
static bool IsDynamicProfileObject(const Json::Value& json);
Json::Value ToJson() const;
hstring EvaluatedStartingDirectory() const;
static guid GetGuidOrGenerateForJson(const Json::Value& json) noexcept;
Model::IAppearanceConfig DefaultAppearance();
Model::FontConfig FontInfo();
@ -109,6 +105,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
WINRT_PROPERTY(bool, Deleted, false);
WINRT_PROPERTY(OriginTag, Origin, OriginTag::None);
WINRT_PROPERTY(guid, Updates);
INHERITABLE_SETTING(Model::Profile, guid, Guid, _GenerateGuidForProfile(Name(), Source()));
INHERITABLE_SETTING(Model::Profile, hstring, Name, L"Default");
INHERITABLE_SETTING(Model::Profile, hstring, Source);
@ -124,7 +121,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::Profile, bool, SuppressApplicationTitle, false);
INHERITABLE_SETTING(Model::Profile, bool, UseAcrylic, false);
INHERITABLE_SETTING(Model::Profile, double, AcrylicOpacity, 0.5);
INHERITABLE_SETTING(Model::Profile, Microsoft::Terminal::Control::ScrollbarState, ScrollState, Microsoft::Terminal::Control::ScrollbarState::Visible);
INHERITABLE_SETTING(Model::Profile, hstring, Padding, DEFAULT_PADDING);
@ -149,7 +145,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Model::FontConfig _FontInfo{ winrt::make<FontConfig>(weak_ref<Model::Profile>(*this)) };
static std::wstring EvaluateStartingDirectory(const std::wstring& directory);
static guid _GenerateGuidForProfile(const hstring& name, const hstring& source) noexcept;
static guid _GenerateGuidForProfile(const std::wstring_view& name, const std::wstring_view& source) noexcept;
friend class SettingsModelLocalTests::DeserializationTests;
friend class SettingsModelLocalTests::ProfileTests;

View file

@ -50,17 +50,11 @@ namespace Microsoft.Terminal.Settings.Model
Boolean Deleted { get; };
OriginTag Origin { get; };
INHERITABLE_PROFILE_SETTING(Guid, Guid);
INHERITABLE_PROFILE_SETTING(String, Name);
Boolean HasGuid();
Guid Guid;
INHERITABLE_PROFILE_SETTING(String, Source);
Boolean HasConnectionType();
Guid ConnectionType;
INHERITABLE_PROFILE_SETTING(Boolean, Hidden);
INHERITABLE_PROFILE_SETTING(Guid, ConnectionType);
INHERITABLE_PROFILE_SETTING(String, Icon);
INHERITABLE_PROFILE_SETTING(CloseOnExitMode, CloseOnExit);
INHERITABLE_PROFILE_SETTING(String, TabTitle);

View file

@ -8,20 +8,19 @@ namespace Microsoft.Terminal.Settings.Model
enum SettingsLoadWarnings
{
MissingDefaultProfile = 0,
DuplicateProfile = 1,
UnknownColorScheme = 2,
InvalidBackgroundImage = 3,
InvalidIcon = 4,
AtLeastOneKeybindingWarning = 5,
TooManyKeysForChord = 6,
MissingRequiredParameter = 7,
LegacyGlobalsProperty = 8,
FailedToParseCommandJson = 9,
FailedToWriteToSettings = 10,
InvalidColorSchemeInCmd = 11,
InvalidSplitSize = 12,
FailedToParseStartupActions = 13,
FailedToParseSubCommands = 14,
DuplicateProfile,
UnknownColorScheme,
InvalidBackgroundImage,
InvalidIcon,
AtLeastOneKeybindingWarning,
TooManyKeysForChord,
MissingRequiredParameter,
FailedToParseCommandJson,
FailedToWriteToSettings,
InvalidColorSchemeInCmd,
InvalidSplitSize,
FailedToParseStartupActions,
FailedToParseSubCommands,
WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
};

View file

@ -4,7 +4,7 @@
#include "pch.h"
#include "VsDevCmdGenerator.h"
using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model;
std::wstring VsDevCmdGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const
{

View file

@ -16,12 +16,12 @@ Author(s):
#pragma once
#include "BaseVisualStudioGenerator.h"
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
class VsDevCmdGenerator : public BaseVisualStudioGenerator
class VsDevCmdGenerator final : public BaseVisualStudioGenerator
{
public:
std::wstring_view GetNamespace() override
std::wstring_view GetNamespace() const noexcept override
{
return std::wstring_view{ L"Windows.Terminal.VisualStudio.CommandPrompt" };
}

View file

@ -5,7 +5,7 @@
#include "VsDevShellGenerator.h"
#include "VsSetupConfiguration.h"
using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model;
std::wstring VsDevShellGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const
{

View file

@ -16,12 +16,12 @@ Author(s):
#pragma once
#include "BaseVisualStudioGenerator.h"
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
class VsDevShellGenerator : public BaseVisualStudioGenerator
class VsDevShellGenerator final : public BaseVisualStudioGenerator
{
public:
std::wstring_view GetNamespace() override
std::wstring_view GetNamespace() const noexcept override
{
return std::wstring_view{ L"Windows.Terminal.VisualStudio.Powershell" };
}

View file

@ -4,7 +4,7 @@
#include "pch.h"
#include "VsSetupConfiguration.h"
using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model;
std::vector<VsSetupConfiguration::VsSetupInstance> VsSetupConfiguration::QueryInstances()
{

View file

@ -17,7 +17,7 @@ Author(s):
#include "Setup.Configuration.h"
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
/// <summary>
/// See https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration?view=visualstudiosdk-2019

View file

@ -5,13 +5,11 @@
#include "WslDistroGenerator.h"
#include "LegacyProfileGeneratorNamespaces.h"
#include "../../types/inc/utils.hpp"
#include "../../inc/DefaultSettings.h"
#include "Utils.h"
#include <io.h>
#include <fcntl.h>
#include "DefaultProfileUtils.h"
#include "DynamicProfileUtils.h"
static constexpr std::wstring_view DockerDistributionPrefix{ L"docker-desktop" };
@ -31,21 +29,34 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
// - Alpine 1777cdf0-b2c4-5a63-a204-eb60f349ea7c
// - Ubuntu-18.04 c6eaf9f4-32a7-5fdc-b5cf-066e8a4b1e40
std::wstring_view WslDistroGenerator::GetNamespace()
std::wstring_view WslDistroGenerator::GetNamespace() const noexcept
{
return WslGeneratorNamespace;
}
static winrt::com_ptr<implementation::Profile> makeProfile(const std::wstring& distName)
{
const auto WSLDistro{ CreateDynamicProfile(distName) };
// GH#11096 - make sure the WSL path starts explicitly with
// C:\Windows\System32. Don't want someone path hijacking wsl.exe.
wil::unique_cotaskmem_string systemPath;
THROW_IF_FAILED(wil::GetSystemDirectoryW(systemPath));
std::wstring command(systemPath.get());
WSLDistro->Commandline(command + L"\\wsl.exe -d " + distName);
WSLDistro->DefaultAppearance().ColorSchemeName(L"Campbell");
WSLDistro->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY });
WSLDistro->Icon(L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png");
return WSLDistro;
}
// Method Description:
// - Enumerates all the installed WSL distros to create profiles for them.
// Arguments:
// - <none>
// Return Value:
// - a vector with all distros for all the installed WSL distros
static std::vector<Profile> legacyGenerate()
static void legacyGenerate(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
{
std::vector<Profile> profiles;
wil::unique_handle readPipe;
wil::unique_handle writePipe;
SECURITY_ATTRIBUTES sa{ sizeof(sa), nullptr, true };
@ -77,7 +88,7 @@ static std::vector<Profile> legacyGenerate()
break;
case WAIT_ABANDONED:
case WAIT_TIMEOUT:
return profiles;
return;
case WAIT_FAILED:
THROW_LAST_ERROR();
default:
@ -90,7 +101,7 @@ static std::vector<Profile> legacyGenerate()
}
else if (exitCode != 0)
{
return profiles;
return;
}
DWORD bytesAvailable;
THROW_IF_WIN32_BOOL_FALSE(PeekNamedPipe(readPipe.get(), nullptr, NULL, nullptr, &bytesAvailable, nullptr));
@ -117,7 +128,7 @@ static std::vector<Profile> legacyGenerate()
std::wstring distName;
std::getline(wlinestream, distName, L'\r');
if (distName.substr(0, std::min(distName.size(), DockerDistributionPrefix.size())) == DockerDistributionPrefix)
if (til::starts_with(distName, DockerDistributionPrefix))
{
// Docker for Windows creates some utility distributions to handle Docker commands.
// Pursuant to GH#3556, because they are _not_ user-facing we want to hide them.
@ -131,17 +142,10 @@ static std::vector<Profile> legacyGenerate()
{
distName.resize(firstChar);
}
auto WSLDistro{ CreateDefaultProfile(distName) };
WSLDistro.Commandline(L"wsl.exe -d " + distName);
WSLDistro.DefaultAppearance().ColorSchemeName(L"Campbell");
WSLDistro.StartingDirectory(DEFAULT_STARTING_DIRECTORY);
WSLDistro.Icon(L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png");
profiles.emplace_back(WSLDistro);
profiles.emplace_back(makeProfile(distName));
}
}
return profiles;
}
// Function Description:
@ -151,9 +155,8 @@ static std::vector<Profile> legacyGenerate()
// - names: a list of distro names to turn into profiles
// Return Value:
// - the list of profiles we've generated.
static std::vector<Profile> namesToProfiles(const std::vector<std::wstring>& names)
static void namesToProfiles(const std::vector<std::wstring>& names, std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
{
std::vector<Profile> profiles;
for (const auto& distName : names)
{
if (til::starts_with(distName, DockerDistributionPrefix))
@ -163,20 +166,8 @@ static std::vector<Profile> namesToProfiles(const std::vector<std::wstring>& nam
continue;
}
auto WSLDistro{ CreateDefaultProfile(distName) };
// GH#11096 - make sure the WSL path starts explicitly with
// C:\Windows\System32. Don't want someone path hijacking wsl.exe.
wil::unique_cotaskmem_string systemPath;
THROW_IF_FAILED(wil::GetSystemDirectoryW(systemPath));
std::wstring command(systemPath.get());
WSLDistro.Commandline(command + L"\\wsl.exe -d " + distName);
WSLDistro.DefaultAppearance().ColorSchemeName(L"Campbell");
WSLDistro.StartingDirectory(DEFAULT_STARTING_DIRECTORY);
WSLDistro.Icon(L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png");
profiles.emplace_back(WSLDistro);
profiles.emplace_back(makeProfile(distName));
}
return profiles;
}
// Function Description:
@ -309,7 +300,7 @@ static bool getWslNames(const wil::unique_hkey& wslRootKey,
// - <none>
// Return Value:
// - A list of WSL profiles.
std::vector<Profile> WslDistroGenerator::GenerateProfiles()
void WslDistroGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
{
wil::unique_hkey wslRootKey{ openWslRegKey() };
if (wslRootKey)
@ -321,10 +312,10 @@ std::vector<Profile> WslDistroGenerator::GenerateProfiles()
names.reserve(guidStrings.size());
if (getWslNames(wslRootKey, guidStrings, names))
{
return namesToProfiles(names);
return namesToProfiles(names, profiles);
}
}
}
return legacyGenerate();
legacyGenerate(profiles);
}

View file

@ -15,16 +15,15 @@ Author(s):
--*/
#pragma once
#include "IDynamicProfileGenerator.h"
namespace Microsoft::Terminal::Settings::Model
namespace winrt::Microsoft::Terminal::Settings::Model
{
class WslDistroGenerator : public Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator
class WslDistroGenerator final : public IDynamicProfileGenerator
{
public:
WslDistroGenerator() = default;
~WslDistroGenerator() = default;
std::wstring_view GetNamespace() override;
std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() override;
std::wstring_view GetNamespace() const noexcept override;
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
};
};

View file

@ -1,75 +1,31 @@
// This file was initially generated by %PRODUCT% %VERSION%
// It should still be usable in newer versions, but newer versions might have additional
// settings, help text, or changes that you will not see unless you clear this file
// and let us generate a new one for you.
// To view the default settings, hold "alt" while clicking on the "Settings" button.
// For documentation on these settings, see: https://aka.ms/terminal-documentation
{
"$schema": "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "%DEFAULT_PROFILE%",
// You can add more global application settings here.
// To learn more about global settings, visit https://aka.ms/terminal-global-settings
// If enabled, selections are automatically copied to your clipboard.
// "defaultProfile" is filled in by CascadiaSettings, depending on
// what dynamic profiles are present during the first launch.
"copyOnSelect": false,
// If enabled, formatted data is also copied to your clipboard
"copyFormatting": false,
// A profile specifies a command to execute paired with information about how it should look and feel.
// Each one of them will appear in the 'New Tab' dropdown,
// and can be invoked from the commandline with `wt.exe -p xxx`
// To learn more about profiles, visit https://aka.ms/terminal-profile-settings
"profiles":
{
"defaults":
{
// Put settings here that you want to apply to all profiles.
},
"list":
[
{
// Make changes here to the powershell.exe profile.
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"hidden": false
},
{
// Make changes here to the cmd.exe profile.
// "name" is filled in by CascadiaSettings as a localized string.
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"name": "%COMMAND_PROMPT_LOCALIZED_NAME%",
"commandline": "%SystemRoot%\\System32\\cmd.exe",
"hidden": false
}
]
},
// Add custom color schemes to this array.
// To learn more about color schemes, visit https://aka.ms/terminal-color-schemes
"schemes": [],
// Add custom actions and keybindings to this array.
// To unbind a key combination from your defaults.json, set the command to "unbound".
// To learn more about actions and keybindings, visit https://aka.ms/terminal-keybindings
"actions":
[
// Copy and paste are bound to Ctrl+Shift+C and Ctrl+Shift+V in your defaults.json.
// These two lines additionally bind them to Ctrl+C and Ctrl+V.
// To learn more about selection, visit https://aka.ms/terminal-selection
{ "command": {"action": "copy", "singleLine": false }, "keys": "ctrl+c" },
{ "command": "paste", "keys": "ctrl+v" },
// Press Ctrl+Shift+F to open the search box
{ "command": "find", "keys": "ctrl+shift+f" },
// Press Alt+Shift+D to open a new pane.
// - "split": "auto" makes this pane open in the direction that provides the most surface area.
// - "splitMode": "duplicate" makes the new pane use the focused pane's profile.
// To learn more about panes, visit https://aka.ms/terminal-panes
{ "command": { "action": "splitPane", "split": "auto", "splitMode": "duplicate" }, "keys": "alt+shift+d" }
]
}

View file

@ -379,7 +379,6 @@ namespace ControlUnitTests
// For this test, don't use any modifiers
const auto modifiers = ControlKeyStates();
const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 };
@ -530,7 +529,6 @@ namespace ControlUnitTests
// For this test, don't use any modifiers
const auto modifiers = ControlKeyStates();
const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 };
@ -742,7 +740,6 @@ namespace ControlUnitTests
// For this test, don't use any modifiers
const auto modifiers = ControlKeyStates();
const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 };

View file

@ -1,673 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "../TerminalSettingsModel/ColorScheme.h"
#include "../TerminalSettingsModel/Profile.h"
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "../TerminalSettingsModel/LegacyProfileGeneratorNamespaces.h"
#include "../LocalTests_SettingsModel/JsonTestClass.h"
#include "TestDynamicProfileGenerator.h"
using namespace Microsoft::Console;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
namespace TerminalAppUnitTests
{
class DynamicProfileTests : public JsonTestClass
{
BEGIN_TEST_CLASS(DynamicProfileTests)
TEST_CLASS_PROPERTY(L"ActivationContext", L"TerminalApp.Unit.Tests.manifest")
END_TEST_CLASS()
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
return true;
}
TEST_METHOD(TestSimpleGenerate);
// Simple test of CascadiaSettings generating profiles with _LoadDynamicProfiles
TEST_METHOD(TestSimpleGenerateMultipleGenerators);
// Make sure we gen GUIDs for profiles without guids
TEST_METHOD(TestGenGuidsForProfiles);
// Profiles without a source should not be layered on those with one
TEST_METHOD(DontLayerUserProfilesOnDynamicProfiles);
TEST_METHOD(DoLayerUserProfilesOnDynamicsWhenSourceMatches);
// Make sure profiles that are disabled in _userSettings don't get generated
TEST_METHOD(TestDontRunDisabledGenerators);
// Make sure profiles that are disabled in _userSettings don't get generated
TEST_METHOD(TestLegacyProfilesMigrate);
// Both these do similar things:
// This makes sure that a profile with a `source` _only_ layers, it won't create a new profile
TEST_METHOD(UserProfilesWithInvalidSourcesAreIgnored);
// This does the same, but by disabling a profile source
TEST_METHOD(UserProfilesFromDisabledSourcesDontAppear);
};
void DynamicProfileTests::TestSimpleGenerate()
{
TestDynamicProfileGenerator gen{ L"Terminal.App.UnitTest" };
gen.pfnGenerate = []() {
std::vector<Profile> profiles;
Profile p0;
p0.Name(L"profile0");
profiles.push_back(p0);
return profiles;
};
VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest", gen.GetNamespace());
std::vector<Profile> profiles = gen.GenerateProfiles();
VERIFY_ARE_EQUAL(1u, profiles.size());
VERIFY_ARE_EQUAL(L"profile0", profiles.at(0).Name());
VERIFY_IS_FALSE(profiles.at(0).HasGuid());
}
void DynamicProfileTests::TestSimpleGenerateMultipleGenerators()
{
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
gen0->pfnGenerate = []() {
std::vector<Profile> profiles;
Profile p0;
p0.Name(L"profile0");
profiles.push_back(p0);
return profiles;
};
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
gen1->pfnGenerate = []() {
std::vector<Profile> profiles;
Profile p0;
p0.Name(L"profile1");
profiles.push_back(p0);
return profiles;
};
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
settings->_LoadDynamicProfiles();
VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(0).HasGuid());
VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(1).Name());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
}
void DynamicProfileTests::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.
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
gen0->pfnGenerate = []() {
std::vector<Profile> profiles;
Profile p0;
p0.Name(L"profile0"); // this is _allProfiles.at(2)
profiles.push_back(p0);
return profiles;
};
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
gen1->pfnGenerate = []() {
std::vector<Profile> profiles;
Profile p0, p1;
p0.Name(L"profile0"); // this is _allProfiles.at(3)
p1.Name(L"profile1"); // this is _allProfiles.at(4)
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
Profile p0, p1;
p0.Name(L"profile0"); // this is _allProfiles.GetAt(0)
p1.Name(L"profile1"); // this is _allProfiles.GetAt(1)
settings->_allProfiles.Append(p0);
settings->_allProfiles.Append(p1);
settings->_LoadDynamicProfiles();
VERIFY_ARE_EQUAL(5u, settings->_allProfiles.Size());
VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(0).HasGuid());
VERIFY_IS_TRUE(settings->_allProfiles.GetAt(0).Source().empty());
VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(1).Name());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).HasGuid());
VERIFY_IS_TRUE(settings->_allProfiles.GetAt(1).Source().empty());
VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(2).Name());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(2).HasGuid());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(2).Source().empty());
VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(3).Name());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(3).HasGuid());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(3).Source().empty());
VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(4).Name());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(4).HasGuid());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(4).Source().empty());
VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(0).Guid(),
settings->_allProfiles.GetAt(1).Guid());
VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(0).Guid(),
settings->_allProfiles.GetAt(2).Guid());
VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(0).Guid(),
settings->_allProfiles.GetAt(3).Guid());
VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(1).Guid(),
settings->_allProfiles.GetAt(4).Guid());
VERIFY_ARE_NOT_EQUAL(settings->_allProfiles.GetAt(3).Guid(),
settings->_allProfiles.GetAt(4).Guid());
}
void DynamicProfileTests::DontLayerUserProfilesOnDynamicProfiles()
{
winrt::guid guid0 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
winrt::guid guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const std::string userProfiles{ R"(
{
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
}
]
})" };
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
gen0->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
p0.Name(L"profile0"); // this is _allProfiles.at(0)
profiles.push_back(p0);
return profiles;
};
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
gen1->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
Profile p1 = winrt::make<implementation::Profile>(guid1);
p0.Name(L"profile0"); // this is _allProfiles.at(1)
p1.Name(L"profile1"); // this is _allProfiles.at(2)
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
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 sources"));
// 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());
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(5u, 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_IS_TRUE(settings->_allProfiles.GetAt(4).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_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_IS_TRUE(settings->_allProfiles.GetAt(4).HasGuid());
VERIFY_ARE_EQUAL(guid0, settings->_allProfiles.GetAt(0).Guid());
VERIFY_ARE_EQUAL(guid0, settings->_allProfiles.GetAt(1).Guid());
VERIFY_ARE_EQUAL(guid1, settings->_allProfiles.GetAt(2).Guid());
VERIFY_ARE_EQUAL(guid0, settings->_allProfiles.GetAt(3).Guid());
VERIFY_ARE_EQUAL(guid1, settings->_allProfiles.GetAt(4).Guid());
}
void DynamicProfileTests::DoLayerUserProfilesOnDynamicsWhenSourceMatches()
{
winrt::guid guid0 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
winrt::guid guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const std::string userProfiles{ R"(
{
"profiles": [
{
"name" : "profile0FromUserSettings", // this is _allProfiles.at(0)
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.0"
},
{
"name" : "profile1FromUserSettings", // this is _allProfiles.at(2)
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.1"
}
]
})" };
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
gen0->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
p0.Name(L"profile0"); // this is _allProfiles.at(0)
profiles.push_back(p0);
return profiles;
};
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
gen1->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
Profile p1 = winrt::make<implementation::Profile>(guid1);
p0.Name(L"profile0"); // this is _allProfiles.at(1)
p1.Name(L"profile1"); // this is _allProfiles.at(2)
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
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());
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(3u, 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_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_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(guid0, settings->_allProfiles.GetAt(0).Guid());
VERIFY_ARE_EQUAL(guid0, settings->_allProfiles.GetAt(1).Guid());
VERIFY_ARE_EQUAL(guid1, 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());
}
void DynamicProfileTests::TestDontRunDisabledGenerators()
{
const std::string settings0String{ R"(
{
"disabledProfileSources": ["Terminal.App.UnitTest.0"]
})" };
const std::string settings1String{ R"(
{
"disabledProfileSources": ["Terminal.App.UnitTest.0", "Terminal.App.UnitTest.1"]
})" };
const auto settings0Json = VerifyParseSucceeded(settings0String);
auto gen0GenerateFn = []() {
std::vector<Profile> profiles;
Profile p0;
p0.Name(L"profile0");
profiles.push_back(p0);
return profiles;
};
auto gen1GenerateFn = []() {
std::vector<Profile> profiles;
Profile p0, p1;
p0.Name(L"profile1");
p1.Name(L"profile2");
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
auto gen2GenerateFn = []() {
std::vector<Profile> profiles;
Profile p0, p1;
p0.Name(L"profile3");
p1.Name(L"profile4");
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
{
Log::Comment(NoThrowString().Format(
L"Case 1: Disable a single profile generator"));
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
auto gen2 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.2");
gen0->pfnGenerate = gen0GenerateFn;
gen1->pfnGenerate = gen1GenerateFn;
gen2->pfnGenerate = gen2GenerateFn;
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
settings->_profileGenerators.emplace_back(std::move(gen2));
// Parse as the user settings:
settings->_ParseJsonString(settings0String, false);
settings->_LoadDynamicProfiles();
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_FALSE(settings->_allProfiles.GetAt(3).Source().empty());
VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.1", 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.2", settings->_allProfiles.GetAt(2).Source());
VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.2", settings->_allProfiles.GetAt(3).Source());
VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(0).Name());
VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(1).Name());
VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(2).Name());
VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(3).Name());
}
{
Log::Comment(NoThrowString().Format(
L"Case 2: Disable multiple profile generators"));
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
auto gen2 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.2");
gen0->pfnGenerate = gen0GenerateFn;
gen1->pfnGenerate = gen1GenerateFn;
gen2->pfnGenerate = gen2GenerateFn;
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
settings->_profileGenerators.emplace_back(std::move(gen2));
// Parse as the user settings:
settings->_ParseJsonString(settings1String, false);
settings->_LoadDynamicProfiles();
VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(0).Source().empty());
VERIFY_IS_FALSE(settings->_allProfiles.GetAt(1).Source().empty());
VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.2", settings->_allProfiles.GetAt(0).Source());
VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.2", settings->_allProfiles.GetAt(1).Source());
VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(0).Name());
VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(1).Name());
}
}
void DynamicProfileTests::TestLegacyProfilesMigrate()
{
winrt::guid guid0 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
winrt::guid guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
winrt::guid guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
winrt::guid guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
winrt::guid guid4 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-4444-49a3-80bd-e8fdd045185c}");
const std::string settings0String{ R"(
{
"profiles": [
{
// This pwsh profile does not have a source, but should still be layered
"name" : "profile0FromUserSettings", // this is _allProfiles.at(0)
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
},
{
// This Azure profile does not have a source, but should still be layered
"name" : "profile3FromUserSettings", // this is _allProfiles.at(3)
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
},
{
// This profile did not come from a dynamic source
"name" : "profile4FromUserSettings", // this is _allProfiles.at(4)
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}"
},
{
// This WSL profile does not have a source, but should still be layered
"name" : "profile1FromUserSettings", // this is _allProfiles.at(1)
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
},
{
// This WSL profile does have a source, and should be layered
"name" : "profile2FromUserSettings", // this is _allProfiles.at(2)
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"source": "Windows.Terminal.Wsl"
}
]
})" };
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Windows.Terminal.PowershellCore");
gen0->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
p0.Name(L"profile0");
profiles.push_back(p0);
return profiles;
};
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Windows.Terminal.Wsl");
gen1->pfnGenerate = [guid2, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid1);
Profile p1 = winrt::make<implementation::Profile>(guid2);
p0.Name(L"profile1");
p1.Name(L"profile2");
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
auto gen2 = std::make_unique<TestDynamicProfileGenerator>(L"Windows.Terminal.Azure");
gen2->pfnGenerate = [guid3]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid3);
p0.Name(L"profile3");
profiles.push_back(p0);
return profiles;
};
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
settings->_profileGenerators.emplace_back(std::move(gen2));
settings->_ParseJsonString(settings0String, false);
VERIFY_ARE_EQUAL(0u, settings->_allProfiles.Size());
settings->_LoadDynamicProfiles();
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_FALSE(settings->_allProfiles.GetAt(3).Source().empty());
VERIFY_ARE_EQUAL(L"Windows.Terminal.PowershellCore", settings->_allProfiles.GetAt(0).Source());
VERIFY_ARE_EQUAL(L"Windows.Terminal.Wsl", settings->_allProfiles.GetAt(1).Source());
VERIFY_ARE_EQUAL(L"Windows.Terminal.Wsl", settings->_allProfiles.GetAt(2).Source());
VERIFY_ARE_EQUAL(L"Windows.Terminal.Azure", settings->_allProfiles.GetAt(3).Source());
VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
VERIFY_ARE_EQUAL(L"profile1", settings->_allProfiles.GetAt(1).Name());
VERIFY_ARE_EQUAL(L"profile2", settings->_allProfiles.GetAt(2).Name());
VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(3).Name());
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(5u, 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_FALSE(settings->_allProfiles.GetAt(3).Source().empty());
VERIFY_IS_TRUE(settings->_allProfiles.GetAt(4).Source().empty());
VERIFY_ARE_EQUAL(L"Windows.Terminal.PowershellCore", settings->_allProfiles.GetAt(0).Source());
VERIFY_ARE_EQUAL(L"Windows.Terminal.Wsl", settings->_allProfiles.GetAt(1).Source());
VERIFY_ARE_EQUAL(L"Windows.Terminal.Wsl", settings->_allProfiles.GetAt(2).Source());
VERIFY_ARE_EQUAL(L"Windows.Terminal.Azure", settings->_allProfiles.GetAt(3).Source());
// settings->_allProfiles.GetAt(4) does not have a source
VERIFY_ARE_EQUAL(L"profile0FromUserSettings", settings->_allProfiles.GetAt(0).Name());
VERIFY_ARE_EQUAL(L"profile1FromUserSettings", settings->_allProfiles.GetAt(1).Name());
VERIFY_ARE_EQUAL(L"profile2FromUserSettings", settings->_allProfiles.GetAt(2).Name());
VERIFY_ARE_EQUAL(L"profile3FromUserSettings", settings->_allProfiles.GetAt(3).Name());
VERIFY_ARE_EQUAL(L"profile4FromUserSettings", settings->_allProfiles.GetAt(4).Name());
}
void DynamicProfileTests::UserProfilesWithInvalidSourcesAreIgnored()
{
winrt::guid guid0 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
winrt::guid guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const std::string settings0String{ R"(
{
"profiles": [
{
"name" : "profile0FromUserSettings", // this is _allProfiles.at(0)
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.0"
},
{
"name" : "profile2", // this shouldn't be in the profiles at all
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.1"
},
{
"name" : "profile3", // this is _allProfiles.at(3)
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}"
}
]
})" };
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
gen0->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
p0.Name(L"profile0"); // this is _allProfiles.at(0)
profiles.push_back(p0);
return profiles;
};
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
gen1->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
Profile p1 = winrt::make<implementation::Profile>(guid1);
p0.Name(L"profile0"); // this is _allProfiles.at(1)
p1.Name(L"profile1"); // this is _allProfiles.at(2)
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
settings->_ParseJsonString(settings0String, false);
VERIFY_ARE_EQUAL(0u, settings->_allProfiles.Size());
settings->_LoadDynamicProfiles();
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(4u, settings->_allProfiles.Size());
}
void DynamicProfileTests::UserProfilesFromDisabledSourcesDontAppear()
{
winrt::guid guid0 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
winrt::guid guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const std::string settings0String{ R"(
{
"disabledProfileSources": ["Terminal.App.UnitTest.1"],
"profiles": [
{
"name" : "profile0FromUserSettings", // this is _allProfiles.at(0)
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.0"
},
{
"name" : "profile1FromUserSettings", // this shouldn't be in the profiles at all
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.1"
},
{
"name" : "profile3", // this is _allProfiles.at(1)
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}"
}
]
})" };
auto gen0 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.0");
gen0->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
p0.Name(L"profile0"); // this is _allProfiles.at(0)
profiles.push_back(p0);
return profiles;
};
auto gen1 = std::make_unique<TestDynamicProfileGenerator>(L"Terminal.App.UnitTest.1");
gen1->pfnGenerate = [guid0, guid1]() {
std::vector<Profile> profiles;
Profile p0 = winrt::make<implementation::Profile>(guid0);
Profile p1 = winrt::make<implementation::Profile>(guid1);
p0.Name(L"profile0"); // this shouldn't be in the profiles at all
p1.Name(L"profile1"); // this shouldn't be in the profiles at all
profiles.push_back(p0);
profiles.push_back(p1);
return profiles;
};
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_profileGenerators.emplace_back(std::move(gen0));
settings->_profileGenerators.emplace_back(std::move(gen1));
settings->_ParseJsonString(settings0String, false);
VERIFY_ARE_EQUAL(0u, settings->_allProfiles.Size());
settings->_LoadDynamicProfiles();
VERIFY_ARE_EQUAL(1u, settings->_allProfiles.Size());
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
}
};

View file

@ -1,175 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "../TerminalSettingsModel/ColorScheme.h"
#include "../TerminalSettingsModel/Profile.h"
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "../LocalTests_SettingsModel/JsonTestClass.h"
#include "../types/inc/colorTable.hpp"
using namespace Microsoft::Console;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
namespace TerminalAppUnitTests
{
class JsonTests : public JsonTestClass
{
BEGIN_TEST_CLASS(JsonTests)
TEST_CLASS_PROPERTY(L"ActivationContext", L"TerminalApp.Unit.Tests.manifest")
END_TEST_CLASS()
TEST_METHOD(ParseInvalidJson);
TEST_METHOD(ParseSimpleColorScheme);
TEST_METHOD(ProfileGeneratesGuid);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
// Use 4 spaces to indent instead of \t
_builder.settings_["indentation"] = " ";
return true;
}
Json::Value VerifyParseSucceeded(std::string_view content);
void VerifyParseFailed(std::string_view content);
private:
Json::StreamWriterBuilder _builder;
};
Json::Value JsonTests::VerifyParseSucceeded(std::string_view content)
{
Json::Value root;
std::string 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;
}
void JsonTests::VerifyParseFailed(std::string_view content)
{
Json::Value root;
std::string errs;
const bool parseResult = _reader->parse(content.data(), content.data() + content.size(), &root, &errs);
VERIFY_IS_FALSE(parseResult);
}
void JsonTests::ParseInvalidJson()
{
const std::string badJson{ "{ foo : bar : baz }" };
VerifyParseFailed(badJson);
}
void JsonTests::ParseSimpleColorScheme()
{
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 schemeObject = VerifyParseSucceeded(campbellScheme);
auto scheme = implementation::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() });
std::array<COLORREF, COLOR_TABLE_SIZE> expectedCampbellTable;
auto campbellSpan = gsl::span<COLORREF>(&expectedCampbellTable[0], COLOR_TABLE_SIZE);
Utils::InitializeCampbellColorTable(campbellSpan);
Utils::SetColorTableAlpha(campbellSpan, 0);
for (size_t i = 0; i < expectedCampbellTable.size(); i++)
{
const auto& expected = expectedCampbellTable.at(i);
const til::color actual{ scheme->Table().at(static_cast<uint32_t>(i)) };
VERIFY_ARE_EQUAL(expected, actual);
}
Log::Comment(L"Roundtrip Test for Color Scheme");
Json::Value outJson{ scheme->ToJson() };
VERIFY_ARE_EQUAL(schemeObject, outJson);
}
void JsonTests::ProfileGeneratesGuid()
{
// 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
// 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);
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 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_ARE_EQUAL(profile3->Guid(), nullGuid);
VERIFY_ARE_EQUAL(profile4->Guid(), cmdGuid);
}
}

View file

@ -29,9 +29,9 @@
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="ColorHelperTests.cpp" />
<ClCompile Include="JsonTests.cpp" />
<ClCompile Include="JsonUtilsTests.cpp" />
<ClCompile Include="DynamicProfileTests.cpp" />
<ClCompile Include="precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>

View file

@ -1,44 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- TestDynamicProfileGenerator.hpp
Abstract:
- This is a helper class for writing tests using dynamic profiles. Lets you
easily set a arbitrary namespace and generation function for the profiles.
Author(s):
- Mike Griese - August 2019
--*/
#include "../TerminalSettingsModel/IDynamicProfileGenerator.h"
namespace TerminalAppUnitTests
{
class TestDynamicProfileGenerator;
};
class TerminalAppUnitTests::TestDynamicProfileGenerator final :
public Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator
{
public:
TestDynamicProfileGenerator(std::wstring_view ns) :
_namespace{ ns } {};
std::wstring_view GetNamespace() override { return _namespace; };
std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() override
{
if (pfnGenerate)
{
return pfnGenerate();
}
return std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile>{};
}
std::wstring _namespace;
std::function<std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile>()> pfnGenerate{ nullptr };
};

View file

@ -33,8 +33,8 @@ constexpr uint16_t DEFAULT_FONT_WEIGHT = 400; // normal
constexpr int DEFAULT_ROWS = 30;
constexpr int DEFAULT_COLS = 120;
const std::wstring DEFAULT_PADDING{ L"8, 8, 8, 8" };
const std::wstring DEFAULT_STARTING_DIRECTORY{ L"%USERPROFILE%" };
constexpr std::wstring_view DEFAULT_PADDING{ L"8, 8, 8, 8" };
constexpr std::wstring_view DEFAULT_STARTING_DIRECTORY{ L"%USERPROFILE%" };
constexpr auto DEFAULT_CURSOR_COLOR = COLOR_WHITE;
constexpr COLORREF DEFAULT_CURSOR_HEIGHT = 25;

View file

@ -70,7 +70,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}
operator COLORREF() const noexcept
constexpr operator COLORREF() const noexcept
{
return static_cast<COLORREF>(abgr & 0x00FFFFFFu);
}
@ -147,14 +147,9 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}
operator winrt::Windows::UI::Color() const
constexpr operator winrt::Windows::UI::Color() const
{
winrt::Windows::UI::Color ret;
ret.R = r;
ret.G = g;
ret.B = b;
ret.A = a;
return ret;
return { a, r, g, b };
}
#endif
@ -164,14 +159,9 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}
operator winrt::Microsoft::Terminal::Core::Color() const noexcept
constexpr operator winrt::Microsoft::Terminal::Core::Color() const noexcept
{
winrt::Microsoft::Terminal::Core::Color ret;
ret.R = r;
ret.G = g;
ret.B = b;
ret.A = a;
return ret;
return { r, g, b, a };
}
#endif

View file

@ -89,7 +89,9 @@ constexpr til::point segment1LmidHistory{ segment1, midHistory.y() };
constexpr til::point segment1LmidTop{ segment1, midTop.y() };
constexpr til::point segment1LmidTopP1L{ segment1, midTopP1L.y() };
constexpr til::point segment2LmidDocEnd{ segment2, midDocEnd.y() };
constexpr til::point segment2LmidDocEndM1L{ segment2, midDocEndM1L.y() };
constexpr til::point segment2LmidHistory{ segment2, midHistory.y() };
constexpr til::point segment2LmidHistoryM1L{ segment2, midHistoryM1L.y() };
constexpr til::point segment2LmidHistoryP1L{ segment2, midHistoryP1L.y() };
constexpr til::point segment2LmidTop{ segment2, midTop.y() };
constexpr til::point segment2LmidTopP1L{ segment2, midTopP1L.y() };
@ -102,9 +104,7 @@ constexpr til::point segment3LmidTop{ segment3, midTop.y() };
constexpr til::point segment3LmidTopP1L{ segment3, midTopP1L.y() };
constexpr til::point segment4LlastCharPosM1L{ segment4, lastCharPosM1L.y() };
constexpr til::point segment4LmidDocEnd{ segment4, midDocEnd.y() };
constexpr til::point segment4LmidDocEndM1L{ segment4, midDocEndM1L.y() };
constexpr til::point segment4LmidHistory{ segment4, midHistory.y() };
constexpr til::point segment4LmidHistoryM1L{ segment4, midHistoryM1L.y() };
constexpr til::point segment4LmidTop{ segment4, midTop.y() };
struct GeneratedMovementTestInput
{
@ -3319,7 +3319,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-3,
origin,
origin },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 2 -1 times by Word",
GeneratedMovementTestInput{
@ -3331,7 +3331,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment2LmidTop,
segment2LmidTop },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 2 0 times by Word",
GeneratedMovementTestInput{
@ -3439,7 +3439,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
segment3LmidHistoryM1L,
segment3LmidHistoryM1L },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 3 -1 times by Word",
GeneratedMovementTestInput{
@ -3451,7 +3451,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment2LmidHistory,
segment2LmidHistory },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 3 0 times by Word",
GeneratedMovementTestInput{
@ -3497,9 +3497,9 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
midHistoryP1C },
GeneratedMovementTestExpected{
-5,
segment3LmidHistoryM1L,
segment4LmidHistoryM1L },
true },
segment2LmidHistoryM1L,
segment3LmidHistoryM1L },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 3 -1 times by Word",
GeneratedMovementTestInput{
@ -3559,7 +3559,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
segment3LmidDocEndM1L,
segment3LmidDocEndM1L },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 4 -1 times by Word",
GeneratedMovementTestInput{
@ -3571,7 +3571,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment2LmidDocEnd,
segment2LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 4 0 times by Word",
GeneratedMovementTestInput{
@ -3617,9 +3617,9 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
midDocEndP1C },
GeneratedMovementTestExpected{
-5,
segment3LmidDocEndM1L,
segment4LmidDocEndM1L },
true },
segment2LmidDocEndM1L,
segment3LmidDocEndM1L },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 4 -1 times by Word",
GeneratedMovementTestInput{
@ -3679,7 +3679,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
lastCharPosLeft,
lastCharPosLeft },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 5 -1 times by Word",
GeneratedMovementTestInput{
@ -3691,7 +3691,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 5 0 times by Word",
GeneratedMovementTestInput{
@ -3799,7 +3799,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
midDocEndLeft,
midDocEndLeft },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 6 -1 times by Word",
GeneratedMovementTestInput{
@ -3811,7 +3811,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 6 0 times by Word",
GeneratedMovementTestInput{
@ -3859,7 +3859,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
midDocEndLeft,
midDocEndLeft },
true },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 6 -1 times by Word",
GeneratedMovementTestInput{
@ -3871,7 +3871,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 6 0 times by Word",
GeneratedMovementTestInput{
@ -3919,7 +3919,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
midDocEndLeft,
midDocEndLeft },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 7 -1 times by Word",
GeneratedMovementTestInput{
@ -3931,7 +3931,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 7 0 times by Word",
GeneratedMovementTestInput{
@ -3979,7 +3979,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
midDocEndLeft,
midDocEndLeft },
true },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 7 -1 times by Word",
GeneratedMovementTestInput{
@ -3991,7 +3991,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 7 0 times by Word",
GeneratedMovementTestInput{
@ -4039,7 +4039,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
midDocEndLeft,
midDocEndLeft },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 8 -1 times by Word",
GeneratedMovementTestInput{
@ -4051,7 +4051,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 8 0 times by Word",
GeneratedMovementTestInput{
@ -4099,7 +4099,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
midDocEndLeft,
midDocEndLeft },
true },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 8 -1 times by Word",
GeneratedMovementTestInput{
@ -4111,7 +4111,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move non-degenerate range at position 8 0 times by Word",
GeneratedMovementTestInput{
@ -4159,7 +4159,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-5,
midDocEndLeft,
midDocEndLeft },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 9 -1 times by Word",
GeneratedMovementTestInput{
@ -4171,7 +4171,7 @@ static constexpr std::array<GeneratedMovementTest, 340> s_movementTests{
-1,
segment4LmidDocEnd,
segment4LmidDocEnd },
true },
false },
GeneratedMovementTest{
L"Move degenerate range at position 9 0 times by Word",
GeneratedMovementTestInput{

View file

@ -1417,11 +1417,13 @@ class UiaTextRangeTests
// reset the UTR
if (degenerate)
{
// UTR: (exclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
}
else
{
// UTR: (inclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
}
@ -1443,8 +1445,8 @@ class UiaTextRangeTests
else if (textUnit <= TextUnit::TextUnit_Word)
{
VERIFY_ARE_EQUAL(-1, moveAmt);
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? origin : writeTarget, til::point{ utr->_end });
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? writeTarget : origin, til::point{ utr->_start });
VERIFY_ARE_EQUAL(writeTarget, til::point{ utr->_end });
}
else if (textUnit <= TextUnit::TextUnit_Line)
{

View file

@ -44,7 +44,7 @@ namespace Microsoft::Console::VirtualTerminal
}
// Note that the 94-character sets are deliberately defined with a size of
// 95 to avoid having to test the lower bound. We just alway leave the first
// 95 to avoid having to test the lower bound. We just always leave the first
// entry - which is not meant to be mapped - as a SPACE or NBSP, which is at
// least visually equivalent to leaving it untranslated.

View file

@ -1522,6 +1522,14 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
{
success = false;
}
else if (allowBottomExclusive && _tryMoveToWordStart(buffer, documentEnd, resultPos))
{
// IMPORTANT: _tryMoveToWordStart modifies resultPos if successful
// Degenerate ranges first move to the beginning of the word,
// but if we're already at the beginning of the word, we continue
// to the next branch and move to the previous word!
(*pAmountMoved)--;
}
else if (buffer.MoveToPreviousWord(nextPos, _wordDelimiters))
{
resultPos = nextPos;
@ -1541,6 +1549,26 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
SetEndpoint(endpoint, resultPos);
}
// Routine Description:
// - tries to move resultingPos to the beginning of the word
// Arguments:
// - buffer - the text buffer we're operating on
// - documentEnd - the document end of the buffer (see _getDocumentEnd())
// - resultingPos - the position we're starting from and modifying
// Return Value:
// - true --> we were not at the beginning of the word, and we updated resultingPos to be so
// - false --> otherwise (we're already at the beginning of the word)
bool UiaTextRangeBase::_tryMoveToWordStart(const TextBuffer& buffer, const til::point documentEnd, COORD& resultingPos) const
{
const auto wordStart{ buffer.GetWordStart(resultingPos, _wordDelimiters, true, documentEnd) };
if (resultingPos != wordStart)
{
resultingPos = wordStart;
return true;
}
return false;
}
// Routine Description:
// - moves the UTR's endpoint by moveCount times by line.
// - if endpoints crossed, the degenerate range is created and both endpoints are moved

View file

@ -182,6 +182,7 @@ namespace Microsoft::Console::Types
std::optional<bool> _verifyAttr(TEXTATTRIBUTEID attributeId, VARIANT val, const TextAttribute& attr) const;
bool _initializeAttrQuery(TEXTATTRIBUTEID attributeId, VARIANT* pRetVal, const TextAttribute& attr) const;
bool _tryMoveToWordStart(const TextBuffer& buffer, const til::point documentEnd, COORD& resultingPos) const;
COORD _getInclusiveEnd() noexcept;

View file

@ -40,7 +40,7 @@ namespace Microsoft::Console::Utils
}
std::wstring GuidToString(const GUID guid);
GUID GuidFromString(const std::wstring wstr);
GUID GuidFromString(_Null_terminated_ const wchar_t* str);
GUID CreateGuid();
std::string ColorToHexString(const til::color color);

View file

@ -41,10 +41,10 @@ std::wstring Utils::GuidToString(const GUID guid)
// Return Value:
// - A GUID if the string could successfully be parsed. On failure, throws the
// failing HRESULT.
GUID Utils::GuidFromString(const std::wstring wstr)
GUID Utils::GuidFromString(_Null_terminated_ const wchar_t* str)
{
GUID result{};
THROW_IF_FAILED(IIDFromString(wstr.c_str(), &result));
GUID result;
THROW_IF_FAILED(IIDFromString(str, &result));
return result;
}

View file

@ -13,7 +13,7 @@ param (
)
$fullPath = Resolve-Path $JsonFile
$jsonData = Get-Content $JsonFile
$jsonData = Get-Content -Raw $JsonFile | ConvertFrom-Json | ConvertTo-Json -Compress -Depth 100
@(
"// Copyright (c) Microsoft Corporation",
@ -21,7 +21,5 @@ $jsonData = Get-Content $JsonFile
"",
"// THIS IS AN AUTO-GENERATED FILE",
"// Generated from $($fullPath.Path)",
"constexpr std::string_view $($VariableName){",
($jsonData | ForEach-Object { "R`"#($_`n)#`"" }),
"};"
"constexpr std::string_view $VariableName{ R`"#($jsonData)#`" };"
) | Out-File -FilePath $OutPath -Encoding utf8

View file

@ -264,8 +264,8 @@ FALSE,1,TextUnit_Word,-1,origin,originP1C,0,origin,segment1LmidTop,FALSE
FALSE,1,TextUnit_Word,0,origin,originP1C,0,origin,segment1LmidTop,FALSE
FALSE,1,TextUnit_Word,1,origin,originP1C,1,segment1LmidTop,segment2LmidTop,FALSE
FALSE,1,TextUnit_Word,5,origin,originP1C,5,segment0LmidTopP1L,segment1LmidTopP1L,FALSE
TRUE,2,TextUnit_Word,-5,midTop,midTop,-3,origin,origin,TRUE
TRUE,2,TextUnit_Word,-1,midTop,midTop,-1,segment2LmidTop,segment2LmidTop,TRUE
TRUE,2,TextUnit_Word,-5,midTop,midTop,-3,origin,origin,FALSE
TRUE,2,TextUnit_Word,-1,midTop,midTop,-1,segment2LmidTop,segment2LmidTop,FALSE
TRUE,2,TextUnit_Word,0,midTop,midTop,0,midTop,midTop,FALSE
TRUE,2,TextUnit_Word,1,midTop,midTop,1,segment3LmidTop,segment3LmidTop,FALSE
TRUE,2,TextUnit_Word,5,midTop,midTop,5,segment2LmidTopP1L,segment2LmidTopP1L,FALSE
@ -274,28 +274,28 @@ FALSE,2,TextUnit_Word,-1,midTop,midTopP1C,-1,segment1LmidTop,segment2LmidTop,FAL
FALSE,2,TextUnit_Word,0,midTop,midTopP1C,0,segment2LmidTop,segment3LmidTop,FALSE
FALSE,2,TextUnit_Word,1,midTop,midTopP1C,1,segment3LmidTop,segment4LmidTop,FALSE
FALSE,2,TextUnit_Word,5,midTop,midTopP1C,5,segment2LmidTopP1L,segment3LmidTopP1L,FALSE
TRUE,3,TextUnit_Word,-5,midHistory,midHistory,-5,segment3LmidHistoryM1L,segment3LmidHistoryM1L,TRUE
TRUE,3,TextUnit_Word,-1,midHistory,midHistory,-1,segment2LmidHistory,segment2LmidHistory,TRUE
TRUE,3,TextUnit_Word,-5,midHistory,midHistory,-5,segment3LmidHistoryM1L,segment3LmidHistoryM1L,FALSE
TRUE,3,TextUnit_Word,-1,midHistory,midHistory,-1,segment2LmidHistory,segment2LmidHistory,FALSE
TRUE,3,TextUnit_Word,0,midHistory,midHistory,0,midHistory,midHistory,FALSE
TRUE,3,TextUnit_Word,1,midHistory,midHistory,1,segment3LmidHistory,segment3LmidHistory,FALSE
TRUE,3,TextUnit_Word,5,midHistory,midHistory,5,segment2LmidHistoryP1L,segment2LmidHistoryP1L,FALSE
FALSE,3,TextUnit_Word,-5,midHistory,midHistoryP1C,-5,segment3LmidHistoryM1L,segment4LmidHistoryM1L,TRUE
FALSE,3,TextUnit_Word,-5,midHistory,midHistoryP1C,-5,segment2LmidHistoryM1L,segment3LmidHistoryM1L,FALSE
FALSE,3,TextUnit_Word,-1,midHistory,midHistoryP1C,-1,segment1LmidHistory,segment2LmidHistory,FALSE
FALSE,3,TextUnit_Word,0,midHistory,midHistoryP1C,0,segment2LmidHistory,segment3LmidHistory,FALSE
FALSE,3,TextUnit_Word,1,midHistory,midHistoryP1C,1,segment3LmidHistory,segment4LmidHistory,FALSE
FALSE,3,TextUnit_Word,5,midHistory,midHistoryP1C,5,segment2LmidHistoryP1L,segment3LmidHistoryP1L,FALSE
TRUE,4,TextUnit_Word,-5,midDocEnd,midDocEnd,-5,segment3LmidDocEndM1L,segment3LmidDocEndM1L,TRUE
TRUE,4,TextUnit_Word,-1,midDocEnd,midDocEnd,-1,segment2LmidDocEnd,segment2LmidDocEnd,TRUE
TRUE,4,TextUnit_Word,-5,midDocEnd,midDocEnd,-5,segment3LmidDocEndM1L,segment3LmidDocEndM1L,FALSE
TRUE,4,TextUnit_Word,-1,midDocEnd,midDocEnd,-1,segment2LmidDocEnd,segment2LmidDocEnd,FALSE
TRUE,4,TextUnit_Word,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Word,1,midDocEnd,midDocEnd,1,segment3LmidDocEnd,segment3LmidDocEnd,FALSE
TRUE,4,TextUnit_Word,5,midDocEnd,midDocEnd,3,docEnd,docEnd,FALSE
FALSE,4,TextUnit_Word,-5,midDocEnd,midDocEndP1C,-5,segment3LmidDocEndM1L,segment4LmidDocEndM1L,TRUE
FALSE,4,TextUnit_Word,-5,midDocEnd,midDocEndP1C,-5,segment2LmidDocEndM1L,segment3LmidDocEndM1L,FALSE
FALSE,4,TextUnit_Word,-1,midDocEnd,midDocEndP1C,-1,segment1LmidDocEnd,segment2LmidDocEnd,FALSE
FALSE,4,TextUnit_Word,0,midDocEnd,midDocEndP1C,0,segment2LmidDocEnd,segment3LmidDocEnd,FALSE
FALSE,4,TextUnit_Word,1,midDocEnd,midDocEndP1C,1,segment3LmidDocEnd,segment4LmidDocEnd,FALSE
FALSE,4,TextUnit_Word,5,midDocEnd,midDocEndP1C,2,segment4LmidDocEnd,docEnd,FALSE
TRUE,5,TextUnit_Word,-5,lastCharPos,lastCharPos,-5,lastCharPosLeft,lastCharPosLeft,TRUE
TRUE,5,TextUnit_Word,-1,lastCharPos,lastCharPos,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,5,TextUnit_Word,-5,lastCharPos,lastCharPos,-5,lastCharPosLeft,lastCharPosLeft,FALSE
TRUE,5,TextUnit_Word,-1,lastCharPos,lastCharPos,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
TRUE,5,TextUnit_Word,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Word,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
TRUE,5,TextUnit_Word,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
@ -304,38 +304,38 @@ FALSE,5,TextUnit_Word,-1,lastCharPos,lastCharPosP1C,-1,segment3LmidDocEnd,segmen
FALSE,5,TextUnit_Word,0,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
FALSE,5,TextUnit_Word,1,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
FALSE,5,TextUnit_Word,5,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
TRUE,6,TextUnit_Word,-5,docEnd,docEnd,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,6,TextUnit_Word,-1,docEnd,docEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,6,TextUnit_Word,-5,docEnd,docEnd,-5,midDocEndLeft,midDocEndLeft,FALSE
TRUE,6,TextUnit_Word,-1,docEnd,docEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
TRUE,6,TextUnit_Word,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Word,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Word,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Word,-5,docEnd,docEndP1C,-5,midDocEndLeft,midDocEndLeft,TRUE
FALSE,6,TextUnit_Word,-1,docEnd,docEndP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
FALSE,6,TextUnit_Word,-5,docEnd,docEndP1C,-5,midDocEndLeft,midDocEndLeft,FALSE
FALSE,6,TextUnit_Word,-1,docEnd,docEndP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
FALSE,6,TextUnit_Word,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Word,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Word,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpace,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpace,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpace,-5,midDocEndLeft,midDocEndLeft,FALSE
TRUE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpace,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
TRUE,7,TextUnit_Word,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Word,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Word,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpaceP1C,-5,midDocEndLeft,midDocEndLeft,TRUE
FALSE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpaceP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
FALSE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpaceP1C,-5,midDocEndLeft,midDocEndLeft,FALSE
FALSE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpaceP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
FALSE,7,TextUnit_Word,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Word,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Word,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Word,-5,bufferEnd,bufferEnd,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,8,TextUnit_Word,-1,bufferEnd,bufferEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,8,TextUnit_Word,-5,bufferEnd,bufferEnd,-5,midDocEndLeft,midDocEndLeft,FALSE
TRUE,8,TextUnit_Word,-1,bufferEnd,bufferEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
TRUE,8,TextUnit_Word,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Word,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Word,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Word,-5,bufferEnd,endExclusive,-5,midDocEndLeft,midDocEndLeft,TRUE
FALSE,8,TextUnit_Word,-1,bufferEnd,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
FALSE,8,TextUnit_Word,-5,bufferEnd,endExclusive,-5,midDocEndLeft,midDocEndLeft,FALSE
FALSE,8,TextUnit_Word,-1,bufferEnd,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
FALSE,8,TextUnit_Word,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Word,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Word,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Word,-5,endExclusive,endExclusive,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,9,TextUnit_Word,-1,endExclusive,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,9,TextUnit_Word,-5,endExclusive,endExclusive,-5,midDocEndLeft,midDocEndLeft,FALSE
TRUE,9,TextUnit_Word,-1,endExclusive,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE
TRUE,9,TextUnit_Word,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Word,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Word,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE

1 Degenerate Position TextUnit MoveAmount Start End Result_MoveAmount Result_Start Result_End Skip
264 FALSE 1 TextUnit_Word 0 origin originP1C 0 origin segment1LmidTop FALSE
265 FALSE 1 TextUnit_Word 1 origin originP1C 1 segment1LmidTop segment2LmidTop FALSE
266 FALSE 1 TextUnit_Word 5 origin originP1C 5 segment0LmidTopP1L segment1LmidTopP1L FALSE
267 TRUE 2 TextUnit_Word -5 midTop midTop -3 origin origin TRUE FALSE
268 TRUE 2 TextUnit_Word -1 midTop midTop -1 segment2LmidTop segment2LmidTop TRUE FALSE
269 TRUE 2 TextUnit_Word 0 midTop midTop 0 midTop midTop FALSE
270 TRUE 2 TextUnit_Word 1 midTop midTop 1 segment3LmidTop segment3LmidTop FALSE
271 TRUE 2 TextUnit_Word 5 midTop midTop 5 segment2LmidTopP1L segment2LmidTopP1L FALSE
274 FALSE 2 TextUnit_Word 0 midTop midTopP1C 0 segment2LmidTop segment3LmidTop FALSE
275 FALSE 2 TextUnit_Word 1 midTop midTopP1C 1 segment3LmidTop segment4LmidTop FALSE
276 FALSE 2 TextUnit_Word 5 midTop midTopP1C 5 segment2LmidTopP1L segment3LmidTopP1L FALSE
277 TRUE 3 TextUnit_Word -5 midHistory midHistory -5 segment3LmidHistoryM1L segment3LmidHistoryM1L TRUE FALSE
278 TRUE 3 TextUnit_Word -1 midHistory midHistory -1 segment2LmidHistory segment2LmidHistory TRUE FALSE
279 TRUE 3 TextUnit_Word 0 midHistory midHistory 0 midHistory midHistory FALSE
280 TRUE 3 TextUnit_Word 1 midHistory midHistory 1 segment3LmidHistory segment3LmidHistory FALSE
281 TRUE 3 TextUnit_Word 5 midHistory midHistory 5 segment2LmidHistoryP1L segment2LmidHistoryP1L FALSE
282 FALSE 3 TextUnit_Word -5 midHistory midHistoryP1C -5 segment3LmidHistoryM1L segment2LmidHistoryM1L segment4LmidHistoryM1L segment3LmidHistoryM1L TRUE FALSE
283 FALSE 3 TextUnit_Word -1 midHistory midHistoryP1C -1 segment1LmidHistory segment2LmidHistory FALSE
284 FALSE 3 TextUnit_Word 0 midHistory midHistoryP1C 0 segment2LmidHistory segment3LmidHistory FALSE
285 FALSE 3 TextUnit_Word 1 midHistory midHistoryP1C 1 segment3LmidHistory segment4LmidHistory FALSE
286 FALSE 3 TextUnit_Word 5 midHistory midHistoryP1C 5 segment2LmidHistoryP1L segment3LmidHistoryP1L FALSE
287 TRUE 4 TextUnit_Word -5 midDocEnd midDocEnd -5 segment3LmidDocEndM1L segment3LmidDocEndM1L TRUE FALSE
288 TRUE 4 TextUnit_Word -1 midDocEnd midDocEnd -1 segment2LmidDocEnd segment2LmidDocEnd TRUE FALSE
289 TRUE 4 TextUnit_Word 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd FALSE
290 TRUE 4 TextUnit_Word 1 midDocEnd midDocEnd 1 segment3LmidDocEnd segment3LmidDocEnd FALSE
291 TRUE 4 TextUnit_Word 5 midDocEnd midDocEnd 3 docEnd docEnd FALSE
292 FALSE 4 TextUnit_Word -5 midDocEnd midDocEndP1C -5 segment3LmidDocEndM1L segment2LmidDocEndM1L segment4LmidDocEndM1L segment3LmidDocEndM1L TRUE FALSE
293 FALSE 4 TextUnit_Word -1 midDocEnd midDocEndP1C -1 segment1LmidDocEnd segment2LmidDocEnd FALSE
294 FALSE 4 TextUnit_Word 0 midDocEnd midDocEndP1C 0 segment2LmidDocEnd segment3LmidDocEnd FALSE
295 FALSE 4 TextUnit_Word 1 midDocEnd midDocEndP1C 1 segment3LmidDocEnd segment4LmidDocEnd FALSE
296 FALSE 4 TextUnit_Word 5 midDocEnd midDocEndP1C 2 segment4LmidDocEnd docEnd FALSE
297 TRUE 5 TextUnit_Word -5 lastCharPos lastCharPos -5 lastCharPosLeft lastCharPosLeft TRUE FALSE
298 TRUE 5 TextUnit_Word -1 lastCharPos lastCharPos -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
299 TRUE 5 TextUnit_Word 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos FALSE
300 TRUE 5 TextUnit_Word 1 lastCharPos lastCharPos 1 docEnd docEnd FALSE
301 TRUE 5 TextUnit_Word 5 lastCharPos lastCharPos 1 docEnd docEnd FALSE
304 FALSE 5 TextUnit_Word 0 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
305 FALSE 5 TextUnit_Word 1 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
306 FALSE 5 TextUnit_Word 5 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
307 TRUE 6 TextUnit_Word -5 docEnd docEnd -5 midDocEndLeft midDocEndLeft TRUE FALSE
308 TRUE 6 TextUnit_Word -1 docEnd docEnd -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
309 TRUE 6 TextUnit_Word 0 docEnd docEnd 0 docEnd docEnd FALSE
310 TRUE 6 TextUnit_Word 1 docEnd docEnd 0 docEnd docEnd FALSE
311 TRUE 6 TextUnit_Word 5 docEnd docEnd 0 docEnd docEnd FALSE
312 FALSE 6 TextUnit_Word -5 docEnd docEndP1C -5 midDocEndLeft midDocEndLeft TRUE FALSE
313 FALSE 6 TextUnit_Word -1 docEnd docEndP1C -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
314 FALSE 6 TextUnit_Word 0 docEnd docEndP1C 0 docEnd docEnd FALSE
315 FALSE 6 TextUnit_Word 1 docEnd docEndP1C 0 docEnd docEnd FALSE
316 FALSE 6 TextUnit_Word 5 docEnd docEndP1C 0 docEnd docEnd FALSE
317 TRUE 7 TextUnit_Word -5 midEmptySpace midEmptySpace -5 midDocEndLeft midDocEndLeft TRUE FALSE
318 TRUE 7 TextUnit_Word -1 midEmptySpace midEmptySpace -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
319 TRUE 7 TextUnit_Word 0 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
320 TRUE 7 TextUnit_Word 1 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
321 TRUE 7 TextUnit_Word 5 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
322 FALSE 7 TextUnit_Word -5 midEmptySpace midEmptySpaceP1C -5 midDocEndLeft midDocEndLeft TRUE FALSE
323 FALSE 7 TextUnit_Word -1 midEmptySpace midEmptySpaceP1C -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
324 FALSE 7 TextUnit_Word 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
325 FALSE 7 TextUnit_Word 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
326 FALSE 7 TextUnit_Word 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
327 TRUE 8 TextUnit_Word -5 bufferEnd bufferEnd -5 midDocEndLeft midDocEndLeft TRUE FALSE
328 TRUE 8 TextUnit_Word -1 bufferEnd bufferEnd -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
329 TRUE 8 TextUnit_Word 0 bufferEnd bufferEnd 0 docEnd docEnd FALSE
330 TRUE 8 TextUnit_Word 1 bufferEnd bufferEnd 0 docEnd docEnd FALSE
331 TRUE 8 TextUnit_Word 5 bufferEnd bufferEnd 0 docEnd docEnd FALSE
332 FALSE 8 TextUnit_Word -5 bufferEnd endExclusive -5 midDocEndLeft midDocEndLeft TRUE FALSE
333 FALSE 8 TextUnit_Word -1 bufferEnd endExclusive -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
334 FALSE 8 TextUnit_Word 0 bufferEnd endExclusive 0 docEnd docEnd FALSE
335 FALSE 8 TextUnit_Word 1 bufferEnd endExclusive 0 docEnd docEnd FALSE
336 FALSE 8 TextUnit_Word 5 bufferEnd endExclusive 0 docEnd docEnd FALSE
337 TRUE 9 TextUnit_Word -5 endExclusive endExclusive -5 midDocEndLeft midDocEndLeft TRUE FALSE
338 TRUE 9 TextUnit_Word -1 endExclusive endExclusive -1 segment4LmidDocEnd segment4LmidDocEnd TRUE FALSE
339 TRUE 9 TextUnit_Word 0 endExclusive endExclusive 0 docEnd docEnd FALSE
340 TRUE 9 TextUnit_Word 1 endExclusive endExclusive 0 docEnd docEnd FALSE
341 TRUE 9 TextUnit_Word 5 endExclusive endExclusive 0 docEnd docEnd FALSE