terminal/src/cascadia/TerminalSettingsModel/ColorScheme.cpp
Leonard Hecker 168d28b036
Reduce usage of Json::Value throughout Terminal.Settings.Model (#11184)
This commit reduces the code surface that interacts with raw JSON data,
reducing code complexity and improving maintainability.
Files that needed to be changed drastically were additionally
cleaned up to remove any code cruft that has accrued over time.

In order to facility this the following changes were made:
* Move JSON handling from `CascadiaSettings` into `SettingsLoader`
  This allows us to use STL containers for data model instances.
  For instance profiles are now added to a hashmap for O(1) lookup.
* JSON parsing within `SettingsLoader` doesn't differentiate between user,
  inbox and fragment JSON data, reducing code complexity and size.
  It also centralizes common concerns, like profile deduplication and
  ensuring that all profiles are assigned a GUID.
* Direct JSON modification, like the insertion of dynamic profiles into
  settings.json were removed. This vastly reduces code complexity,
  but unfortunately removes support for comments in JSON on first start.
* `ColorScheme`s cannot be layered. As such its `LayerJson` API was replaced
  with `FromJson`, allowing us to remove JSON-based color scheme validation.
* `Profile`s used to test their wish to layer using `ShouldBeLayered`, which
  was replaced with a GUID-based hashmap lookup on previously parsed profiles.

Further changes were made as improvements upon the previous changes:
* Compact the JSON files embedded binary, saving 28kB
* Prevent double-initialization of the color table in `ColorScheme`
* Making `til::color` getters `constexpr`, allow better optimizations

The result is a reduction of:
* 48kB binary size for the Settings.Model.dll
* 5-10% startup duration
* 26% code for the `CascadiaSettings` class
* 1% overall code in this project

Furthermore this results in the following breaking changes:
* The long deprecated "globals" settings object will not be detected and no
  warning will be created during load.
* The initial creation of a new settings.json will not produce helpful comments.

Both cases are caused by the removal of manual JSON handling and the
move to representing the settings file with model objects instead

## PR Checklist
* [x] Closes #5276
* [x] Closes #7421
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed

* Out-of-box-experience is identical to before ✔️
  (Except for the settings.json file lacking comments.)
* Existing user settings load correctly ✔️
* New WSL instances are added to user settings ✔️
* New fragments are added to user settings ✔️
* All profiles are assigned GUIDs ✔️
2021-09-22 16:27:31 +00:00

150 lines
4.8 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ColorScheme.h"
#include "../../types/inc/Utils.hpp"
#include "../../types/inc/colorTable.hpp"
#include "Utils.h"
#include "JsonUtils.h"
#include "ColorScheme.g.cpp"
using namespace ::Microsoft::Console;
using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
using namespace winrt::Windows::UI;
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view ForegroundKey{ "foreground" };
static constexpr std::string_view BackgroundKey{ "background" };
static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" };
static constexpr std::string_view CursorColorKey{ "cursorColor" };
static constexpr std::array<std::string_view, 16> TableColors = {
"black",
"red",
"green",
"yellow",
"blue",
"purple",
"cyan",
"white",
"brightBlack",
"brightRed",
"brightGreen",
"brightYellow",
"brightBlue",
"brightPurple",
"brightCyan",
"brightWhite"
};
ColorScheme::ColorScheme() noexcept :
ColorScheme{ winrt::hstring{} }
{
}
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
{
auto scheme{ winrt::make_self<ColorScheme>() };
scheme->_Name = _Name;
scheme->_Foreground = _Foreground;
scheme->_Background = _Background;
scheme->_SelectionBackground = _SelectionBackground;
scheme->_CursorColor = _CursorColor;
scheme->_table = _table;
return scheme;
}
// Method Description:
// - Create a new instance of this class from a serialized JsonObject.
// Arguments:
// - json: an object which should be a serialization of a ColorScheme object.
// Return Value:
// - Returns nullptr for invalid JSON.
winrt::com_ptr<ColorScheme> ColorScheme::FromJson(const Json::Value& json)
{
auto result = winrt::make_self<ColorScheme>(uninitialized_t{});
return result->_layerJson(json) ? result : nullptr;
}
// 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
// given object, we'll parse them and replace our settings with values from
// the new json object. Properties that _aren't_ in the json object will _not_
// be replaced.
// Arguments:
// - json: an object which should be a full serialization of a ColorScheme object.
// Return Value:
// - Returns true if the given JSON was valid.
bool ColorScheme::_layerJson(const Json::Value& json)
{
// 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)
{
isValid &= JsonUtils::GetValueForKey(json, til::at(TableColors, i), til::at(_table, i));
}
return isValid;
}
// Method Description:
// - Create a new serialized JsonObject from an instance of this class
// Arguments:
// - <none>
// Return Value:
// - the JsonObject representing this instance
Json::Value ColorScheme::ToJson() const
{
Json::Value json{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(json, NameKey, _Name);
JsonUtils::SetValueForKey(json, ForegroundKey, _Foreground);
JsonUtils::SetValueForKey(json, BackgroundKey, _Background);
JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground);
JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor);
for (unsigned int i = 0; i < TableColors.size(); ++i)
{
JsonUtils::SetValueForKey(json, til::at(TableColors, i), til::at(_table, i));
}
return json;
}
winrt::com_array<winrt::Microsoft::Terminal::Core::Color> ColorScheme::Table() const noexcept
{
return winrt::com_array<Core::Color>{ _table };
}
// Method Description:
// - Set a color in the color table
// Arguments:
// - index: the index of the desired color within the table
// - value: the color value we are setting the color table color to
// Return Value:
// - none
void ColorScheme::SetColorTableEntry(uint8_t index, const Core::Color& value) noexcept
{
THROW_HR_IF(E_INVALIDARG, index >= _table.size());
_table[index] = value;
}