Add serialization error handling to settings projection layer (#7576)

Now that CascadiaSettings is a WinRT object, we need to update the error
handling a bit. Making it a WinRT object limits our errors to be
hresults. So we moved all the error handling down a layer to when we
load the settings object.

- Warnings encountered during validation are saved to `Warnings()`.
- Errors encountered during validation are saved to `GetLoadingError()`.
- Deserialization errors (mainly from JsonUtils) are saved to
  `GetDeserializationErrorMessage()`.

## References
#7141 - CascadiaSettings is a settings object
#885 - this makes ripping out CascadiaSettings into
     TerminalSettingsModel much easier

## Validation Steps Performed
* [x] Tests passed
- [x] Deployment succeeded
   - tested with invalid JSON (deserialization error)
   - tested with missing DefaultProfile (validation error)
This commit is contained in:
Carlos Zamora 2020-09-10 17:57:02 -07:00 committed by GitHub
parent 1377dbcbf4
commit 892cf05fe6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 365 additions and 304 deletions

View file

@ -177,9 +177,9 @@ namespace TerminalAppLocalTests
{
settings->_ValidateProfilesExist();
}
catch (const ::TerminalApp::SettingsException& ex)
catch (const winrt::TerminalApp::implementation::SettingsException& ex)
{
VERIFY_IS_TRUE(ex.Error() == ::TerminalApp::SettingsLoadErrors::NoProfiles);
VERIFY_IS_TRUE(ex.Error() == winrt::TerminalApp::SettingsLoadErrors::NoProfiles);
caughtExpectedException = true;
}
VERIFY_IS_TRUE(caughtExpectedException);
@ -193,9 +193,9 @@ namespace TerminalAppLocalTests
{
settings->_ValidateProfilesExist();
}
catch (const ::TerminalApp::SettingsException& ex)
catch (const winrt::TerminalApp::implementation::SettingsException& ex)
{
VERIFY_IS_TRUE(ex.Error() == ::TerminalApp::SettingsLoadErrors::NoProfiles);
VERIFY_IS_TRUE(ex.Error() == winrt::TerminalApp::SettingsLoadErrors::NoProfiles);
caughtExpectedException = true;
}
VERIFY_IS_TRUE(caughtExpectedException);
@ -272,7 +272,7 @@ namespace TerminalAppLocalTests
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
settings->_ResolveDefaultProfile();
settings->_ValidateDefaultProfileExists();
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->_warnings.size());
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->_warnings.Size());
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_profiles.GetAt(0).Guid());
}
@ -284,8 +284,8 @@ namespace TerminalAppLocalTests
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
settings->_ResolveDefaultProfile();
settings->_ValidateDefaultProfileExists();
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_profiles.GetAt(0).Guid());
@ -298,8 +298,8 @@ namespace TerminalAppLocalTests
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
settings->_ResolveDefaultProfile();
settings->_ValidateDefaultProfileExists();
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_profiles.GetAt(0).Guid());
@ -312,7 +312,7 @@ namespace TerminalAppLocalTests
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
settings->_ResolveDefaultProfile();
settings->_ValidateDefaultProfileExists();
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->_warnings.size());
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->_warnings.Size());
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_profiles.GetAt(1).Guid());
}
@ -407,7 +407,7 @@ namespace TerminalAppLocalTests
settings->_ValidateNoDuplicateProfiles();
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->_warnings.size());
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->_warnings.Size());
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->_profiles.Size());
}
{
@ -421,8 +421,8 @@ namespace TerminalAppLocalTests
settings->_ValidateNoDuplicateProfiles();
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_profiles.Size());
VERIFY_ARE_EQUAL(L"profile2", settings->_profiles.GetAt(0).Name());
@ -443,8 +443,8 @@ namespace TerminalAppLocalTests
settings->_ValidateNoDuplicateProfiles();
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(1), settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(4), settings->_profiles.Size());
VERIFY_ARE_EQUAL(L"profile0", settings->_profiles.GetAt(0).Name());
@ -490,10 +490,10 @@ namespace TerminalAppLocalTests
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(3u, settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.at(1));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::UnknownColorScheme, settings->_warnings.at(2));
VERIFY_ARE_EQUAL(3u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.GetAt(1));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::UnknownColorScheme, settings->_warnings.GetAt(2));
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_profiles.GetAt(0).Guid());
@ -905,7 +905,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(settings->_profiles.GetAt(1).HasGuid());
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(2u, settings->_profiles.Size());
VERIFY_IS_TRUE(settings->_profiles.GetAt(0).HasGuid());
VERIFY_IS_TRUE(settings->_profiles.GetAt(1).HasGuid());
@ -956,7 +956,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"profile1", settings->_profiles.GetAt(3).Name());
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(4u, settings->_profiles.Size());
VERIFY_IS_TRUE(settings->_profiles.GetAt(0).HasGuid());
VERIFY_IS_TRUE(settings->_profiles.GetAt(1).HasGuid());
@ -1057,7 +1057,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"Ubuntu", settings->_profiles.GetAt(3).Name());
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(4u, settings->_profiles.Size());
VERIFY_IS_TRUE(settings->_profiles.GetAt(0).HasGuid());
VERIFY_IS_TRUE(settings->_profiles.GetAt(1).HasGuid());
@ -1280,9 +1280,9 @@ namespace TerminalAppLocalTests
{
settings->_RemoveHiddenProfiles();
}
catch (const ::TerminalApp::SettingsException& ex)
catch (const winrt::TerminalApp::implementation::SettingsException& ex)
{
VERIFY_IS_TRUE(ex.Error() == ::TerminalApp::SettingsLoadErrors::AllProfilesHidden);
VERIFY_IS_TRUE(ex.Error() == winrt::TerminalApp::SettingsLoadErrors::AllProfilesHidden);
caughtExpectedException = true;
}
VERIFY_IS_TRUE(caughtExpectedException);
@ -1337,8 +1337,8 @@ namespace TerminalAppLocalTests
settings->_ValidateAllSchemesExist();
VERIFY_ARE_EQUAL(1u, settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::UnknownColorScheme, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(1u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::UnknownColorScheme, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
VERIFY_ARE_EQUAL(2u, settings->_globals->GetColorSchemes().Size());
@ -2179,7 +2179,7 @@ namespace TerminalAppLocalTests
auto settings = implementation::CascadiaSettings::FromJson(settingsJsonObj);
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(2u, settings->_warnings.size());
VERIFY_ARE_EQUAL(2u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(2u, settings->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), settings->_profiles.GetAt(0).Guid());
try
@ -2291,17 +2291,17 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings->_globals->_keybindings->_keyShortcuts.size());
VERIFY_ARE_EQUAL(3u, settings->_globals->_keybindingsWarnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord, settings->_globals->_keybindingsWarnings.at(0));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(1));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(2));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord, settings->_globals->_keybindingsWarnings.at(0));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(1));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(2));
settings->_ValidateKeybindings();
VERIFY_ARE_EQUAL(4u, settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord, settings->_warnings.at(1));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(2));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(3));
VERIFY_ARE_EQUAL(4u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord, settings->_warnings.GetAt(1));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(2));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(3));
}
void SettingsTests::ValidateExecuteCommandlineWarning()
@ -2338,17 +2338,17 @@ namespace TerminalAppLocalTests
L"warning:%d", warning));
}
VERIFY_ARE_EQUAL(3u, settings->_globals->_keybindingsWarnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(0));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(1));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(2));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(0));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(1));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(2));
settings->_ValidateKeybindings();
VERIFY_ARE_EQUAL(4u, settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(1));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(2));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(3));
VERIFY_ARE_EQUAL(4u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(1));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(2));
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(3));
}
void SettingsTests::ValidateLegacyGlobalsWarning()
@ -2376,15 +2376,15 @@ namespace TerminalAppLocalTests
settings->LayerJson(settings->_defaultSettings);
settings->_ValidateNoGlobalsKey();
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
// Now layer on the user's settings
settings->_ParseJsonString(badSettings, false);
settings->LayerJson(settings->_userSettings);
settings->_ValidateNoGlobalsKey();
VERIFY_ARE_EQUAL(1u, settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::LegacyGlobalsProperty, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(1u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(winrt::TerminalApp::SettingsLoadWarnings::LegacyGlobalsProperty, settings->_warnings.GetAt(0));
}
void SettingsTests::TestTrailingCommas()
@ -2668,7 +2668,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
@ -2696,7 +2696,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
{
@ -2799,7 +2799,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
@ -2827,7 +2827,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
{
@ -2932,7 +2932,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
@ -2961,7 +2961,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
{
@ -3075,7 +3075,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
@ -3083,7 +3083,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto rootCommandProj = expandedCommands.Lookup(L"Connect to ssh...");
@ -3184,7 +3184,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
@ -3192,7 +3192,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto grandparentCommandProj = expandedCommands.Lookup(L"grandparent");
@ -3324,7 +3324,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
@ -3332,7 +3332,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
@ -3478,7 +3478,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
@ -3486,7 +3486,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto rootCommandProj = expandedCommands.Lookup(L"New Tab With Profile...");
@ -3592,7 +3592,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
@ -3600,7 +3600,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto rootCommandProj = expandedCommands.Lookup(L"New Pane...");
@ -3754,14 +3754,14 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
settings->_ValidateSettings();
_logCommandNames(commands);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
// Because the "parent" command didn't have a name, it couldn't be
// placed into the list of commands. It and it's children are just
@ -3831,14 +3831,14 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
settings->_ValidateSettings();
_logCommandNames(commands);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, commands.Size());
Log::Comment(L"Layer second bit of json, to unbind the original command.");
@ -3847,7 +3847,7 @@ namespace TerminalAppLocalTests
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
_logCommandNames(commands);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(0u, commands.Size());
}
@ -3914,14 +3914,14 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
auto commands = settings->_globals->GetCommands();
settings->_ValidateSettings();
_logCommandNames(commands);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, commands.Size());
{
@ -3941,7 +3941,7 @@ namespace TerminalAppLocalTests
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
_logCommandNames(commands);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, commands.Size());
{
@ -4010,7 +4010,7 @@ namespace TerminalAppLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
@ -4038,7 +4038,7 @@ namespace TerminalAppLocalTests
auto expandedCommands = implementation::TerminalPage::_ExpandCommands(commands, settings->Profiles().GetView(), settings->_globals->GetColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings->_warnings.size());
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
// Yes, this test is testing splitPane with profiles named after each

View file

@ -105,7 +105,7 @@ namespace winrt::TerminalApp::implementation
{ UnboundKey, ShortcutAction::Invalid },
};
using ParseResult = std::tuple<IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
using ParseResult = std::tuple<IActionArgs, std::vector<TerminalApp::SettingsLoadWarnings>>;
using ParseActionFunction = std::function<ParseResult(const Json::Value&)>;
// This is a map of ShortcutAction->function<IActionArgs(Json::Value)>. It holds
@ -169,7 +169,7 @@ namespace winrt::TerminalApp::implementation
// - a deserialized ActionAndArgs corresponding to the values in json, or
// null if we failed to deserialize an action.
winrt::com_ptr<ActionAndArgs> ActionAndArgs::FromJson(const Json::Value& json,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
std::vector<TerminalApp::SettingsLoadWarnings>& warnings)
{
// Invalid is our placeholder that the action was not parsed.
ShortcutAction action = ShortcutAction::Invalid;
@ -208,7 +208,7 @@ namespace winrt::TerminalApp::implementation
// does, we'll try to deserialize any "args" that were provided with
// the binding.
IActionArgs args{ nullptr };
std::vector<::TerminalApp::SettingsLoadWarnings> parseWarnings;
std::vector<TerminalApp::SettingsLoadWarnings> parseWarnings;
const auto deserializersIter = argParsers.find(action);
if (deserializersIter != argParsers.end())
{

View file

@ -9,7 +9,7 @@ namespace winrt::TerminalApp::implementation
{
static const std::map<std::string_view, ShortcutAction, std::less<>> ActionKeyNamesMap;
static winrt::com_ptr<ActionAndArgs> FromJson(const Json::Value& json,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
std::vector<TerminalApp::SettingsLoadWarnings>& warnings);
ActionAndArgs() = default;
hstring GenerateName() const;

View file

@ -39,7 +39,7 @@
namespace winrt::TerminalApp::implementation
{
using namespace ::TerminalApp;
using FromJsonResult = std::tuple<winrt::TerminalApp::IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
using FromJsonResult = std::tuple<winrt::TerminalApp::IActionArgs, std::vector<TerminalApp::SettingsLoadWarnings>>;
struct ActionEventArgs : public ActionEventArgsT<ActionEventArgs>
{
@ -202,7 +202,7 @@ namespace winrt::TerminalApp::implementation
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
if (args->_Direction == TerminalApp::Direction::None)
{
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
return { nullptr, { TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
}
else
{
@ -237,7 +237,7 @@ namespace winrt::TerminalApp::implementation
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
if (args->_Direction == TerminalApp::Direction::None)
{
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
return { nullptr, { TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
}
else
{
@ -299,7 +299,7 @@ namespace winrt::TerminalApp::implementation
JsonUtils::GetValueForKey(json, InputKey, args->_Input);
if (args->_Input.empty())
{
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
return { nullptr, { TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
}
return { *args, {} };
}
@ -395,7 +395,7 @@ namespace winrt::TerminalApp::implementation
JsonUtils::GetValueForKey(json, NameKey, args->_SchemeName);
if (args->_SchemeName.empty())
{
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
return { nullptr, { TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
}
return { *args, {} };
}
@ -486,7 +486,7 @@ namespace winrt::TerminalApp::implementation
JsonUtils::GetValueForKey(json, CommandlineKey, args->_Commandline);
if (args->_Commandline.empty())
{
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
return { nullptr, { TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
}
return { *args, {} };
}

View file

@ -53,7 +53,7 @@ namespace winrt::TerminalApp::implementation
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::TerminalControl::KeyModifiers modifiers);
// Defined in AppKeyBindingsSerialization.cpp
std::vector<::TerminalApp::SettingsLoadWarnings> LayerJson(const Json::Value& json);
std::vector<TerminalApp::SettingsLoadWarnings> LayerJson(const Json::Value& json);
Json::Value ToJson();
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);

View file

@ -93,13 +93,13 @@ Json::Value winrt::TerminalApp::implementation::AppKeyBindings::ToJson()
// `"unbound"`, then we'll clear the keybinding from the existing keybindings.
// Arguments:
// - json: an array of Json::Value's to deserialize into our _keyShortcuts mapping.
std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::Value& json)
std::vector<SettingsLoadWarnings> winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::Value& json)
{
// It's possible that the user provided keybindings have some warnings in
// them - problems that we should alert the user to, but we can recover
// from. Most of these warnings cannot be detected later in the Validate
// settings phase, so we'll collect them now.
std::vector<::TerminalApp::SettingsLoadWarnings> warnings;
std::vector<SettingsLoadWarnings> warnings;
for (const auto& value : json)
{
@ -121,7 +121,7 @@ std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementat
// TODO: GH#1334 - remove this check.
if (keys.isArray() && keys.size() > 1)
{
warnings.push_back(::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord);
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
}
if (!validString && !validArray)

View file

@ -29,7 +29,7 @@ static const winrt::hstring StartupTaskName = L"StartTerminalOnLoginTask";
// !!! 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<std::wstring_view, static_cast<uint32_t>(winrt::TerminalApp::SettingsLoadWarnings::WARNINGS_SIZE)> settingsLoadWarningsLabels {
USES_RESOURCE(L"MissingDefaultProfileText"),
USES_RESOURCE(L"DuplicateProfileText"),
USES_RESOURCE(L"UnknownColorSchemeText"),
@ -41,7 +41,7 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWar
USES_RESOURCE(L"LegacyGlobalsProperty"),
USES_RESOURCE(L"FailedToParseCommandJson")
};
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels {
static const std::array<std::wstring_view, static_cast<uint32_t>(winrt::TerminalApp::SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels {
USES_RESOURCE(L"NoProfilesText"),
USES_RESOURCE(L"AllProfilesHiddenText")
};
@ -76,7 +76,7 @@ static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_vi
// - warning: the SettingsLoadWarnings value to get the localized text for.
// Return Value:
// - localized text for the given warning
static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warning)
static winrt::hstring _GetWarningText(winrt::TerminalApp::SettingsLoadWarnings warning)
{
return _GetMessageText(static_cast<uint32_t>(warning), settingsLoadWarningsLabels);
}
@ -89,7 +89,7 @@ static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warnin
// - error: the SettingsLoadErrors value to get the localized text for.
// Return Value:
// - localized text for the given error
static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error)
static winrt::hstring _GetErrorText(winrt::TerminalApp::SettingsLoadErrors error)
{
return _GetMessageText(static_cast<uint32_t>(error), settingsLoadErrorsLabels);
}
@ -411,8 +411,7 @@ namespace winrt::TerminalApp::implementation
// Make sure the lines of text wrap
warningsTextBlock.TextWrapping(TextWrapping::Wrap);
const auto settingsImpl{ winrt::get_self<implementation::CascadiaSettings>(_settings) };
const auto& warnings = settingsImpl->GetWarnings();
const auto warnings = _settings.Warnings();
for (const auto& warning : warnings)
{
// Try looking up the warning message key for each warning.
@ -631,9 +630,18 @@ namespace winrt::TerminalApp::implementation
auto newSettings = _isUwp ? CascadiaSettings::LoadUniversal() : CascadiaSettings::LoadAll();
_settings = newSettings;
const auto settingsImpl{ winrt::get_self<implementation::CascadiaSettings>(_settings) };
const auto& warnings = settingsImpl->GetWarnings();
hr = warnings.size() == 0 ? S_OK : S_FALSE;
if (_settings.GetLoadingError())
{
_settingsLoadExceptionText = _GetErrorText(_settings.GetLoadingError().Value());
return E_INVALIDARG;
}
else if (!_settings.GetSerializationErrorMessage().empty())
{
_settingsLoadExceptionText = _settings.GetSerializationErrorMessage();
return E_INVALIDARG;
}
hr = _settings.Warnings().Size() == 0 ? S_OK : S_FALSE;
}
catch (const winrt::hresult_error& e)
{
@ -641,17 +649,6 @@ namespace winrt::TerminalApp::implementation
_settingsLoadExceptionText = e.message();
LOG_HR(hr);
}
catch (const ::TerminalApp::SettingsException& ex)
{
hr = E_INVALIDARG;
_settingsLoadExceptionText = _GetErrorText(ex.Error());
}
catch (const ::TerminalApp::SettingsTypedDeserializationException& e)
{
hr = E_INVALIDARG;
std::string_view what{ e.what() };
_settingsLoadExceptionText = til::u8u16(what);
}
catch (...)
{
hr = wil::ResultFromCaughtException();

View file

@ -46,7 +46,9 @@ CascadiaSettings::CascadiaSettings() :
// - addDynamicProfiles: if true, we'll add the built-in DPGs.
CascadiaSettings::CascadiaSettings(const bool addDynamicProfiles) :
_globals{ winrt::make_self<implementation::GlobalAppSettings>() },
_profiles{ winrt::single_threaded_observable_vector<TerminalApp::Profile>() }
_profiles{ winrt::single_threaded_observable_vector<TerminalApp::Profile>() },
_warnings{ winrt::single_threaded_vector<SettingsLoadWarnings>() },
_deserializationErrorMessage{ L"" }
{
if (addDynamicProfiles)
{
@ -119,9 +121,19 @@ winrt::TerminalApp::GlobalAppSettings CascadiaSettings::GlobalSettings()
// knew were bad when we called `_ValidateSettings` last.
// Return Value:
// - a reference to our list of warnings.
std::vector<TerminalApp::SettingsLoadWarnings>& CascadiaSettings::GetWarnings()
IVectorView<winrt::TerminalApp::SettingsLoadWarnings> CascadiaSettings::Warnings()
{
return _warnings;
return _warnings.GetView();
}
winrt::Windows::Foundation::IReference<winrt::TerminalApp::SettingsLoadErrors> CascadiaSettings::GetLoadingError()
{
return _loadError;
}
winrt::hstring CascadiaSettings::GetSerializationErrorMessage()
{
return _deserializationErrorMessage;
}
// Method Description:
@ -136,7 +148,7 @@ std::vector<TerminalApp::SettingsLoadWarnings>& CascadiaSettings::GetWarnings()
// - <none>
void CascadiaSettings::_ValidateSettings()
{
_warnings.clear();
_warnings.Clear();
// Make sure to check that profiles exists at all first and foremost:
_ValidateProfilesExist();
@ -198,7 +210,7 @@ void CascadiaSettings::_ValidateProfilesExist()
// We can't add the warning to the list of warnings here, because this
// object is not going to be returned at any point.
throw ::TerminalApp::SettingsException(::TerminalApp::SettingsLoadErrors::NoProfiles);
throw SettingsException(TerminalApp::SettingsLoadErrors::NoProfiles);
}
}
@ -252,7 +264,7 @@ void CascadiaSettings::_ValidateDefaultProfileExists()
if (nullDefaultProfile || defaultProfileNotInProfiles)
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile);
_warnings.Append(TerminalApp::SettingsLoadWarnings::MissingDefaultProfile);
// Use the first profile as the new default
// _temporarily_ set the default profile to the first profile. Because
@ -295,7 +307,7 @@ void CascadiaSettings::_ValidateNoDuplicateProfiles()
if (foundDupe)
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::DuplicateProfile);
_warnings.Append(TerminalApp::SettingsLoadWarnings::DuplicateProfile);
}
}
@ -384,7 +396,7 @@ void CascadiaSettings::_RemoveHiddenProfiles()
{
// Throw an exception. This is an invalid state, and we want the app to
// be able to gracefully use the default settings.
throw ::TerminalApp::SettingsException(::TerminalApp::SettingsLoadErrors::AllProfilesHidden);
throw SettingsException(TerminalApp::SettingsLoadErrors::AllProfilesHidden);
}
}
@ -413,7 +425,7 @@ void CascadiaSettings::_ValidateAllSchemesExist()
if (foundInvalidScheme)
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::UnknownColorScheme);
_warnings.Append(SettingsLoadWarnings::UnknownColorScheme);
}
}
@ -468,12 +480,12 @@ void CascadiaSettings::_ValidateMediaResources()
if (invalidBackground)
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::InvalidBackgroundImage);
_warnings.Append(TerminalApp::SettingsLoadWarnings::InvalidBackgroundImage);
}
if (invalidIcon)
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::InvalidIcon);
_warnings.Append(TerminalApp::SettingsLoadWarnings::InvalidIcon);
}
}
@ -659,8 +671,11 @@ void CascadiaSettings::_ValidateKeybindings()
if (!keybindingWarnings.empty())
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning);
_warnings.insert(_warnings.end(), keybindingWarnings.begin(), keybindingWarnings.end());
_warnings.Append(TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning);
for (auto warning : keybindingWarnings)
{
_warnings.Append(warning);
}
}
}
@ -679,7 +694,7 @@ void CascadiaSettings::_ValidateNoGlobalsKey()
{
if (auto oldGlobalsProperty{ _userSettings["globals"] })
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::LegacyGlobalsProperty);
_warnings.Append(TerminalApp::SettingsLoadWarnings::LegacyGlobalsProperty);
}
}

View file

@ -84,14 +84,18 @@ namespace winrt::TerminalApp::implementation
TerminalApp::Profile FindProfile(guid profileGuid) const noexcept;
TerminalApp::ColorScheme GetColorSchemeForProfile(const guid profileGuid) const;
std::vector<::TerminalApp::SettingsLoadWarnings>& GetWarnings();
Windows::Foundation::Collections::IVectorView<SettingsLoadWarnings> Warnings();
Windows::Foundation::IReference<SettingsLoadErrors> GetLoadingError();
hstring GetSerializationErrorMessage();
bool ApplyColorScheme(Microsoft::Terminal::TerminalControl::IControlSettings settings, hstring schemeName);
private:
com_ptr<GlobalAppSettings> _globals;
Windows::Foundation::Collections::IObservableVector<TerminalApp::Profile> _profiles;
std::vector<::TerminalApp::SettingsLoadWarnings> _warnings;
Windows::Foundation::Collections::IVector<TerminalApp::SettingsLoadWarnings> _warnings;
Windows::Foundation::IReference<SettingsLoadErrors> _loadError;
hstring _deserializationErrorMessage;
std::vector<std::unique_ptr<::TerminalApp::IDynamicProfileGenerator>> _profileGenerators;

View file

@ -3,6 +3,7 @@
import "GlobalAppSettings.idl";
import "Profile.idl";
import "TerminalWarnings.idl";
namespace TerminalApp
{
@ -17,6 +18,10 @@ namespace TerminalApp
AppKeyBindings Keybindings { get; };
Windows.Foundation.Collections.IVectorView<SettingsLoadWarnings> Warnings { get; };
Windows.Foundation.IReference<SettingsLoadErrors> GetLoadingError { get; };
String GetSerializationErrorMessage { get; };
Profile FindProfile(Guid profileGuid);
ColorScheme GetColorSchemeForProfile(Guid profileGuid);
}

View file

@ -18,7 +18,6 @@
// Both defaults.h and userDefaults.h are generated at build time into the
// "Generated Files" directory.
using namespace ::TerminalApp;
using namespace winrt::TerminalApp::implementation;
using namespace ::Microsoft::Console;
@ -103,145 +102,161 @@ static void _CatchRethrowSerializationExceptionWithLocationInfo(std::string_view
// - a unique_ptr containing a new CascadiaSettings object.
winrt::TerminalApp::CascadiaSettings CascadiaSettings::LoadAll()
{
auto settings = LoadDefaults();
auto resultPtr = winrt::get_self<CascadiaSettings>(settings);
// GH 3588, we need this below to know if the user chose something that wasn't our default.
// Collect it up here in case it gets modified by any of the other layers between now and when
// the user's preferences are loaded and layered.
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
std::optional<std::string> fileData = _ReadUserSettings();
const bool foundFile = fileData.has_value();
// Make sure the file isn't totally empty. If it is, we'll treat the file
// like it doesn't exist at all.
const bool fileHasData = foundFile && !fileData.value().empty();
bool needToWriteFile = false;
if (fileHasData)
{
resultPtr->_ParseJsonString(fileData.value(), false);
}
// Load profiles from dynamic profile generators. _userSettings should be
// created by now, because we're going to check in there for any generators
// that should be disabled (if the user had any settings.)
resultPtr->_LoadDynamicProfiles();
if (!fileHasData)
{
// We didn't find the user settings. We'll need to create a file
// to use as the user defaults.
// For now, just parse our user settings template as their user settings.
auto userSettings{ resultPtr->_ApplyFirstRunChangesToSettingsTemplate(UserSettingsJson) };
resultPtr->_ParseJsonString(userSettings, false);
needToWriteFile = true;
}
try
{
// See microsoft/terminal#2325: find the defaultSettings from the user's
// settings. Layer those settings upon all the existing profiles we have
// (defaults and dynamic profiles). We'll also set
// _userDefaultProfileSettings here. When we LayerJson below to apply the
// user settings, we'll make sure to use these defaultSettings _before_ any
// profiles the user might have.
resultPtr->_ApplyDefaultsFromUserSettings();
auto settings = LoadDefaults();
auto resultPtr = winrt::get_self<CascadiaSettings>(settings);
// Apply the user's settings
resultPtr->LayerJson(resultPtr->_userSettings);
}
catch (...)
{
_CatchRethrowSerializationExceptionWithLocationInfo(resultPtr->_userSettingsString);
}
// GH 3588, we need this below to know if the user chose something that wasn't our default.
// Collect it up here in case it gets modified by any of the other layers between now and when
// the user's preferences are loaded and layered.
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
// After layering the user settings, check if there are any new profiles
// that need to be inserted into their user settings file.
needToWriteFile = resultPtr->_AppendDynamicProfilesToUserSettings() || needToWriteFile;
std::optional<std::string> fileData = _ReadUserSettings();
const bool foundFile = fileData.has_value();
if (needToWriteFile)
{
// For safety's sake, we need to re-parse the JSON document to ensure that
// all future patches are applied with updated object offsets.
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
}
// Make sure there's a $schema at the top of the file.
needToWriteFile = resultPtr->_PrependSchemaDirective() || needToWriteFile;
// TODO:GH#2721 If powershell core is installed, we need to set that to the
// default profile, but only when the settings file was newly created. We'll
// re-write the segment of the user settings for "default profile" to have
// the powershell core GUID instead.
// If we created the file, or found new dynamic profiles, write the user
// settings string back to the file.
if (needToWriteFile)
{
// If AppendDynamicProfilesToUserSettings (or the pwsh check above)
// changed the file, then our local settings JSON is no longer accurate.
// We should re-parse, but not re-layer
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
_WriteSettings(resultPtr->_userSettingsString);
}
// If this throws, the app will catch it and use the default settings
resultPtr->_ValidateSettings();
// GH 3855 - Gathering Data on custom profiles to inform better defaults
// Do it after everything else so it won't happen unless validation passed.
// Also, avoid processing unless someone's listening for measures. The keybindings work, at least,
// is a lot of computation we can skip if no one cares.
if (TraceLoggingProviderEnabled(g_hTerminalAppProvider, 0, MICROSOFT_KEYWORD_MEASURES))
{
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
// Compare to the defaults.json one that we set on install.
// If it's different, log what the user chose.
if (hardcodedDefaultGuid != guid)
// Make sure the file isn't totally empty. If it is, we'll treat the file
// like it doesn't exist at all.
const bool fileHasData = foundFile && !fileData.value().empty();
bool needToWriteFile = false;
if (fileHasData)
{
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"CustomDefaultProfile",
TraceLoggingDescription("Event emitted when user has chosen a different default profile than hardcoded one on load/reload"),
TraceLoggingGuid(guid, "DefaultProfile", "ID of user-chosen default profile"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
resultPtr->_ParseJsonString(fileData.value(), false);
}
// If the user had keybinding settings preferences, we want to learn from them to make better defaults
auto userKeybindings = resultPtr->_userSettings[JsonKey(KeybindingsKey)];
if (!userKeybindings.empty())
// Load profiles from dynamic profile generators. _userSettings should be
// created by now, because we're going to check in there for any generators
// that should be disabled (if the user had any settings.)
resultPtr->_LoadDynamicProfiles();
if (!fileHasData)
{
// If there are custom key bindings, let's understand what they are because maybe the defaults aren't good enough
// Run it through the object so we can parse it apart and then only serialize the fields we're interested in
// and avoid extraneous data.
auto akb = winrt::make_self<AppKeyBindings>();
akb->LayerJson(userKeybindings);
auto value = akb->ToJson();
// Reserialize the keybindings
Json::StreamWriterBuilder wbuilder;
// Use 4 spaces to indent instead of \t
wbuilder.settings_["indentation"] = " ";
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
const auto keybindingsString = Json::writeString(wbuilder, value);
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"CustomKeybindings",
TraceLoggingDescription("Event emitted when custom keybindings are identified on load/reload"),
TraceLoggingUtf8String(keybindingsString.c_str(), "Keybindings", "Keybindings as JSON"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
// We didn't find the user settings. We'll need to create a file
// to use as the user defaults.
// For now, just parse our user settings template as their user settings.
auto userSettings{ resultPtr->_ApplyFirstRunChangesToSettingsTemplate(UserSettingsJson) };
resultPtr->_ParseJsonString(userSettings, false);
needToWriteFile = true;
}
}
return *resultPtr;
try
{
// See microsoft/terminal#2325: find the defaultSettings from the user's
// settings. Layer those settings upon all the existing profiles we have
// (defaults and dynamic profiles). We'll also set
// _userDefaultProfileSettings here. When we LayerJson below to apply the
// user settings, we'll make sure to use these defaultSettings _before_ any
// profiles the user might have.
resultPtr->_ApplyDefaultsFromUserSettings();
// Apply the user's settings
resultPtr->LayerJson(resultPtr->_userSettings);
}
catch (...)
{
_CatchRethrowSerializationExceptionWithLocationInfo(resultPtr->_userSettingsString);
}
// After layering the user settings, check if there are any new profiles
// that need to be inserted into their user settings file.
needToWriteFile = resultPtr->_AppendDynamicProfilesToUserSettings() || needToWriteFile;
if (needToWriteFile)
{
// For safety's sake, we need to re-parse the JSON document to ensure that
// all future patches are applied with updated object offsets.
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
}
// Make sure there's a $schema at the top of the file.
needToWriteFile = resultPtr->_PrependSchemaDirective() || needToWriteFile;
// TODO:GH#2721 If powershell core is installed, we need to set that to the
// default profile, but only when the settings file was newly created. We'll
// re-write the segment of the user settings for "default profile" to have
// the powershell core GUID instead.
// If we created the file, or found new dynamic profiles, write the user
// settings string back to the file.
if (needToWriteFile)
{
// If AppendDynamicProfilesToUserSettings (or the pwsh check above)
// changed the file, then our local settings JSON is no longer accurate.
// We should re-parse, but not re-layer
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
_WriteSettings(resultPtr->_userSettingsString);
}
// If this throws, the app will catch it and use the default settings
resultPtr->_ValidateSettings();
// GH 3855 - Gathering Data on custom profiles to inform better defaults
// Do it after everything else so it won't happen unless validation passed.
// Also, avoid processing unless someone's listening for measures. The keybindings work, at least,
// is a lot of computation we can skip if no one cares.
if (TraceLoggingProviderEnabled(g_hTerminalAppProvider, 0, MICROSOFT_KEYWORD_MEASURES))
{
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
// Compare to the defaults.json one that we set on install.
// If it's different, log what the user chose.
if (hardcodedDefaultGuid != guid)
{
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"CustomDefaultProfile",
TraceLoggingDescription("Event emitted when user has chosen a different default profile than hardcoded one on load/reload"),
TraceLoggingGuid(guid, "DefaultProfile", "ID of user-chosen default profile"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
// If the user had keybinding settings preferences, we want to learn from them to make better defaults
auto userKeybindings = resultPtr->_userSettings[JsonKey(KeybindingsKey)];
if (!userKeybindings.empty())
{
// If there are custom key bindings, let's understand what they are because maybe the defaults aren't good enough
// Run it through the object so we can parse it apart and then only serialize the fields we're interested in
// and avoid extraneous data.
auto akb = winrt::make_self<AppKeyBindings>();
akb->LayerJson(userKeybindings);
auto value = akb->ToJson();
// Reserialize the keybindings
Json::StreamWriterBuilder wbuilder;
// Use 4 spaces to indent instead of \t
wbuilder.settings_["indentation"] = " ";
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
const auto keybindingsString = Json::writeString(wbuilder, value);
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"CustomKeybindings",
TraceLoggingDescription("Event emitted when custom keybindings are identified on load/reload"),
TraceLoggingUtf8String(keybindingsString.c_str(), "Keybindings", "Keybindings as JSON"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
}
return *resultPtr;
}
catch (const SettingsException& ex)
{
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
settings->_loadError = ex.Error();
return *settings;
}
catch (const SettingsTypedDeserializationException& e)
{
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
std::string_view what{ e.what() };
settings->_deserializationErrorMessage = til::u8u16(what);
return *settings;
}
}
// Function Description:
@ -255,16 +270,32 @@ winrt::TerminalApp::CascadiaSettings CascadiaSettings::LoadUniversal()
// We're going to do this ourselves because we want to exclude almost everything
// from the special Universal-for-developers configuration
// Create settings and get the universal defaults loaded up.
auto resultPtr = winrt::make_self<CascadiaSettings>();
resultPtr->_ParseJsonString(DefaultUniversalJson, true);
resultPtr->LayerJson(resultPtr->_defaultSettings);
try
{
// Create settings and get the universal defaults loaded up.
auto resultPtr = winrt::make_self<CascadiaSettings>();
resultPtr->_ParseJsonString(DefaultUniversalJson, true);
resultPtr->LayerJson(resultPtr->_defaultSettings);
// Now validate.
// If this throws, the app will catch it and use the default settings
resultPtr->_ValidateSettings();
// Now validate.
// If this throws, the app will catch it and use the default settings
resultPtr->_ValidateSettings();
return *resultPtr;
return *resultPtr;
}
catch (const SettingsException& ex)
{
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
settings->_loadError = ex.Error();
return *settings;
}
catch (const SettingsTypedDeserializationException& e)
{
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
std::string_view what{ e.what() };
settings->_deserializationErrorMessage = til::u8u16(what);
return *settings;
}
}
// Function Description:
@ -276,7 +307,7 @@ winrt::TerminalApp::CascadiaSettings CascadiaSettings::LoadUniversal()
// - a unique_ptr to a CascadiaSettings with the settings from defaults.json
winrt::TerminalApp::CascadiaSettings CascadiaSettings::LoadDefaults()
{
auto resultPtr = winrt::make_self<CascadiaSettings>();
auto resultPtr{ winrt::make_self<CascadiaSettings>() };
// We already have the defaults in memory, because we stamp them into a
// header as part of the build process. We don't need to bother with reading

View file

@ -187,7 +187,7 @@ namespace winrt::TerminalApp::implementation
// Return Value:
// - the newly constructed Command object.
winrt::com_ptr<Command> Command::FromJson(const Json::Value& json,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
std::vector<SettingsLoadWarnings>& warnings)
{
auto result = winrt::make_self<Command>();
@ -280,10 +280,10 @@ namespace winrt::TerminalApp::implementation
// - json: A Json::Value containing an array of serialized commands
// Return Value:
// - A vector containing any warnings detected while parsing
std::vector<::TerminalApp::SettingsLoadWarnings> Command::LayerJson(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
const Json::Value& json)
std::vector<SettingsLoadWarnings> Command::LayerJson(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
const Json::Value& json)
{
std::vector<::TerminalApp::SettingsLoadWarnings> warnings;
std::vector<SettingsLoadWarnings> warnings;
for (const auto& value : json)
{
@ -355,7 +355,7 @@ namespace winrt::TerminalApp::implementation
void Command::ExpandCommands(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
Windows::Foundation::Collections::IVectorView<winrt::TerminalApp::Profile> profiles,
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
std::vector<SettingsLoadWarnings>& warnings)
{
std::vector<winrt::hstring> commandsToRemove;
std::vector<winrt::TerminalApp::Command> commandsToAdd;
@ -411,7 +411,7 @@ namespace winrt::TerminalApp::implementation
std::vector<winrt::TerminalApp::Command> Command::_expandCommand(Command* const expandable,
Windows::Foundation::Collections::IVectorView<winrt::TerminalApp::Profile> profiles,
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
std::vector<SettingsLoadWarnings>& warnings)
{
std::vector<winrt::TerminalApp::Command> newCommands;
@ -438,7 +438,7 @@ namespace winrt::TerminalApp::implementation
const auto actualDataEnd = newJsonString.data() + newJsonString.size();
if (!reader->parse(actualDataStart, actualDataEnd, &newJsonValue, &errs))
{
warnings.push_back(::TerminalApp::SettingsLoadWarnings::FailedToParseCommandJson);
warnings.push_back(SettingsLoadWarnings::FailedToParseCommandJson);
// If we encounter a re-parsing error, just stop processing the rest of the commands.
return false;
}

View file

@ -38,15 +38,15 @@ namespace winrt::TerminalApp::implementation
Command();
static winrt::com_ptr<Command> FromJson(const Json::Value& json,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
std::vector<TerminalApp::SettingsLoadWarnings>& warnings);
static void ExpandCommands(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
Windows::Foundation::Collections::IVectorView<winrt::TerminalApp::Profile> profiles,
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
std::vector<TerminalApp::SettingsLoadWarnings>& warnings);
static std::vector<::TerminalApp::SettingsLoadWarnings> LayerJson(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
const Json::Value& json);
static std::vector<TerminalApp::SettingsLoadWarnings> LayerJson(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
const Json::Value& json);
bool HasNestedCommands();
Windows::Foundation::Collections::IMapView<winrt::hstring, TerminalApp::Command> NestedCommands();
@ -71,7 +71,7 @@ namespace winrt::TerminalApp::implementation
static std::vector<winrt::TerminalApp::Command> _expandCommand(Command* const expandable,
Windows::Foundation::Collections::IVectorView<winrt::TerminalApp::Profile> profiles,
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
std::vector<TerminalApp::SettingsLoadWarnings>& warnings);
friend class TerminalAppLocalTests::SettingsTests;
friend class TerminalAppLocalTests::CommandTests;
};

View file

@ -218,7 +218,7 @@ void GlobalAppSettings::AddColorScheme(const winrt::TerminalApp::ColorScheme& sc
// - <none>
// Return Value:
// - <none>
std::vector<TerminalApp::SettingsLoadWarnings> GlobalAppSettings::GetKeybindingsWarnings() const
std::vector<winrt::TerminalApp::SettingsLoadWarnings> GlobalAppSettings::GetKeybindingsWarnings() const
{
return _keybindingsWarnings;
}

View file

@ -45,7 +45,7 @@ namespace winrt::TerminalApp::implementation
void ApplyToSettings(const TerminalApp::TerminalSettings& settings) const noexcept;
std::vector<::TerminalApp::SettingsLoadWarnings> GetKeybindingsWarnings() const;
std::vector<TerminalApp::SettingsLoadWarnings> GetKeybindingsWarnings() const;
Windows::Foundation::Collections::IMapView<hstring, TerminalApp::Command> GetCommands() noexcept;
@ -84,7 +84,7 @@ namespace winrt::TerminalApp::implementation
guid _defaultProfile;
com_ptr<AppKeyBindings> _keybindings;
std::vector<::TerminalApp::SettingsLoadWarnings> _keybindingsWarnings;
std::vector<TerminalApp::SettingsLoadWarnings> _keybindingsWarnings;
Windows::Foundation::Collections::IMap<hstring, TerminalApp::ColorScheme> _colorSchemes;
Windows::Foundation::Collections::IMap<hstring, TerminalApp::Command> _commands;

View file

@ -122,7 +122,9 @@
<ClInclude Include="Utils.h" />
<ClInclude Include="DefaultProfileUtils.h" />
<ClInclude Include="TerminalSettingsSerializationHelpers.h" />
<ClInclude Include="TerminalWarnings.h" />
<ClInclude Include="TerminalWarnings.h">
<DependentUpon>TerminalWarnings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="IDynamicProfileGenerator.h" />
<ClInclude Include="PowershellCoreProfileGenerator.h" />
<ClInclude Include="WslDistroGenerator.h" />
@ -295,6 +297,7 @@
<Midl Include="Profile.idl" />
<Midl Include="GlobalAppSettings.idl" />
<Midl Include="CascadiaSettings.idl" />
<Midl Include="TerminalWarnings.idl" />
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>

View file

@ -2120,7 +2120,7 @@ namespace winrt::TerminalApp::implementation
IVectorView<winrt::TerminalApp::Profile> profiles,
IMapView<winrt::hstring, winrt::TerminalApp::ColorScheme> schemes)
{
std::vector<::TerminalApp::SettingsLoadWarnings> warnings;
std::vector<TerminalApp::SettingsLoadWarnings> warnings;
std::vector<winrt::TerminalApp::ColorScheme> sortedSchemes;
sortedSchemes.reserve(schemes.Size());

View file

@ -15,34 +15,8 @@ Author(s):
--*/
#pragma once
namespace TerminalApp
namespace winrt::TerminalApp::implementation
{
// SettingsLoadWarnings are scenarios where the settings contained
// information we knew was invalid, but we could recover from.
enum class SettingsLoadWarnings : uint32_t
{
MissingDefaultProfile = 0,
DuplicateProfile = 1,
UnknownColorScheme = 2,
InvalidBackgroundImage = 3,
InvalidIcon = 4,
AtLeastOneKeybindingWarning = 5,
TooManyKeysForChord = 6,
MissingRequiredParameter = 7,
LegacyGlobalsProperty = 8,
FailedToParseCommandJson = 9,
WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
};
// SettingsLoadWarnings are scenarios where the settings had invalid state
// that we could not recover from.
enum class SettingsLoadErrors : uint32_t
{
NoProfiles = 0,
AllProfilesHidden = 1,
ERRORS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
};
// This is a helper class to wrap up a SettingsLoadErrors into a proper
// exception type.
class SettingsException : public std::runtime_error

View file

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// Licensed under the MIT license.
#pragma once
namespace TerminalApp
{
// SettingsLoadWarnings are scenarios where the settings contained
// information we knew was invalid, but we could recover from.
enum SettingsLoadWarnings
{
MissingDefaultProfile = 0,
DuplicateProfile = 1,
UnknownColorScheme = 2,
InvalidBackgroundImage = 3,
InvalidIcon = 4,
AtLeastOneKeybindingWarning = 5,
TooManyKeysForChord = 6,
MissingRequiredParameter = 7,
LegacyGlobalsProperty = 8,
FailedToParseCommandJson = 9,
WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
};
// SettingsLoadWarnings are scenarios where the settings had invalid state
// that we could not recover from.
enum SettingsLoadErrors
{
NoProfiles = 0,
AllProfilesHidden = 1,
ERRORS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
};
}