terminal/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
Leonard Hecker fe26a6efc0
Fix loading of fragments that update multiple profiles (#11598)
The "updates" key is an alternative "guid" key for fragment profiles.
But SettingsLoader::_appendProfile stores and deduplicates profiles according
to their "guid" only. We need to modify the function to optionally store
profiles by their "updates" key as well, otherwise multiple fragment
profiles without "guid" might collide as they produce the same default GUID.

## PR Checklist
* [x] Closes #11597
* [x] I work here
* [ ] Tests added/passed
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

## Validation Steps Performed
* Unit tests pass ✔️
* Issue #11597 doesn't reproduce anymore ✔️
2021-10-27 01:43:10 +00:00

185 lines
8.6 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CascadiaSettings.h
Abstract:
- This class acts as the container for all app settings. It's composed of two
parts: Globals, which are app-wide settings, and Profiles, which contain
a set of settings that apply to a single instance of the terminal.
Also contains the logic for serializing and deserializing this object.
Author(s):
- Mike Griese - March 2019
--*/
#pragma once
#include "CascadiaSettings.g.h"
#include "GlobalAppSettings.h"
#include "Profile.h"
namespace winrt::Microsoft::Terminal::Settings::Model
{
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;
void clear();
};
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 MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content);
void FinalizeLayering();
bool DisableDeletedProfiles();
ParsedSettings inboxSettings;
ParsedSettings userSettings;
bool duplicateProfile = false;
private:
struct JsonSettings
{
Json::Value root;
const Json::Value& colorSchemes;
const Json::Value& profileDefaults;
const Json::Value& profilesList;
};
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;
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 _parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
static JsonSettings _parseJson(const std::string_view& content);
static winrt::com_ptr<implementation::Profile> _parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson);
void _appendProfile(winrt::com_ptr<Profile>&& profile, const winrt::guid& guid, ParsedSettings& settings);
static void _addParentProfile(const 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:
static Model::CascadiaSettings LoadDefaults();
static Model::CascadiaSettings LoadAll();
static Model::CascadiaSettings LoadUniversal();
static winrt::hstring SettingsPath();
static winrt::hstring DefaultSettingsPath();
static winrt::hstring ApplicationDisplayName();
static winrt::hstring ApplicationVersion();
static void ExportFile(winrt::hstring path, winrt::hstring content);
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 winrt::guid& guid) const noexcept;
Model::ColorScheme GetColorSchemeForProfile(const Model::Profile& profile) const;
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);
// 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;
static bool IsDefaultTerminalSet() noexcept;
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> DefaultTerminals() noexcept;
Model::DefaultTerminal CurrentDefaultTerminal() noexcept;
void CurrentDefaultTerminal(const Model::DefaultTerminal& terminal);
private:
static const std::filesystem::path& _settingsPath();
static std::wstring _normalizeCommandLine(LPCWSTR commandLine);
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
Model::Profile _getProfileForCommandLine(const winrt::hstring& commandLine) const;
void _refreshDefaultTerminals();
void _resolveDefaultProfile() const;
void _validateSettings();
void _validateAllSchemesExist();
void _validateMediaResources();
void _validateKeybindings() const;
void _validateColorSchemesInCommands() const;
bool _hasInvalidColorScheme(const Model::Command& command) const;
// user settings
winrt::com_ptr<implementation::GlobalAppSettings> _globals = winrt::make_self<implementation::GlobalAppSettings>();
winrt::com_ptr<implementation::Profile> _baseLayerProfile = winrt::make_self<implementation::Profile>();
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _allProfiles = winrt::single_threaded_observable_vector<Model::Profile>();
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _activeProfiles = winrt::single_threaded_observable_vector<Model::Profile>();
// load errors
winrt::Windows::Foundation::Collections::IVector<Model::SettingsLoadWarnings> _warnings = winrt::single_threaded_vector<Model::SettingsLoadWarnings>();
winrt::Windows::Foundation::IReference<Model::SettingsLoadErrors> _loadError;
winrt::hstring _deserializationErrorMessage;
// defterm
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> _defaultTerminals{ nullptr };
Model::DefaultTerminal _currentDefaultTerminal{ nullptr };
// GetProfileForArgs cache
mutable std::once_flag _commandLinesCacheOnce;
mutable std::vector<std::pair<std::wstring, Model::Profile>> _commandLinesCache;
};
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(CascadiaSettings);
}