diff --git a/src/cascadia/LocalTests_SettingsModel/pch.h b/src/cascadia/LocalTests_SettingsModel/pch.h index 53f6145d2..01b4cdfe8 100644 --- a/src/cascadia/LocalTests_SettingsModel/pch.h +++ b/src/cascadia/LocalTests_SettingsModel/pch.h @@ -61,6 +61,9 @@ Author(s): // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" +#include +#include + // Common includes for most tests: #include "../../inc/argb.h" #include "../../inc/conattrs.hpp" diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp index f0f536ecf..f16f6e71f 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp @@ -110,31 +110,44 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return *state; } - ApplicationState::ApplicationState(std::filesystem::path path) noexcept : - _path{ std::move(path) }, - _throttler{ std::chrono::seconds(1), [this]() { _write(); } } - { - _read(); - } + // ApplicationState::ApplicationState(std::filesystem::path path) noexcept : + // _path{ std::move(path) }, + // _throttler{ std::chrono::seconds(1), [this]() { _write(); } } + // { + // _read(); + // } - // The destructor ensures that the last write is flushed to disk before returning. - ApplicationState::~ApplicationState() - { - // This will ensure that we not just cancel the last outstanding timer, - // but instead force it to run as soon as possible and wait for it to complete. - _throttler.flush(); - } + // // The destructor ensures that the last write is flushed to disk before returning. + // ApplicationState::~ApplicationState() + // { + // // This will ensure that we not just cancel the last outstanding timer, + // // but instead force it to run as soon as possible and wait for it to complete. + // _throttler.flush(); + // } - // Re-read the state.json from disk. - void ApplicationState::Reload() const noexcept + void ApplicationState::FromJson(const Json::Value& root) const noexcept { - _read(); + auto state = _state.lock(); + // GetValueForKey() comes in two variants: + // * take a std::optional reference + // * return std::optional by value + // At the time of writing the former version skips missing fields in the json, + // but we want to explicitly clear state fields that were removed from state.json. +#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) state->name = JsonUtils::GetValueForKey>(root, key); + MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN) +#undef MTSM_APPLICATION_STATE_GEN } - - // Returns the state.json path on the disk. - winrt::hstring ApplicationState::FilePath() const noexcept + Json::Value ApplicationState::ToJson() const noexcept { - return winrt::hstring{ _path.wstring() }; + Json::Value root{ Json::objectValue }; + + { + auto state = _state.lock_shared(); +#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) JsonUtils::SetValueForKey(root, key, state->name); + MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN) +#undef MTSM_APPLICATION_STATE_GEN + } + return root; } // Generate all getter/setters @@ -158,58 +171,4 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN) #undef MTSM_APPLICATION_STATE_GEN - // Deserializes the state.json at _path into this ApplicationState. - // * ANY errors during app state will result in the creation of a new empty state. - // * ANY errors during runtime will result in changes being partially ignored. - void ApplicationState::_read() const noexcept - try - { - const auto data = ReadUTF8FileIfExists(_path).value_or(std::string{}); - if (data.empty()) - { - return; - } - - std::string errs; - std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() }; - - Json::Value root; - if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs)) - { - throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs)); - } - - auto state = _state.lock(); - // GetValueForKey() comes in two variants: - // * take a std::optional reference - // * return std::optional by value - // At the time of writing the former version skips missing fields in the json, - // but we want to explicitly clear state fields that were removed from state.json. -#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) state->name = JsonUtils::GetValueForKey>(root, key); - MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN) -#undef MTSM_APPLICATION_STATE_GEN - } - CATCH_LOG() - - // Serialized this ApplicationState (in `context`) into the state.json at _path. - // * Errors are only logged. - // * _state->_writeScheduled is set to false, signaling our - // setters that _synchronize() needs to be called again. - void ApplicationState::_write() const noexcept - try - { - Json::Value root{ Json::objectValue }; - - { - auto state = _state.lock_shared(); -#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) JsonUtils::SetValueForKey(root, key, state->name); - MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN) -#undef MTSM_APPLICATION_STATE_GEN - } - - Json::StreamWriterBuilder wbuilder; - const auto content = Json::writeString(wbuilder, root); - WriteUTF8FileAtomic(_path, content); - } - CATCH_LOG() } diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.h b/src/cascadia/TerminalSettingsModel/ApplicationState.h index 3a9a1e8d7..1e17d7819 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.h +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.h @@ -12,11 +12,12 @@ Abstract: --*/ #pragma once +#include "BaseApplicationState.h" #include "ApplicationState.g.h" #include -#include -#include +// #include +// #include // This macro generates all getters and setters for ApplicationState. // It provides X with the following arguments: @@ -27,18 +28,21 @@ Abstract: namespace winrt::Microsoft::Terminal::Settings::Model::implementation { - struct ApplicationState : ApplicationStateT + struct ApplicationState : ApplicationStateT, public BaseApplicationState { static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance(); ApplicationState(std::filesystem::path path) noexcept; ~ApplicationState(); - // Methods - void Reload() const noexcept; + void FromJson(const Json::Value& root) const noexcept override; + Json::Value ToJson() const noexcept override; - // General getters/setters - winrt::hstring FilePath() const noexcept; + // // Methods + // void Reload() const noexcept; + + // // General getters/setters + // winrt::hstring FilePath() const noexcept; // State getters/setters #define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) \ @@ -54,13 +58,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN) #undef MTSM_APPLICATION_STATE_GEN }; - - void _write() const noexcept; - void _read() const noexcept; - - std::filesystem::path _path; til::shared_mutex _state; - til::throttled_func_trailing<> _throttler; + + // void _write() const noexcept; + // void _read() const noexcept; + + // std::filesystem::path _path; + // til::throttled_func_trailing<> _throttler; }; } diff --git a/src/cascadia/TerminalSettingsModel/BaseApplicationState.cpp b/src/cascadia/TerminalSettingsModel/BaseApplicationState.cpp new file mode 100644 index 000000000..e27acaf95 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/BaseApplicationState.cpp @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "BaseApplicationState.h" +#include "CascadiaSettings.h" +// #include "ApplicationState.g.cpp" + +#include "JsonUtils.h" +#include "FileUtils.h" + +constexpr std::wstring_view stateFileName{ L"state.json" }; +using namespace ::Microsoft::Terminal::Settings::Model; + +// namespace winrt::Microsoft::Terminal::Settings::Model::implementation +// { +BaseApplicationState::BaseApplicationState(std::filesystem::path path) noexcept : + _path{ std::move(path) }, + _throttler{ std::chrono::seconds(1), [this]() { _write(); } } +{ + _read(); +} + +// The destructor ensures that the last write is flushed to disk before returning. +BaseApplicationState::~BaseApplicationState() +{ + // This will ensure that we not just cancel the last outstanding timer, + // but instead force it to run as soon as possible and wait for it to complete. + _throttler.flush(); +} + +// Re-read the state.json from disk. +void BaseApplicationState::Reload() const noexcept +{ + _read(); +} + +// Returns the state.json path on the disk. +winrt::hstring BaseApplicationState::FilePath() const noexcept +{ + return winrt::hstring{ _path.wstring() }; +} + +// Deserializes the state.json at _path into this ApplicationState. +// * ANY errors during app state will result in the creation of a new empty state. +// * ANY errors during runtime will result in changes being partially ignored. +void BaseApplicationState::_read() const noexcept +try +{ + const auto data = ReadUTF8FileIfExists(_path).value_or(std::string{}); + if (data.empty()) + { + return; + } + + std::string errs; + std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() }; + + Json::Value root; + if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs)) + { + throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs)); + } + + this->FromJson(root); +} +CATCH_LOG() + +// Serialized this ApplicationState (in `context`) into the state.json at _path. +// * Errors are only logged. +// * _state->_writeScheduled is set to false, signaling our +// setters that _synchronize() needs to be called again. +void BaseApplicationState::_write() const noexcept +try +{ + Json::Value root{ this->ToJson() }; + + Json::StreamWriterBuilder wbuilder; + const auto content = Json::writeString(wbuilder, root); + WriteUTF8FileAtomic(_path, content); +} +CATCH_LOG() +// } diff --git a/src/cascadia/TerminalSettingsModel/BaseApplicationState.h b/src/cascadia/TerminalSettingsModel/BaseApplicationState.h new file mode 100644 index 000000000..5713beb93 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/BaseApplicationState.h @@ -0,0 +1,36 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- ApplicationState.h + +Abstract: +- TODO! +--*/ +#pragma once + +// namespace winrt::Microsoft::Terminal::Settings::Model::implementation +// { +struct BaseApplicationState +{ + BaseApplicationState(std::filesystem::path path) noexcept; + ~BaseApplicationState(); + + // Methods + void Reload() const noexcept; + + // General getters/setters + winrt::hstring FilePath() const noexcept; + + virtual void FromJson(const Json::Value& root) const noexcept = 0; + virtual Json::Value ToJson() const noexcept = 0; + +protected: + void _write() const noexcept; + void _read() const noexcept; + + std::filesystem::path _path; + til::throttled_func_trailing<> _throttler; +}; +// } diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index 202f07808..de191507e 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -21,6 +21,7 @@ IconPathConverter.idl + ActionArgs.idl @@ -88,6 +89,7 @@ IconPathConverter.idl + Create diff --git a/src/cascadia/TerminalSettingsModel/pch.h b/src/cascadia/TerminalSettingsModel/pch.h index d5473939c..25773a45d 100644 --- a/src/cascadia/TerminalSettingsModel/pch.h +++ b/src/cascadia/TerminalSettingsModel/pch.h @@ -53,3 +53,6 @@ TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider); // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" + +#include +#include