From d0121a10f4b238db28df7c7d76ced8cf37accef8 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 May 2019 09:39:08 -0500 Subject: [PATCH 01/26] start work on an error dialog --- .../Resources/en-US/Resources.resw | 9 ++ src/cascadia/TerminalApp/App.cpp | 88 ++++++++++++++++++- src/cascadia/TerminalApp/App.h | 2 + src/cascadia/TerminalApp/CascadiaSettings.cpp | 2 +- src/cascadia/TerminalApp/CascadiaSettings.h | 3 +- .../CascadiaSettingsSerialization.cpp | 9 +- src/inc/DefaultSettings.h | 2 +- 7 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw b/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw index f759e568acc..8370880efc6 100644 --- a/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw +++ b/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw @@ -123,4 +123,13 @@ Windows Terminal (Preview) + + Bar + + + Settings could not be loaded from file - temporarily using the default settings. Check for syntax errors, including trailing commas. + + + Failed to load settings + \ No newline at end of file diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 0d8f39b2754..1b12f0dd347 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace winrt::Windows::ApplicationModel::DataTransfer; using namespace winrt::Windows::UI::Xaml; @@ -166,6 +167,47 @@ namespace winrt::TerminalApp::implementation _ApplyTheme(_settings->GlobalSettings().GetRequestedTheme()); _OpenNewTab(std::nullopt); + + // _root.Loaded([this](auto&&, auto&&) { + // if (FAILED(_settingsLoadedResult)) + // { + // Controls::ContentDialog dialog; + + // dialog.Title(winrt::box_value(L"Failed to parse settings")); + // dialog.Content(winrt::box_value(L"Failed to parse settings asdfa")); + // dialog.CloseButtonText(L"Ok"); + + // // IMPORTANT: Add the dialog to the _root UIElementw before you show it, so it knows how to attach to the XAML content. + // _root.Children().Append(dialog); + // dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); + + // } + // }); + + //auto l = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(); + //l.GetString(L"InitialJsonParse.Text"); + + + _root.Loaded([this](auto&&, auto&&) { + if (FAILED(_settingsLoadedResult)) + { + + // Windows::ApplicationModel::Resources::ResourceLoader loader{}; + auto l = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(); + auto s = l.GetString(L"InitialJsonParse.Text"); + auto bar = l.GetStringForUri(L"InitialJsonParse"); + auto foo = l.GetString(L"Foo"); + + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(L"Failed to parse settings")); + // dialog.Content(winrt::box_value(L"Failed to parse settings asdfa")); + dialog.Content(winrt::box_value(s)); + dialog.CloseButtonText(L"Ok"); + // IMPORTANT: Add the dialog to the _root UIElementw before you show it, so it knows how to attach to the XAML content. + _root.Children().Append(dialog); + dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); + } + }); } // Method Description: @@ -340,7 +382,42 @@ namespace winrt::TerminalApp::implementation // happening during startup, it'll need to happen on a background thread. void App::LoadSettings() { - _settings = CascadiaSettings::LoadAll(); + bool successfullyLoadedSettings = false; + HRESULT hr = E_FAIL; + // TODO: Try this. + try + { + auto newSettings = CascadiaSettings::LoadAll(); + _settings = std::move(newSettings); + successfullyLoadedSettings = true; + hr = S_OK; + } + catch (const winrt::hresult_error& e) + { + hr = e.code(); + LOG_HR(hr); + } + catch (...) + { + // TODO can this ever be hit? or will it always be a winrt::hresult_error? + LOG_HR(wil::ResultFromCaughtException()); + } + + _settingsLoadedResult = hr; + + if (!successfullyLoadedSettings) + { + // If it fails, + // - use Default settings, + // - don't persist them. + // - Set a flag saying that we should display the loading error. + // * Settings could not be loaded from file - temporarily using + // default settings. Check for syntax errors, including trailing + // commas. + _settings = std::make_unique(); + _settings->CreateDefaults(); + + } _HookupKeyBindings(_settings->GetKeybindings()); @@ -403,7 +480,16 @@ namespace winrt::TerminalApp::implementation // - void App::_ReloadSettings() { + // TODO: Try this. _settings = CascadiaSettings::LoadAll(); + // If it fails, + // - don't change the settings (and don't actually apply the new settings) + // - don't persist them. + // - display a loading error + // * Settings could not be reloaded from file. Check for syntax + // errors, including trailing commas. + + // Re-wire the keybindings to their handlers, as we'll have created a // new AppKeyBindings object. _HookupKeyBindings(_settings->GetKeybindings()); diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 87b3915748f..dd40d74afef 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -64,6 +64,8 @@ namespace winrt::TerminalApp::implementation std::unique_ptr<::TerminalApp::CascadiaSettings> _settings; + HRESULT _settingsLoadedResult = S_OK; + bool _loadedInitialSettings; wil::unique_folder_change_reader_nothrow _reader; diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index aa41e1602b6..b4dd5178f80 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -229,7 +229,7 @@ void CascadiaSettings::_CreateDefaultKeybindings() // - // Return Value: // - -void CascadiaSettings::_CreateDefaults() +void CascadiaSettings::CreateDefaults() { _CreateDefaultProfiles(); _CreateDefaultSchemes(); diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 102d208830d..b874c6edfa3 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -50,6 +50,8 @@ class TerminalApp::CascadiaSettings final static winrt::hstring GetSettingsPath(); const Profile* FindProfile(GUID profileGuid) const noexcept; + + void CreateDefaults(); private: GlobalAppSettings _globals; std::vector _profiles; @@ -58,7 +60,6 @@ class TerminalApp::CascadiaSettings final void _CreateDefaultKeybindings(); void _CreateDefaultSchemes(); void _CreateDefaultProfiles(); - void _CreateDefaults(); static bool _IsPackaged(); static void _SaveAsPackagedApp(const winrt::hstring content); diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 5adc4b38d5a..7b1ce9bcf47 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -47,12 +47,13 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa { const auto actualData = fileData.value(); - JsonValue root{ nullptr }; - bool parsedSuccessfully = JsonValue::TryParse(actualData, root); + JsonObject root{ nullptr }; + // bool parsedSuccessfully = JsonValue::TryParse(actualData, root); + root = JsonObject::Parse(actualData); bool parsedSuccessfully = true; // TODO:MSFT:20737698 - Display an error if we failed to parse settings if (parsedSuccessfully) { - JsonObject obj = root.GetObjectW(); + JsonObject obj = root;//.GetObjectW(); resultPtr = FromJson(obj); // Update profile only if it has changed. @@ -77,7 +78,7 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa else { resultPtr = std::make_unique(); - resultPtr->_CreateDefaults(); + resultPtr->CreateDefaults(); // The settings file does not exist. Let's commit one. resultPtr->SaveAll(); diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index d959e41f7dc..421dc185976 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -24,7 +24,7 @@ constexpr COLORREF DEFAULT_BACKGROUND = COLOR_BLACK; constexpr COLORREF DEFAULT_BACKGROUND_WITH_ALPHA = OPACITY_OPAQUE | DEFAULT_BACKGROUND; constexpr short DEFAULT_HISTORY_SIZE = 9001; const std::wstring DEFAULT_FONT_FACE { L"Consolas" }; -constexpr int DEFAULT_FONT_SIZE = 12; +constexpr int DEFAULT_FONT_SIZE = 10; constexpr int DEFAULT_ROWS = 30; constexpr int DEFAULT_COLS = 120; From effefeb95918abfc5775fad46dbb183227a22bd9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 May 2019 15:15:24 -0500 Subject: [PATCH 02/26] Continue work on an error dialog * Load messages from the Resources.resw file * Display a message when we fail to parse the settings on an initial parse, or on a reload. * Unfortunately, the dialog is styled incorrectly when the app theme is opposite the system theme. This can result in the button being invisible. --- .../Resources/en-US/Resources.resw | 16 ++- src/cascadia/TerminalApp/App.cpp | 129 +++++++++++------- src/cascadia/TerminalApp/App.h | 6 +- src/cascadia/TerminalApp/App.xaml | 3 + 4 files changed, 100 insertions(+), 54 deletions(-) diff --git a/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw b/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw index 8370880efc6..ef5a497b19e 100644 --- a/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw +++ b/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw @@ -123,13 +123,19 @@ Windows Terminal (Preview) - - Bar - - + Settings could not be loaded from file - temporarily using the default settings. Check for syntax errors, including trailing commas. - + Failed to load settings + + Ok + + + Settings could not be reloaded from file. Check for syntax errors, including trailing commas. + + + Failed to reload settings + \ No newline at end of file diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 1b12f0dd347..b5a996eb971 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -43,7 +43,9 @@ namespace winrt::TerminalApp::implementation base_type(parentProvider), _settings{ }, _tabs{ }, - _loadedInitialSettings{ false } + _loadedInitialSettings{ false }, + _settingsLoadedResult{ S_OK }, + _dialogLock{} { // For your own sanity, it's better to do setup outside the ctor. // If you do any setup in the ctor that ends up throwing an exception, @@ -168,46 +170,58 @@ namespace winrt::TerminalApp::implementation _OpenNewTab(std::nullopt); - // _root.Loaded([this](auto&&, auto&&) { - // if (FAILED(_settingsLoadedResult)) - // { - // Controls::ContentDialog dialog; + _root.Loaded({ this, &App::_OnLoaded }); + } + + fire_and_forget App::_ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& textKey) + { + // DON'T release this lock in a wil::scope_exit. The scope_exit will get + // called when we await, which is not what we want. + auto gotLock = _dialogLock.try_lock(); + if (!gotLock) + { + // Another dialog is visible. + return; + } - // dialog.Title(winrt::box_value(L"Failed to parse settings")); - // dialog.Content(winrt::box_value(L"Failed to parse settings asdfa")); - // dialog.CloseButtonText(L"Ok"); + auto l = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(); + auto title = l.GetString(titleKey); + auto message = l.GetString(textKey); + auto buttonText = l.GetString(L"Ok"); - // // IMPORTANT: Add the dialog to the _root UIElementw before you show it, so it knows how to attach to the XAML content. - // _root.Children().Append(dialog); - // dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(title)); + dialog.Content(winrt::box_value(message)); + dialog.CloseButtonText(buttonText); - // } - // }); + // This doesn't work. + // dialog.RequestedTheme(_settings->GlobalSettings().GetRequestedTheme()); - //auto l = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(); - //l.GetString(L"InitialJsonParse.Text"); + // auto res = Resources(); + // IInspectable key = winrt::box_value(L"BackgroundContentDialogThemeStyle"); + // if (res.HasKey(key)) + // { + // IInspectable g = res.Lookup(key); + // winrt::Windows::UI::Xaml::Style style = g.try_as(); + // dialog.Style(style); + // } + // IMPORTANT: Add the dialog to the _root UIElementw before you show it, so it knows how to attach to the XAML content. + _root.Children().Append(dialog); + Controls::ContentDialogResult result = await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); - _root.Loaded([this](auto&&, auto&&) { - if (FAILED(_settingsLoadedResult)) - { - - // Windows::ApplicationModel::Resources::ResourceLoader loader{}; - auto l = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(); - auto s = l.GetString(L"InitialJsonParse.Text"); - auto bar = l.GetStringForUri(L"InitialJsonParse"); - auto foo = l.GetString(L"Foo"); - - Controls::ContentDialog dialog; - dialog.Title(winrt::box_value(L"Failed to parse settings")); - // dialog.Content(winrt::box_value(L"Failed to parse settings asdfa")); - dialog.Content(winrt::box_value(s)); - dialog.CloseButtonText(L"Ok"); - // IMPORTANT: Add the dialog to the _root UIElementw before you show it, so it knows how to attach to the XAML content. - _root.Children().Append(dialog); - dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); - } - }); + // After the dialog is dismissed, release the dialog lock so another can be shown. + _dialogLock.unlock(); + } + + void App::_OnLoaded(const IInspectable& sender, const RoutedEventArgs& eventArgs) + { + if (FAILED(_settingsLoadedResult)) + { + const winrt::hstring titleKey = L"InitialJsonParseErrorTitle"; + const winrt::hstring textKey = L"InitialJsonParseErrorText"; + _ShowOkDialog(titleKey, textKey); + } } // Method Description: @@ -382,20 +396,18 @@ namespace winrt::TerminalApp::implementation // happening during startup, it'll need to happen on a background thread. void App::LoadSettings() { - bool successfullyLoadedSettings = false; - HRESULT hr = E_FAIL; + _settingsLoadedResult = E_FAIL; // TODO: Try this. try { auto newSettings = CascadiaSettings::LoadAll(); _settings = std::move(newSettings); - successfullyLoadedSettings = true; - hr = S_OK; + _settingsLoadedResult = S_OK; } catch (const winrt::hresult_error& e) { - hr = e.code(); - LOG_HR(hr); + _settingsLoadedResult = e.code(); + LOG_HR(_settingsLoadedResult); } catch (...) { @@ -403,9 +415,7 @@ namespace winrt::TerminalApp::implementation LOG_HR(wil::ResultFromCaughtException()); } - _settingsLoadedResult = hr; - - if (!successfullyLoadedSettings) + if (FAILED(_settingsLoadedResult)) { // If it fails, // - use Default settings, @@ -416,7 +426,6 @@ namespace winrt::TerminalApp::implementation // commas. _settings = std::make_unique(); _settings->CreateDefaults(); - } _HookupKeyBindings(_settings->GetKeybindings()); @@ -481,14 +490,38 @@ namespace winrt::TerminalApp::implementation void App::_ReloadSettings() { // TODO: Try this. - _settings = CascadiaSettings::LoadAll(); // If it fails, // - don't change the settings (and don't actually apply the new settings) // - don't persist them. // - display a loading error - // * Settings could not be reloaded from file. Check for syntax - // errors, including trailing commas. + _settingsLoadedResult = E_FAIL; + try + { + auto newSettings = CascadiaSettings::LoadAll(false); + _settings = std::move(newSettings); + _settingsLoadedResult = S_OK; + } + catch (const winrt::hresult_error& e) + { + _settingsLoadedResult = e.code(); + LOG_HR(_settingsLoadedResult); + } + catch (...) + { + // TODO can this ever be hit? or will it always be a winrt::hresult_error? + LOG_HR(wil::ResultFromCaughtException()); + } + if (FAILED(_settingsLoadedResult)) + { + _root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { + const winrt::hstring titleKey = L"ReloadJsonParseErrorTitle"; + const winrt::hstring textKey = L"ReloadJsonParseErrorText"; + _ShowOkDialog(titleKey, textKey); + }); + + return; + } // Re-wire the keybindings to their handlers, as we'll have created a // new AppKeyBindings object. diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index dd40d74afef..9f349990a12 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -64,15 +64,18 @@ namespace winrt::TerminalApp::implementation std::unique_ptr<::TerminalApp::CascadiaSettings> _settings; - HRESULT _settingsLoadedResult = S_OK; + HRESULT _settingsLoadedResult; bool _loadedInitialSettings; + std::shared_mutex _dialogLock; wil::unique_folder_change_reader_nothrow _reader; void _Create(); void _CreateNewTabFlyout(); + fire_and_forget _ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& textKey); + void _LoadSettings(); void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept; @@ -98,6 +101,7 @@ namespace winrt::TerminalApp::implementation // Todo: add more event implementations here // MSFT:20641986: Add keybindings for New Window + void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs); void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs); void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs); diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index ce79d196b7e..c33c35d1913 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -28,6 +28,9 @@ + From 00a8da4045769f057ea32e64c06fc837e9070496 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 15 May 2019 17:56:26 -0500 Subject: [PATCH 03/26] This unfortunately didn't work --- src/cascadia/TerminalApp/App.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index b5a996eb971..5510e0c6903 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -162,6 +162,7 @@ namespace winrt::TerminalApp::implementation { IInspectable g = res.Lookup(key); winrt::Windows::UI::Xaml::Style style = g.try_as(); + _root.Style(style); _tabRow.Style(style); } From b51d03fa664d223db0ff76a23eba612324585977 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 20 May 2019 11:47:48 -0500 Subject: [PATCH 04/26] I'm just committing all of this, even though I really don't need most of it. --- src/cascadia/TerminalApp/App.cpp | 7 +- src/cascadia/TerminalApp/App.xaml | 18 ++- .../TerminalApp/CustomContentDialog.xaml | 139 ++++++++++++++++++ src/cascadia/TerminalApp/TerminalApp.vcxproj | 11 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 33 +++++ 5 files changed, 197 insertions(+), 11 deletions(-) create mode 100644 src/cascadia/TerminalApp/CustomContentDialog.xaml diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 09e0f424cc6..a50bdc434ad 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -193,8 +193,8 @@ namespace winrt::TerminalApp::implementation Controls::ContentDialog dialog; dialog.Title(winrt::box_value(title)); dialog.Content(winrt::box_value(message)); - dialog.CloseButtonText(buttonText); - + // dialog.CloseButtonText(buttonText); + dialog.PrimaryButtonText(buttonText); // This doesn't work. // dialog.RequestedTheme(_settings->GlobalSettings().GetRequestedTheme()); @@ -206,8 +206,9 @@ namespace winrt::TerminalApp::implementation // winrt::Windows::UI::Xaml::Style style = g.try_as(); // dialog.Style(style); // } - + // IMPORTANT: Add the dialog to the _root UIElementw before you show it, so it knows how to attach to the XAML content. + _root.Children().Append(dialog); Controls::ContentDialogResult result = await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index c33c35d1913..a66573d1dfa 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -1,3 +1,5 @@ + - + + + + + @@ -42,8 +53,11 @@ + + + diff --git a/src/cascadia/TerminalApp/CustomContentDialog.xaml b/src/cascadia/TerminalApp/CustomContentDialog.xaml new file mode 100644 index 00000000000..9965f31a7be --- /dev/null +++ b/src/cascadia/TerminalApp/CustomContentDialog.xaml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index 6a70d00dda9..948ad7bf6fc 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -28,12 +28,11 @@ - - + @@ -128,4 +127,4 @@ - \ No newline at end of file + diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index c7e00745512..bbd48306178 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -4,6 +4,9 @@ #include "pch.h" #include "IslandWindow.h" #include "../types/inc/Viewport.hpp" +#include +using namespace std::chrono_literals; +using namespace winrt; extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -273,9 +276,39 @@ void IslandWindow::OnRestore() // TODO MSFT#21315817 Stop rendering island content when the app is minimized. } +winrt::fire_and_forget DoTheThing(winrt::Windows::UI::Xaml::Controls::Grid grid) { + winrt::Windows::UI::Xaml::Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(L"Foo")); + dialog.Content(winrt::box_value(L"Bar")); + // dialog.CloseButtonText(buttonText); + // dialog.PrimaryButtonText(L"Primary"); + dialog.CloseButtonText(L"Close"); + // dialog.PrimaryButtonStyle() Do this thing here + grid.Children().Append(dialog); + // grid.RequestedTheme(ElementTheme::Dark); + co_await 3ms; + co_await winrt::resume_foreground(grid.Dispatcher()); + // grid.RequestedTheme(ElementTheme::Light); + Controls::ContentDialogResult result = await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); + +} + +winrt::fire_and_forget _ShowOkDialog(winrt::Windows::UI::Xaml::Controls::Grid grid) +{ + co_await 5s; + grid.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [grid]() { + grid.RequestedTheme(ElementTheme::Light); + DoTheThing(grid); + }); + +} + void IslandWindow::SetRootContent(winrt::Windows::UI::Xaml::UIElement content) { _rootGrid.Children().Clear(); ApplyCorrection(_scale.ScaleX()); _rootGrid.Children().Append(content); + + _ShowOkDialog(_rootGrid); } + From c1d17f69103c9c022dcb32eb9097983f2501d068 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 20 May 2019 12:23:39 -0500 Subject: [PATCH 05/26] Cleanup for PR --- src/cascadia/TerminalApp/App.cpp | 122 +++++++++--------- src/cascadia/TerminalApp/App.h | 1 + src/cascadia/TerminalApp/App.xaml | 19 +-- .../CascadiaSettingsSerialization.cpp | 35 ++--- src/cascadia/TerminalApp/TerminalApp.vcxproj | 9 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 32 ----- 6 files changed, 92 insertions(+), 126 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index a50bdc434ad..d8369031aac 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -174,7 +174,16 @@ namespace winrt::TerminalApp::implementation _root.Loaded({ this, &App::_OnLoaded }); } - fire_and_forget App::_ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& textKey) + // Method Description: + // - Show a ContentDialog with a single "Ok" button to dismiss. Looks up the + // the title and text from our Resources using the provided keys. + // - Only one dialog can be visible at a time. If another dialog is visible + // when this is called, nothing happens. + // Arguments: + // - titleKey: The key to use to lookup the title text from our resources. + // - textKey: The key to use to lookup the content text from our resources. + fire_and_forget App::_ShowOkDialog(const winrt::hstring& titleKey, + const winrt::hstring& textKey) { // DON'T release this lock in a wil::scope_exit. The scope_exit will get // called when we await, which is not what we want. @@ -193,30 +202,29 @@ namespace winrt::TerminalApp::implementation Controls::ContentDialog dialog; dialog.Title(winrt::box_value(title)); dialog.Content(winrt::box_value(message)); - // dialog.CloseButtonText(buttonText); - dialog.PrimaryButtonText(buttonText); - // This doesn't work. - // dialog.RequestedTheme(_settings->GlobalSettings().GetRequestedTheme()); - - // auto res = Resources(); - // IInspectable key = winrt::box_value(L"BackgroundContentDialogThemeStyle"); - // if (res.HasKey(key)) - // { - // IInspectable g = res.Lookup(key); - // winrt::Windows::UI::Xaml::Style style = g.try_as(); - // dialog.Style(style); - // } - - // IMPORTANT: Add the dialog to the _root UIElementw before you show it, so it knows how to attach to the XAML content. - + dialog.CloseButtonText(buttonText); + + // IMPORTANT: Add the dialog to the _root UIElement before you show it, + // so it knows how to attach to the XAML content. _root.Children().Append(dialog); + + // Display the dialog. Controls::ContentDialogResult result = await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); // After the dialog is dismissed, release the dialog lock so another can be shown. _dialogLock.unlock(); } - void App::_OnLoaded(const IInspectable& sender, const RoutedEventArgs& eventArgs) + // Method Description: + // - Triggered when the application is fiished loading. If we failed to load + // the settings, then this will display the error dialog. This is done + // here instead of when loading the settings, because we need our UI to be + // visible to display the dialog, and when we're loading the settings, + // the UI might not be visible yet. + // Arguments: + // - + void App::_OnLoaded(const IInspectable& /*sender*/, + const RoutedEventArgs& /*eventArgs*/) { if (FAILED(_settingsLoadedResult)) { @@ -392,16 +400,18 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Initialized our settings. See CascadiaSettings for more details. - // Additionally hooks up our callbacks for keybinding events to the - // keybindings object. - // NOTE: This must be called from a MTA if we're running as a packaged - // application. The Windows.Storage APIs require a MTA. If this isn't - // happening during startup, it'll need to happen on a background thread. - void App::LoadSettings() + // - Attempt to load the settings. If we fail for any reason, sets + // _settingsLoadedResult to the appropriate HRESULT. + // Arguments: + // - saveOnLoad: If true, after loading the settings, we should re-write + // them to the file, to make sure the schema is updated. See + // `CascadiaSettings::LoadAll` for details. + // Return Value: + // - + void App::_TryLoadSettings(const bool saveOnLoad) noexcept { _settingsLoadedResult = E_FAIL; - // TODO: Try this. + try { auto newSettings = CascadiaSettings::LoadAll(); @@ -415,19 +425,31 @@ namespace winrt::TerminalApp::implementation } catch (...) { - // TODO can this ever be hit? or will it always be a winrt::hresult_error? LOG_HR(wil::ResultFromCaughtException()); } + } + + // Method Description: + // - Initialized our settings. See CascadiaSettings for more details. + // Additionally hooks up our callbacks for keybinding events to the + // keybindings object. + // NOTE: This must be called from a MTA if we're running as a packaged + // application. The Windows.Storage APIs require a MTA. If this isn't + // happening during startup, it'll need to happen on a background thread. + void App::LoadSettings() + { + // Attempt to load the settings. + // If it fails, + // - use Default settings, + // - don't persist them (LoadAll won't save them in this case). + // - _settingsLoadedResult will be set to an error, indicating that + // we should display the loading error. + // * We can't display the error now, because we might not have a + // UI yet. We'll display the error in _OnLoaded. + _TryLoadSettings(true); if (FAILED(_settingsLoadedResult)) { - // If it fails, - // - use Default settings, - // - don't persist them. - // - Set a flag saying that we should display the loading error. - // * Settings could not be loaded from file - temporarily using - // default settings. Check for syntax errors, including trailing - // commas. _settings = std::make_unique(); _settings->CreateDefaults(); } @@ -487,34 +509,14 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Reloads the settings from the profile.json. - // Arguments: - // - - // Return Value: - // - void App::_ReloadSettings() { - // TODO: Try this. + // Attempt to load our settings. // If it fails, // - don't change the settings (and don't actually apply the new settings) // - don't persist them. // - display a loading error - _settingsLoadedResult = E_FAIL; - try - { - auto newSettings = CascadiaSettings::LoadAll(false); - _settings = std::move(newSettings); - _settingsLoadedResult = S_OK; - } - catch (const winrt::hresult_error& e) - { - _settingsLoadedResult = e.code(); - LOG_HR(_settingsLoadedResult); - } - catch (...) - { - // TODO can this ever be hit? or will it always be a winrt::hresult_error? - LOG_HR(wil::ResultFromCaughtException()); - } + _TryLoadSettings(false); if (FAILED(_settingsLoadedResult)) { @@ -527,12 +529,16 @@ namespace winrt::TerminalApp::implementation return; } + // Here, we successfully reloaded the settings, and created a new + // TerminalSettings object. + // Re-wire the keybindings to their handlers, as we'll have created a // new AppKeyBindings object. _HookupKeyBindings(_settings->GetKeybindings()); - auto profiles = _settings->GetProfiles(); + // Refresh UI elements + auto profiles = _settings->GetProfiles(); for (auto &profile : profiles) { const GUID profileGuid = profile.GetGuid(); @@ -811,7 +817,7 @@ namespace winrt::TerminalApp::implementation // Negative values of `delta` will move the view up by one page, and positive values // will move the viewport down by one page. // Arguments: - // - delta: The direction to move the view relative to the current viewport(it + // - delta: The direction to move the view relative to the current viewport(it // is clamped between -1 and 1) void App::_ScrollPage(int delta) { diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 88ac440b6f7..d3aba75bd57 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -77,6 +77,7 @@ namespace winrt::TerminalApp::implementation fire_and_forget _ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& textKey); + void _TryLoadSettings(const bool saveOnLoad) noexcept; void _LoadSettings(); void _OpenSettings(); diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index a66573d1dfa..6b243b00b7c 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -18,7 +18,6 @@ the MIT License. See LICENSE in the project root for license information. --> - - - - + diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 7b1ce9bcf47..e80d8abf1dd 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -47,40 +47,29 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa { const auto actualData = fileData.value(); - JsonObject root{ nullptr }; - // bool parsedSuccessfully = JsonValue::TryParse(actualData, root); - root = JsonObject::Parse(actualData); bool parsedSuccessfully = true; - // TODO:MSFT:20737698 - Display an error if we failed to parse settings - if (parsedSuccessfully) + // If Parse fails, it'll throw a hresult_error + JsonObject root = JsonObject::Parse(actualData); + + resultPtr = FromJson(root); + + // Update profile only if it has changed. + if (saveOnLoad) { - JsonObject obj = root;//.GetObjectW(); - resultPtr = FromJson(obj); + const JsonObject json = resultPtr->ToJson(); + auto serializedSettings = json.Stringify(); - // Update profile only if it has changed. - if (saveOnLoad) + if (actualData != serializedSettings) { - const JsonObject json = resultPtr->ToJson(); - auto serializedSettings = json.Stringify(); - - if (actualData != serializedSettings) - { - resultPtr->SaveAll(); - } + resultPtr->SaveAll(); } } - else - { - // Until 20737698 is done, throw an error, so debugging can trace - // the exception here, instead of later on in unrelated code - THROW_HR(E_INVALIDARG); - } } else { resultPtr = std::make_unique(); resultPtr->CreateDefaults(); - // The settings file does not exist. Let's commit one. + // The settings file does not exist. Let's commit one. resultPtr->SaveAll(); } diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index 948ad7bf6fc..ad48839d926 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -28,11 +28,12 @@ - - - DefaultStyle + + diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index bbd48306178..ad106f23d15 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -4,9 +4,6 @@ #include "pch.h" #include "IslandWindow.h" #include "../types/inc/Viewport.hpp" -#include -using namespace std::chrono_literals; -using namespace winrt; extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -276,39 +273,10 @@ void IslandWindow::OnRestore() // TODO MSFT#21315817 Stop rendering island content when the app is minimized. } -winrt::fire_and_forget DoTheThing(winrt::Windows::UI::Xaml::Controls::Grid grid) { - winrt::Windows::UI::Xaml::Controls::ContentDialog dialog; - dialog.Title(winrt::box_value(L"Foo")); - dialog.Content(winrt::box_value(L"Bar")); - // dialog.CloseButtonText(buttonText); - // dialog.PrimaryButtonText(L"Primary"); - dialog.CloseButtonText(L"Close"); - // dialog.PrimaryButtonStyle() Do this thing here - grid.Children().Append(dialog); - // grid.RequestedTheme(ElementTheme::Dark); - co_await 3ms; - co_await winrt::resume_foreground(grid.Dispatcher()); - // grid.RequestedTheme(ElementTheme::Light); - Controls::ContentDialogResult result = await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); - -} - -winrt::fire_and_forget _ShowOkDialog(winrt::Windows::UI::Xaml::Controls::Grid grid) -{ - co_await 5s; - grid.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [grid]() { - grid.RequestedTheme(ElementTheme::Light); - DoTheThing(grid); - }); - -} - void IslandWindow::SetRootContent(winrt::Windows::UI::Xaml::UIElement content) { _rootGrid.Children().Clear(); ApplyCorrection(_scale.ScaleX()); _rootGrid.Children().Append(content); - - _ShowOkDialog(_rootGrid); } From 809a180e7e9692b0f19cef4318b96cfe921e84de Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 May 2019 11:13:48 -0500 Subject: [PATCH 06/26] Include jsoncpp in the solution --- dep/jsoncpp/README.md | 7 + dep/jsoncpp/json/json-forwards.h | 333 + dep/jsoncpp/json/json.h | 2207 +++++++ dep/jsoncpp/jsoncpp.cpp | 5386 +++++++++++++++++ .../CascadiaSettingsSerialization.cpp | 2 + src/cascadia/TerminalApp/TerminalApp.vcxproj | 9 + 6 files changed, 7944 insertions(+) create mode 100644 dep/jsoncpp/README.md create mode 100644 dep/jsoncpp/json/json-forwards.h create mode 100644 dep/jsoncpp/json/json.h create mode 100644 dep/jsoncpp/jsoncpp.cpp diff --git a/dep/jsoncpp/README.md b/dep/jsoncpp/README.md new file mode 100644 index 00000000000..605eed857b6 --- /dev/null +++ b/dep/jsoncpp/README.md @@ -0,0 +1,7 @@ +# jsoncpp + +[Amalgamated](https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated) +from source commit +[ddabf50](https://github.com/open-source-parsers/jsoncpp/commit/ddabf50f72cf369bf652a95c4d9fe31a1865a781), +release 1.8.4. + diff --git a/dep/jsoncpp/json/json-forwards.h b/dep/jsoncpp/json/json-forwards.h new file mode 100644 index 00000000000..de2e4bd790c --- /dev/null +++ b/dep/jsoncpp/json/json-forwards.h @@ -0,0 +1,333 @@ +/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json-forwards.h" +/// This header provides forward declaration for all JsonCpp types. + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED +# define JSON_FORWARD_AMALGAMATED_H_INCLUDED +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED +#include +#include //typedef String +#include //typedef int64_t, uint64_t + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgamated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) // MSVC +# if _MSC_VER <= 1200 // MSVC 6 + // Microsoft Visual Studio 6 only support conversion from __int64 to double + // (no conversion from unsigned __int64). +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 + // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' + // characters in the debug information) + // All projects I've ever seen with VS6 were using this globally (not bothering + // with pragma push/pop). +# pragma warning(disable : 4786) +# endif // MSVC 6 + +# if _MSC_VER >= 1500 // MSVC 2008 + /// Indicates that the following function is deprecated. +# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +# endif + +#endif // defined(_MSC_VER) + +// In c++11 the override keyword allows you to explicitly define that a function +// is intended to override the base-class version. This makes the code more +// managable and fixes a set of common hard-to-find bugs. +#if __cplusplus >= 201103L +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT noexcept +#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT throw() +#elif defined(_MSC_VER) && _MSC_VER >= 1900 +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT noexcept +#else +# define JSONCPP_OVERRIDE +# define JSONCPP_NOEXCEPT throw() +#endif + +#ifndef JSON_HAS_RVALUE_REFERENCES + +#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // MSVC >= 2010 + +#ifdef __clang__ +#if __has_feature(cxx_rvalue_references) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // has_feature + +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // GXX_EXPERIMENTAL + +#endif // __clang__ || __GNUC__ + +#endif // not defined JSON_HAS_RVALUE_REFERENCES + +#ifndef JSON_HAS_RVALUE_REFERENCES +#define JSON_HAS_RVALUE_REFERENCES 0 +#endif + +#ifdef __clang__ +# if __has_extension(attribute_deprecated_with_message) +# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +# endif +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +# endif // GNUC version +#endif // __clang__ || __GNUC__ + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if __GNUC__ >= 6 +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +# include "version.h" + +# if JSONCPP_USING_SECURE_MEMORY +# include "allocator.h" //typedef Allocator +# endif + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef int64_t Int64; +typedef uint64_t UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +#if JSONCPP_USING_SECURE_MEMORY +#define JSONCPP_STRING std::basic_string, Json::SecureAllocator > +#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > +#define JSONCPP_OSTREAM std::basic_ostream> +#define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > +#define JSONCPP_ISTREAM std::istream +#else +#define JSONCPP_STRING std::string +#define JSONCPP_OSTRINGSTREAM std::ostringstream +#define JSONCPP_OSTREAM std::ostream +#define JSONCPP_ISTRINGSTREAM std::istringstream +#define JSONCPP_ISTREAM std::istream +#endif // if JSONCPP_USING_SECURE_MEMORY +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED diff --git a/dep/jsoncpp/json/json.h b/dep/jsoncpp/json/json.h new file mode 100644 index 00000000000..625ba02e901 --- /dev/null +++ b/dep/jsoncpp/json/json.h @@ -0,0 +1,2207 @@ +/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_AMALGAMATED_H_INCLUDED +# define JSON_AMALGAMATED_H_INCLUDED +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + +// DO NOT EDIT. This file (and "version") is generated by CMake. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "1.8.4" +# define JSONCPP_VERSION_MAJOR 1 +# define JSONCPP_VERSION_MINOR 8 +# define JSONCPP_VERSION_PATCH 4 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 0 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + +#endif // JSON_VERSION_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED +#include +#include //typedef String +#include //typedef int64_t, uint64_t + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgamated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) // MSVC +# if _MSC_VER <= 1200 // MSVC 6 + // Microsoft Visual Studio 6 only support conversion from __int64 to double + // (no conversion from unsigned __int64). +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 + // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' + // characters in the debug information) + // All projects I've ever seen with VS6 were using this globally (not bothering + // with pragma push/pop). +# pragma warning(disable : 4786) +# endif // MSVC 6 + +# if _MSC_VER >= 1500 // MSVC 2008 + /// Indicates that the following function is deprecated. +# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +# endif + +#endif // defined(_MSC_VER) + +// In c++11 the override keyword allows you to explicitly define that a function +// is intended to override the base-class version. This makes the code more +// managable and fixes a set of common hard-to-find bugs. +#if __cplusplus >= 201103L +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT noexcept +#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT throw() +#elif defined(_MSC_VER) && _MSC_VER >= 1900 +# define JSONCPP_OVERRIDE override +# define JSONCPP_NOEXCEPT noexcept +#else +# define JSONCPP_OVERRIDE +# define JSONCPP_NOEXCEPT throw() +#endif + +#ifndef JSON_HAS_RVALUE_REFERENCES + +#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // MSVC >= 2010 + +#ifdef __clang__ +#if __has_feature(cxx_rvalue_references) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // has_feature + +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // GXX_EXPERIMENTAL + +#endif // __clang__ || __GNUC__ + +#endif // not defined JSON_HAS_RVALUE_REFERENCES + +#ifndef JSON_HAS_RVALUE_REFERENCES +#define JSON_HAS_RVALUE_REFERENCES 0 +#endif + +#ifdef __clang__ +# if __has_extension(attribute_deprecated_with_message) +# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +# endif +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +# endif // GNUC version +#endif // __clang__ || __GNUC__ + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if __GNUC__ >= 6 +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +# include "version.h" + +# if JSONCPP_USING_SECURE_MEMORY +# include "allocator.h" //typedef Allocator +# endif + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef int64_t Int64; +typedef uint64_t UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +#if JSONCPP_USING_SECURE_MEMORY +#define JSONCPP_STRING std::basic_string, Json::SecureAllocator > +#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > +#define JSONCPP_OSTREAM std::basic_ostream> +#define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > +#define JSONCPP_ISTREAM std::istream +#else +#define JSONCPP_STRING std::string +#define JSONCPP_OSTRINGSTREAM std::ostringstream +#define JSONCPP_OSTREAM std::ostream +#define JSONCPP_ISTRINGSTREAM std::istringstream +#define JSONCPP_ISTREAM std::istream +#endif // if JSONCPP_USING_SECURE_MEMORY +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +#pragma pack(push, 8) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_; +}; + +} // namespace Json + +#pragma pack(pop) + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +//Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +# if defined(_MSC_VER) +# define JSONCPP_NORETURN __declspec(noreturn) +# elif defined(__GNUC__) +# define JSONCPP_NORETURN __attribute__ ((__noreturn__)) +# else +# define JSONCPP_NORETURN +# endif +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(JSONCPP_STRING const& msg); + ~Exception() JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; + char const* what() const JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; +protected: + JSONCPP_STRING msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(JSONCPP_STRING const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(JSONCPP_STRING const& msg); +}; + +/// used internally +JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignment takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + // Required for boost integration, e. g. BOOST_TEST + typedef std::string value_type; + + static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). + static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null + static Value const& nullSingleton(); ///< Prefer this to null or nullRef. + + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); +#if JSON_HAS_RVALUE_REFERENCES + CZString(CZString&& other); +#endif + ~CZString(); + CZString& operator=(const CZString& other); + +#if JSON_HAS_RVALUE_REFERENCES + CZString& operator=(CZString&& other); +#endif + + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const JSONCPP_STRING& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const Value& other); +#if JSON_HAS_RVALUE_REFERENCES + /// Move constructor + Value(Value&& other); +#endif + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value& operator=(Value other); + + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + /// copy everything. + void copy(const Value& other); + /// copy values but leave comments and source offsets in place. + void copyPayload(const Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! +#if JSONCPP_USING_SECURE_MEMORY + unsigned getCStringLength() const; //Allows you to understand the length of the CString +#endif + JSONCPP_STRING asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return !isNull() + explicit operator bool() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + +#if JSON_HAS_RVALUE_REFERENCES + Value& append(Value&& value); +#endif + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const JSONCPP_STRING& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const JSONCPP_STRING& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const JSONCPP_STRING& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + void removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + void removeMember(const JSONCPP_STRING& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(JSONCPP_STRING const& key, Value* removed); + /// Same as removeMember(JSONCPP_STRING const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const JSONCPP_STRING& key) const; + /// Same as isMember(JSONCPP_STRING const& key)const + bool isMember(const char* begin, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(JSONCPP_STRING const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const JSONCPP_STRING& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + JSONCPP_STRING getComment(CommentPlacement placement) const; + + JSONCPP_STRING toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(ptrdiff_t start); + void setOffsetLimit(ptrdiff_t limit); + ptrdiff_t getOffsetStart() const; + ptrdiff_t getOffsetLimit() const; + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + ObjectValues* map_; + } value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + ptrdiff_t start_; + ptrdiff_t limit_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const JSONCPP_STRING& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + JSONCPP_STRING key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const JSONCPP_STRING& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const JSONCPP_STRING& path, const InArgs& in); + void addPathInArg(const JSONCPP_STRING& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const JSONCPP_STRING& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + JSONCPP_STRING name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + ValueConstIterator(ValueIterator const& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + explicit ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template<> +inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +} + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. + * + */ + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + JSONCPP_STRING message; + }; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(JSONCPP_ISTREAM& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + JSONCPP_STRING getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + JSONCPP_STRING getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured erros encounted while parsing. + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate + * multiple + * errors. This can occur if the parser recovers from a non-fatal + * parse error and then encounters additional errors. + */ + std::vector getStructuredErrors() const; + + /** \brief Add a semantic error message. + * \param value JSON Value location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const JSONCPP_STRING& message); + + /** \brief Add a semantic error message with extra context. + * \param value JSON Value location associated with the error + * \param message The error message. + * \param extra Additional JSON Value location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); + + /** \brief Return whether there are any errors. + * \return \c true if there are no errors to report \c false if + * errors have occurred. + */ + bool good() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + JSONCPP_STRING message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static bool containsNewLine(Location begin, Location end); + static JSONCPP_STRING normalizeEOL(Location begin, Location end); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + JSONCPP_STRING document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + JSONCPP_STRING commentsBefore_; + Features features_; + bool collectComments_; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, JSONCPP_STRING* errs) = 0; + + class JSON_API Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + JSONCPP_STRING errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + - `"allowSpecialFloats": false or true` + - If true, special float values (NaNs and infinities) are allowed + and their values are lossfree restorable. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + ~CharReaderBuilder() JSONCPP_OVERRIDE; + + CharReader* newCharReader() const JSONCPP_OVERRIDE; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](JSONCPP_STRING key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream( + CharReader::Factory const&, + JSONCPP_ISTREAM&, + Value* root, std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + JSONCPP_OSTREAM* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the stream instead.) + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, JSONCPP_OSTREAM* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +JSONCPP_STRING JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); + + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's JavaScript, it makes for smaller output and the + browser can handle the output just fine. + - "useSpecialFloats": false or true + - If true, outputs non-finite floating point values in the following way: + NaN values as "NaN", positive infinity as "Infinity", and negative infinity + as "-Infinity". + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + ~StreamWriterBuilder() JSONCPP_OVERRIDE; + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + StreamWriter* newStreamWriter() const JSONCPP_OVERRIDE; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](JSONCPP_STRING key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer { +public: + virtual ~Writer(); + + virtual JSONCPP_STRING write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter : public Writer { +public: + FastWriter(); + ~FastWriter() JSONCPP_OVERRIDE {} + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; + +private: + void writeValue(const Value& value); + + JSONCPP_STRING document_; + bool yamlCompatibilityEnabled_; + bool dropNullPlaceholders_; + bool omitEndingLineFeed_; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + ~StyledWriter() JSONCPP_OVERRIDE {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const JSONCPP_STRING& value); + void writeIndent(); + void writeWithIndent(const JSONCPP_STRING& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + JSONCPP_STRING document_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + unsigned int indentSize_; + bool addChildValues_; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledStreamWriter { +public: +/** + * \param indentation Each level will be indented by this amount extra. + */ + StyledStreamWriter(JSONCPP_STRING indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(JSONCPP_OSTREAM& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const JSONCPP_STRING& value); + void writeIndent(); + void writeWithIndent(const JSONCPP_STRING& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + JSONCPP_OSTREAM* document_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + JSONCPP_STRING indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(JSON_HAS_INT64) +JSONCPP_STRING JSON_API valueToString(Int value); +JSONCPP_STRING JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +JSONCPP_STRING JSON_API valueToString(LargestInt value); +JSONCPP_STRING JSON_API valueToString(LargestUInt value); +JSONCPP_STRING JSON_API valueToString(double value); +JSONCPP_STRING JSON_API valueToString(bool value); +JSONCPP_STRING JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root); + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ + { \ + JSONCPP_OSTRINGSTREAM oss; oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +# define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ + { \ + JSONCPP_OSTRINGSTREAM oss; oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_AMALGAMATED_H_INCLUDED diff --git a/dep/jsoncpp/jsoncpp.cpp b/dep/jsoncpp/jsoncpp.cpp new file mode 100644 index 00000000000..507a1c6ad35 --- /dev/null +++ b/dep/jsoncpp/jsoncpp.cpp @@ -0,0 +1,5386 @@ +/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "json/json.h" + +#ifndef JSON_IS_AMALGAMATION +#error "Compile with -I PATH_TO_JSON_DIRECTORY" +#endif + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + + +// Also support old flag NO_LOCALE_SUPPORT +#ifdef NO_LOCALE_SUPPORT +#define JSONCPP_NO_LOCALE_SUPPORT +#endif + +#ifndef JSONCPP_NO_LOCALE_SUPPORT +#include +#endif + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { +static char getDecimalPoint() { +#ifdef JSONCPP_NO_LOCALE_SUPPORT + return '\0'; +#else + struct lconv* lc = localeconv(); + return lc ? *(lc->decimal_point) : '\0'; +#endif +} + +/// Converts a unicode code-point to UTF-8. +static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) { + JSONCPP_STRING result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast(0xE0 | (0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned integer to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = static_cast(value % 10U + static_cast('0')); + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +static inline void fixNumericLocale(char* begin, char* end) { + while (begin < end) { + if (*begin == ',') { + *begin = '.'; + } + ++begin; + } +} + +static inline void fixNumericLocaleInput(char* begin, char* end) { + char decimalPoint = getDecimalPoint(); + if (decimalPoint != '\0' && decimalPoint != '.') { + while (begin < end) { + if (*begin == '.') { + *begin = decimalPoint; + } + ++begin; + } + } +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors +// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else +#define snprintf _snprintf +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif + +#if defined(__QNXNTO__) +#define sscanf std::sscanf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile time to change the stack limit +#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) +#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 +#endif + +static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr CharReaderPtr; +#else +typedef std::auto_ptr CharReaderPtr; +#endif + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false), + allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} + +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool +Reader::parse(const std::string& document, Value& root, bool collectComments) { + document_.assign(document.begin(), document.end()); + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { + // std::istream_iterator begin(sin); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since JSONCPP_STRING is reference-counted, this at least does not + // create an extra copy. + JSONCPP_STRING doc; + std::getline(sin, doc, (char)EOF); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // readValue() may call itself only if it calls readObject() or ReadArray(). + // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). + // parse() executes one nodes_.push(), so > instead of >=. + if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + JSONCPP_STRING normalized; + normalized.reserve(static_cast(end - begin)); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } +} + +bool Reader::readString() { + Char c = '\0'; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& tokenStart) { + Token tokenName; + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = JSONCPP_STRING(numberName.asCString()); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + JSONCPP_STRING buffer(token.start_, token.end_); + JSONCPP_ISTRINGSTREAM is(buffer); + if (!(is >> value)) + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool +Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + size_t const errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +JSONCPP_STRING Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +JSONCPP_STRING Reader::getFormattedErrorMessages() const { + JSONCPP_STRING formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector Reader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { + return !errors_.size(); +} + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + JSONCPP_STRING message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + JSONCPP_STRING getFormattedErrorMessages() const; + std::vector getStructuredErrors() const; + bool pushError(const Value& value, const JSONCPP_STRING& message); + bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + JSONCPP_STRING message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static JSONCPP_STRING normalizeEOL(Location begin, Location end); + static bool containsNewLine(Location begin, Location end); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + JSONCPP_STRING document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + JSONCPP_STRING commentsBefore_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +bool OurReader::containsNewLine(OurReader::Location begin, OurReader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), + features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if ((features_.strictRoot_ || token.type_ != tokenError) && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + // To preserve the old behaviour we cast size_t to int. + if (static_cast(nodes_.size()) > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNaN: + { + Value v(std::numeric_limits::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenPosInf: + { + Value v(std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNegInf: + { + Value v(-std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else fall through + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { + ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Location end) { + JSONCPP_STRING normalized; + normalized.reserve(static_cast(end - begin)); + OurReader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + const char *p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& tokenStart) { + Token tokenName; + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + JSONCPP_STRING msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool OurReader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + ptrdiff_t const length = token.end_ - token.start_; + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + size_t const ulength = static_cast(length); + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, ulength); + buffer[length] = 0; + fixNumericLocaleInput(buffer, buffer + length); + count = sscanf(buffer, format, &value); + } else { + JSONCPP_STRING buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool +OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + size_t errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +JSONCPP_STRING OurReader::getFormattedErrorMessages() const { + JSONCPP_STRING formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector OurReader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool OurReader::good() const { + return !errors_.size(); +} + + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + bool parse( + char const* beginDoc, char const* endDoc, + Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); + valid_keys->insert("allowSpecialFloats"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, JSONCPP_ISTREAM& sin, + Value* root, JSONCPP_STRING* errs) +{ + JSONCPP_OSTRINGSTREAM ssin; + ssin << sin.rdbuf(); + JSONCPP_STRING doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) { + CharReaderBuilder b; + JSONCPP_STRING errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + throwRuntimeError(errs); + } + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() + : current_(), isNull_(true) { +} + +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() const { + return current_->second; +} + +void ValueIteratorBase::increment() { + ++current_; +} + +void ValueIteratorBase::decrement() { + --current_; +} + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +JSONCPP_STRING ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) return JSONCPP_STRING(); + return JSONCPP_STRING(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = NULL; + return NULL; + } + *end = cname + (*current_).first.length(); + return cname; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() {} + +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator::ValueConstIterator(ValueIterator const& other) + : ValueIteratorBase(other) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() {} + +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); +} + +ValueIterator::ValueIterator(const ValueIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t +#include // min() + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif +//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +//const unsigned char& kNullRef = kNull[0]; +//const Value& Value::null = reinterpret_cast(kNullRef); +//const Value& Value::nullRef = null; + +// static +Value const& Value::nullSingleton() +{ + static Value const nullStatic; + return nullStatic; +} + +// for backwards compatibility, we'll leave these global references around, but DO NOT +// use them in JSONCPP library code any more! +Value const& Value::null = Value::nullSingleton(); +Value const& Value::nullRef = Value::nullSingleton(); + +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + //return d >= static_cast(min) && d <= static_cast(max); + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + static_cast(Int64(value & 1)); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, + size_t length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= static_cast(Value::maxInt)) + length = Value::maxInt - 1; + + char* newString = static_cast(malloc(length + 1)); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue( + const char* value, + unsigned int length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= static_cast(Value::maxInt) - sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; + char* newString = static_cast(malloc(actualLength)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString( + bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = static_cast(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length==0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + free(value); +} +static inline void releaseStringValue(char* value, unsigned) { + free(value); +} +#endif // JSONCPP_USING_SECURE_MEMORY + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +Exception::Exception(JSONCPP_STRING const& msg) + : msg_(msg) +{} +Exception::~Exception() JSONCPP_NOEXCEPT +{} +char const* Exception::what() const JSONCPP_NOEXCEPT +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +LogicError::LogicError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg) +{ + throw RuntimeError(msg); +} +JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg) +{ + throw LogicError(msg); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) +{} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_, 0u); +} + +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_, 0u); + comment_ = 0; + } + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text, len); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} + +Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = ulength & 0x3FFFFFFF; +} + +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = static_cast(other.cstr_ + ? (static_cast(other.storage_.policy_) == noDuplication + ? noDuplication : duplicate) + : static_cast(other.storage_.policy_)) & 3U; + storage_.length_ = other.storage_.length_; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString::CZString(CZString&& other) + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} +#endif + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary + } +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(const CZString& other) { + cstr_ = other.cstr_; + index_ = other.index_; + return *this; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString& Value::CZString::operator=(CZString&& other) { + cstr_ = other.cstr_; + index_ = other.index_; + other.cstr_ = nullptr; + return *this; +} +#endif + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) return index_ < other.index_; + //return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) return index_ == other.index_; + //return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) return false; + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +//const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType vtype) { + static char const emptyString[] = ""; + initBasic(vtype); + switch (vtype) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + // allocated_ == false, so this is safe. + value_.string_ = const_cast(static_cast(emptyString)); + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); +} + +Value::Value(const JSONCPP_STRING& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(Value const& other) + : type_(other.type_), allocated_(false) + , + comments_(0), start_(other.start_), limit_(other.limit_) +{ + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.allocated_) { + unsigned len; + char const* str; + decodePrefixedString(other.allocated_, other.value_.string_, + &len, &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + allocated_ = true; + } else { + value_.string_ = other.value_.string_; + allocated_ = false; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo& otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment( + otherComment.comment_, strlen(otherComment.comment_)); + } + } +} + +#if JSON_HAS_RVALUE_REFERENCES +// Move constructor +Value::Value(Value&& other) { + initBasic(nullValue); + swap(other); +} +#endif + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } + + delete[] comments_; + + value_.uint_ = 0; +} + +Value& Value::operator=(Value other) { + swap(other); + return *this; +} + +void Value::swapPayload(Value& other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2 & 0x1; +} + +void Value::copyPayload(const Value& other) { + type_ = other.type_; + value_ = other.value_; + allocated_ = other.allocated_; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +void Value::copy(const Value& other) { + copyPayload(other); + comments_ = other.comments_; + start_ = other.start_; + limit_ = other.limit_; +} + +ValueType Value::type() const { return type_; } + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + if (other.value_.string_) return true; + else return false; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + if (this_len != other_len) return false; + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_str; +} + +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_len; +} +#endif + +bool Value::getString(char const** str, char const** cend) const { + if (type_ != stringValue) return false; + if (value_.string_ == 0) return false; + unsigned length; + decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + *cend = *str + length; + return true; +} + +JSONCPP_STRING Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + { + if (value_.string_ == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return JSONCPP_STRING(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const { + unsigned len; + char const* str; + decodePrefixedString(allocated_, value_.string_, + &len, &str); + return CppTL::ConstString(str, len); +} +#endif + +Value::Int Value::asInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type_) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast(integerToDouble(value_.uint_)); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type_) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + // This is kind of strange. Not recommended. + return (value_.real_ != 0.0) ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type_ == booleanValue && value_.bool_ == false) || + (type_ == stringValue && asString().empty()) || + (type_ == arrayValue && value_.map_->size() == 0) || + (type_ == objectValue && value_.map_->size() == 0) || + type_ == nullValue; + case intValue: + return isInt() || + (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || + type_ == booleanValue || type_ == nullValue; + case uintValue: + return isUInt() || + (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || + type_ == booleanValue || type_ == nullValue; + case realValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case booleanValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case stringValue: + return isNumeric() || type_ == booleanValue || type_ == stringValue || + type_ == nullValue; + case arrayValue: + return type_ == arrayValue || type_ == nullValue; + case objectValue: + return type_ == objectValue || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +Value::operator bool() const { return ! isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type_) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + JSON_ASSERT(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return nullSingleton(); + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullSingleton(); + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType vtype, bool allocated) { + type_ = vtype; + allocated_ = allocated; + comments_ = 0; + start_ = 0; + limit_ = 0; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* cend) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(cend-key), CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullSingleton() ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* key, char const* cend) const +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return nullSingleton(); + return *found; +} +Value const& Value::operator[](JSONCPP_STRING const& key) const +{ + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullSingleton(); + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const JSONCPP_STRING& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return resolveReference(key.c_str(), key.end_c_str()); +} +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return nullSingleton(); + return *found; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +#if JSON_HAS_RVALUE_REFERENCES + Value& Value::append(Value&& value) { return (*this)[size()] = std::move(value); } +#endif + +Value Value::get(char const* key, char const* cend, Value const& defaultValue) const +{ + Value const* found = find(key, cend); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const +{ + return get(key.data(), key.data() + key.length(), defaultValue); +} + + +bool Value::removeMember(const char* key, const char* cend, Value* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + *removed = it->second; + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(JSONCPP_STRING const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +void Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return; + + CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); + value_.map_->erase(actualKey); +} +void Value::removeMember(const JSONCPP_STRING& key) +{ + removeMember(key.c_str()); +} + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), key.end_c_str(), defaultValue); +} +#endif + +bool Value::isMember(char const* key, char const* cend) const +{ + Value const* value = find(key, cend); + return NULL != value; +} +bool Value::isMember(char const* key) const +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(JSONCPP_STRING const& key) const +{ + return isMember(key.data(), key.data() + key.length()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str(), key.end_c_str()); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(JSONCPP_STRING((*it).first.data(), + (*it).first.length())); + } + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type_ == nullValue; } + +bool Value::isBool() const { return type_ == booleanValue; } + +bool Value::isInt() const { + switch (type_) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= minInt && value_.int_ <= maxInt; +#else + return true; +#endif + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type_) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); +#else + return value_.int_ >= 0; +#endif + case uintValue: +#if defined(JSON_HAS_INT64) + return value_.uint_ <= maxUInt; +#else + return true; +#endif + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { + switch (type_) { + case intValue: + case uintValue: + return true; + case realValue: +#if defined(JSON_HAS_INT64) + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); +#else + return value_.real_ >= minInt && value_.real_ <= maxUInt && IsIntegral(value_.real_); +#endif // JSON_HAS_INT64 + default: + break; + } + return false; +} + +bool Value::isDouble() const { return type_ == intValue || type_ == uintValue || type_ == realValue; } + +bool Value::isNumeric() const { return isDouble(); } + +bool Value::isString() const { return type_ == stringValue; } + +bool Value::isArray() const { return type_ == arrayValue; } + +bool Value::isObject() const { return type_ == objectValue; } + +void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + if ((len > 0) && (comment[len-1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} + +void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +JSONCPP_STRING Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } + +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } + +ptrdiff_t Value::getOffsetStart() const { return start_; } + +ptrdiff_t Value::getOffsetLimit() const { return limit_; } + +JSONCPP_STRING Value::toStyledString() const { + StreamWriterBuilder builder; + + JSONCPP_STRING out = this->hasComment(commentBefore) ? "\n" : ""; + out += Json::writeString(builder, *this); + out += "\n"; + + return out; +} + +Value::const_iterator Value::begin() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const JSONCPP_STRING& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const JSONCPP_STRING& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.reserve(5); + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *++current != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.' || *current == ']') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(JSONCPP_STRING(beginName, current)); + } + } +} + +void Path::addPathInArg(const JSONCPP_STRING& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg++); + } +} + +void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + return Value::null; + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + return Value::null; + } + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) { + // Error: unable to resolve path (object has no member named '' at + // position...) + return Value::null; + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#elif defined(_AIX) +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#elif defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ + _Isfinitef(x) : _IsFinite(x))) +#else +#include +#define isfinite finite +#endif +#endif +#else +#include +#if !(defined(__QNXNTO__)) // QNX already defines isfinite +#define isfinite std::isfinite +#endif +#endif + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else +#define snprintf _snprintf +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif + +#if defined(__BORLANDC__) +#include +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr StreamWriterPtr; +#else +typedef std::auto_ptr StreamWriterPtr; +#endif + +JSONCPP_STRING valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} + +JSONCPP_STRING valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +JSONCPP_STRING valueToString(Int value) { + return valueToString(LargestInt(value)); +} + +JSONCPP_STRING valueToString(UInt value) { + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +namespace { +JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { + // Allocate a buffer that is more than large enough to store the 16 digits of + // precision requested below. + char buffer[36]; + int len = -1; + + char formatString[15]; + snprintf(formatString, sizeof(formatString), "%%.%ug", precision); + + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distinguish the + // concepts of reals and integers. + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), formatString, value); + fixNumericLocale(buffer, buffer + len); + + // try to ensure we preserve the fact that this was given to us as a double on input + if (!strchr(buffer, '.') && !strchr(buffer, 'e')) { + strcat(buffer, ".0"); + } + + } else { + // IEEE standard states that NaN values will not compare to themselves + if (value != value) { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); + } else if (value < 0) { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); + } else { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); + } + } + assert(len >= 0); + return buffer; +} +} + +JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } + +JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } + +static bool isAnyCharRequiredQuoting(char const* s, size_t n) { + assert(s || !n); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + if (*cur == '\\' || *cur == '\"' || *cur < ' ' + || static_cast(*cur) < 0x80) + return true; + } + return false; +} + +static unsigned int utf8ToCodepoint(const char*& s, const char* e) { + const unsigned int REPLACEMENT_CHARACTER = 0xFFFD; + + unsigned int firstByte = static_cast(*s); + + if (firstByte < 0x80) + return firstByte; + + if (firstByte < 0xE0) { + if (e - s < 2) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x1F) << 6) + | (static_cast(s[1]) & 0x3F); + s += 1; + // oversized encoded characters are invalid + return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF0) { + if (e - s < 3) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x0F) << 12) + | ((static_cast(s[1]) & 0x3F) << 6) + | (static_cast(s[2]) & 0x3F); + s += 2; + // surrogates aren't valid codepoints itself + // shouldn't be UTF-8 encoded + if (calculated >= 0xD800 && calculated <= 0xDFFF) + return REPLACEMENT_CHARACTER; + // oversized encoded characters are invalid + return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF8) { + if (e - s < 4) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x07) << 24) + | ((static_cast(s[1]) & 0x3F) << 12) + | ((static_cast(s[2]) & 0x3F) << 6) + | (static_cast(s[3]) & 0x3F); + s += 3; + // oversized encoded characters are invalid + return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; + } + + return REPLACEMENT_CHARACTER; +} + +static const char hex2[] = + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + +static JSONCPP_STRING toHex16Bit(unsigned int x) { + const unsigned int hi = (x >> 8) & 0xff; + const unsigned int lo = x & 0xff; + JSONCPP_STRING result(4, ' '); + result[0] = hex2[2 * hi]; + result[1] = hex2[2 * hi + 1]; + result[2] = hex2[2 * lo]; + result[3] = hex2[2 * lo + 1]; + return result; +} + +static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + + if (!isAnyCharRequiredQuoting(value, length)) + return JSONCPP_STRING("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + JSONCPP_STRING::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + JSONCPP_STRING result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid = 0x20) + result += static_cast(cp); + else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane + result += "\\u"; + result += toHex16Bit(cp); + } + else { // codepoint is not in Basic Multilingual Plane + // convert to surrogate pair first + cp -= 0x10000; + result += "\\u"; + result += toHex16Bit((cp >> 10) + 0xD800); + result += "\\u"; + result += toHex16Bit((cp & 0x3FF) + 0xDC00); + } + } + break; + } + } + result += "\""; + return result; +} + +JSONCPP_STRING valueToQuotedString(const char* value) { + return valueToQuotedStringN(value, static_cast(strlen(value))); +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatibilityEnabled_(false), dropNullPlaceholders_(false), + omitEndingLineFeed_(false) {} + +void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +JSONCPP_STRING FastWriter::write(const Value& root) { + document_.clear(); + writeValue(root); + if (!omitEndingLineFeed_) + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const JSONCPP_STRING& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); + document_ += yamlCompatibilityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +JSONCPP_STRING StyledWriter::write(const Value& root) { + document_.clear(); + addChildValues_ = false; + indentString_.clear(); + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const JSONCPP_STRING& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const JSONCPP_STRING& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(indentString_.size() >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && + ((iter+1) != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += root.getComment(commentAfter); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_.clear(); + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const JSONCPP_STRING& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) { + if (!indented_) writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + ((iter+1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision); + int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultilineArray(Value const& value); + void pushValue(JSONCPP_STRING const& value); + void writeIndent(); + void writeWithIndent(JSONCPP_STRING const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector ChildValues; + + ChildValues childValues_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + JSONCPP_STRING indentation_; + CommentStyle::Enum cs_; + JSONCPP_STRING colonSymbol_; + JSONCPP_STRING nullSymbol_; + JSONCPP_STRING endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + unsigned int precision_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision) + : rightMargin_(74) + , indentation_(indentation) + , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) + , addChildValues_(false) + , indented_(false) + , useSpecialFloats_(useSpecialFloats) + , precision_(precision) +{ +} +int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_.clear(); + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + break; + case stringValue: + { + // Is NULL is possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + JSONCPP_STRING const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ((!indentation_.empty()) ? ", " : ","); + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + ((iter+1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + JSONCPP_STRING indentation = settings_["indentation"].asString(); + JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + bool usf = settings_["useSpecialFloats"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + JSONCPP_STRING colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + JSONCPP_STRING nullSymbol = "null"; + if (dnp) { + nullSymbol.clear(); + } + if (pre > 17) pre = 17; + JSONCPP_STRING endingLineFeedSymbol; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); +} +static void getValidWriterKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); + valid_keys->insert("useSpecialFloats"); + valid_keys->insert("precision"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["precision"] = 17; + //! [StreamWriterBuilderDefaults] +} + +JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) { + JSONCPP_OSTRINGSTREAM sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 156cf7233ee..ec1a30b03b5 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include using namespace ::TerminalApp; using namespace winrt::Microsoft::Terminal::TerminalControl; diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index 0aa7e8fb1f8..6ab6ae4cd22 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -70,6 +70,11 @@ App.xaml + + + + @@ -117,6 +122,10 @@ true + + $(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); + + WindowsApp.lib;shell32.lib;%(AdditionalDependencies) From 5547ca56efeaa74b0b71176dd39cf408b53831a2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 May 2019 12:13:58 -0500 Subject: [PATCH 07/26] PR feedback --- src/cascadia/TerminalApp/App.cpp | 34 ++--- src/cascadia/TerminalApp/App.h | 4 +- .../TerminalApp/CustomContentDialog.xaml | 139 ------------------ src/inc/DefaultSettings.h | 2 +- 4 files changed, 20 insertions(+), 159 deletions(-) delete mode 100644 src/cascadia/TerminalApp/CustomContentDialog.xaml diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index d8369031aac..67784a49933 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -180,22 +180,21 @@ namespace winrt::TerminalApp::implementation // - Only one dialog can be visible at a time. If another dialog is visible // when this is called, nothing happens. // Arguments: - // - titleKey: The key to use to lookup the title text from our resources. + // - contentKey: The key to use to lookup the title text from our resources. // - textKey: The key to use to lookup the content text from our resources. - fire_and_forget App::_ShowOkDialog(const winrt::hstring& titleKey, + fire_and_forget App::_ShowOkDialog(const winrt::hstring& contentKey, const winrt::hstring& textKey) { // DON'T release this lock in a wil::scope_exit. The scope_exit will get // called when we await, which is not what we want. - auto gotLock = _dialogLock.try_lock(); - if (!gotLock) + if (!_dialogLock.try_lock()) { // Another dialog is visible. return; } auto l = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(); - auto title = l.GetString(titleKey); + auto title = l.GetString(contentKey); auto message = l.GetString(textKey); auto buttonText = l.GetString(L"Ok"); @@ -400,33 +399,34 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Attempt to load the settings. If we fail for any reason, sets - // _settingsLoadedResult to the appropriate HRESULT. + // - Attempt to load the settings. If we fail for any reason, returns an error. // Arguments: // - saveOnLoad: If true, after loading the settings, we should re-write // them to the file, to make sure the schema is updated. See // `CascadiaSettings::LoadAll` for details. // Return Value: - // - - void App::_TryLoadSettings(const bool saveOnLoad) noexcept + // - S_OK if we successfully parsed the settings, otherwise an appropriate HRESULT. + HRESULT App::_TryLoadSettings(const bool saveOnLoad) noexcept { - _settingsLoadedResult = E_FAIL; + HRESULT hr = E_FAIL; try { - auto newSettings = CascadiaSettings::LoadAll(); + auto newSettings = CascadiaSettings::LoadAll(saveOnLoad); _settings = std::move(newSettings); - _settingsLoadedResult = S_OK; + hr = S_OK; } catch (const winrt::hresult_error& e) { - _settingsLoadedResult = e.code(); - LOG_HR(_settingsLoadedResult); + hr = e.code(); + LOG_HR(hr); } catch (...) { - LOG_HR(wil::ResultFromCaughtException()); + hr = wil::ResultFromCaughtException() + LOG_HR(hr); } + return hr; } // Method Description: @@ -446,7 +446,7 @@ namespace winrt::TerminalApp::implementation // we should display the loading error. // * We can't display the error now, because we might not have a // UI yet. We'll display the error in _OnLoaded. - _TryLoadSettings(true); + _settingsLoadedResult = _TryLoadSettings(true); if (FAILED(_settingsLoadedResult)) { @@ -516,7 +516,7 @@ namespace winrt::TerminalApp::implementation // - don't change the settings (and don't actually apply the new settings) // - don't persist them. // - display a loading error - _TryLoadSettings(false); + _settingsLoadedResult = _TryLoadSettings(false); if (FAILED(_settingsLoadedResult)) { diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index d3aba75bd57..db4840223be 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -75,9 +75,9 @@ namespace winrt::TerminalApp::implementation void _Create(); void _CreateNewTabFlyout(); - fire_and_forget _ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& textKey); + fire_and_forget _ShowOkDialog(const winrt::hstring& contentKey, const winrt::hstring& textKey); - void _TryLoadSettings(const bool saveOnLoad) noexcept; + HRESULT _TryLoadSettings(const bool saveOnLoad) noexcept; void _LoadSettings(); void _OpenSettings(); diff --git a/src/cascadia/TerminalApp/CustomContentDialog.xaml b/src/cascadia/TerminalApp/CustomContentDialog.xaml deleted file mode 100644 index 9965f31a7be..00000000000 --- a/src/cascadia/TerminalApp/CustomContentDialog.xaml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index 421dc185976..d959e41f7dc 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -24,7 +24,7 @@ constexpr COLORREF DEFAULT_BACKGROUND = COLOR_BLACK; constexpr COLORREF DEFAULT_BACKGROUND_WITH_ALPHA = OPACITY_OPAQUE | DEFAULT_BACKGROUND; constexpr short DEFAULT_HISTORY_SIZE = 9001; const std::wstring DEFAULT_FONT_FACE { L"Consolas" }; -constexpr int DEFAULT_FONT_SIZE = 10; +constexpr int DEFAULT_FONT_SIZE = 12; constexpr int DEFAULT_ROWS = 30; constexpr int DEFAULT_COLS = 120; From dae4213d2891ff2ee478dc49e4a2aa0af69d35c3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 May 2019 14:07:16 -0500 Subject: [PATCH 08/26] Serialize just colorschemes They're no longer ordered, which I hate, but we'll live with. --- src/cascadia/TerminalApp/App.cpp | 2 +- src/cascadia/TerminalApp/CascadiaSettings.h | 1 + .../CascadiaSettingsSerialization.cpp | 31 ++++++++++- src/cascadia/TerminalApp/ColorScheme.cpp | 54 ++++++++++++++++++- src/cascadia/TerminalApp/ColorScheme.h | 1 + src/cascadia/TerminalApp/TerminalApp.vcxproj | 4 +- src/cascadia/TerminalApp/pch.h | 4 ++ 7 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 67784a49933..3486cdccac5 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -423,7 +423,7 @@ namespace winrt::TerminalApp::implementation } catch (...) { - hr = wil::ResultFromCaughtException() + hr = wil::ResultFromCaughtException(); LOG_HR(hr); } return hr; diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index b874c6edfa3..1dc18fec855 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -46,6 +46,7 @@ class TerminalApp::CascadiaSettings final winrt::Windows::Data::Json::JsonObject ToJson() const; static std::unique_ptr FromJson(winrt::Windows::Data::Json::JsonObject json); + Json::Value ToJson2() const; static winrt::hstring GetSettingsPath(); diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 9f96852a84f..0878c94cae2 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -9,8 +9,6 @@ #include #include #include -#include -#include using namespace ::TerminalApp; using namespace winrt::Microsoft::Terminal::TerminalControl; @@ -27,6 +25,11 @@ static const std::wstring PROFILES_KEY{ L"profiles" }; static const std::wstring KEYBINDINGS_KEY{ L"keybindings" }; static const std::wstring SCHEMES_KEY{ L"schemes" }; + +static constexpr std::string_view PROFILES_KEY_2{ "profiles" }; +static constexpr std::string_view KEYBINDINGS_KEY_2{ "keybindings" }; +static constexpr std::string_view SCHEMES_KEY_2{ "schemes" }; + // Method Description: // - Creates a CascadiaSettings from whatever's saved on disk, or instantiates // a new one with the default values. If we're running as a packaged app, @@ -91,6 +94,11 @@ void CascadiaSettings::SaveAll() const const JsonObject json = ToJson(); auto serializedSettings = json.Stringify(); + const auto json2 = ToJson2(); + Json::StreamWriterBuilder wbuilder; + + const auto s = Json::writeString(wbuilder, json2); + if (_IsPackaged()) { _SaveAsPackagedApp(serializedSettings); @@ -135,6 +143,25 @@ JsonObject CascadiaSettings::ToJson() const return jsonObject; } +Json::Value CascadiaSettings::ToJson2() const +{ + Json::Value root; + + Json::Value schemesArray; + const auto& colorSchemes = _globals.GetColorSchemes(); + for (auto& scheme : colorSchemes) + { + schemesArray.append(scheme.ToJson2()); + } + + // root[PROFILES_KEY] = profilesArray; + root[SCHEMES_KEY_2.data()] = schemesArray; + // root[KEYBINDINGS_KEY] = _globals.GetKeybindings().ToJson2(); + + return root; +} + + // Method Description: // - Create a new instance of this class from a serialized JsonObject. // Arguments: diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index 8b5392407ad..9245f0aa792 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -35,6 +35,33 @@ static const std::array TABLE_COLORS = L"brightCyan", L"brightWhite" }; +//////////////////////////////////////////////////////////////////////////////// + +static constexpr std::string_view NAME_KEY_2{ "name" }; +static constexpr std::string_view TABLE_KEY_2{ "colors" }; +static constexpr std::string_view FOREGROUND_KEY_2{ "foreground" }; +static constexpr std::string_view BACKGROUND_KEY_2{ "background" }; +static constexpr std::array TABLE_COLORS_2 = +{ + "black", + "red", + "green", + "yellow", + "blue", + "purple", + "cyan", + "white", + "brightBlack", + "brightRed", + "brightGreen", + "brightYellow", + "brightBlue", + "brightPurple", + "brightCyan", + "brightWhite" +}; +//////////////////////////////////////////////////////////////////////////////// + ColorScheme::ColorScheme() : _schemeName{ L"" }, @@ -94,7 +121,7 @@ JsonObject ColorScheme::ToJson() const jsonObject.Insert(NAME_KEY, name); jsonObject.Insert(FOREGROUND_KEY, fg); jsonObject.Insert(BACKGROUND_KEY, bg); - + int i = 0; for (const auto& current : TABLE_COLORS) { @@ -108,6 +135,31 @@ JsonObject ColorScheme::ToJson() const return jsonObject; } +Json::Value ColorScheme::ToJson2() const +{ + Json::Value root; + auto fg { Utils::ColorToHexString(_defaultForeground) }; + auto bg { Utils::ColorToHexString(_defaultBackground) }; + auto name { _schemeName }; + + root[NAME_KEY_2.data()] = winrt::to_string(name); + root[FOREGROUND_KEY_2.data()] = winrt::to_string(fg); + root[BACKGROUND_KEY_2.data()] = winrt::to_string(bg); + + int i = 0; + for (const auto& colorName : TABLE_COLORS_2) + { + auto& colorValue = _table.at(i); + auto colorHexString = Utils::ColorToHexString(colorValue); + + root[colorName.data()] = winrt::to_string(colorHexString); + i++; + } + + return root; +} + + // Method Description: // - Create a new instance of this class from a serialized JsonObject. // Arguments: diff --git a/src/cascadia/TerminalApp/ColorScheme.h b/src/cascadia/TerminalApp/ColorScheme.h index afc1818c081..768f2dc4674 100644 --- a/src/cascadia/TerminalApp/ColorScheme.h +++ b/src/cascadia/TerminalApp/ColorScheme.h @@ -38,6 +38,7 @@ class TerminalApp::ColorScheme winrt::Windows::Data::Json::JsonObject ToJson() const; static ColorScheme FromJson(winrt::Windows::Data::Json::JsonObject json); + Json::Value ToJson2() const; std::wstring_view GetName() const noexcept; std::array& GetTable() noexcept; diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index 6ab6ae4cd22..4a143597f83 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -73,7 +73,9 @@ - + + NotUsing + diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index 53279a09a32..2e005f064d1 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -48,3 +48,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalWin32Provider); #include #include + +// JsonCpp +#include +#include From c88f69f423bf6e27385188d7d8a70b377de0e23c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 May 2019 14:29:22 -0500 Subject: [PATCH 09/26] Serialize Profiles --- .../CascadiaSettingsSerialization.cpp | 11 ++- src/cascadia/TerminalApp/Profile.cpp | 98 +++++++++++++++++++ src/cascadia/TerminalApp/Profile.h | 1 + 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 0878c94cae2..92a4c4dfb50 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -96,7 +96,8 @@ void CascadiaSettings::SaveAll() const const auto json2 = ToJson2(); Json::StreamWriterBuilder wbuilder; - + // Use 4 spaces to indent instead of \t + wbuilder.settings_["indentation"] = " "; const auto s = Json::writeString(wbuilder, json2); if (_IsPackaged()) @@ -147,6 +148,12 @@ Json::Value CascadiaSettings::ToJson2() const { Json::Value root; + Json::Value profilesArray; + for (const auto& profile : _profiles) + { + profilesArray.append(profile.ToJson2()); + } + Json::Value schemesArray; const auto& colorSchemes = _globals.GetColorSchemes(); for (auto& scheme : colorSchemes) @@ -154,7 +161,7 @@ Json::Value CascadiaSettings::ToJson2() const schemesArray.append(scheme.ToJson2()); } - // root[PROFILES_KEY] = profilesArray; + root[PROFILES_KEY_2.data()] = profilesArray; root[SCHEMES_KEY_2.data()] = schemesArray; // root[KEYBINDINGS_KEY] = _globals.GetKeybindings().ToJson2(); diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 95dae7c60c8..dfbe02d8353 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -48,6 +48,34 @@ static const std::wstring CURSORSHAPE_UNDERSCORE{ L"underscore" }; static const std::wstring CURSORSHAPE_FILLEDBOX{ L"filledBox" }; static const std::wstring CURSORSHAPE_EMPTYBOX{ L"emptyBox" }; +//////////////////////////////////////////////////////////////////////////////// +static constexpr std::string_view NAME_KEY_2{ "name" }; +static constexpr std::string_view GUID_KEY_2{ "guid" }; +static constexpr std::string_view COLORSCHEME_KEY_2{ "colorscheme" }; + +static constexpr std::string_view FOREGROUND_KEY_2{ "foreground" }; +static constexpr std::string_view BACKGROUND_KEY_2{ "background" }; +static constexpr std::string_view COLORTABLE_KEY_2{ "colorTable" }; +static constexpr std::string_view HISTORYSIZE_KEY_2{ "historySize" }; +static constexpr std::string_view SNAPONINPUT_KEY_2{ "snapOnInput" }; +static constexpr std::string_view CURSORCOLOR_KEY_2{ "cursorColor" }; +static constexpr std::string_view CURSORSHAPE_KEY_2{ "cursorShape" }; +static constexpr std::string_view CURSORHEIGHT_KEY_2{ "cursorHeight" }; + +static constexpr std::string_view COMMANDLINE_KEY_2{ "commandline" }; +static constexpr std::string_view FONTFACE_KEY_2{ "fontFace" }; +static constexpr std::string_view FONTSIZE_KEY_2{ "fontSize" }; +static constexpr std::string_view ACRYLICTRANSPARENCY_KEY_2{ "acrylicOpacity" }; +static constexpr std::string_view USEACRYLIC_KEY_2{ "useAcrylic" }; +static constexpr std::string_view SCROLLBARSTATE_KEY_2{ "scrollbarState" }; +static constexpr std::string_view CLOSEONEXIT_KEY_2{ "closeOnExit" }; +static constexpr std::string_view PADDING_KEY_2{ "padding" }; +static constexpr std::string_view STARTINGDIRECTORY_KEY_2{ "startingDirectory" }; +static constexpr std::string_view ICON_KEY_2{ "icon" }; +//////////////////////////////////////////////////////////////////////////////// + + + Profile::Profile() : _guid{}, _name{ L"Default" }, @@ -273,6 +301,76 @@ JsonObject Profile::ToJson() const return jsonObject; } +Json::Value Profile::ToJson2() const +{ + Json::Value root; + + ///// Profile-specific settings ///// + root[GUID_KEY_2.data()] = winrt::to_string(Utils::GuidToString(_guid)); + root[NAME_KEY_2.data()] = winrt::to_string(_name); + + ///// Core Settings ///// + if (_defaultForeground) + { + const auto defaultForeground = winrt::to_string(Utils::ColorToHexString(_defaultForeground.value())); + root[FOREGROUND_KEY_2.data()] = defaultForeground; + } + if (_defaultBackground) + { + const auto defaultBackground = winrt::to_string(Utils::ColorToHexString(_defaultBackground.value())); + root[BACKGROUND_KEY_2.data()] = defaultBackground; + } + if (_schemeName) + { + const auto scheme = winrt::to_string(_schemeName.value()); + root[COLORSCHEME_KEY_2.data()] = scheme; + } + else + { + Json::Value tableArray{}; + for (auto& color : _colorTable) + { + auto s = Utils::ColorToHexString(color); + tableArray.append(winrt::to_string(s)); + } + root[COLORTABLE_KEY_2.data()] = tableArray; + } + root[HISTORYSIZE_KEY_2.data()] = _historySize; + root[SNAPONINPUT_KEY_2.data()] = _snapOnInput; + root[CURSORCOLOR_KEY_2.data()] = winrt::to_string(Utils::ColorToHexString(_cursorColor)); + // Only add the cursor height property if we're a legacy-style cursor. + if (_cursorShape == CursorStyle::Vintage) + { + root[CURSORHEIGHT_KEY_2.data()] = _cursorHeight; + } + root[CURSORSHAPE_KEY_2.data()] = winrt::to_string(_SerializeCursorStyle(_cursorShape)); + + ///// Control Settings ///// + root[COMMANDLINE_KEY_2.data()] = winrt::to_string(_commandline); + root[FONTFACE_KEY_2.data()] = winrt::to_string(_fontFace); + root[FONTSIZE_KEY_2.data()] = _fontSize; + root[ACRYLICTRANSPARENCY_KEY_2.data()] = _acrylicTransparency; + root[USEACRYLIC_KEY_2.data()] = _useAcrylic; + root[CLOSEONEXIT_KEY_2.data()] = _closeOnExit; + root[PADDING_KEY_2.data()] = winrt::to_string(_padding); + + if (_scrollbarState) + { + const auto scrollbarState = winrt::to_string(_scrollbarState.value()); + root[SCROLLBARSTATE_KEY_2.data()] = scrollbarState; + } + + if (_icon) + { + const auto icon = winrt::to_string(_icon.value()); + root[ICON_KEY_2.data()] = icon; + } + + return root; +} + + + // Method Description: // - Create a new instance of this class from a serialized JsonObject. // Arguments: diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 1605ba0d7ad..8bcce4f1b7a 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -32,6 +32,7 @@ class TerminalApp::Profile final winrt::Windows::Data::Json::JsonObject ToJson() const; static Profile FromJson(winrt::Windows::Data::Json::JsonObject json); + Json::Value ToJson2() const; GUID GetGuid() const noexcept; std::wstring_view GetName() const noexcept; From c84b8b3a87be1c4177a69a68c32de8ddaea84f1b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 May 2019 17:24:49 -0500 Subject: [PATCH 10/26] Serialize the keybindings Involves moving serialization out of AppKeyBindings, because all we have is a cppwinrt type, and Json::Value is not a cppwinrt type --- src/cascadia/TerminalApp/AppKeyBindings.cpp | 18 +++ src/cascadia/TerminalApp/AppKeyBindings.h | 3 + src/cascadia/TerminalApp/AppKeyBindings.idl | 2 + .../AppKeyBindingsSerialization.cpp | 136 ++++++++++++++++++ .../TerminalApp/AppKeyBindingsSerialization.h | 12 ++ .../CascadiaSettingsSerialization.cpp | 4 +- src/cascadia/TerminalApp/TerminalApp.vcxproj | 2 + 7 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp create mode 100644 src/cascadia/TerminalApp/AppKeyBindingsSerialization.h diff --git a/src/cascadia/TerminalApp/AppKeyBindings.cpp b/src/cascadia/TerminalApp/AppKeyBindings.cpp index d9a00d42e21..275e64cbad2 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindings.cpp @@ -12,6 +12,9 @@ using namespace winrt::Windows::Data::Json; static constexpr std::wstring_view KEYS_KEY{ L"keys" }; static constexpr std::wstring_view COMMAND_KEY{ L"command" }; +static constexpr std::string_view KEYS_KEY_2{ "keys" }; +static constexpr std::string_view COMMAND_KEY_2{ "command" }; + static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" }; static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" }; static constexpr std::wstring_view NEWTAB_KEY{ L"newTab" }; @@ -344,4 +347,19 @@ namespace winrt::TerminalApp::implementation return bindingsArray; } + + winrt::Microsoft::Terminal::Settings::KeyChord AppKeyBindings::LookupKeyBinding(TerminalApp::ShortcutAction const& action) + { + for (const auto& kv : _keyShortcuts) + { + const auto chord = kv.first; + const auto command = kv.second; + if (command == action) + { + return chord; + } + } + return nullptr; + } + } diff --git a/src/cascadia/TerminalApp/AppKeyBindings.h b/src/cascadia/TerminalApp/AppKeyBindings.h index d3f0290e1c1..8b2de03deaa 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.h +++ b/src/cascadia/TerminalApp/AppKeyBindings.h @@ -34,10 +34,13 @@ namespace winrt::TerminalApp::implementation static TerminalApp::AppKeyBindings FromJson(Windows::Data::Json::JsonArray const& json); Windows::Data::Json::JsonArray ToJson(); + Json::Value ToJson2(); bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc); void SetKeyBinding(TerminalApp::ShortcutAction const& action, winrt::Microsoft::Terminal::Settings::KeyChord const& chord); + winrt::Microsoft::Terminal::Settings::KeyChord LookupKeyBinding(TerminalApp::ShortcutAction const& action); + DECLARE_EVENT(CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs); DECLARE_EVENT(PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs); DECLARE_EVENT(NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs); diff --git a/src/cascadia/TerminalApp/AppKeyBindings.idl b/src/cascadia/TerminalApp/AppKeyBindings.idl index b2d5144790c..a7f760410ea 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.idl +++ b/src/cascadia/TerminalApp/AppKeyBindings.idl @@ -68,6 +68,8 @@ namespace TerminalApp void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord); + Microsoft.Terminal.Settings.KeyChord LookupKeyBinding(ShortcutAction action); + event CopyTextEventArgs CopyText; event PasteTextEventArgs PasteText; event NewTabEventArgs NewTab; diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp new file mode 100644 index 00000000000..a72cc23268e --- /dev/null +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "AppKeyBindingsSerialization.h" +#include "KeyChordSerialization.h" +#include + +using namespace winrt::Microsoft::Terminal::Settings; +using namespace winrt::TerminalApp; + +static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" }; +static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" }; +static constexpr std::wstring_view NEWTAB_KEY{ L"newTab" }; +static constexpr std::wstring_view NEWTABWITHPROFILE0_KEY{ L"newTabProfile0" }; +static constexpr std::wstring_view NEWTABWITHPROFILE1_KEY{ L"newTabProfile1" }; +static constexpr std::wstring_view NEWTABWITHPROFILE2_KEY{ L"newTabProfile2" }; +static constexpr std::wstring_view NEWTABWITHPROFILE3_KEY{ L"newTabProfile3" }; +static constexpr std::wstring_view NEWTABWITHPROFILE4_KEY{ L"newTabProfile4" }; +static constexpr std::wstring_view NEWTABWITHPROFILE5_KEY{ L"newTabProfile5" }; +static constexpr std::wstring_view NEWTABWITHPROFILE6_KEY{ L"newTabProfile6" }; +static constexpr std::wstring_view NEWTABWITHPROFILE7_KEY{ L"newTabProfile7" }; +static constexpr std::wstring_view NEWTABWITHPROFILE8_KEY{ L"newTabProfile8" }; +static constexpr std::wstring_view NEWWINDOW_KEY{ L"newWindow" }; +static constexpr std::wstring_view CLOSEWINDOW_KEY{ L"closeWindow" }; +static constexpr std::wstring_view CLOSETAB_KEY{ L"closeTab" }; +static constexpr std::wstring_view SWITCHTOTAB_KEY{ L"switchToTab" }; +static constexpr std::wstring_view NEXTTAB_KEY{ L"nextTab" }; +static constexpr std::wstring_view PREVTAB_KEY{ L"prevTab" }; +static constexpr std::wstring_view INCREASEFONTSIZE_KEY{ L"increaseFontSize" }; +static constexpr std::wstring_view DECREASEFONTSIZE_KEY{ L"decreaseFontSize" }; +static constexpr std::wstring_view SCROLLUP_KEY{ L"scrollUp" }; +static constexpr std::wstring_view SCROLLDOWN_KEY{ L"scrollDown" }; +static constexpr std::wstring_view SCROLLUPPAGE_KEY{ L"scrollUpPage" }; +static constexpr std::wstring_view SCROLLDOWNPAGE_KEY{ L"scrollDownPage" }; +static constexpr std::wstring_view SWITCHTOTAB0_KEY{ L"switchToTab0" }; +static constexpr std::wstring_view SWITCHTOTAB1_KEY{ L"switchToTab1" }; +static constexpr std::wstring_view SWITCHTOTAB2_KEY{ L"switchToTab2" }; +static constexpr std::wstring_view SWITCHTOTAB3_KEY{ L"switchToTab3" }; +static constexpr std::wstring_view SWITCHTOTAB4_KEY{ L"switchToTab4" }; +static constexpr std::wstring_view SWITCHTOTAB5_KEY{ L"switchToTab5" }; +static constexpr std::wstring_view SWITCHTOTAB6_KEY{ L"switchToTab6" }; +static constexpr std::wstring_view SWITCHTOTAB7_KEY{ L"switchToTab7" }; +static constexpr std::wstring_view SWITCHTOTAB8_KEY{ L"switchToTab8" }; +static constexpr std::wstring_view OPENSETTINGS_KEY{ L"openSettings" }; + +// Specifically use a map here over an unordered_map. We want to be able to +// iterate over these entries in-order when we're serializing the keybindings. +static const std::map commandNames { + { COPYTEXT_KEY, ShortcutAction::CopyText }, + { PASTETEXT_KEY, ShortcutAction::PasteText }, + { NEWTAB_KEY, ShortcutAction::NewTab }, + { NEWTABWITHPROFILE0_KEY, ShortcutAction::NewTabProfile0 }, + { NEWTABWITHPROFILE1_KEY, ShortcutAction::NewTabProfile1 }, + { NEWTABWITHPROFILE2_KEY, ShortcutAction::NewTabProfile2 }, + { NEWTABWITHPROFILE3_KEY, ShortcutAction::NewTabProfile3 }, + { NEWTABWITHPROFILE4_KEY, ShortcutAction::NewTabProfile4 }, + { NEWTABWITHPROFILE5_KEY, ShortcutAction::NewTabProfile5 }, + { NEWTABWITHPROFILE6_KEY, ShortcutAction::NewTabProfile6 }, + { NEWTABWITHPROFILE7_KEY, ShortcutAction::NewTabProfile7 }, + { NEWTABWITHPROFILE8_KEY, ShortcutAction::NewTabProfile8 }, + { NEWWINDOW_KEY, ShortcutAction::NewWindow }, + { CLOSEWINDOW_KEY, ShortcutAction::CloseWindow }, + { CLOSETAB_KEY, ShortcutAction::CloseTab }, + { NEXTTAB_KEY, ShortcutAction::NextTab }, + { PREVTAB_KEY, ShortcutAction::PrevTab }, + { INCREASEFONTSIZE_KEY, ShortcutAction::IncreaseFontSize }, + { DECREASEFONTSIZE_KEY, ShortcutAction::DecreaseFontSize }, + { SCROLLUP_KEY, ShortcutAction::ScrollUp }, + { SCROLLDOWN_KEY, ShortcutAction::ScrollDown }, + { SCROLLUPPAGE_KEY, ShortcutAction::ScrollUpPage }, + { SCROLLDOWNPAGE_KEY, ShortcutAction::ScrollDownPage }, + { SWITCHTOTAB0_KEY, ShortcutAction::SwitchToTab0 }, + { SWITCHTOTAB1_KEY, ShortcutAction::SwitchToTab1 }, + { SWITCHTOTAB2_KEY, ShortcutAction::SwitchToTab2 }, + { SWITCHTOTAB3_KEY, ShortcutAction::SwitchToTab3 }, + { SWITCHTOTAB4_KEY, ShortcutAction::SwitchToTab4 }, + { SWITCHTOTAB5_KEY, ShortcutAction::SwitchToTab5 }, + { SWITCHTOTAB6_KEY, ShortcutAction::SwitchToTab6 }, + { SWITCHTOTAB7_KEY, ShortcutAction::SwitchToTab7 }, + { SWITCHTOTAB8_KEY, ShortcutAction::SwitchToTab8 }, +}; +static constexpr std::string_view KEYS_KEY_2{ "keys" }; +static constexpr std::string_view COMMAND_KEY_2{ "command" }; + +// Function Description: +// - Small helper to insert a given KeyChord, ShortcutAction pair into the +// given json array +// Arguments: +// - bindingsArray: The JsonArray to insert the object into. +// - chord: The KeyChord to serailize and place in the json array +// - actionName: the name of the ShortcutAction to use with this KeyChord +static void _AddShortcutToJsonArray2(Json::Value& bindingsArray, + const KeyChord& chord, + const std::wstring_view& actionName) +{ + const auto keyString = KeyChordSerialization::ToString(chord); + if (keyString == L"") + { + return; + } + + Json::Value jsonObject; + Json::Value keysArray; + keysArray.append(winrt::to_string(keyString)); + + jsonObject[KEYS_KEY_2.data()] = keysArray; + jsonObject[COMMAND_KEY_2.data()] = winrt::to_string(actionName); + + bindingsArray.append(jsonObject); +} + +Json::Value AppKeyBindingsSerialization::ToJson2(const winrt::TerminalApp::AppKeyBindings& bindings) +{ + Json::Value bindingsArray; + + // Iterate over all the possible actions in the names list, and see if + // it has a binding. + for (auto& actionName : commandNames) + { + const auto searchedForName = actionName.first; + const auto searchedForAction = actionName.second; + const auto chord = bindings.LookupKeyBinding(searchedForAction); + if (chord) + { + _AddShortcutToJsonArray2(bindingsArray, chord, searchedForName); + } + } + + return bindingsArray; +} + +winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson2(const Json::Value& json) +{ + return nullptr; +} diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h new file mode 100644 index 00000000000..b8b64094019 --- /dev/null +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "AppKeyBindings.h" + +class AppKeyBindingsSerialization final +{ +public: + static winrt::TerminalApp::AppKeyBindings FromJson2(const Json::Value& json); + static Json::Value ToJson2(const winrt::TerminalApp::AppKeyBindings& bindings); +}; diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 92a4c4dfb50..03911840386 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -9,6 +9,7 @@ #include #include #include +#include "AppKeyBindingsSerialization.h" using namespace ::TerminalApp; using namespace winrt::Microsoft::Terminal::TerminalControl; @@ -163,12 +164,11 @@ Json::Value CascadiaSettings::ToJson2() const root[PROFILES_KEY_2.data()] = profilesArray; root[SCHEMES_KEY_2.data()] = schemesArray; - // root[KEYBINDINGS_KEY] = _globals.GetKeybindings().ToJson2(); + root[KEYBINDINGS_KEY_2.data()] = AppKeyBindingsSerialization::ToJson2(_globals.GetKeybindings()); return root; } - // Method Description: // - Create a new instance of this class from a serialized JsonObject. // Arguments: diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index 4a143597f83..f02e3750a8c 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -41,6 +41,7 @@ + @@ -59,6 +60,7 @@ + Create From d2d8df6ade99ee54e0980d1921b30c7c0be99f0e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 May 2019 10:40:16 -0500 Subject: [PATCH 11/26] Start work on parsing the json with jsoncpp --- src/cascadia/TerminalApp/CascadiaSettings.h | 1 + .../CascadiaSettingsSerialization.cpp | 90 ++++++++++++++++++- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 1dc18fec855..df7a1cceb4d 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -47,6 +47,7 @@ class TerminalApp::CascadiaSettings final winrt::Windows::Data::Json::JsonObject ToJson() const; static std::unique_ptr FromJson(winrt::Windows::Data::Json::JsonObject json); Json::Value ToJson2() const; + static std::unique_ptr FromJson2(const Json::Value& json); static winrt::hstring GetSettingsPath(); diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 03911840386..127499e2667 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -53,10 +53,20 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa { const auto actualData = fileData.value(); - // If Parse fails, it'll throw a hresult_error - JsonObject root = JsonObject::Parse(actualData); - - resultPtr = FromJson(root); + // // If Parse fails, it'll throw a hresult_error + // JsonObject root = JsonObject::Parse(actualData); + // resultPtr = FromJson(root); + +// Json::Reader reader; + Json::Value root; + Json::CharReader* reader = Json::CharReaderBuilder::CharReaderBuilder().newCharReader(); + auto raw = winrt::to_string(actualData); + std::string errs; + bool b = reader->parse(raw.c_str(), raw.c_str()+raw.size(), &root, &errs); + // TODO: need better error. `reader` might have the exception in a + // better format. + if (!b) throw winrt::hresult_error(); + resultPtr = FromJson2(root); // Update profile only if it has changed. if (saveOnLoad) @@ -148,6 +158,10 @@ JsonObject CascadiaSettings::ToJson() const Json::Value CascadiaSettings::ToJson2() const { Json::Value root; + // TODO: Globals + + // TODO: put the keybindings in the globals (now that the order doesn't + // really matter). Json::Value profilesArray; for (const auto& profile : _profiles) @@ -235,6 +249,74 @@ std::unique_ptr CascadiaSettings::FromJson(JsonObject json) return resultPtr; } + +// Method Description: +// - Create a new instance of this class from a serialized JsonObject. +// Arguments: +// - json: an object which should be a serialization of a CascadiaSettings object. +// Return Value: +// - a new CascadiaSettings instance created from the values in `json` +std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& json) +{ + std::unique_ptr resultPtr = std::make_unique(); + + // resultPtr->_globals = GlobalAppSettings::FromJson(json); + + // TODO:MSFT:20737698 - Display an error if we failed to parse settings + // What should we do here if these keys aren't found?For default profile, + // we could always pick the first profile and just set that as the default. + // Finding no schemes is probably fine, unless of course one profile + // references a scheme. We could fail with come error saying the + // profiles file is corrupted. + // Not having any profiles is also bad - should we say the file is corrupted? + // Or should we just recreate the default profiles? + + auto& resultSchemes = resultPtr->_globals.GetColorSchemes(); + if (auto schemes{ json[SCHEMES_KEY_2.data()] }) + { + for (auto schemeJson : schemes) + { + if (schemeJson.isObject()) + { + // auto schemeObj = schemeJson.GetObjectW(); +// auto scheme = ColorScheme::FromJson2(schemeJson); + //resultSchemes.emplace_back(std::move(scheme)); + } + } + } + + + // if (json.HasKey(PROFILES_KEY)) + // { + // auto profiles = json.GetNamedArray(PROFILES_KEY); + // for (auto profileJson : profiles) + // { + // if (profileJson.ValueType() == JsonValueType::Object) + // { + // auto profileObj = profileJson.GetObjectW(); + // auto profile = Profile::FromJson(profileObj); + // resultPtr->_profiles.emplace_back(std::move(profile)); + // } + // } + // } + + // // Load the keybindings from the file as well + // if (json.HasKey(KEYBINDINGS_KEY)) + // { + // const auto keybindingsObj = json.GetNamedArray(KEYBINDINGS_KEY); + // auto loadedBindings = AppKeyBindings::FromJson(keybindingsObj); + // resultPtr->_globals.SetKeybindings(loadedBindings); + // } + // else + // { + // // Create the default keybindings if we couldn't find any keybindings. + // resultPtr->_CreateDefaultKeybindings(); + // } + + return resultPtr; +} + + // Function Description: // - Returns true if we're running in a packaged context. If we are, then we // have to use the Windows.Storage API's to save/load our files. If we're From 4b8fcd188be1ab99d1b50ec61d44d97330e576df Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 May 2019 16:13:13 -0500 Subject: [PATCH 12/26] Serialize Colorschemes --- .../CascadiaSettingsSerialization.cpp | 4 +- src/cascadia/TerminalApp/ColorScheme.cpp | 63 +++++++++++++++++++ src/cascadia/TerminalApp/ColorScheme.h | 5 +- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 127499e2667..07b7c4758f1 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -279,8 +279,8 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& if (schemeJson.isObject()) { // auto schemeObj = schemeJson.GetObjectW(); -// auto scheme = ColorScheme::FromJson2(schemeJson); - //resultSchemes.emplace_back(std::move(scheme)); + auto scheme = ColorScheme::FromJson2(schemeJson); + resultSchemes.emplace_back(std::move(scheme)); } } } diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index 9245f0aa792..a9c32146900 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -220,6 +220,62 @@ ColorScheme ColorScheme::FromJson(winrt::Windows::Data::Json::JsonObject json) return result; } +// 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: +// - a new ColorScheme instance created from the values in `json` +ColorScheme ColorScheme::FromJson2(Json::Value json) +{ + ColorScheme result{}; + + if (auto name{ json[NAME_KEY_2.data()] }) + { + result._schemeName = winrt::to_hstring(name.asString()); + } + if (auto fgString{ json[FOREGROUND_KEY_2.data()] }) + { + const auto color = Utils::ColorFromHexString(GetWstringFromJson(fgString)); + result._defaultForeground = color; + } + if (auto bgString{ json[BACKGROUND_KEY_2.data()] }) + { + const auto color = Utils::ColorFromHexString(GetWstringFromJson(bgString)); + result._defaultBackground = color; + } + + // Legacy Deserialization. Leave in place to allow forward compatibility + if (auto table{ json[TABLE_KEY_2.data()] }) + { + int i = 0; + + for (auto tableEntry : table) + { + if (tableEntry.isString()) + { + auto color = Utils::ColorFromHexString(GetWstringFromJson(tableEntry)); + result._table.at(i) = color; + } + i++; + } + } + + int i = 0; + for (const auto& current : TABLE_COLORS_2) + { + if (auto str{ json[current.data()] }) + { + const auto color = Utils::ColorFromHexString(GetWstringFromJson(str)); + result._table.at(i) = color; + } + i++; + } + + return result; +} + + std::wstring_view ColorScheme::GetName() const noexcept { return { _schemeName }; @@ -239,3 +295,10 @@ COLORREF ColorScheme::GetBackground() const noexcept { return _defaultBackground; } + + +// TODO put this somewhere reasonable +std::wstring GetWstringFromJson(const Json::Value& json) +{ + return winrt::to_hstring(json.asString()).c_str(); +} \ No newline at end of file diff --git a/src/cascadia/TerminalApp/ColorScheme.h b/src/cascadia/TerminalApp/ColorScheme.h index 768f2dc4674..226d6783db2 100644 --- a/src/cascadia/TerminalApp/ColorScheme.h +++ b/src/cascadia/TerminalApp/ColorScheme.h @@ -19,7 +19,9 @@ Author(s): #include #include #include "../../inc/conattrs.hpp" -#include + +std::wstring GetWstringFromJson(const Json::Value& json); + namespace TerminalApp { @@ -39,6 +41,7 @@ class TerminalApp::ColorScheme winrt::Windows::Data::Json::JsonObject ToJson() const; static ColorScheme FromJson(winrt::Windows::Data::Json::JsonObject json); Json::Value ToJson2() const; + static ColorScheme FromJson2(Json::Value json); std::wstring_view GetName() const noexcept; std::array& GetTable() noexcept; From 96f49ac32e1810c62230f782f9c4804e4367eaa3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 May 2019 16:52:15 -0500 Subject: [PATCH 13/26] Deserialize profiles --- .../AppKeyBindingsSerialization.cpp | 1 + .../CascadiaSettingsSerialization.cpp | 26 ++-- src/cascadia/TerminalApp/ColorScheme.cpp | 8 +- src/cascadia/TerminalApp/ColorScheme.h | 3 - src/cascadia/TerminalApp/Profile.cpp | 118 ++++++++++++++++++ src/cascadia/TerminalApp/Profile.h | 1 + src/cascadia/TerminalApp/TerminalApp.vcxproj | 2 + src/cascadia/TerminalApp/Utils.cpp | 10 ++ src/cascadia/TerminalApp/Utils.h | 16 +++ 9 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 src/cascadia/TerminalApp/Utils.cpp create mode 100644 src/cascadia/TerminalApp/Utils.h diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index a72cc23268e..84d49859d47 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "AppKeyBindingsSerialization.h" #include "KeyChordSerialization.h" +#include "Utils.h" #include using namespace winrt::Microsoft::Terminal::Settings; diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 07b7c4758f1..ea8439b2946 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -278,27 +278,23 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& { if (schemeJson.isObject()) { - // auto schemeObj = schemeJson.GetObjectW(); auto scheme = ColorScheme::FromJson2(schemeJson); resultSchemes.emplace_back(std::move(scheme)); } } } - - // if (json.HasKey(PROFILES_KEY)) - // { - // auto profiles = json.GetNamedArray(PROFILES_KEY); - // for (auto profileJson : profiles) - // { - // if (profileJson.ValueType() == JsonValueType::Object) - // { - // auto profileObj = profileJson.GetObjectW(); - // auto profile = Profile::FromJson(profileObj); - // resultPtr->_profiles.emplace_back(std::move(profile)); - // } - // } - // } + if (auto profiles{ json[PROFILES_KEY_2.data()] }) + { + for (auto profileJson : profiles) + { + if (profileJson.isObject()) + { + auto profile = Profile::FromJson2(profileJson); + resultPtr->_profiles.emplace_back(profile); + } + } + } // // Load the keybindings from the file as well // if (json.HasKey(KEYBINDINGS_KEY)) diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index a9c32146900..caca186c69f 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "ColorScheme.h" #include "../../types/inc/Utils.hpp" +#include "Utils.h" using namespace TerminalApp; using namespace ::Microsoft::Console; @@ -295,10 +296,3 @@ COLORREF ColorScheme::GetBackground() const noexcept { return _defaultBackground; } - - -// TODO put this somewhere reasonable -std::wstring GetWstringFromJson(const Json::Value& json) -{ - return winrt::to_hstring(json.asString()).c_str(); -} \ No newline at end of file diff --git a/src/cascadia/TerminalApp/ColorScheme.h b/src/cascadia/TerminalApp/ColorScheme.h index 226d6783db2..79afceea6cc 100644 --- a/src/cascadia/TerminalApp/ColorScheme.h +++ b/src/cascadia/TerminalApp/ColorScheme.h @@ -20,9 +20,6 @@ Author(s): #include #include "../../inc/conattrs.hpp" -std::wstring GetWstringFromJson(const Json::Value& json); - - namespace TerminalApp { class ColorScheme; diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index dfbe02d8353..097f299c8f7 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "Profile.h" +#include "Utils.h" #include "../../types/inc/Utils.hpp" #include @@ -503,7 +504,124 @@ Profile Profile::FromJson(winrt::Windows::Data::Json::JsonObject json) return result; } +// Method Description: +// - Create a new instance of this class from a serialized JsonObject. +// Arguments: +// - json: an object which should be a serialization of a Profile object. +// Return Value: +// - a new Profile instance created from the values in `json` +Profile Profile::FromJson2(Json::Value json) +{ + Profile result{}; + + // Profile-specific Settings + if (auto name{ json[NAME_KEY_2.data()] }) + { + result._name = GetWstringFromJson(name); + } + if (auto guid{ json[GUID_KEY_2.data()] }) + { + result._guid = Utils::GuidFromString(GetWstringFromJson(guid)); + } + + // Core Settings + if (auto foreground{ json[FOREGROUND_KEY_2.data()] }) + { + const auto color = Utils::ColorFromHexString(GetWstringFromJson(foreground)); + result._defaultForeground = color; + } + if (auto background{ json[BACKGROUND_KEY_2.data()] }) + { + const auto color = Utils::ColorFromHexString(GetWstringFromJson(background)); + result._defaultBackground = color; + } + if (auto colorScheme{ json[COLORSCHEME_KEY_2.data()] }) + { + result._schemeName = GetWstringFromJson(colorScheme); + } + else + { + if (auto colortable{ json[COLORTABLE_KEY_2.data()] }) + { + int i = 0; + for (auto tableEntry : colortable) + { + if (tableEntry.isString()) + { + const auto color = Utils::ColorFromHexString(GetWstringFromJson(tableEntry)); + result._colorTable[i] = color; + } + i++; + } + } + } + if (auto historySize{ json[HISTORYSIZE_KEY_2.data()] }) + { + // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" + result._historySize = historySize.asInt(); + } + if (auto snapOnInput{ json[SNAPONINPUT_KEY_2.data()] }) + { + result._snapOnInput = snapOnInput.asBool(); + } + if (auto cursorColor{ json[CURSORCOLOR_KEY_2.data()] }) + { + const auto color = Utils::ColorFromHexString(GetWstringFromJson(cursorColor)); + result._cursorColor = color; + } + if (auto cursorHeight{ json[CURSORHEIGHT_KEY_2.data()] }) + { + result._cursorHeight = cursorHeight.asUInt(); + } + if (auto cursorShape{ json[CURSORSHAPE_KEY_2.data()] }) + { + result._cursorShape = _ParseCursorShape(GetWstringFromJson(cursorShape)); + } + + // Control Settings + if (auto commandline{ json[COMMANDLINE_KEY_2.data()] }) + { + result._commandline = GetWstringFromJson(commandline); + } + if (auto fontFace{ json[FONTFACE_KEY_2.data()] }) + { + result._fontFace = GetWstringFromJson(fontFace); + } + if (auto fontSize{ json[FONTSIZE_KEY_2.data()] }) + { + result._fontSize = fontSize.asInt(); + } + if (auto acrylicTransparency{ json[ACRYLICTRANSPARENCY_KEY_2.data()] }) + { + result._acrylicTransparency = acrylicTransparency.asFloat(); + } + if (auto useAcrylic{ json[USEACRYLIC_KEY_2.data()] }) + { + result._useAcrylic = useAcrylic.asBool(); + } + if (auto closeOnExit{ json[CLOSEONEXIT_KEY_2.data()] }) + { + result._closeOnExit = closeOnExit.asBool(); + } + if (auto padding{ json[PADDING_KEY_2.data()] }) + { + result._padding = GetWstringFromJson(padding); + } + if (auto scrollbarState{ json[SCROLLBARSTATE_KEY_2.data()] }) + { + result._scrollbarState = GetWstringFromJson(scrollbarState); + } + if (auto startingDirectory{ json[STARTINGDIRECTORY_KEY_2.data()] }) + { + result._startingDirectory = GetWstringFromJson(startingDirectory); + } + if (auto icon{ json[ICON_KEY_2.data()] }) + { + result._icon = GetWstringFromJson(icon); + } + return result; +} void Profile::SetFontFace(std::wstring fontFace) noexcept { diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 8bcce4f1b7a..649270ab915 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -33,6 +33,7 @@ class TerminalApp::Profile final winrt::Windows::Data::Json::JsonObject ToJson() const; static Profile FromJson(winrt::Windows::Data::Json::JsonObject json); Json::Value ToJson2() const; + static Profile FromJson2(Json::Value json); GUID GetGuid() const noexcept; std::wstring_view GetName() const noexcept; diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index f02e3750a8c..d890f6add45 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -43,6 +43,7 @@ + AppKeyBindings.idl @@ -62,6 +63,7 @@ + Create diff --git a/src/cascadia/TerminalApp/Utils.cpp b/src/cascadia/TerminalApp/Utils.cpp new file mode 100644 index 00000000000..c64514617b9 --- /dev/null +++ b/src/cascadia/TerminalApp/Utils.cpp @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Utils.h" + +std::wstring GetWstringFromJson(const Json::Value& json) +{ + return winrt::to_hstring(json.asString()).c_str(); +} diff --git a/src/cascadia/TerminalApp/Utils.h b/src/cascadia/TerminalApp/Utils.h new file mode 100644 index 00000000000..337e72f2b2e --- /dev/null +++ b/src/cascadia/TerminalApp/Utils.h @@ -0,0 +1,16 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- Utils.h + +Abstract: +- Helpers for the TerminalApp project +Author(s): +- Mike Griese - May 2019 + +--*/ +#pragma once + +std::wstring GetWstringFromJson(const Json::Value& json); From a8a1bba0d7a4f74a2f2d2bd8da80160fa8e44eae Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 May 2019 11:36:33 -0500 Subject: [PATCH 14/26] AppKeyBindings deserialization --- .../AppKeyBindingsSerialization.cpp | 45 ++++++++++++++++++- .../CascadiaSettingsSerialization.cpp | 23 +++++----- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index 84d49859d47..6969849d1c1 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -133,5 +133,48 @@ Json::Value AppKeyBindingsSerialization::ToJson2(const winrt::TerminalApp::AppKe winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson2(const Json::Value& json) { - return nullptr; + winrt::TerminalApp::AppKeyBindings newBindings{}; + + for (const auto& value : json) + { + if (value.isObject()) + { + const auto commandString = value[COMMAND_KEY_2.data()]; + const auto keys = value[KEYS_KEY_2.data()]; + + if (commandString && keys) + { + if (!keys.isArray() || keys.size() != 1) + { + continue; + } + const auto keyChordString = winrt::to_hstring(keys[0].asString()); + ShortcutAction action; + + // Try matching the command to one we have + auto found = commandNames.find(GetWstringFromJson(commandString)); + if (found != commandNames.end()) + { + action = found->second; + } + else + { + continue; + } + + // Try parsing the chord + try + { + auto chord = KeyChordSerialization::FromString(keyChordString); + newBindings.SetKeyBinding(action, chord); + } + catch (...) + { + continue; + } + } + } + } + return newBindings; + } diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index ea8439b2946..5beb047ba9b 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -296,18 +296,17 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& } } - // // Load the keybindings from the file as well - // if (json.HasKey(KEYBINDINGS_KEY)) - // { - // const auto keybindingsObj = json.GetNamedArray(KEYBINDINGS_KEY); - // auto loadedBindings = AppKeyBindings::FromJson(keybindingsObj); - // resultPtr->_globals.SetKeybindings(loadedBindings); - // } - // else - // { - // // Create the default keybindings if we couldn't find any keybindings. - // resultPtr->_CreateDefaultKeybindings(); - // } + // Load the keybindings from the file as well + if (auto keybindings{ json[KEYBINDINGS_KEY_2.data()] }) + { + auto loadedBindings = AppKeyBindingsSerialization::FromJson2(keybindings); + resultPtr->_globals.SetKeybindings(loadedBindings); + } + else + { + // Create the default keybindings if we couldn't find any keybindings. + resultPtr->_CreateDefaultKeybindings(); + } return resultPtr; } From 87b795ad60e02a67ae6b13e2cc38e9f64398469a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 May 2019 12:33:05 -0500 Subject: [PATCH 15/26] Roundtrip the everything --- .../CascadiaSettingsSerialization.cpp | 61 +++++++------ .../TerminalApp/GlobalAppSettings.cpp | 90 ++++++++++++++++++- src/cascadia/TerminalApp/GlobalAppSettings.h | 2 + 3 files changed, 125 insertions(+), 28 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 5beb047ba9b..d2499d6ad41 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -4,12 +4,12 @@ #include "pch.h" #include #include "CascadiaSettings.h" +#include "AppKeyBindingsSerialization.h" #include "../../types/inc/utils.hpp" #include #include #include #include -#include "AppKeyBindingsSerialization.h" using namespace ::TerminalApp; using namespace winrt::Microsoft::Terminal::TerminalControl; @@ -29,6 +29,7 @@ static const std::wstring SCHEMES_KEY{ L"schemes" }; static constexpr std::string_view PROFILES_KEY_2{ "profiles" }; static constexpr std::string_view KEYBINDINGS_KEY_2{ "keybindings" }; +static constexpr std::string_view GLOBALS_KEY_2{ "globals" }; static constexpr std::string_view SCHEMES_KEY_2{ "schemes" }; // Method Description: @@ -53,11 +54,6 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa { const auto actualData = fileData.value(); - // // If Parse fails, it'll throw a hresult_error - // JsonObject root = JsonObject::Parse(actualData); - // resultPtr = FromJson(root); - -// Json::Reader reader; Json::Value root; Json::CharReader* reader = Json::CharReaderBuilder::CharReaderBuilder().newCharReader(); auto raw = winrt::to_string(actualData); @@ -68,13 +64,13 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa if (!b) throw winrt::hresult_error(); resultPtr = FromJson2(root); - // Update profile only if it has changed. if (saveOnLoad) { - const JsonObject json = resultPtr->ToJson(); - auto serializedSettings = json.Stringify(); - - if (actualData != serializedSettings) + // Logically compare the json we've parsed from the file to what + // we'd serialize at runtime. If the values are different, then + // write the updated schema back out. + const Json::Value reserialized = resultPtr->ToJson2(); + if (reserialized != root) { resultPtr->SaveAll(); } @@ -109,15 +105,15 @@ void CascadiaSettings::SaveAll() const Json::StreamWriterBuilder wbuilder; // Use 4 spaces to indent instead of \t wbuilder.settings_["indentation"] = " "; - const auto s = Json::writeString(wbuilder, json2); + const auto s = winrt::to_hstring(Json::writeString(wbuilder, json2)); if (_IsPackaged()) { - _SaveAsPackagedApp(serializedSettings); + _SaveAsPackagedApp(s); } else { - _SaveAsUnpackagedApp(serializedSettings); + _SaveAsUnpackagedApp(s); } } @@ -176,9 +172,9 @@ Json::Value CascadiaSettings::ToJson2() const schemesArray.append(scheme.ToJson2()); } + root[GLOBALS_KEY_2.data()] = _globals.ToJson2(); root[PROFILES_KEY_2.data()] = profilesArray; root[SCHEMES_KEY_2.data()] = schemesArray; - root[KEYBINDINGS_KEY_2.data()] = AppKeyBindingsSerialization::ToJson2(_globals.GetKeybindings()); return root; } @@ -260,7 +256,29 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& { std::unique_ptr resultPtr = std::make_unique(); - // resultPtr->_globals = GlobalAppSettings::FromJson(json); + if (auto globals{ json[GLOBALS_KEY_2.data()] }) + { + if (globals.isObject()) + { + resultPtr->_globals = GlobalAppSettings::FromJson2(globals); + } + } + else + { + // If there's no globals key in the root object, then try looking at the + // root object for those properties instead, to gracefully upgrade. + // This will attempt to do the legacy keybindings loading too + resultPtr->_globals = GlobalAppSettings::FromJson2(json); + + // If we didn't find keybindings in the legacy path, then they probably + // don't exist in the file. Create the default keybindings if we + // couldn't find any keybindings. + auto keybindings{ json[KEYBINDINGS_KEY_2.data()] }; + if (!keybindings) + { + resultPtr->_CreateDefaultKeybindings(); + } + } // TODO:MSFT:20737698 - Display an error if we failed to parse settings // What should we do here if these keys aren't found?For default profile, @@ -296,17 +314,6 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& } } - // Load the keybindings from the file as well - if (auto keybindings{ json[KEYBINDINGS_KEY_2.data()] }) - { - auto loadedBindings = AppKeyBindingsSerialization::FromJson2(keybindings); - resultPtr->_globals.SetKeybindings(loadedBindings); - } - else - { - // Create the default keybindings if we couldn't find any keybindings. - resultPtr->_CreateDefaultKeybindings(); - } return resultPtr; } diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 3129296c8e9..fe89cb8b84c 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -5,6 +5,8 @@ #include "GlobalAppSettings.h" #include "../../types/inc/Utils.hpp" #include "../../inc/DefaultSettings.h" +#include "AppKeyBindingsSerialization.h" +#include "Utils.h" using namespace TerminalApp; using namespace winrt::Microsoft::Terminal::Settings; @@ -19,9 +21,19 @@ static const std::wstring INITIALROWS_KEY{ L"initialRows" }; static const std::wstring INITIALCOLS_KEY{ L"initialCols" }; static const std::wstring SHOW_TITLE_IN_TITLEBAR_KEY{ L"showTerminalTitleInTitlebar" }; static const std::wstring REQUESTED_THEME_KEY{ L"requestedTheme" }; - static const std::wstring SHOW_TABS_IN_TITLEBAR_KEY{ L"experimental_showTabsInTitlebar" }; +//////////////////////////////////////////////////////////////////////////////// +static constexpr std::string_view KEYBINDINGS_KEY_2{ "keybindings" }; +static constexpr std::string_view DEFAULTPROFILE_KEY_2{ "defaultProfile" }; +static constexpr std::string_view ALWAYS_SHOW_TABS_KEY_2{ "alwaysShowTabs" }; +static constexpr std::string_view INITIALROWS_KEY_2{ "initialRows" }; +static constexpr std::string_view INITIALCOLS_KEY_2{ "initialCols" }; +static constexpr std::string_view SHOW_TITLE_IN_TITLEBAR_KEY_2{ "showTerminalTitleInTitlebar" }; +static constexpr std::string_view REQUESTED_THEME_KEY_2{ "requestedTheme" }; +static constexpr std::string_view SHOW_TABS_IN_TITLEBAR_KEY_2{ "experimental_showTabsInTitlebar" }; +//////////////////////////////////////////////////////////////////////////////// + static const std::wstring LIGHT_THEME_VALUE{ L"light" }; static const std::wstring DARK_THEME_VALUE{ L"dark" }; static const std::wstring SYSTEM_THEME_VALUE{ L"system" }; @@ -214,6 +226,82 @@ GlobalAppSettings GlobalAppSettings::FromJson(winrt::Windows::Data::Json::JsonOb return result; } + +// Method Description: +// - Serialize this object to a JsonObject. +// Arguments: +// - +// Return Value: +// - a JsonObject which is an equivalent serialization of this object. +Json::Value GlobalAppSettings::ToJson2() const +{ + Json::Value jsonObject; + + jsonObject[DEFAULTPROFILE_KEY_2.data()] = winrt::to_string(Utils::GuidToString(_defaultProfile)); + jsonObject[INITIALROWS_KEY_2.data()] = _initialRows; + jsonObject[INITIALCOLS_KEY_2.data()] = _initialCols; + jsonObject[ALWAYS_SHOW_TABS_KEY_2.data()] = _alwaysShowTabs; + jsonObject[SHOW_TITLE_IN_TITLEBAR_KEY_2.data()] = _showTitleInTitlebar; + jsonObject[SHOW_TABS_IN_TITLEBAR_KEY_2.data()] = _showTabsInTitlebar; + jsonObject[REQUESTED_THEME_KEY_2.data()] = winrt::to_string(_SerializeTheme(_requestedTheme)); + jsonObject[KEYBINDINGS_KEY_2.data()] = AppKeyBindingsSerialization::ToJson2(_keybindings); + + return jsonObject; +} + +// Method Description: +// - Create a new instance of this class from a serialized JsonObject. +// Arguments: +// - json: an object which should be a serialization of a GlobalAppSettings object. +// Return Value: +// - a new GlobalAppSettings instance created from the values in `json` +GlobalAppSettings GlobalAppSettings::FromJson2(Json::Value json) +{ + GlobalAppSettings result{}; + + if (auto defaultProfile{ json[DEFAULTPROFILE_KEY_2.data()] }) + { + auto guid = Utils::GuidFromString(GetWstringFromJson(defaultProfile)); + result._defaultProfile = guid; + } + + if (auto alwaysShowTabs{ json[ALWAYS_SHOW_TABS_KEY_2.data()] }) + { + result._alwaysShowTabs = alwaysShowTabs.asBool(); + } + if (auto initialRows{ json[INITIALROWS_KEY_2.data()] }) + { + result._initialRows = initialRows.asInt(); + } + if (auto initialCols{ json[INITIALCOLS_KEY_2.data()] }) + { + result._initialCols = initialCols.asInt(); + } + + if (auto showTitleInTitlebar{ json[SHOW_TITLE_IN_TITLEBAR_KEY_2.data()] }) + { + result._showTitleInTitlebar = showTitleInTitlebar.asBool(); + } + + if (auto showTabsInTitlebar{ json[SHOW_TABS_IN_TITLEBAR_KEY_2.data()] }) + { + result._showTabsInTitlebar = showTabsInTitlebar.asBool(); + } + + if (auto requestedTheme{ json[REQUESTED_THEME_KEY_2.data()] }) + { + result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme)); + } + + if (auto keybindings{ json[KEYBINDINGS_KEY_2.data()] }) + { + result._keybindings = AppKeyBindingsSerialization::FromJson2(keybindings); + } + + return result; +} + + // Method Description: // - Helper function for converting a user-specified cursor style corresponding // CursorStyle enum value diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index e091bab0410..6e7a2212d04 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -52,6 +52,8 @@ class TerminalApp::GlobalAppSettings final winrt::Windows::Data::Json::JsonObject ToJson() const; static GlobalAppSettings FromJson(winrt::Windows::Data::Json::JsonObject json); + Json::Value ToJson2() const; + static GlobalAppSettings FromJson2(Json::Value json); void ApplyToSettings(winrt::Microsoft::Terminal::Settings::TerminalSettings& settings) const noexcept; From ddbbc120611c85e93c13ed4977c3acb2e1e2c924 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 May 2019 13:48:31 -0500 Subject: [PATCH 16/26] Remove all the old serialization code --- src/cascadia/TerminalApp/AppKeyBindings.cpp | 192 ------------ src/cascadia/TerminalApp/AppKeyBindings.h | 4 - src/cascadia/TerminalApp/AppKeyBindings.idl | 3 - .../AppKeyBindingsSerialization.cpp | 9 +- .../TerminalApp/AppKeyBindingsSerialization.h | 4 +- src/cascadia/TerminalApp/CascadiaSettings.h | 6 +- .../CascadiaSettingsSerialization.cpp | 129 +------- src/cascadia/TerminalApp/ColorScheme.cpp | 117 +------- src/cascadia/TerminalApp/ColorScheme.h | 6 +- .../TerminalApp/GlobalAppSettings.cpp | 102 +------ src/cascadia/TerminalApp/GlobalAppSettings.h | 6 +- src/cascadia/TerminalApp/Profile.cpp | 280 +----------------- src/cascadia/TerminalApp/Profile.h | 6 +- 13 files changed, 44 insertions(+), 820 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindings.cpp b/src/cascadia/TerminalApp/AppKeyBindings.cpp index 275e64cbad2..912e2681088 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindings.cpp @@ -9,84 +9,6 @@ using namespace winrt::Microsoft::Terminal; using namespace winrt::TerminalApp; using namespace winrt::Windows::Data::Json; -static constexpr std::wstring_view KEYS_KEY{ L"keys" }; -static constexpr std::wstring_view COMMAND_KEY{ L"command" }; - -static constexpr std::string_view KEYS_KEY_2{ "keys" }; -static constexpr std::string_view COMMAND_KEY_2{ "command" }; - -static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" }; -static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" }; -static constexpr std::wstring_view NEWTAB_KEY{ L"newTab" }; -static constexpr std::wstring_view NEWTABWITHPROFILE0_KEY{ L"newTabProfile0" }; -static constexpr std::wstring_view NEWTABWITHPROFILE1_KEY{ L"newTabProfile1" }; -static constexpr std::wstring_view NEWTABWITHPROFILE2_KEY{ L"newTabProfile2" }; -static constexpr std::wstring_view NEWTABWITHPROFILE3_KEY{ L"newTabProfile3" }; -static constexpr std::wstring_view NEWTABWITHPROFILE4_KEY{ L"newTabProfile4" }; -static constexpr std::wstring_view NEWTABWITHPROFILE5_KEY{ L"newTabProfile5" }; -static constexpr std::wstring_view NEWTABWITHPROFILE6_KEY{ L"newTabProfile6" }; -static constexpr std::wstring_view NEWTABWITHPROFILE7_KEY{ L"newTabProfile7" }; -static constexpr std::wstring_view NEWTABWITHPROFILE8_KEY{ L"newTabProfile8" }; -static constexpr std::wstring_view NEWWINDOW_KEY{ L"newWindow" }; -static constexpr std::wstring_view CLOSEWINDOW_KEY{ L"closeWindow" }; -static constexpr std::wstring_view CLOSETAB_KEY{ L"closeTab" }; -static constexpr std::wstring_view SWITCHTOTAB_KEY{ L"switchToTab" }; -static constexpr std::wstring_view NEXTTAB_KEY{ L"nextTab" }; -static constexpr std::wstring_view PREVTAB_KEY{ L"prevTab" }; -static constexpr std::wstring_view INCREASEFONTSIZE_KEY{ L"increaseFontSize" }; -static constexpr std::wstring_view DECREASEFONTSIZE_KEY{ L"decreaseFontSize" }; -static constexpr std::wstring_view SCROLLUP_KEY{ L"scrollUp" }; -static constexpr std::wstring_view SCROLLDOWN_KEY{ L"scrollDown" }; -static constexpr std::wstring_view SCROLLUPPAGE_KEY{ L"scrollUpPage" }; -static constexpr std::wstring_view SCROLLDOWNPAGE_KEY{ L"scrollDownPage" }; -static constexpr std::wstring_view SWITCHTOTAB0_KEY{ L"switchToTab0" }; -static constexpr std::wstring_view SWITCHTOTAB1_KEY{ L"switchToTab1" }; -static constexpr std::wstring_view SWITCHTOTAB2_KEY{ L"switchToTab2" }; -static constexpr std::wstring_view SWITCHTOTAB3_KEY{ L"switchToTab3" }; -static constexpr std::wstring_view SWITCHTOTAB4_KEY{ L"switchToTab4" }; -static constexpr std::wstring_view SWITCHTOTAB5_KEY{ L"switchToTab5" }; -static constexpr std::wstring_view SWITCHTOTAB6_KEY{ L"switchToTab6" }; -static constexpr std::wstring_view SWITCHTOTAB7_KEY{ L"switchToTab7" }; -static constexpr std::wstring_view SWITCHTOTAB8_KEY{ L"switchToTab8" }; -static constexpr std::wstring_view OPENSETTINGS_KEY{ L"openSettings" }; - -// Specifically use a map here over an unordered_map. We want to be able to -// iterate over these entries in-order when we're serializing the keybindings. -static const std::map commandNames { - { COPYTEXT_KEY, ShortcutAction::CopyText }, - { PASTETEXT_KEY, ShortcutAction::PasteText }, - { NEWTAB_KEY, ShortcutAction::NewTab }, - { NEWTABWITHPROFILE0_KEY, ShortcutAction::NewTabProfile0 }, - { NEWTABWITHPROFILE1_KEY, ShortcutAction::NewTabProfile1 }, - { NEWTABWITHPROFILE2_KEY, ShortcutAction::NewTabProfile2 }, - { NEWTABWITHPROFILE3_KEY, ShortcutAction::NewTabProfile3 }, - { NEWTABWITHPROFILE4_KEY, ShortcutAction::NewTabProfile4 }, - { NEWTABWITHPROFILE5_KEY, ShortcutAction::NewTabProfile5 }, - { NEWTABWITHPROFILE6_KEY, ShortcutAction::NewTabProfile6 }, - { NEWTABWITHPROFILE7_KEY, ShortcutAction::NewTabProfile7 }, - { NEWTABWITHPROFILE8_KEY, ShortcutAction::NewTabProfile8 }, - { NEWWINDOW_KEY, ShortcutAction::NewWindow }, - { CLOSEWINDOW_KEY, ShortcutAction::CloseWindow }, - { CLOSETAB_KEY, ShortcutAction::CloseTab }, - { NEXTTAB_KEY, ShortcutAction::NextTab }, - { PREVTAB_KEY, ShortcutAction::PrevTab }, - { INCREASEFONTSIZE_KEY, ShortcutAction::IncreaseFontSize }, - { DECREASEFONTSIZE_KEY, ShortcutAction::DecreaseFontSize }, - { SCROLLUP_KEY, ShortcutAction::ScrollUp }, - { SCROLLDOWN_KEY, ShortcutAction::ScrollDown }, - { SCROLLUPPAGE_KEY, ShortcutAction::ScrollUpPage }, - { SCROLLDOWNPAGE_KEY, ShortcutAction::ScrollDownPage }, - { SWITCHTOTAB0_KEY, ShortcutAction::SwitchToTab0 }, - { SWITCHTOTAB1_KEY, ShortcutAction::SwitchToTab1 }, - { SWITCHTOTAB2_KEY, ShortcutAction::SwitchToTab2 }, - { SWITCHTOTAB3_KEY, ShortcutAction::SwitchToTab3 }, - { SWITCHTOTAB4_KEY, ShortcutAction::SwitchToTab4 }, - { SWITCHTOTAB5_KEY, ShortcutAction::SwitchToTab5 }, - { SWITCHTOTAB6_KEY, ShortcutAction::SwitchToTab6 }, - { SWITCHTOTAB7_KEY, ShortcutAction::SwitchToTab7 }, - { SWITCHTOTAB8_KEY, ShortcutAction::SwitchToTab8 }, -}; - namespace winrt::TerminalApp::implementation { void AppKeyBindings::SetKeyBinding(const TerminalApp::ShortcutAction& action, @@ -234,120 +156,6 @@ namespace winrt::TerminalApp::implementation DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs); DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs); - // Method Description: - // - Deserialize an AppKeyBindings from the key mappings that are in the - // array `json`. The json array should contain an array of objects with - // both a `command` string and a `keys` array, where `command` is one of - // the names listed in `commandNames`, and `keys` is an array of - // keypresses. Currently, the array should contain a single string, which - // can be deserialized into a KeyChord. - // Arguments: - // - json: and array of JsonObject's to deserialize into our _keyShortcuts mapping. - // Return Value: - // - the newly constructed AppKeyBindings object. - TerminalApp::AppKeyBindings AppKeyBindings::FromJson(const JsonArray& json) - { - TerminalApp::AppKeyBindings newBindings{}; - - for (const auto& value : json) - { - if (value.ValueType() == JsonValueType::Object) - { - JsonObject obj = value.GetObjectW(); - if (obj.HasKey(COMMAND_KEY) && obj.HasKey(KEYS_KEY)) - { - const auto commandString = obj.GetNamedString(COMMAND_KEY); - const auto keys = obj.GetNamedArray(KEYS_KEY); - if (keys.Size() != 1) - { - continue; - } - const auto keyChordString = keys.GetAt(0).GetString(); - ShortcutAction action; - - // Try matching the command to one we have - auto found = commandNames.find(commandString); - if (found != commandNames.end()) - { - action = found->second; - } - else - { - continue; - } - - // Try parsing the chord - try - { - auto chord = KeyChordSerialization::FromString(keyChordString); - newBindings.SetKeyBinding(action, chord); - } - catch (...) - { - continue; - } - } - } - } - return newBindings; - } - - // Function Description: - // - Small helper to insert a given KeyChord, ShortcutAction pair into the - // given json array - // Arguments: - // - bindingsArray: The JsonArray to insert the object into. - // - chord: The KeyChord to serailize and place in the json array - // - actionName: the name of the ShortcutAction to use with this KeyChord - static void _AddShortcutToJsonArray(const JsonArray& bindingsArray, - const Settings::KeyChord& chord, - const std::wstring_view& actionName) - { - const auto keyString = KeyChordSerialization::ToString(chord); - if (keyString == L"") - { - return; - } - - winrt::Windows::Data::Json::JsonObject jsonObject; - winrt::Windows::Data::Json::JsonArray keysArray; - keysArray.Append(JsonValue::CreateStringValue(keyString)); - jsonObject.Insert(KEYS_KEY, keysArray); - jsonObject.Insert(COMMAND_KEY, JsonValue::CreateStringValue(actionName)); - - bindingsArray.Append(jsonObject); - } - - // Method Description: - // - Serialize this AppKeyBindings to a json array of objects. Each object - // in the array represents a single keybinding, mapping a KeyChord to a - // ShortcutAction. - // Return Value: - // - a JsonArray which is an equivalent serialization of this object. - Windows::Data::Json::JsonArray AppKeyBindings::ToJson() - { - winrt::Windows::Data::Json::JsonArray bindingsArray; - - // Iterate over all the possible actions in the names list, and see if - // it has a binding. - for (auto& actionName : commandNames) - { - const auto searchedForName = actionName.first; - const auto searchedForAction = actionName.second; - for (const auto& kv : _keyShortcuts) - { - const auto chord = kv.first; - const auto command = kv.second; - if (command == searchedForAction) - { - _AddShortcutToJsonArray(bindingsArray, chord, searchedForName); - } - } - } - - return bindingsArray; - } - winrt::Microsoft::Terminal::Settings::KeyChord AppKeyBindings::LookupKeyBinding(TerminalApp::ShortcutAction const& action) { for (const auto& kv : _keyShortcuts) diff --git a/src/cascadia/TerminalApp/AppKeyBindings.h b/src/cascadia/TerminalApp/AppKeyBindings.h index 8b2de03deaa..9b8f6a36810 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.h +++ b/src/cascadia/TerminalApp/AppKeyBindings.h @@ -32,10 +32,6 @@ namespace winrt::TerminalApp::implementation { AppKeyBindings() = default; - static TerminalApp::AppKeyBindings FromJson(Windows::Data::Json::JsonArray const& json); - Windows::Data::Json::JsonArray ToJson(); - Json::Value ToJson2(); - bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc); void SetKeyBinding(TerminalApp::ShortcutAction const& action, winrt::Microsoft::Terminal::Settings::KeyChord const& chord); diff --git a/src/cascadia/TerminalApp/AppKeyBindings.idl b/src/cascadia/TerminalApp/AppKeyBindings.idl index a7f760410ea..4dc357d0d5c 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.idl +++ b/src/cascadia/TerminalApp/AppKeyBindings.idl @@ -63,9 +63,6 @@ namespace TerminalApp { AppKeyBindings(); - Windows.Data.Json.JsonArray ToJson(); - static AppKeyBindings FromJson(Windows.Data.Json.JsonArray json); - void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord); Microsoft.Terminal.Settings.KeyChord LookupKeyBinding(ShortcutAction action); diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index 6969849d1c1..3ae7da3e178 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -10,6 +10,9 @@ using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::TerminalApp; +static constexpr std::string_view KEYS_KEY_2{ "keys" }; +static constexpr std::string_view COMMAND_KEY_2{ "command" }; + static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" }; static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" }; static constexpr std::wstring_view NEWTAB_KEY{ L"newTab" }; @@ -81,8 +84,6 @@ static const std::map commandNames { { SWITCHTOTAB7_KEY, ShortcutAction::SwitchToTab7 }, { SWITCHTOTAB8_KEY, ShortcutAction::SwitchToTab8 }, }; -static constexpr std::string_view KEYS_KEY_2{ "keys" }; -static constexpr std::string_view COMMAND_KEY_2{ "command" }; // Function Description: // - Small helper to insert a given KeyChord, ShortcutAction pair into the @@ -111,7 +112,7 @@ static void _AddShortcutToJsonArray2(Json::Value& bindingsArray, bindingsArray.append(jsonObject); } -Json::Value AppKeyBindingsSerialization::ToJson2(const winrt::TerminalApp::AppKeyBindings& bindings) +Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKeyBindings& bindings) { Json::Value bindingsArray; @@ -131,7 +132,7 @@ Json::Value AppKeyBindingsSerialization::ToJson2(const winrt::TerminalApp::AppKe return bindingsArray; } -winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson2(const Json::Value& json) +winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const Json::Value& json) { winrt::TerminalApp::AppKeyBindings newBindings{}; diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h index b8b64094019..54b3a9e862e 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h @@ -7,6 +7,6 @@ class AppKeyBindingsSerialization final { public: - static winrt::TerminalApp::AppKeyBindings FromJson2(const Json::Value& json); - static Json::Value ToJson2(const winrt::TerminalApp::AppKeyBindings& bindings); + static winrt::TerminalApp::AppKeyBindings FromJson(const Json::Value& json); + static Json::Value ToJson(const winrt::TerminalApp::AppKeyBindings& bindings); }; diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index df7a1cceb4d..b11af753552 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -44,10 +44,8 @@ class TerminalApp::CascadiaSettings final winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept; - winrt::Windows::Data::Json::JsonObject ToJson() const; - static std::unique_ptr FromJson(winrt::Windows::Data::Json::JsonObject json); - Json::Value ToJson2() const; - static std::unique_ptr FromJson2(const Json::Value& json); + Json::Value ToJson() const; + static std::unique_ptr FromJson(const Json::Value& json); static winrt::hstring GetSettingsPath(); diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index d2499d6ad41..a6fb6a09980 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -22,11 +22,6 @@ using namespace ::Microsoft::Console; static const std::wstring FILENAME { L"profiles.json" }; static const std::wstring SETTINGS_FOLDER_NAME{ L"\\Microsoft\\Windows Terminal\\" }; -static const std::wstring PROFILES_KEY{ L"profiles" }; -static const std::wstring KEYBINDINGS_KEY{ L"keybindings" }; -static const std::wstring SCHEMES_KEY{ L"schemes" }; - - static constexpr std::string_view PROFILES_KEY_2{ "profiles" }; static constexpr std::string_view KEYBINDINGS_KEY_2{ "keybindings" }; static constexpr std::string_view GLOBALS_KEY_2{ "globals" }; @@ -62,14 +57,14 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa // TODO: need better error. `reader` might have the exception in a // better format. if (!b) throw winrt::hresult_error(); - resultPtr = FromJson2(root); + resultPtr = FromJson(root); if (saveOnLoad) { // Logically compare the json we've parsed from the file to what // we'd serialize at runtime. If the values are different, then // write the updated schema back out. - const Json::Value reserialized = resultPtr->ToJson2(); + const Json::Value reserialized = resultPtr->ToJson(); if (reserialized != root) { resultPtr->SaveAll(); @@ -98,10 +93,7 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa // - void CascadiaSettings::SaveAll() const { - const JsonObject json = ToJson(); - auto serializedSettings = json.Stringify(); - - const auto json2 = ToJson2(); + const auto json2 = ToJson(); Json::StreamWriterBuilder wbuilder; // Use 4 spaces to indent instead of \t wbuilder.settings_["indentation"] = " "; @@ -123,35 +115,7 @@ void CascadiaSettings::SaveAll() const // - // Return Value: // - a JsonObject which is an equivalent serialization of this object. -JsonObject CascadiaSettings::ToJson() const -{ - // _globals.ToJson will initialize the settings object will all the global - // settings in the root of the object. - winrt::Windows::Data::Json::JsonObject jsonObject = _globals.ToJson(); - - JsonArray schemesArray{}; - const auto& colorSchemes = _globals.GetColorSchemes(); - for (auto& scheme : colorSchemes) - { - schemesArray.Append(scheme.ToJson()); - } - - JsonArray profilesArray{}; - for (auto& profile : _profiles) - { - profilesArray.Append(profile.ToJson()); - } - - jsonObject.Insert(PROFILES_KEY, profilesArray); - jsonObject.Insert(SCHEMES_KEY, schemesArray); - - jsonObject.Insert(KEYBINDINGS_KEY, - _globals.GetKeybindings().ToJson()); - - return jsonObject; -} - -Json::Value CascadiaSettings::ToJson2() const +Json::Value CascadiaSettings::ToJson() const { Json::Value root; // TODO: Globals @@ -162,17 +126,17 @@ Json::Value CascadiaSettings::ToJson2() const Json::Value profilesArray; for (const auto& profile : _profiles) { - profilesArray.append(profile.ToJson2()); + profilesArray.append(profile.ToJson()); } Json::Value schemesArray; const auto& colorSchemes = _globals.GetColorSchemes(); for (auto& scheme : colorSchemes) { - schemesArray.append(scheme.ToJson2()); + schemesArray.append(scheme.ToJson()); } - root[GLOBALS_KEY_2.data()] = _globals.ToJson2(); + root[GLOBALS_KEY_2.data()] = _globals.ToJson(); root[PROFILES_KEY_2.data()] = profilesArray; root[SCHEMES_KEY_2.data()] = schemesArray; @@ -185,74 +149,7 @@ Json::Value CascadiaSettings::ToJson2() const // - json: an object which should be a serialization of a CascadiaSettings object. // Return Value: // - a new CascadiaSettings instance created from the values in `json` -std::unique_ptr CascadiaSettings::FromJson(JsonObject json) -{ - std::unique_ptr resultPtr = std::make_unique(); - - resultPtr->_globals = GlobalAppSettings::FromJson(json); - - // TODO:MSFT:20737698 - Display an error if we failed to parse settings - // What should we do here if these keys aren't found?For default profile, - // we could always pick the first profile and just set that as the default. - // Finding no schemes is probably fine, unless of course one profile - // references a scheme. We could fail with come error saying the - // profiles file is corrupted. - // Not having any profiles is also bad - should we say the file is corrupted? - // Or should we just recreate the default profiles? - - auto& resultSchemes = resultPtr->_globals.GetColorSchemes(); - if (json.HasKey(SCHEMES_KEY)) - { - auto schemes = json.GetNamedArray(SCHEMES_KEY); - for (auto schemeJson : schemes) - { - if (schemeJson.ValueType() == JsonValueType::Object) - { - auto schemeObj = schemeJson.GetObjectW(); - auto scheme = ColorScheme::FromJson(schemeObj); - resultSchemes.emplace_back(std::move(scheme)); - } - } - } - - if (json.HasKey(PROFILES_KEY)) - { - auto profiles = json.GetNamedArray(PROFILES_KEY); - for (auto profileJson : profiles) - { - if (profileJson.ValueType() == JsonValueType::Object) - { - auto profileObj = profileJson.GetObjectW(); - auto profile = Profile::FromJson(profileObj); - resultPtr->_profiles.emplace_back(std::move(profile)); - } - } - } - - // Load the keybindings from the file as well - if (json.HasKey(KEYBINDINGS_KEY)) - { - const auto keybindingsObj = json.GetNamedArray(KEYBINDINGS_KEY); - auto loadedBindings = AppKeyBindings::FromJson(keybindingsObj); - resultPtr->_globals.SetKeybindings(loadedBindings); - } - else - { - // Create the default keybindings if we couldn't find any keybindings. - resultPtr->_CreateDefaultKeybindings(); - } - - return resultPtr; -} - - -// Method Description: -// - Create a new instance of this class from a serialized JsonObject. -// Arguments: -// - json: an object which should be a serialization of a CascadiaSettings object. -// Return Value: -// - a new CascadiaSettings instance created from the values in `json` -std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& json) +std::unique_ptr CascadiaSettings::FromJson(const Json::Value& json) { std::unique_ptr resultPtr = std::make_unique(); @@ -260,7 +157,7 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& { if (globals.isObject()) { - resultPtr->_globals = GlobalAppSettings::FromJson2(globals); + resultPtr->_globals = GlobalAppSettings::FromJson(globals); } } else @@ -268,7 +165,7 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& // If there's no globals key in the root object, then try looking at the // root object for those properties instead, to gracefully upgrade. // This will attempt to do the legacy keybindings loading too - resultPtr->_globals = GlobalAppSettings::FromJson2(json); + resultPtr->_globals = GlobalAppSettings::FromJson(json); // If we didn't find keybindings in the legacy path, then they probably // don't exist in the file. Create the default keybindings if we @@ -296,7 +193,7 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& { if (schemeJson.isObject()) { - auto scheme = ColorScheme::FromJson2(schemeJson); + auto scheme = ColorScheme::FromJson(schemeJson); resultSchemes.emplace_back(std::move(scheme)); } } @@ -308,17 +205,15 @@ std::unique_ptr CascadiaSettings::FromJson2(const Json::Value& { if (profileJson.isObject()) { - auto profile = Profile::FromJson2(profileJson); + auto profile = Profile::FromJson(profileJson); resultPtr->_profiles.emplace_back(profile); } } } - return resultPtr; } - // Function Description: // - Returns true if we're running in a packaged context. If we are, then we // have to use the Windows.Storage API's to save/load our files. If we're diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index caca186c69f..112e6c328a9 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -13,31 +13,6 @@ using namespace winrt::Microsoft::Terminal::TerminalControl; using namespace winrt::TerminalApp; using namespace winrt::Windows::Data::Json; -static const std::wstring NAME_KEY{ L"name" }; -static const std::wstring TABLE_KEY{ L"colors" }; -static const std::wstring FOREGROUND_KEY{ L"foreground" }; -static const std::wstring BACKGROUND_KEY{ L"background" }; -static const std::array TABLE_COLORS = -{ - L"black", - L"red", - L"green", - L"yellow", - L"blue", - L"purple", - L"cyan", - L"white", - L"brightBlack", - L"brightRed", - L"brightGreen", - L"brightYellow", - L"brightBlue", - L"brightPurple", - L"brightCyan", - L"brightWhite" -}; -//////////////////////////////////////////////////////////////////////////////// - static constexpr std::string_view NAME_KEY_2{ "name" }; static constexpr std::string_view TABLE_KEY_2{ "colors" }; static constexpr std::string_view FOREGROUND_KEY_2{ "foreground" }; @@ -61,8 +36,6 @@ static constexpr std::array TABLE_COLORS_2 = "brightCyan", "brightWhite" }; -//////////////////////////////////////////////////////////////////////////////// - ColorScheme::ColorScheme() : _schemeName{ L"" }, @@ -111,32 +84,7 @@ void ColorScheme::ApplyScheme(TerminalSettings terminalSettings) const // - // Return Value: // - a JsonObject which is an equivalent serialization of this object. -JsonObject ColorScheme::ToJson() const -{ - winrt::Windows::Data::Json::JsonObject jsonObject; - - auto fg = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultForeground)); - auto bg = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultBackground)); - auto name = JsonValue::CreateStringValue(_schemeName); - - jsonObject.Insert(NAME_KEY, name); - jsonObject.Insert(FOREGROUND_KEY, fg); - jsonObject.Insert(BACKGROUND_KEY, bg); - - int i = 0; - for (const auto& current : TABLE_COLORS) - { - auto& color = _table.at(i); - auto s = JsonValue::CreateStringValue(Utils::ColorToHexString(color)); - - jsonObject.Insert(current, s); - i++; - } - - return jsonObject; -} - -Json::Value ColorScheme::ToJson2() const +Json::Value ColorScheme::ToJson() const { Json::Value root; auto fg { Utils::ColorToHexString(_defaultForeground) }; @@ -160,74 +108,13 @@ Json::Value ColorScheme::ToJson2() const return root; } - -// 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: -// - a new ColorScheme instance created from the values in `json` -ColorScheme ColorScheme::FromJson(winrt::Windows::Data::Json::JsonObject json) -{ - ColorScheme result{}; - - if (json.HasKey(NAME_KEY)) - { - result._schemeName = json.GetNamedString(NAME_KEY); - } - if (json.HasKey(FOREGROUND_KEY)) - { - const auto fgString = json.GetNamedString(FOREGROUND_KEY); - const auto color = Utils::ColorFromHexString(fgString.c_str()); - result._defaultForeground = color; - } - if (json.HasKey(BACKGROUND_KEY)) - { - const auto bgString = json.GetNamedString(BACKGROUND_KEY); - const auto color = Utils::ColorFromHexString(bgString.c_str()); - result._defaultBackground = color; - } - - // Legacy Deserialization. Leave in place to allow forward compatibility - if (json.HasKey(TABLE_KEY)) - { - const auto table = json.GetNamedArray(TABLE_KEY); - int i = 0; - - for (auto v : table) - { - if (v.ValueType() == JsonValueType::String) - { - auto str = v.GetString(); - auto color = Utils::ColorFromHexString(str.c_str()); - result._table.at(i) = color; - } - i++; - } - } - - int i = 0; - for (const auto& current : TABLE_COLORS) - { - if (json.HasKey(current)) - { - const auto str = json.GetNamedString(current); - const auto color = Utils::ColorFromHexString(str.c_str()); - result._table.at(i) = color; - } - i++; - } - - return result; -} - // 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: // - a new ColorScheme instance created from the values in `json` -ColorScheme ColorScheme::FromJson2(Json::Value json) +ColorScheme ColorScheme::FromJson(Json::Value json) { ColorScheme result{}; diff --git a/src/cascadia/TerminalApp/ColorScheme.h b/src/cascadia/TerminalApp/ColorScheme.h index 79afceea6cc..0182f12ec4e 100644 --- a/src/cascadia/TerminalApp/ColorScheme.h +++ b/src/cascadia/TerminalApp/ColorScheme.h @@ -35,10 +35,8 @@ class TerminalApp::ColorScheme void ApplyScheme(winrt::Microsoft::Terminal::Settings::TerminalSettings terminalSettings) const; - winrt::Windows::Data::Json::JsonObject ToJson() const; - static ColorScheme FromJson(winrt::Windows::Data::Json::JsonObject json); - Json::Value ToJson2() const; - static ColorScheme FromJson2(Json::Value json); + Json::Value ToJson() const; + static ColorScheme FromJson(Json::Value json); std::wstring_view GetName() const noexcept; std::array& GetTable() noexcept; diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index fe89cb8b84c..738417aca75 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -15,15 +15,6 @@ using namespace winrt::Windows::Data::Json; using namespace winrt::Windows::UI::Xaml; using namespace ::Microsoft::Console; -static const std::wstring DEFAULTPROFILE_KEY{ L"defaultProfile" }; -static const std::wstring ALWAYS_SHOW_TABS_KEY{ L"alwaysShowTabs" }; -static const std::wstring INITIALROWS_KEY{ L"initialRows" }; -static const std::wstring INITIALCOLS_KEY{ L"initialCols" }; -static const std::wstring SHOW_TITLE_IN_TITLEBAR_KEY{ L"showTerminalTitleInTitlebar" }; -static const std::wstring REQUESTED_THEME_KEY{ L"requestedTheme" }; -static const std::wstring SHOW_TABS_IN_TITLEBAR_KEY{ L"experimental_showTabsInTitlebar" }; - -//////////////////////////////////////////////////////////////////////////////// static constexpr std::string_view KEYBINDINGS_KEY_2{ "keybindings" }; static constexpr std::string_view DEFAULTPROFILE_KEY_2{ "defaultProfile" }; static constexpr std::string_view ALWAYS_SHOW_TABS_KEY_2{ "alwaysShowTabs" }; @@ -32,7 +23,6 @@ static constexpr std::string_view INITIALCOLS_KEY_2{ "initialCols" }; static constexpr std::string_view SHOW_TITLE_IN_TITLEBAR_KEY_2{ "showTerminalTitleInTitlebar" }; static constexpr std::string_view REQUESTED_THEME_KEY_2{ "requestedTheme" }; static constexpr std::string_view SHOW_TABS_IN_TITLEBAR_KEY_2{ "experimental_showTabsInTitlebar" }; -//////////////////////////////////////////////////////////////////////////////// static const std::wstring LIGHT_THEME_VALUE{ L"light" }; static const std::wstring DARK_THEME_VALUE{ L"dark" }; @@ -149,91 +139,7 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce // - // Return Value: // - a JsonObject which is an equivalent serialization of this object. -JsonObject GlobalAppSettings::ToJson() const -{ - winrt::Windows::Data::Json::JsonObject jsonObject; - - const auto guidStr = Utils::GuidToString(_defaultProfile); - const auto defaultProfile = JsonValue::CreateStringValue(guidStr); - const auto initialRows = JsonValue::CreateNumberValue(_initialRows); - const auto initialCols = JsonValue::CreateNumberValue(_initialCols); - - jsonObject.Insert(DEFAULTPROFILE_KEY, defaultProfile); - jsonObject.Insert(INITIALROWS_KEY, initialRows); - jsonObject.Insert(INITIALCOLS_KEY, initialCols); - jsonObject.Insert(ALWAYS_SHOW_TABS_KEY, - JsonValue::CreateBooleanValue(_alwaysShowTabs)); - jsonObject.Insert(SHOW_TITLE_IN_TITLEBAR_KEY, - JsonValue::CreateBooleanValue(_showTitleInTitlebar)); - - jsonObject.Insert(SHOW_TABS_IN_TITLEBAR_KEY, - JsonValue::CreateBooleanValue(_showTabsInTitlebar)); - jsonObject.Insert(REQUESTED_THEME_KEY, - JsonValue::CreateStringValue(_SerializeTheme(_requestedTheme))); - - // We'll add the keybindings later in CascadiaSettings, because if we do it - // here, they'll appear before the profiles. - - return jsonObject; -} - -// Method Description: -// - Create a new instance of this class from a serialized JsonObject. -// Arguments: -// - json: an object which should be a serialization of a GlobalAppSettings object. -// Return Value: -// - a new GlobalAppSettings instance created from the values in `json` -GlobalAppSettings GlobalAppSettings::FromJson(winrt::Windows::Data::Json::JsonObject json) -{ - GlobalAppSettings result{}; - - if (json.HasKey(DEFAULTPROFILE_KEY)) - { - auto guidString = json.GetNamedString(DEFAULTPROFILE_KEY); - auto guid = Utils::GuidFromString(guidString.c_str()); - result._defaultProfile = guid; - } - - if (json.HasKey(ALWAYS_SHOW_TABS_KEY)) - { - result._alwaysShowTabs = json.GetNamedBoolean(ALWAYS_SHOW_TABS_KEY); - } - if (json.HasKey(INITIALROWS_KEY)) - { - result._initialRows = static_cast(json.GetNamedNumber(INITIALROWS_KEY)); - } - if (json.HasKey(INITIALCOLS_KEY)) - { - result._initialCols = static_cast(json.GetNamedNumber(INITIALCOLS_KEY)); - } - - if (json.HasKey(SHOW_TITLE_IN_TITLEBAR_KEY)) - { - result._showTitleInTitlebar = json.GetNamedBoolean(SHOW_TITLE_IN_TITLEBAR_KEY); - } - - if (json.HasKey(SHOW_TABS_IN_TITLEBAR_KEY)) - { - result._showTabsInTitlebar = json.GetNamedBoolean(SHOW_TABS_IN_TITLEBAR_KEY); - } - - if (json.HasKey(REQUESTED_THEME_KEY)) - { - const auto themeStr = json.GetNamedString(REQUESTED_THEME_KEY); - result._requestedTheme = _ParseTheme(themeStr.c_str()); - } - - return result; -} - - -// Method Description: -// - Serialize this object to a JsonObject. -// Arguments: -// - -// Return Value: -// - a JsonObject which is an equivalent serialization of this object. -Json::Value GlobalAppSettings::ToJson2() const +Json::Value GlobalAppSettings::ToJson() const { Json::Value jsonObject; @@ -244,7 +150,7 @@ Json::Value GlobalAppSettings::ToJson2() const jsonObject[SHOW_TITLE_IN_TITLEBAR_KEY_2.data()] = _showTitleInTitlebar; jsonObject[SHOW_TABS_IN_TITLEBAR_KEY_2.data()] = _showTabsInTitlebar; jsonObject[REQUESTED_THEME_KEY_2.data()] = winrt::to_string(_SerializeTheme(_requestedTheme)); - jsonObject[KEYBINDINGS_KEY_2.data()] = AppKeyBindingsSerialization::ToJson2(_keybindings); + jsonObject[KEYBINDINGS_KEY_2.data()] = AppKeyBindingsSerialization::ToJson(_keybindings); return jsonObject; } @@ -255,7 +161,7 @@ Json::Value GlobalAppSettings::ToJson2() const // - json: an object which should be a serialization of a GlobalAppSettings object. // Return Value: // - a new GlobalAppSettings instance created from the values in `json` -GlobalAppSettings GlobalAppSettings::FromJson2(Json::Value json) +GlobalAppSettings GlobalAppSettings::FromJson(Json::Value json) { GlobalAppSettings result{}; @@ -295,7 +201,7 @@ GlobalAppSettings GlobalAppSettings::FromJson2(Json::Value json) if (auto keybindings{ json[KEYBINDINGS_KEY_2.data()] }) { - result._keybindings = AppKeyBindingsSerialization::FromJson2(keybindings); + result._keybindings = AppKeyBindingsSerialization::FromJson(keybindings); } return result; diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 6e7a2212d04..1a28290e045 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -50,10 +50,8 @@ class TerminalApp::GlobalAppSettings final winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept; - winrt::Windows::Data::Json::JsonObject ToJson() const; - static GlobalAppSettings FromJson(winrt::Windows::Data::Json::JsonObject json); - Json::Value ToJson2() const; - static GlobalAppSettings FromJson2(Json::Value json); + Json::Value ToJson() const; + static GlobalAppSettings FromJson(Json::Value json); void ApplyToSettings(winrt::Microsoft::Terminal::Settings::TerminalSettings& settings) const noexcept; diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 097f299c8f7..5e91e9527f2 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -13,43 +13,6 @@ using namespace winrt::TerminalApp; using namespace winrt::Windows::Data::Json; using namespace ::Microsoft::Console; - -static const std::wstring NAME_KEY{ L"name" }; -static const std::wstring GUID_KEY{ L"guid" }; -static const std::wstring COLORSCHEME_KEY{ L"colorscheme" }; - -static const std::wstring FOREGROUND_KEY{ L"foreground" }; -static const std::wstring BACKGROUND_KEY{ L"background" }; -static const std::wstring COLORTABLE_KEY{ L"colorTable" }; -static const std::wstring HISTORYSIZE_KEY{ L"historySize" }; -static const std::wstring SNAPONINPUT_KEY{ L"snapOnInput" }; -static const std::wstring CURSORCOLOR_KEY{ L"cursorColor" }; -static const std::wstring CURSORSHAPE_KEY{ L"cursorShape" }; -static const std::wstring CURSORHEIGHT_KEY{ L"cursorHeight" }; - -static const std::wstring COMMANDLINE_KEY{ L"commandline" }; -static const std::wstring FONTFACE_KEY{ L"fontFace" }; -static const std::wstring FONTSIZE_KEY{ L"fontSize" }; -static const std::wstring ACRYLICTRANSPARENCY_KEY{ L"acrylicOpacity" }; -static const std::wstring USEACRYLIC_KEY{ L"useAcrylic" }; -static const std::wstring SCROLLBARSTATE_KEY{ L"scrollbarState" }; -static const std::wstring CLOSEONEXIT_KEY{ L"closeOnExit" }; -static const std::wstring PADDING_KEY{ L"padding" }; -static const std::wstring STARTINGDIRECTORY_KEY{ L"startingDirectory" }; -static const std::wstring ICON_KEY{ L"icon" }; - -// Possible values for Scrollbar state -static const std::wstring ALWAYS_VISIBLE{ L"visible" }; -static const std::wstring ALWAYS_HIDE{ L"hidden" }; - -// Possible values for Cursor Shape -static const std::wstring CURSORSHAPE_VINTAGE{ L"vintage" }; -static const std::wstring CURSORSHAPE_BAR{ L"bar" }; -static const std::wstring CURSORSHAPE_UNDERSCORE{ L"underscore" }; -static const std::wstring CURSORSHAPE_FILLEDBOX{ L"filledBox" }; -static const std::wstring CURSORSHAPE_EMPTYBOX{ L"emptyBox" }; - -//////////////////////////////////////////////////////////////////////////////// static constexpr std::string_view NAME_KEY_2{ "name" }; static constexpr std::string_view GUID_KEY_2{ "guid" }; static constexpr std::string_view COLORSCHEME_KEY_2{ "colorscheme" }; @@ -73,9 +36,17 @@ static constexpr std::string_view CLOSEONEXIT_KEY_2{ "closeOnExit" }; static constexpr std::string_view PADDING_KEY_2{ "padding" }; static constexpr std::string_view STARTINGDIRECTORY_KEY_2{ "startingDirectory" }; static constexpr std::string_view ICON_KEY_2{ "icon" }; -//////////////////////////////////////////////////////////////////////////////// +// Possible values for Scrollbar state +static const std::wstring ALWAYS_VISIBLE{ L"visible" }; +static const std::wstring ALWAYS_HIDE{ L"hidden" }; +// Possible values for Cursor Shape +static const std::wstring CURSORSHAPE_VINTAGE{ L"vintage" }; +static const std::wstring CURSORSHAPE_BAR{ L"bar" }; +static const std::wstring CURSORSHAPE_UNDERSCORE{ L"underscore" }; +static const std::wstring CURSORSHAPE_FILLEDBOX{ L"filledBox" }; +static const std::wstring CURSORSHAPE_EMPTYBOX{ L"emptyBox" }; Profile::Profile() : _guid{}, @@ -207,102 +178,7 @@ TerminalSettings Profile::CreateTerminalSettings(const std::vector& // - // Return Value: // - a JsonObject which is an equivalent serialization of this object. -JsonObject Profile::ToJson() const -{ - winrt::Windows::Data::Json::JsonObject jsonObject; - - // Profile-specific settings - const auto guidStr = Utils::GuidToString(_guid); - const auto guid = JsonValue::CreateStringValue(guidStr); - const auto name = JsonValue::CreateStringValue(_name); - - // Core Settings - const auto historySize = JsonValue::CreateNumberValue(_historySize); - const auto snapOnInput = JsonValue::CreateBooleanValue(_snapOnInput); - const auto cursorColor = JsonValue::CreateStringValue(Utils::ColorToHexString(_cursorColor)); - - // Control Settings - const auto cmdline = JsonValue::CreateStringValue(_commandline); - const auto fontFace = JsonValue::CreateStringValue(_fontFace); - const auto fontSize = JsonValue::CreateNumberValue(_fontSize); - const auto acrylicTransparency = JsonValue::CreateNumberValue(_acrylicTransparency); - const auto useAcrylic = JsonValue::CreateBooleanValue(_useAcrylic); - const auto closeOnExit = JsonValue::CreateBooleanValue(_closeOnExit); - const auto padding = JsonValue::CreateStringValue(_padding); - - if (_startingDirectory) - { - const auto startingDirectory = JsonValue::CreateStringValue(_startingDirectory.value()); - jsonObject.Insert(STARTINGDIRECTORY_KEY, startingDirectory); - } - - jsonObject.Insert(GUID_KEY, guid); - jsonObject.Insert(NAME_KEY, name); - - // Core Settings - if (_defaultForeground) - { - const auto defaultForeground = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultForeground.value())); - jsonObject.Insert(FOREGROUND_KEY, defaultForeground); - } - if (_defaultBackground) - { - const auto defaultBackground = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultBackground.value())); - jsonObject.Insert(BACKGROUND_KEY, defaultBackground); - } - if (_schemeName) - { - const auto scheme = JsonValue::CreateStringValue(_schemeName.value()); - jsonObject.Insert(COLORSCHEME_KEY, scheme); - } - else - { - JsonArray tableArray{}; - for (auto& color : _colorTable) - { - auto s = Utils::ColorToHexString(color); - tableArray.Append(JsonValue::CreateStringValue(s)); - } - - jsonObject.Insert(COLORTABLE_KEY, tableArray); - - } - jsonObject.Insert(HISTORYSIZE_KEY, historySize); - jsonObject.Insert(SNAPONINPUT_KEY, snapOnInput); - jsonObject.Insert(CURSORCOLOR_KEY, cursorColor); - - // Only add the cursor height property if we're a legacy-style cursor. - if (_cursorShape == CursorStyle::Vintage) - { - jsonObject.Insert(CURSORHEIGHT_KEY, JsonValue::CreateNumberValue(_cursorHeight)); - } - jsonObject.Insert(CURSORSHAPE_KEY, JsonValue::CreateStringValue(_SerializeCursorStyle(_cursorShape))); - - // Control Settings - jsonObject.Insert(COMMANDLINE_KEY, cmdline); - jsonObject.Insert(FONTFACE_KEY, fontFace); - jsonObject.Insert(FONTSIZE_KEY, fontSize); - jsonObject.Insert(ACRYLICTRANSPARENCY_KEY, acrylicTransparency); - jsonObject.Insert(USEACRYLIC_KEY, useAcrylic); - jsonObject.Insert(CLOSEONEXIT_KEY, closeOnExit); - jsonObject.Insert(PADDING_KEY, padding); - - if (_scrollbarState) - { - const auto scrollbarState = JsonValue::CreateStringValue(_scrollbarState.value()); - jsonObject.Insert(SCROLLBARSTATE_KEY, scrollbarState); - } - - if (_icon) - { - const auto icon = JsonValue::CreateStringValue(_icon.value()); - jsonObject.Insert(ICON_KEY, icon); - } - - return jsonObject; -} - -Json::Value Profile::ToJson2() const +Json::Value Profile::ToJson() const { Json::Value root; @@ -370,147 +246,13 @@ Json::Value Profile::ToJson2() const return root; } - - -// Method Description: -// - Create a new instance of this class from a serialized JsonObject. -// Arguments: -// - json: an object which should be a serialization of a Profile object. -// Return Value: -// - a new Profile instance created from the values in `json` -Profile Profile::FromJson(winrt::Windows::Data::Json::JsonObject json) -{ - Profile result{}; - - // Profile-specific Settings - if (json.HasKey(NAME_KEY)) - { - result._name = json.GetNamedString(NAME_KEY); - } - if (json.HasKey(GUID_KEY)) - { - const auto guidString = json.GetNamedString(GUID_KEY); - // TODO: MSFT:20737698 - if this fails, display an approriate error - const auto guid = Utils::GuidFromString(guidString.c_str()); - result._guid = guid; - } - - // Core Settings - if (json.HasKey(FOREGROUND_KEY)) - { - const auto fgString = json.GetNamedString(FOREGROUND_KEY); - // TODO: MSFT:20737698 - if this fails, display an approriate error - const auto color = Utils::ColorFromHexString(fgString.c_str()); - result._defaultForeground = color; - } - if (json.HasKey(BACKGROUND_KEY)) - { - const auto bgString = json.GetNamedString(BACKGROUND_KEY); - // TODO: MSFT:20737698 - if this fails, display an approriate error - const auto color = Utils::ColorFromHexString(bgString.c_str()); - result._defaultBackground = color; - } - if (json.HasKey(COLORSCHEME_KEY)) - { - result._schemeName = json.GetNamedString(COLORSCHEME_KEY); - } - else - { - if (json.HasKey(COLORTABLE_KEY)) - { - const auto table = json.GetNamedArray(COLORTABLE_KEY); - int i = 0; - for (auto v : table) - { - if (v.ValueType() == JsonValueType::String) - { - const auto str = v.GetString(); - // TODO: MSFT:20737698 - if this fails, display an approriate error - const auto color = Utils::ColorFromHexString(str.c_str()); - result._colorTable[i] = color; - } - i++; - } - } - } - if (json.HasKey(HISTORYSIZE_KEY)) - { - // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" - result._historySize = static_cast(json.GetNamedNumber(HISTORYSIZE_KEY)); - } - if (json.HasKey(SNAPONINPUT_KEY)) - { - result._snapOnInput = json.GetNamedBoolean(SNAPONINPUT_KEY); - } - if (json.HasKey(CURSORCOLOR_KEY)) - { - const auto cursorString = json.GetNamedString(CURSORCOLOR_KEY); - // TODO: MSFT:20737698 - if this fails, display an approriate error - const auto color = Utils::ColorFromHexString(cursorString.c_str()); - result._cursorColor = color; - } - if (json.HasKey(CURSORHEIGHT_KEY)) - { - result._cursorHeight = static_cast(json.GetNamedNumber(CURSORHEIGHT_KEY)); - } - if (json.HasKey(CURSORSHAPE_KEY)) - { - const auto shapeString = json.GetNamedString(CURSORSHAPE_KEY); - result._cursorShape = _ParseCursorShape(shapeString.c_str()); - } - - // Control Settings - if (json.HasKey(COMMANDLINE_KEY)) - { - result._commandline = json.GetNamedString(COMMANDLINE_KEY); - } - if (json.HasKey(FONTFACE_KEY)) - { - result._fontFace = json.GetNamedString(FONTFACE_KEY); - } - if (json.HasKey(FONTSIZE_KEY)) - { - result._fontSize = static_cast(json.GetNamedNumber(FONTSIZE_KEY)); - } - if (json.HasKey(ACRYLICTRANSPARENCY_KEY)) - { - result._acrylicTransparency = json.GetNamedNumber(ACRYLICTRANSPARENCY_KEY); - } - if (json.HasKey(USEACRYLIC_KEY)) - { - result._useAcrylic = json.GetNamedBoolean(USEACRYLIC_KEY); - } - if (json.HasKey(CLOSEONEXIT_KEY)) - { - result._closeOnExit = json.GetNamedBoolean(CLOSEONEXIT_KEY); - } - if (json.HasKey(PADDING_KEY)) - { - result._padding = json.GetNamedString(PADDING_KEY); - } - if (json.HasKey(SCROLLBARSTATE_KEY)) - { - result._scrollbarState = json.GetNamedString(SCROLLBARSTATE_KEY); - } - if (json.HasKey(STARTINGDIRECTORY_KEY)) - { - result._startingDirectory = json.GetNamedString(STARTINGDIRECTORY_KEY); - } - if (json.HasKey(ICON_KEY)) - { - result._icon = json.GetNamedString(ICON_KEY); - } - - return result; -} - // Method Description: // - Create a new instance of this class from a serialized JsonObject. // Arguments: // - json: an object which should be a serialization of a Profile object. // Return Value: // - a new Profile instance created from the values in `json` -Profile Profile::FromJson2(Json::Value json) +Profile Profile::FromJson(Json::Value json) { Profile result{}; diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 649270ab915..14102b54b63 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -30,10 +30,8 @@ class TerminalApp::Profile final winrt::Microsoft::Terminal::Settings::TerminalSettings CreateTerminalSettings(const std::vector<::TerminalApp::ColorScheme>& schemes) const; - winrt::Windows::Data::Json::JsonObject ToJson() const; - static Profile FromJson(winrt::Windows::Data::Json::JsonObject json); - Json::Value ToJson2() const; - static Profile FromJson2(Json::Value json); + Json::Value ToJson() const; + static Profile FromJson(Json::Value json); GUID GetGuid() const noexcept; std::wstring_view GetName() const noexcept; From 6c268a0e660572d4262b5dc86e6e997b879564a9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 24 May 2019 12:16:03 -0500 Subject: [PATCH 17/26] gah serialize the starting directory too --- src/cascadia/TerminalApp/Profile.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 04571a89801..21d1e7b251f 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -247,6 +247,11 @@ Json::Value Profile::ToJson() const root[ICON_KEY_2.data()] = icon; } + if (_startingDirectory) + { + root[STARTINGDIRECTORY_KEY_2.data()] = winrt::to_string(_startingDirectory.value()); + } + return root; } From a2aa9db9ce9585de68c2b276cde2aeec188ee666 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 24 May 2019 12:18:02 -0500 Subject: [PATCH 18/26] Remove all the `_2`'s --- .../AppKeyBindingsSerialization.cpp | 12 +- .../CascadiaSettingsSerialization.cpp | 22 +-- src/cascadia/TerminalApp/ColorScheme.cpp | 28 ++-- .../TerminalApp/GlobalAppSettings.cpp | 48 +++---- src/cascadia/TerminalApp/Profile.cpp | 130 +++++++++--------- 5 files changed, 120 insertions(+), 120 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index e4a0df4ca72..4840e004027 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -10,8 +10,8 @@ using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::TerminalApp; -static constexpr std::string_view KEYS_KEY_2{ "keys" }; -static constexpr std::string_view COMMAND_KEY_2{ "command" }; +static constexpr std::string_view KEYS_KEY{ "keys" }; +static constexpr std::string_view COMMAND_KEY{ "command" }; static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" }; static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" }; @@ -106,8 +106,8 @@ static void _AddShortcutToJsonArray2(Json::Value& bindingsArray, Json::Value keysArray; keysArray.append(winrt::to_string(keyString)); - jsonObject[KEYS_KEY_2.data()] = keysArray; - jsonObject[COMMAND_KEY_2.data()] = winrt::to_string(actionName); + jsonObject[KEYS_KEY.data()] = keysArray; + jsonObject[COMMAND_KEY.data()] = winrt::to_string(actionName); bindingsArray.append(jsonObject); } @@ -140,8 +140,8 @@ winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const J { if (value.isObject()) { - const auto commandString = value[COMMAND_KEY_2.data()]; - const auto keys = value[KEYS_KEY_2.data()]; + const auto commandString = value[COMMAND_KEY.data()]; + const auto keys = value[KEYS_KEY.data()]; if (commandString && keys) { diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 67f295994d4..1950bbd342a 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -22,10 +22,10 @@ using namespace ::Microsoft::Console; static constexpr std::wstring_view FILENAME { L"profiles.json" }; static constexpr std::wstring_view SETTINGS_FOLDER_NAME{ L"\\Microsoft\\Windows Terminal\\" }; -static constexpr std::string_view PROFILES_KEY_2{ "profiles" }; -static constexpr std::string_view KEYBINDINGS_KEY_2{ "keybindings" }; -static constexpr std::string_view GLOBALS_KEY_2{ "globals" }; -static constexpr std::string_view SCHEMES_KEY_2{ "schemes" }; +static constexpr std::string_view PROFILES_KEY{ "profiles" }; +static constexpr std::string_view KEYBINDINGS_KEY{ "keybindings" }; +static constexpr std::string_view GLOBALS_KEY{ "globals" }; +static constexpr std::string_view SCHEMES_KEY{ "schemes" }; // Method Description: // - Creates a CascadiaSettings from whatever's saved on disk, or instantiates @@ -137,9 +137,9 @@ Json::Value CascadiaSettings::ToJson() const schemesArray.append(scheme.ToJson()); } - root[GLOBALS_KEY_2.data()] = _globals.ToJson(); - root[PROFILES_KEY_2.data()] = profilesArray; - root[SCHEMES_KEY_2.data()] = schemesArray; + root[GLOBALS_KEY.data()] = _globals.ToJson(); + root[PROFILES_KEY.data()] = profilesArray; + root[SCHEMES_KEY.data()] = schemesArray; return root; } @@ -154,7 +154,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& { std::unique_ptr resultPtr = std::make_unique(); - if (auto globals{ json[GLOBALS_KEY_2.data()] }) + if (auto globals{ json[GLOBALS_KEY.data()] }) { if (globals.isObject()) { @@ -171,7 +171,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& // If we didn't find keybindings in the legacy path, then they probably // don't exist in the file. Create the default keybindings if we // couldn't find any keybindings. - auto keybindings{ json[KEYBINDINGS_KEY_2.data()] }; + auto keybindings{ json[KEYBINDINGS_KEY.data()] }; if (!keybindings) { resultPtr->_CreateDefaultKeybindings(); @@ -188,7 +188,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& // Or should we just recreate the default profiles? auto& resultSchemes = resultPtr->_globals.GetColorSchemes(); - if (auto schemes{ json[SCHEMES_KEY_2.data()] }) + if (auto schemes{ json[SCHEMES_KEY.data()] }) { for (auto schemeJson : schemes) { @@ -200,7 +200,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& } } - if (auto profiles{ json[PROFILES_KEY_2.data()] }) + if (auto profiles{ json[PROFILES_KEY.data()] }) { for (auto profileJson : profiles) { diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index 112e6c328a9..feafb65903f 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -13,11 +13,11 @@ using namespace winrt::Microsoft::Terminal::TerminalControl; using namespace winrt::TerminalApp; using namespace winrt::Windows::Data::Json; -static constexpr std::string_view NAME_KEY_2{ "name" }; -static constexpr std::string_view TABLE_KEY_2{ "colors" }; -static constexpr std::string_view FOREGROUND_KEY_2{ "foreground" }; -static constexpr std::string_view BACKGROUND_KEY_2{ "background" }; -static constexpr std::array TABLE_COLORS_2 = +static constexpr std::string_view NAME_KEY{ "name" }; +static constexpr std::string_view TABLE_KEY{ "colors" }; +static constexpr std::string_view FOREGROUND_KEY{ "foreground" }; +static constexpr std::string_view BACKGROUND_KEY{ "background" }; +static constexpr std::array TABLE_COLORS = { "black", "red", @@ -91,12 +91,12 @@ Json::Value ColorScheme::ToJson() const auto bg { Utils::ColorToHexString(_defaultBackground) }; auto name { _schemeName }; - root[NAME_KEY_2.data()] = winrt::to_string(name); - root[FOREGROUND_KEY_2.data()] = winrt::to_string(fg); - root[BACKGROUND_KEY_2.data()] = winrt::to_string(bg); + root[NAME_KEY.data()] = winrt::to_string(name); + root[FOREGROUND_KEY.data()] = winrt::to_string(fg); + root[BACKGROUND_KEY.data()] = winrt::to_string(bg); int i = 0; - for (const auto& colorName : TABLE_COLORS_2) + for (const auto& colorName : TABLE_COLORS) { auto& colorValue = _table.at(i); auto colorHexString = Utils::ColorToHexString(colorValue); @@ -118,23 +118,23 @@ ColorScheme ColorScheme::FromJson(Json::Value json) { ColorScheme result{}; - if (auto name{ json[NAME_KEY_2.data()] }) + if (auto name{ json[NAME_KEY.data()] }) { result._schemeName = winrt::to_hstring(name.asString()); } - if (auto fgString{ json[FOREGROUND_KEY_2.data()] }) + if (auto fgString{ json[FOREGROUND_KEY.data()] }) { const auto color = Utils::ColorFromHexString(GetWstringFromJson(fgString)); result._defaultForeground = color; } - if (auto bgString{ json[BACKGROUND_KEY_2.data()] }) + if (auto bgString{ json[BACKGROUND_KEY.data()] }) { const auto color = Utils::ColorFromHexString(GetWstringFromJson(bgString)); result._defaultBackground = color; } // Legacy Deserialization. Leave in place to allow forward compatibility - if (auto table{ json[TABLE_KEY_2.data()] }) + if (auto table{ json[TABLE_KEY.data()] }) { int i = 0; @@ -150,7 +150,7 @@ ColorScheme ColorScheme::FromJson(Json::Value json) } int i = 0; - for (const auto& current : TABLE_COLORS_2) + for (const auto& current : TABLE_COLORS) { if (auto str{ json[current.data()] }) { diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 57141aae309..82e104d151f 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -15,14 +15,14 @@ using namespace winrt::Windows::Data::Json; using namespace winrt::Windows::UI::Xaml; using namespace ::Microsoft::Console; -static constexpr std::string_view KEYBINDINGS_KEY_2{ "keybindings" }; -static constexpr std::string_view DEFAULTPROFILE_KEY_2{ "defaultProfile" }; -static constexpr std::string_view ALWAYS_SHOW_TABS_KEY_2{ "alwaysShowTabs" }; -static constexpr std::string_view INITIALROWS_KEY_2{ "initialRows" }; -static constexpr std::string_view INITIALCOLS_KEY_2{ "initialCols" }; -static constexpr std::string_view SHOW_TITLE_IN_TITLEBAR_KEY_2{ "showTerminalTitleInTitlebar" }; -static constexpr std::string_view REQUESTED_THEME_KEY_2{ "requestedTheme" }; -static constexpr std::string_view SHOW_TABS_IN_TITLEBAR_KEY_2{ "showTabsInTitlebar" }; +static constexpr std::string_view KEYBINDINGS_KEY{ "keybindings" }; +static constexpr std::string_view DEFAULTPROFILE_KEY{ "defaultProfile" }; +static constexpr std::string_view ALWAYS_SHOW_TABS_KEY{ "alwaysShowTabs" }; +static constexpr std::string_view INITIALROWS_KEY{ "initialRows" }; +static constexpr std::string_view INITIALCOLS_KEY{ "initialCols" }; +static constexpr std::string_view SHOW_TITLE_IN_TITLEBAR_KEY{ "showTerminalTitleInTitlebar" }; +static constexpr std::string_view REQUESTED_THEME_KEY{ "requestedTheme" }; +static constexpr std::string_view SHOW_TABS_IN_TITLEBAR_KEY{ "showTabsInTitlebar" }; static constexpr std::wstring_view LIGHT_THEME_VALUE{ L"light" }; static constexpr std::wstring_view DARK_THEME_VALUE{ L"dark" }; @@ -143,14 +143,14 @@ Json::Value GlobalAppSettings::ToJson() const { Json::Value jsonObject; - jsonObject[DEFAULTPROFILE_KEY_2.data()] = winrt::to_string(Utils::GuidToString(_defaultProfile)); - jsonObject[INITIALROWS_KEY_2.data()] = _initialRows; - jsonObject[INITIALCOLS_KEY_2.data()] = _initialCols; - jsonObject[ALWAYS_SHOW_TABS_KEY_2.data()] = _alwaysShowTabs; - jsonObject[SHOW_TITLE_IN_TITLEBAR_KEY_2.data()] = _showTitleInTitlebar; - jsonObject[SHOW_TABS_IN_TITLEBAR_KEY_2.data()] = _showTabsInTitlebar; - jsonObject[REQUESTED_THEME_KEY_2.data()] = winrt::to_string(_SerializeTheme(_requestedTheme)); - jsonObject[KEYBINDINGS_KEY_2.data()] = AppKeyBindingsSerialization::ToJson(_keybindings); + jsonObject[DEFAULTPROFILE_KEY.data()] = winrt::to_string(Utils::GuidToString(_defaultProfile)); + jsonObject[INITIALROWS_KEY.data()] = _initialRows; + jsonObject[INITIALCOLS_KEY.data()] = _initialCols; + jsonObject[ALWAYS_SHOW_TABS_KEY.data()] = _alwaysShowTabs; + jsonObject[SHOW_TITLE_IN_TITLEBAR_KEY.data()] = _showTitleInTitlebar; + jsonObject[SHOW_TABS_IN_TITLEBAR_KEY.data()] = _showTabsInTitlebar; + jsonObject[REQUESTED_THEME_KEY.data()] = winrt::to_string(_SerializeTheme(_requestedTheme)); + jsonObject[KEYBINDINGS_KEY.data()] = AppKeyBindingsSerialization::ToJson(_keybindings); return jsonObject; } @@ -165,41 +165,41 @@ GlobalAppSettings GlobalAppSettings::FromJson(Json::Value json) { GlobalAppSettings result{}; - if (auto defaultProfile{ json[DEFAULTPROFILE_KEY_2.data()] }) + if (auto defaultProfile{ json[DEFAULTPROFILE_KEY.data()] }) { auto guid = Utils::GuidFromString(GetWstringFromJson(defaultProfile)); result._defaultProfile = guid; } - if (auto alwaysShowTabs{ json[ALWAYS_SHOW_TABS_KEY_2.data()] }) + if (auto alwaysShowTabs{ json[ALWAYS_SHOW_TABS_KEY.data()] }) { result._alwaysShowTabs = alwaysShowTabs.asBool(); } - if (auto initialRows{ json[INITIALROWS_KEY_2.data()] }) + if (auto initialRows{ json[INITIALROWS_KEY.data()] }) { result._initialRows = initialRows.asInt(); } - if (auto initialCols{ json[INITIALCOLS_KEY_2.data()] }) + if (auto initialCols{ json[INITIALCOLS_KEY.data()] }) { result._initialCols = initialCols.asInt(); } - if (auto showTitleInTitlebar{ json[SHOW_TITLE_IN_TITLEBAR_KEY_2.data()] }) + if (auto showTitleInTitlebar{ json[SHOW_TITLE_IN_TITLEBAR_KEY.data()] }) { result._showTitleInTitlebar = showTitleInTitlebar.asBool(); } - if (auto showTabsInTitlebar{ json[SHOW_TABS_IN_TITLEBAR_KEY_2.data()] }) + if (auto showTabsInTitlebar{ json[SHOW_TABS_IN_TITLEBAR_KEY.data()] }) { result._showTabsInTitlebar = showTabsInTitlebar.asBool(); } - if (auto requestedTheme{ json[REQUESTED_THEME_KEY_2.data()] }) + if (auto requestedTheme{ json[REQUESTED_THEME_KEY.data()] }) { result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme)); } - if (auto keybindings{ json[KEYBINDINGS_KEY_2.data()] }) + if (auto keybindings{ json[KEYBINDINGS_KEY.data()] }) { result._keybindings = AppKeyBindingsSerialization::FromJson(keybindings); } diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 21d1e7b251f..cbefcca0a30 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -13,29 +13,29 @@ using namespace winrt::TerminalApp; using namespace winrt::Windows::Data::Json; using namespace ::Microsoft::Console; -static constexpr std::string_view NAME_KEY_2{ "name" }; -static constexpr std::string_view GUID_KEY_2{ "guid" }; -static constexpr std::string_view COLORSCHEME_KEY_2{ "colorscheme" }; - -static constexpr std::string_view FOREGROUND_KEY_2{ "foreground" }; -static constexpr std::string_view BACKGROUND_KEY_2{ "background" }; -static constexpr std::string_view COLORTABLE_KEY_2{ "colorTable" }; -static constexpr std::string_view HISTORYSIZE_KEY_2{ "historySize" }; -static constexpr std::string_view SNAPONINPUT_KEY_2{ "snapOnInput" }; -static constexpr std::string_view CURSORCOLOR_KEY_2{ "cursorColor" }; -static constexpr std::string_view CURSORSHAPE_KEY_2{ "cursorShape" }; -static constexpr std::string_view CURSORHEIGHT_KEY_2{ "cursorHeight" }; - -static constexpr std::string_view COMMANDLINE_KEY_2{ "commandline" }; -static constexpr std::string_view FONTFACE_KEY_2{ "fontFace" }; -static constexpr std::string_view FONTSIZE_KEY_2{ "fontSize" }; -static constexpr std::string_view ACRYLICTRANSPARENCY_KEY_2{ "acrylicOpacity" }; -static constexpr std::string_view USEACRYLIC_KEY_2{ "useAcrylic" }; -static constexpr std::string_view SCROLLBARSTATE_KEY_2{ "scrollbarState" }; -static constexpr std::string_view CLOSEONEXIT_KEY_2{ "closeOnExit" }; -static constexpr std::string_view PADDING_KEY_2{ "padding" }; -static constexpr std::string_view STARTINGDIRECTORY_KEY_2{ "startingDirectory" }; -static constexpr std::string_view ICON_KEY_2{ "icon" }; +static constexpr std::string_view NAME_KEY{ "name" }; +static constexpr std::string_view GUID_KEY{ "guid" }; +static constexpr std::string_view COLORSCHEME_KEY{ "colorscheme" }; + +static constexpr std::string_view FOREGROUND_KEY{ "foreground" }; +static constexpr std::string_view BACKGROUND_KEY{ "background" }; +static constexpr std::string_view COLORTABLE_KEY{ "colorTable" }; +static constexpr std::string_view HISTORYSIZE_KEY{ "historySize" }; +static constexpr std::string_view SNAPONINPUT_KEY{ "snapOnInput" }; +static constexpr std::string_view CURSORCOLOR_KEY{ "cursorColor" }; +static constexpr std::string_view CURSORSHAPE_KEY{ "cursorShape" }; +static constexpr std::string_view CURSORHEIGHT_KEY{ "cursorHeight" }; + +static constexpr std::string_view COMMANDLINE_KEY{ "commandline" }; +static constexpr std::string_view FONTFACE_KEY{ "fontFace" }; +static constexpr std::string_view FONTSIZE_KEY{ "fontSize" }; +static constexpr std::string_view ACRYLICTRANSPARENCY_KEY{ "acrylicOpacity" }; +static constexpr std::string_view USEACRYLIC_KEY{ "useAcrylic" }; +static constexpr std::string_view SCROLLBARSTATE_KEY{ "scrollbarState" }; +static constexpr std::string_view CLOSEONEXIT_KEY{ "closeOnExit" }; +static constexpr std::string_view PADDING_KEY{ "padding" }; +static constexpr std::string_view STARTINGDIRECTORY_KEY{ "startingDirectory" }; +static constexpr std::string_view ICON_KEY{ "icon" }; // Possible values for Scrollbar state static constexpr std::wstring_view ALWAYS_VISIBLE{ L"visible" }; @@ -187,24 +187,24 @@ Json::Value Profile::ToJson() const Json::Value root; ///// Profile-specific settings ///// - root[GUID_KEY_2.data()] = winrt::to_string(Utils::GuidToString(_guid)); - root[NAME_KEY_2.data()] = winrt::to_string(_name); + root[GUID_KEY.data()] = winrt::to_string(Utils::GuidToString(_guid)); + root[NAME_KEY.data()] = winrt::to_string(_name); ///// Core Settings ///// if (_defaultForeground) { const auto defaultForeground = winrt::to_string(Utils::ColorToHexString(_defaultForeground.value())); - root[FOREGROUND_KEY_2.data()] = defaultForeground; + root[FOREGROUND_KEY.data()] = defaultForeground; } if (_defaultBackground) { const auto defaultBackground = winrt::to_string(Utils::ColorToHexString(_defaultBackground.value())); - root[BACKGROUND_KEY_2.data()] = defaultBackground; + root[BACKGROUND_KEY.data()] = defaultBackground; } if (_schemeName) { const auto scheme = winrt::to_string(_schemeName.value()); - root[COLORSCHEME_KEY_2.data()] = scheme; + root[COLORSCHEME_KEY.data()] = scheme; } else { @@ -214,42 +214,42 @@ Json::Value Profile::ToJson() const auto s = Utils::ColorToHexString(color); tableArray.append(winrt::to_string(s)); } - root[COLORTABLE_KEY_2.data()] = tableArray; + root[COLORTABLE_KEY.data()] = tableArray; } - root[HISTORYSIZE_KEY_2.data()] = _historySize; - root[SNAPONINPUT_KEY_2.data()] = _snapOnInput; - root[CURSORCOLOR_KEY_2.data()] = winrt::to_string(Utils::ColorToHexString(_cursorColor)); + root[HISTORYSIZE_KEY.data()] = _historySize; + root[SNAPONINPUT_KEY.data()] = _snapOnInput; + root[CURSORCOLOR_KEY.data()] = winrt::to_string(Utils::ColorToHexString(_cursorColor)); // Only add the cursor height property if we're a legacy-style cursor. if (_cursorShape == CursorStyle::Vintage) { - root[CURSORHEIGHT_KEY_2.data()] = _cursorHeight; + root[CURSORHEIGHT_KEY.data()] = _cursorHeight; } - root[CURSORSHAPE_KEY_2.data()] = winrt::to_string(_SerializeCursorStyle(_cursorShape)); + root[CURSORSHAPE_KEY.data()] = winrt::to_string(_SerializeCursorStyle(_cursorShape)); ///// Control Settings ///// - root[COMMANDLINE_KEY_2.data()] = winrt::to_string(_commandline); - root[FONTFACE_KEY_2.data()] = winrt::to_string(_fontFace); - root[FONTSIZE_KEY_2.data()] = _fontSize; - root[ACRYLICTRANSPARENCY_KEY_2.data()] = _acrylicTransparency; - root[USEACRYLIC_KEY_2.data()] = _useAcrylic; - root[CLOSEONEXIT_KEY_2.data()] = _closeOnExit; - root[PADDING_KEY_2.data()] = winrt::to_string(_padding); + root[COMMANDLINE_KEY.data()] = winrt::to_string(_commandline); + root[FONTFACE_KEY.data()] = winrt::to_string(_fontFace); + root[FONTSIZE_KEY.data()] = _fontSize; + root[ACRYLICTRANSPARENCY_KEY.data()] = _acrylicTransparency; + root[USEACRYLIC_KEY.data()] = _useAcrylic; + root[CLOSEONEXIT_KEY.data()] = _closeOnExit; + root[PADDING_KEY.data()] = winrt::to_string(_padding); if (_scrollbarState) { const auto scrollbarState = winrt::to_string(_scrollbarState.value()); - root[SCROLLBARSTATE_KEY_2.data()] = scrollbarState; + root[SCROLLBARSTATE_KEY.data()] = scrollbarState; } if (_icon) { const auto icon = winrt::to_string(_icon.value()); - root[ICON_KEY_2.data()] = icon; + root[ICON_KEY.data()] = icon; } if (_startingDirectory) { - root[STARTINGDIRECTORY_KEY_2.data()] = winrt::to_string(_startingDirectory.value()); + root[STARTINGDIRECTORY_KEY.data()] = winrt::to_string(_startingDirectory.value()); } return root; @@ -266,33 +266,33 @@ Profile Profile::FromJson(Json::Value json) Profile result{}; // Profile-specific Settings - if (auto name{ json[NAME_KEY_2.data()] }) + if (auto name{ json[NAME_KEY.data()] }) { result._name = GetWstringFromJson(name); } - if (auto guid{ json[GUID_KEY_2.data()] }) + if (auto guid{ json[GUID_KEY.data()] }) { result._guid = Utils::GuidFromString(GetWstringFromJson(guid)); } // Core Settings - if (auto foreground{ json[FOREGROUND_KEY_2.data()] }) + if (auto foreground{ json[FOREGROUND_KEY.data()] }) { const auto color = Utils::ColorFromHexString(GetWstringFromJson(foreground)); result._defaultForeground = color; } - if (auto background{ json[BACKGROUND_KEY_2.data()] }) + if (auto background{ json[BACKGROUND_KEY.data()] }) { const auto color = Utils::ColorFromHexString(GetWstringFromJson(background)); result._defaultBackground = color; } - if (auto colorScheme{ json[COLORSCHEME_KEY_2.data()] }) + if (auto colorScheme{ json[COLORSCHEME_KEY.data()] }) { result._schemeName = GetWstringFromJson(colorScheme); } else { - if (auto colortable{ json[COLORTABLE_KEY_2.data()] }) + if (auto colortable{ json[COLORTABLE_KEY.data()] }) { int i = 0; for (auto tableEntry : colortable) @@ -306,67 +306,67 @@ Profile Profile::FromJson(Json::Value json) } } } - if (auto historySize{ json[HISTORYSIZE_KEY_2.data()] }) + if (auto historySize{ json[HISTORYSIZE_KEY.data()] }) { // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" result._historySize = historySize.asInt(); } - if (auto snapOnInput{ json[SNAPONINPUT_KEY_2.data()] }) + if (auto snapOnInput{ json[SNAPONINPUT_KEY.data()] }) { result._snapOnInput = snapOnInput.asBool(); } - if (auto cursorColor{ json[CURSORCOLOR_KEY_2.data()] }) + if (auto cursorColor{ json[CURSORCOLOR_KEY.data()] }) { const auto color = Utils::ColorFromHexString(GetWstringFromJson(cursorColor)); result._cursorColor = color; } - if (auto cursorHeight{ json[CURSORHEIGHT_KEY_2.data()] }) + if (auto cursorHeight{ json[CURSORHEIGHT_KEY.data()] }) { result._cursorHeight = cursorHeight.asUInt(); } - if (auto cursorShape{ json[CURSORSHAPE_KEY_2.data()] }) + if (auto cursorShape{ json[CURSORSHAPE_KEY.data()] }) { result._cursorShape = _ParseCursorShape(GetWstringFromJson(cursorShape)); } // Control Settings - if (auto commandline{ json[COMMANDLINE_KEY_2.data()] }) + if (auto commandline{ json[COMMANDLINE_KEY.data()] }) { result._commandline = GetWstringFromJson(commandline); } - if (auto fontFace{ json[FONTFACE_KEY_2.data()] }) + if (auto fontFace{ json[FONTFACE_KEY.data()] }) { result._fontFace = GetWstringFromJson(fontFace); } - if (auto fontSize{ json[FONTSIZE_KEY_2.data()] }) + if (auto fontSize{ json[FONTSIZE_KEY.data()] }) { result._fontSize = fontSize.asInt(); } - if (auto acrylicTransparency{ json[ACRYLICTRANSPARENCY_KEY_2.data()] }) + if (auto acrylicTransparency{ json[ACRYLICTRANSPARENCY_KEY.data()] }) { result._acrylicTransparency = acrylicTransparency.asFloat(); } - if (auto useAcrylic{ json[USEACRYLIC_KEY_2.data()] }) + if (auto useAcrylic{ json[USEACRYLIC_KEY.data()] }) { result._useAcrylic = useAcrylic.asBool(); } - if (auto closeOnExit{ json[CLOSEONEXIT_KEY_2.data()] }) + if (auto closeOnExit{ json[CLOSEONEXIT_KEY.data()] }) { result._closeOnExit = closeOnExit.asBool(); } - if (auto padding{ json[PADDING_KEY_2.data()] }) + if (auto padding{ json[PADDING_KEY.data()] }) { result._padding = GetWstringFromJson(padding); } - if (auto scrollbarState{ json[SCROLLBARSTATE_KEY_2.data()] }) + if (auto scrollbarState{ json[SCROLLBARSTATE_KEY.data()] }) { result._scrollbarState = GetWstringFromJson(scrollbarState); } - if (auto startingDirectory{ json[STARTINGDIRECTORY_KEY_2.data()] }) + if (auto startingDirectory{ json[STARTINGDIRECTORY_KEY.data()] }) { result._startingDirectory = GetWstringFromJson(startingDirectory); } - if (auto icon{ json[ICON_KEY_2.data()] }) + if (auto icon{ json[ICON_KEY.data()] }) { result._icon = GetWstringFromJson(icon); } From f60bfb39d2e17ccf25b60f789d6fc4d5e408393e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 24 May 2019 13:35:40 -0500 Subject: [PATCH 19/26] throw a proper exception --- .../CascadiaSettingsSerialization.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 1950bbd342a..845c8ca860b 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -49,15 +49,19 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa { const auto actualData = fileData.value(); + // Parse the json data. Json::Value root; - // TODO: unique_ptr this guy VVVV - Json::CharReader* reader = Json::CharReaderBuilder::CharReaderBuilder().newCharReader(); - auto raw = winrt::to_string(actualData); - std::string errs; - bool b = reader->parse(raw.c_str(), raw.c_str()+raw.size(), &root, &errs); - // TODO: need better error. `reader` might have the exception in a - // better format. - if (!b) throw winrt::hresult_error(); + std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() }; + const auto raw = winrt::to_string(actualData); + std::string errs; // This string will recieve any error text from failing to parse. + + // `parse` will return false if it fails. + if (!reader->parse(raw.c_str(), raw.c_str() + raw.size(), &root, &errs)) + { + // TODO:GH#990 display this exception text to the user, in a + // copy-pasteable way. + throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs)); + } resultPtr = FromJson(root); if (saveOnLoad) From aeec8f5f84a5728ecddeb78e35b0ffbde71de476 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 May 2019 09:43:21 -0500 Subject: [PATCH 20/26] My PR nits --- .../TerminalApp/AppKeyBindingsSerialization.cpp | 17 +++++++++++++++++ .../CascadiaSettingsSerialization.cpp | 10 +++------- src/cascadia/TerminalApp/Utils.cpp | 8 ++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index 4840e004027..b1304254e3b 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -112,6 +112,12 @@ static void _AddShortcutToJsonArray2(Json::Value& bindingsArray, bindingsArray.append(jsonObject); } +// Method Description: +// - Serialize this AppKeyBindings to a json array of objects. Each object in +// the array represents a single keybinding, mapping a KeyChord to a +// ShortcutAction. +// Return Value: +// - a JsonArray which is an equivalent serialization of this object. Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKeyBindings& bindings) { Json::Value bindingsArray; @@ -132,6 +138,17 @@ Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKey return bindingsArray; } +// Method Description: +// - Deserialize an AppKeyBindings from the key mappings that are in the array +// `json`. The json array should contain an array of objects with both a +// `command` string and a `keys` array, where `command` is one of the names +// listed in `commandNames`, and `keys` is an array of keypresses. Currently, +// the array should contain a single string, which can be deserialized into a +// KeyChord. +// Arguments: +// - json: and array of JsonObject's to deserialize into our _keyShortcuts mapping. +// Return Value: +// - the newly constructed AppKeyBindings object. winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const Json::Value& json) { winrt::TerminalApp::AppKeyBindings newBindings{}; diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 845c8ca860b..7c5144f92eb 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -102,15 +102,15 @@ void CascadiaSettings::SaveAll() const Json::StreamWriterBuilder wbuilder; // Use 4 spaces to indent instead of \t wbuilder.settings_["indentation"] = " "; - const auto s = winrt::to_hstring(Json::writeString(wbuilder, json2)); + const auto serializedString = winrt::to_hstring(Json::writeString(wbuilder, json2)); if (_IsPackaged()) { - _SaveAsPackagedApp(s); + _SaveAsPackagedApp(serializedString); } else { - _SaveAsUnpackagedApp(s); + _SaveAsUnpackagedApp(serializedString); } } @@ -123,10 +123,6 @@ void CascadiaSettings::SaveAll() const Json::Value CascadiaSettings::ToJson() const { Json::Value root; - // TODO: Globals - - // TODO: put the keybindings in the globals (now that the order doesn't - // really matter). Json::Value profilesArray; for (const auto& profile : _profiles) diff --git a/src/cascadia/TerminalApp/Utils.cpp b/src/cascadia/TerminalApp/Utils.cpp index c64514617b9..6bf6bdc87d4 100644 --- a/src/cascadia/TerminalApp/Utils.cpp +++ b/src/cascadia/TerminalApp/Utils.cpp @@ -4,6 +4,14 @@ #include "pch.h" #include "Utils.h" +// Method Description: +// - Contstructs a wstring from a given Json::Value object. Reads the object as +// a std::string using asString, then builds an hstring from that std::string, +// then converts that hstring into a std::wstring. +// Arguments: +// - json: the Json::Value to parse as a string +// Return Value: +// - the wstring equivalent of the value in json std::wstring GetWstringFromJson(const Json::Value& json) { return winrt::to_hstring(json.asString()).c_str(); From 459d86c53950cb234a8f31c1cca703de8928e895 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 May 2019 10:31:35 -0500 Subject: [PATCH 21/26] Load as UTF-8 without rounttripping through an hstring --- .../AppKeyBindingsSerialization.cpp | 104 ++++++++++-------- src/cascadia/TerminalApp/CascadiaSettings.h | 4 +- .../CascadiaSettingsSerialization.cpp | 32 +++--- 3 files changed, 76 insertions(+), 64 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index b1304254e3b..50c32c1ba7b 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -13,44 +13,44 @@ using namespace winrt::TerminalApp; static constexpr std::string_view KEYS_KEY{ "keys" }; static constexpr std::string_view COMMAND_KEY{ "command" }; -static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" }; -static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" }; -static constexpr std::wstring_view NEWTAB_KEY{ L"newTab" }; -static constexpr std::wstring_view NEWTABWITHPROFILE0_KEY{ L"newTabProfile0" }; -static constexpr std::wstring_view NEWTABWITHPROFILE1_KEY{ L"newTabProfile1" }; -static constexpr std::wstring_view NEWTABWITHPROFILE2_KEY{ L"newTabProfile2" }; -static constexpr std::wstring_view NEWTABWITHPROFILE3_KEY{ L"newTabProfile3" }; -static constexpr std::wstring_view NEWTABWITHPROFILE4_KEY{ L"newTabProfile4" }; -static constexpr std::wstring_view NEWTABWITHPROFILE5_KEY{ L"newTabProfile5" }; -static constexpr std::wstring_view NEWTABWITHPROFILE6_KEY{ L"newTabProfile6" }; -static constexpr std::wstring_view NEWTABWITHPROFILE7_KEY{ L"newTabProfile7" }; -static constexpr std::wstring_view NEWTABWITHPROFILE8_KEY{ L"newTabProfile8" }; -static constexpr std::wstring_view NEWWINDOW_KEY{ L"newWindow" }; -static constexpr std::wstring_view CLOSEWINDOW_KEY{ L"closeWindow" }; -static constexpr std::wstring_view CLOSETAB_KEY{ L"closeTab" }; -static constexpr std::wstring_view SWITCHTOTAB_KEY{ L"switchToTab" }; -static constexpr std::wstring_view NEXTTAB_KEY{ L"nextTab" }; -static constexpr std::wstring_view PREVTAB_KEY{ L"prevTab" }; -static constexpr std::wstring_view INCREASEFONTSIZE_KEY{ L"increaseFontSize" }; -static constexpr std::wstring_view DECREASEFONTSIZE_KEY{ L"decreaseFontSize" }; -static constexpr std::wstring_view SCROLLUP_KEY{ L"scrollUp" }; -static constexpr std::wstring_view SCROLLDOWN_KEY{ L"scrollDown" }; -static constexpr std::wstring_view SCROLLUPPAGE_KEY{ L"scrollUpPage" }; -static constexpr std::wstring_view SCROLLDOWNPAGE_KEY{ L"scrollDownPage" }; -static constexpr std::wstring_view SWITCHTOTAB0_KEY{ L"switchToTab0" }; -static constexpr std::wstring_view SWITCHTOTAB1_KEY{ L"switchToTab1" }; -static constexpr std::wstring_view SWITCHTOTAB2_KEY{ L"switchToTab2" }; -static constexpr std::wstring_view SWITCHTOTAB3_KEY{ L"switchToTab3" }; -static constexpr std::wstring_view SWITCHTOTAB4_KEY{ L"switchToTab4" }; -static constexpr std::wstring_view SWITCHTOTAB5_KEY{ L"switchToTab5" }; -static constexpr std::wstring_view SWITCHTOTAB6_KEY{ L"switchToTab6" }; -static constexpr std::wstring_view SWITCHTOTAB7_KEY{ L"switchToTab7" }; -static constexpr std::wstring_view SWITCHTOTAB8_KEY{ L"switchToTab8" }; -static constexpr std::wstring_view OPENSETTINGS_KEY{ L"openSettings" }; +static constexpr std::string_view COPYTEXT_KEY{ "copy" }; +static constexpr std::string_view PASTETEXT_KEY{ "paste" }; +static constexpr std::string_view NEWTAB_KEY{ "newTab" }; +static constexpr std::string_view NEWTABWITHPROFILE0_KEY{ "newTabProfile0" }; +static constexpr std::string_view NEWTABWITHPROFILE1_KEY{ "newTabProfile1" }; +static constexpr std::string_view NEWTABWITHPROFILE2_KEY{ "newTabProfile2" }; +static constexpr std::string_view NEWTABWITHPROFILE3_KEY{ "newTabProfile3" }; +static constexpr std::string_view NEWTABWITHPROFILE4_KEY{ "newTabProfile4" }; +static constexpr std::string_view NEWTABWITHPROFILE5_KEY{ "newTabProfile5" }; +static constexpr std::string_view NEWTABWITHPROFILE6_KEY{ "newTabProfile6" }; +static constexpr std::string_view NEWTABWITHPROFILE7_KEY{ "newTabProfile7" }; +static constexpr std::string_view NEWTABWITHPROFILE8_KEY{ "newTabProfile8" }; +static constexpr std::string_view NEWWINDOW_KEY{ "newWindow" }; +static constexpr std::string_view CLOSEWINDOW_KEY{ "closeWindow" }; +static constexpr std::string_view CLOSETAB_KEY{ "closeTab" }; +static constexpr std::string_view SWITCHTOTAB_KEY{ "switchToTab" }; +static constexpr std::string_view NEXTTAB_KEY{ "nextTab" }; +static constexpr std::string_view PREVTAB_KEY{ "prevTab" }; +static constexpr std::string_view INCREASEFONTSIZE_KEY{ "increaseFontSize" }; +static constexpr std::string_view DECREASEFONTSIZE_KEY{ "decreaseFontSize" }; +static constexpr std::string_view SCROLLUP_KEY{ "scrollUp" }; +static constexpr std::string_view SCROLLDOWN_KEY{ "scrollDown" }; +static constexpr std::string_view SCROLLUPPAGE_KEY{ "scrollUpPage" }; +static constexpr std::string_view SCROLLDOWNPAGE_KEY{ "scrollDownPage" }; +static constexpr std::string_view SWITCHTOTAB0_KEY{ "switchToTab0" }; +static constexpr std::string_view SWITCHTOTAB1_KEY{ "switchToTab1" }; +static constexpr std::string_view SWITCHTOTAB2_KEY{ "switchToTab2" }; +static constexpr std::string_view SWITCHTOTAB3_KEY{ "switchToTab3" }; +static constexpr std::string_view SWITCHTOTAB4_KEY{ "switchToTab4" }; +static constexpr std::string_view SWITCHTOTAB5_KEY{ "switchToTab5" }; +static constexpr std::string_view SWITCHTOTAB6_KEY{ "switchToTab6" }; +static constexpr std::string_view SWITCHTOTAB7_KEY{ "switchToTab7" }; +static constexpr std::string_view SWITCHTOTAB8_KEY{ "switchToTab8" }; +static constexpr std::string_view OPENSETTINGS_KEY{ "openSettings" }; // Specifically use a map here over an unordered_map. We want to be able to // iterate over these entries in-order when we're serializing the keybindings. -static const std::map commandNames { +static const std::map commandNames { { COPYTEXT_KEY, ShortcutAction::CopyText }, { PASTETEXT_KEY, ShortcutAction::PasteText }, { NEWTAB_KEY, ShortcutAction::NewTab }, @@ -86,20 +86,24 @@ static const std::map commandNames { }; // Function Description: -// - Small helper to insert a given KeyChord, ShortcutAction pair into the -// given json array +// - Small helper to create a json value serialization of a single +// KeyBinding->Action maping. The created object is of schema: +// { +// keys:[String], +// command:String +// } // Arguments: -// - bindingsArray: The JsonArray to insert the object into. -// - chord: The KeyChord to serailize and place in the json array +// - chord: The KeyChord to serialize // - actionName: the name of the ShortcutAction to use with this KeyChord -static void _AddShortcutToJsonArray2(Json::Value& bindingsArray, - const KeyChord& chord, - const std::wstring_view& actionName) +// Return Value: +// - a Json::Value which is an equivalent serialization of this object. +static Json::Value _ShortcutAsJsonObject(const KeyChord& chord, + const std::string_view& actionName) { const auto keyString = KeyChordSerialization::ToString(chord); if (keyString == L"") { - return; + return nullptr; } Json::Value jsonObject; @@ -107,9 +111,9 @@ static void _AddShortcutToJsonArray2(Json::Value& bindingsArray, keysArray.append(winrt::to_string(keyString)); jsonObject[KEYS_KEY.data()] = keysArray; - jsonObject[COMMAND_KEY.data()] = winrt::to_string(actionName); + jsonObject[COMMAND_KEY.data()] = actionName.data(); - bindingsArray.append(jsonObject); + return jsonObject; } // Method Description: @@ -117,7 +121,7 @@ static void _AddShortcutToJsonArray2(Json::Value& bindingsArray, // the array represents a single keybinding, mapping a KeyChord to a // ShortcutAction. // Return Value: -// - a JsonArray which is an equivalent serialization of this object. +// - a Json::Value which is an equivalent serialization of this object. Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKeyBindings& bindings) { Json::Value bindingsArray; @@ -131,7 +135,11 @@ Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKey const auto chord = bindings.GetKeyBinding(searchedForAction); if (chord) { - _AddShortcutToJsonArray2(bindingsArray, chord, searchedForName); + const auto serialization = _ShortcutAsJsonObject(chord, searchedForName); + if (serialization) + { + bindingsArray.append(serialization); + } } } @@ -170,7 +178,7 @@ winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const J ShortcutAction action; // Try matching the command to one we have - auto found = commandNames.find(GetWstringFromJson(commandString)); + auto found = commandNames.find(commandString.asString()); if (found != commandNames.end()) { action = found->second; diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 11b017a155c..88a80f3d745 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -66,8 +66,8 @@ class TerminalApp::CascadiaSettings final static void _SaveAsUnpackagedApp(const winrt::hstring content); static std::wstring _GetFullPathToUnpackagedSettingsFile(); static winrt::hstring _GetPackagedSettingsPath(); - static std::optional _LoadAsPackagedApp(); - static std::optional _LoadAsUnpackagedApp(); + static std::optional _LoadAsPackagedApp(); + static std::optional _LoadAsUnpackagedApp(); static bool _isPowerShellCoreInstalledInPath(const std::wstring_view programFileEnv, std::filesystem::path& cmdline); static bool _isPowerShellCoreInstalled(std::filesystem::path& cmdline); static std::wstring ExpandEnvironmentVariableString(std::wstring_view source); diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 7c5144f92eb..176676be865 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -41,8 +41,8 @@ static constexpr std::string_view SCHEMES_KEY{ "schemes" }; std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoad) { std::unique_ptr resultPtr; - std::optional fileData = _IsPackaged() ? - _LoadAsPackagedApp() : _LoadAsUnpackagedApp(); + std::optional fileData = _IsPackaged() ? + _LoadAsPackagedApp() : _LoadAsUnpackagedApp(); const bool foundFile = fileData.has_value(); if (foundFile) @@ -52,11 +52,10 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa // Parse the json data. Json::Value root; std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() }; - const auto raw = winrt::to_string(actualData); - std::string errs; // This string will recieve any error text from failing to parse. + std::string errs; // This string will recieve any error text from failing to parse. // `parse` will return false if it fails. - if (!reader->parse(raw.c_str(), raw.c_str() + raw.size(), &root, &errs)) + if (!reader->parse(actualData.c_str(), actualData.c_str() + actualData.size(), &root, &errs)) { // TODO:GH#990 display this exception text to the user, in a // copy-pasteable way. @@ -98,11 +97,11 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa // - void CascadiaSettings::SaveAll() const { - const auto json2 = ToJson(); + const auto json = ToJson(); Json::StreamWriterBuilder wbuilder; // Use 4 spaces to indent instead of \t wbuilder.settings_["indentation"] = " "; - const auto serializedString = winrt::to_hstring(Json::writeString(wbuilder, json2)); + const auto serializedString = winrt::to_hstring(Json::writeString(wbuilder, json)); if (_IsPackaged()) { @@ -324,7 +323,7 @@ std::wstring CascadiaSettings::_GetFullPathToUnpackagedSettingsFile() // Return Value: // - an optional with the content of the file if we were able to open it, // otherwise the optional will be empty -std::optional CascadiaSettings::_LoadAsPackagedApp() +std::optional CascadiaSettings::_LoadAsPackagedApp() { auto curr = ApplicationData::Current(); auto folder = curr.RoamingFolder(); @@ -338,7 +337,15 @@ std::optional CascadiaSettings::_LoadAsPackagedApp() const auto storageFile = file.as(); // settings file is UTF-8 without BOM - return { FileIO::ReadTextAsync(storageFile, UnicodeEncoding::Utf8).get() }; + auto buffer = FileIO::ReadBufferAsync(storageFile).get(); + auto bufferData = buffer.data(); + std::string resultString; + resultString.reserve(buffer.Length()); + for (int i = 0; i < buffer.Length(); i++) + { + resultString.push_back(bufferData[i]); + } + return { resultString }; } @@ -351,7 +358,7 @@ std::optional CascadiaSettings::_LoadAsPackagedApp() // otherwise the optional will be empty. // If the file exists, but we fail to read it, this can throw an exception // from reading the file -std::optional CascadiaSettings::_LoadAsUnpackagedApp() +std::optional CascadiaSettings::_LoadAsUnpackagedApp() { std::wstring pathToSettingsFile = CascadiaSettings::_GetFullPathToUnpackagedSettingsFile(); const auto hFile = CreateFileW(pathToSettingsFile.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); @@ -376,10 +383,7 @@ std::optional CascadiaSettings::_LoadAsUnpackagedApp() // convert buffer to UTF-8 string std::string utf8string(utf8buffer.get(), fileSize); - // UTF-8 to UTF-16 - const winrt::hstring fileData = winrt::to_hstring(utf8string); - - return { fileData }; + return { utf8string }; } // function Description: From 3ebb5048201a93cfe8e11635af78fb8ce2a3e139 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 May 2019 11:03:23 -0500 Subject: [PATCH 22/26] Write the json content as utf-8 w/o roundtripping through an hstring --- src/cascadia/TerminalApp/CascadiaSettings.h | 4 +-- .../CascadiaSettingsSerialization.cpp | 25 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 88a80f3d745..3c8f0b12d58 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -62,8 +62,8 @@ class TerminalApp::CascadiaSettings final void _CreateDefaultProfiles(); static bool _IsPackaged(); - static void _SaveAsPackagedApp(const winrt::hstring content); - static void _SaveAsUnpackagedApp(const winrt::hstring content); + static void _SaveAsPackagedApp(const std::string content); + static void _SaveAsUnpackagedApp(const std::string content); static std::wstring _GetFullPathToUnpackagedSettingsFile(); static winrt::hstring _GetPackagedSettingsPath(); static std::optional _LoadAsPackagedApp(); diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 176676be865..b7ad08b5c72 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -52,7 +52,7 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa // Parse the json data. Json::Value root; std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() }; - std::string errs; // This string will recieve any error text from failing to parse. + std::string errs; // This string will recieve any error text from failing to parse. // `parse` will return false if it fails. if (!reader->parse(actualData.c_str(), actualData.c_str() + actualData.size(), &root, &errs)) @@ -101,7 +101,7 @@ void CascadiaSettings::SaveAll() const Json::StreamWriterBuilder wbuilder; // Use 4 spaces to indent instead of \t wbuilder.settings_["indentation"] = " "; - const auto serializedString = winrt::to_hstring(Json::writeString(wbuilder, json)); + const auto serializedString = Json::writeString(wbuilder, json); if (_IsPackaged()) { @@ -238,7 +238,7 @@ bool CascadiaSettings::_IsPackaged() // - content: the given string of content to write to the file. // Return Value: // - -void CascadiaSettings::_SaveAsPackagedApp(const winrt::hstring content) +void CascadiaSettings::_SaveAsPackagedApp(const std::string content) { auto curr = ApplicationData::Current(); auto folder = curr.RoamingFolder(); @@ -249,11 +249,13 @@ void CascadiaSettings::_SaveAsPackagedApp(const winrt::hstring content) auto file = file_async.get(); DataWriter dw = DataWriter(); - - // DataWriter will convert UTF-16 string to UTF-8 (expected settings file encoding) - dw.UnicodeEncoding(UnicodeEncoding::Utf8); - - dw.WriteString(content); + const char* firstChar = content.c_str(); + const char* lastChar = firstChar + content.size(); + // Manually cast to a uint8_t*, because the compiler is unhappy with a static_cast here. + const uint8_t* firstByte = (const uint8_t*)firstChar; + const uint8_t* lastByte = (const uint8_t*)lastChar; + winrt::array_view bytes{ firstByte, lastByte }; + dw.WriteBytes(bytes); FileIO::WriteBufferAsync(file, dw.DetachBuffer()).get(); } @@ -267,11 +269,8 @@ void CascadiaSettings::_SaveAsPackagedApp(const winrt::hstring content) // - // This can throw an exception if we fail to open the file for writing, or we // fail to write the file -void CascadiaSettings::_SaveAsUnpackagedApp(const winrt::hstring content) +void CascadiaSettings::_SaveAsUnpackagedApp(const std::string content) { - // convert UTF-16 to UTF-8 - auto contentString = winrt::to_string(content); - // Get path to output file // In this scenario, the settings file will end up under e.g. C:\Users\admin\AppData\Roaming\Microsoft\Windows Terminal\profiles.json std::wstring pathToSettingsFile = CascadiaSettings::_GetFullPathToUnpackagedSettingsFile(); @@ -281,7 +280,7 @@ void CascadiaSettings::_SaveAsUnpackagedApp(const winrt::hstring content) { THROW_LAST_ERROR(); } - THROW_LAST_ERROR_IF(!WriteFile(hOut, contentString.c_str(), gsl::narrow(contentString.length()), 0, 0)); + THROW_LAST_ERROR_IF(!WriteFile(hOut, content.c_str(), gsl::narrow(content.length()), 0, 0)); CloseHandle(hOut); } From 5769a6287a4092f9d5afa3953c4e0a4687cb73c2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 May 2019 11:49:01 -0500 Subject: [PATCH 23/26] Remaining PR feedback --- .../AppKeyBindingsSerialization.cpp | 8 +- .../CascadiaSettingsSerialization.cpp | 2 +- src/cascadia/TerminalApp/ColorScheme.cpp | 36 +++---- src/cascadia/TerminalApp/ColorScheme.h | 2 +- .../TerminalApp/GlobalAppSettings.cpp | 34 +++---- src/cascadia/TerminalApp/GlobalAppSettings.h | 2 +- src/cascadia/TerminalApp/Profile.cpp | 99 +++++++++---------- src/cascadia/TerminalApp/Profile.h | 2 +- src/cascadia/TerminalApp/Utils.h | 14 +++ src/cascadia/TerminalApp/pch.h | 1 - src/types/inc/utils.hpp | 6 +- src/types/utils.cpp | 43 +++++++- 12 files changed, 147 insertions(+), 102 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index 50c32c1ba7b..d34381155ff 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -110,8 +110,8 @@ static Json::Value _ShortcutAsJsonObject(const KeyChord& chord, Json::Value keysArray; keysArray.append(winrt::to_string(keyString)); - jsonObject[KEYS_KEY.data()] = keysArray; - jsonObject[COMMAND_KEY.data()] = actionName.data(); + jsonObject[JsonKey(KEYS_KEY)] = keysArray; + jsonObject[JsonKey(COMMAND_KEY)] = actionName.data(); return jsonObject; } @@ -165,8 +165,8 @@ winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const J { if (value.isObject()) { - const auto commandString = value[COMMAND_KEY.data()]; - const auto keys = value[KEYS_KEY.data()]; + const auto commandString = value[JsonKey(COMMAND_KEY)]; + const auto keys = value[JsonKey(KEYS_KEY)]; if (commandString && keys) { diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index b7ad08b5c72..f373a3ef059 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -340,7 +340,7 @@ std::optional CascadiaSettings::_LoadAsPackagedApp() auto bufferData = buffer.data(); std::string resultString; resultString.reserve(buffer.Length()); - for (int i = 0; i < buffer.Length(); i++) + for (size_t i = 0; i < buffer.Length(); i++) { resultString.push_back(bufferData[i]); } diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index feafb65903f..7f6e404581f 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -87,21 +87,15 @@ void ColorScheme::ApplyScheme(TerminalSettings terminalSettings) const Json::Value ColorScheme::ToJson() const { Json::Value root; - auto fg { Utils::ColorToHexString(_defaultForeground) }; - auto bg { Utils::ColorToHexString(_defaultBackground) }; - auto name { _schemeName }; - - root[NAME_KEY.data()] = winrt::to_string(name); - root[FOREGROUND_KEY.data()] = winrt::to_string(fg); - root[BACKGROUND_KEY.data()] = winrt::to_string(bg); + root[JsonKey(NAME_KEY)] = winrt::to_string(_schemeName); + root[JsonKey(FOREGROUND_KEY)] = Utils::ColorToHexString(_defaultForeground); + root[JsonKey(BACKGROUND_KEY)] = Utils::ColorToHexString(_defaultBackground); int i = 0; for (const auto& colorName : TABLE_COLORS) { auto& colorValue = _table.at(i); - auto colorHexString = Utils::ColorToHexString(colorValue); - - root[colorName.data()] = winrt::to_string(colorHexString); + root[JsonKey(colorName)] = Utils::ColorToHexString(colorValue); i++; } @@ -114,35 +108,35 @@ Json::Value ColorScheme::ToJson() const // - json: an object which should be a serialization of a ColorScheme object. // Return Value: // - a new ColorScheme instance created from the values in `json` -ColorScheme ColorScheme::FromJson(Json::Value json) +ColorScheme ColorScheme::FromJson(const Json::Value& json) { ColorScheme result{}; - if (auto name{ json[NAME_KEY.data()] }) + if (auto name{ json[JsonKey(NAME_KEY)] }) { result._schemeName = winrt::to_hstring(name.asString()); } - if (auto fgString{ json[FOREGROUND_KEY.data()] }) + if (auto fgString{ json[JsonKey(FOREGROUND_KEY)] }) { - const auto color = Utils::ColorFromHexString(GetWstringFromJson(fgString)); + const auto color = Utils::ColorFromHexString(fgString.asString()); result._defaultForeground = color; } - if (auto bgString{ json[BACKGROUND_KEY.data()] }) + if (auto bgString{ json[JsonKey(BACKGROUND_KEY)] }) { - const auto color = Utils::ColorFromHexString(GetWstringFromJson(bgString)); + const auto color = Utils::ColorFromHexString(bgString.asString()); result._defaultBackground = color; } // Legacy Deserialization. Leave in place to allow forward compatibility - if (auto table{ json[TABLE_KEY.data()] }) + if (auto table{ json[JsonKey(TABLE_KEY)] }) { int i = 0; - for (auto tableEntry : table) + for (const auto& tableEntry : table) { if (tableEntry.isString()) { - auto color = Utils::ColorFromHexString(GetWstringFromJson(tableEntry)); + auto color = Utils::ColorFromHexString(tableEntry.asString()); result._table.at(i) = color; } i++; @@ -152,9 +146,9 @@ ColorScheme ColorScheme::FromJson(Json::Value json) int i = 0; for (const auto& current : TABLE_COLORS) { - if (auto str{ json[current.data()] }) + if (auto str{ json[JsonKey(current)] }) { - const auto color = Utils::ColorFromHexString(GetWstringFromJson(str)); + const auto color = Utils::ColorFromHexString(str.asString()); result._table.at(i) = color; } i++; diff --git a/src/cascadia/TerminalApp/ColorScheme.h b/src/cascadia/TerminalApp/ColorScheme.h index 0182f12ec4e..9ff65ad5ebc 100644 --- a/src/cascadia/TerminalApp/ColorScheme.h +++ b/src/cascadia/TerminalApp/ColorScheme.h @@ -36,7 +36,7 @@ class TerminalApp::ColorScheme void ApplyScheme(winrt::Microsoft::Terminal::Settings::TerminalSettings terminalSettings) const; Json::Value ToJson() const; - static ColorScheme FromJson(Json::Value json); + static ColorScheme FromJson(const Json::Value& json); std::wstring_view GetName() const noexcept; std::array& GetTable() noexcept; diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 82e104d151f..543ab55d376 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -143,14 +143,14 @@ Json::Value GlobalAppSettings::ToJson() const { Json::Value jsonObject; - jsonObject[DEFAULTPROFILE_KEY.data()] = winrt::to_string(Utils::GuidToString(_defaultProfile)); - jsonObject[INITIALROWS_KEY.data()] = _initialRows; - jsonObject[INITIALCOLS_KEY.data()] = _initialCols; - jsonObject[ALWAYS_SHOW_TABS_KEY.data()] = _alwaysShowTabs; - jsonObject[SHOW_TITLE_IN_TITLEBAR_KEY.data()] = _showTitleInTitlebar; - jsonObject[SHOW_TABS_IN_TITLEBAR_KEY.data()] = _showTabsInTitlebar; - jsonObject[REQUESTED_THEME_KEY.data()] = winrt::to_string(_SerializeTheme(_requestedTheme)); - jsonObject[KEYBINDINGS_KEY.data()] = AppKeyBindingsSerialization::ToJson(_keybindings); + jsonObject[JsonKey(DEFAULTPROFILE_KEY)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); + jsonObject[JsonKey(INITIALROWS_KEY)] = _initialRows; + jsonObject[JsonKey(INITIALCOLS_KEY)] = _initialCols; + jsonObject[JsonKey(ALWAYS_SHOW_TABS_KEY)] = _alwaysShowTabs; + jsonObject[JsonKey(SHOW_TITLE_IN_TITLEBAR_KEY)] = _showTitleInTitlebar; + jsonObject[JsonKey(SHOW_TABS_IN_TITLEBAR_KEY)] = _showTabsInTitlebar; + jsonObject[JsonKey(REQUESTED_THEME_KEY)] = winrt::to_string(_SerializeTheme(_requestedTheme)); + jsonObject[JsonKey(KEYBINDINGS_KEY)] = AppKeyBindingsSerialization::ToJson(_keybindings); return jsonObject; } @@ -161,45 +161,45 @@ Json::Value GlobalAppSettings::ToJson() const // - json: an object which should be a serialization of a GlobalAppSettings object. // Return Value: // - a new GlobalAppSettings instance created from the values in `json` -GlobalAppSettings GlobalAppSettings::FromJson(Json::Value json) +GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) { GlobalAppSettings result{}; - if (auto defaultProfile{ json[DEFAULTPROFILE_KEY.data()] }) + if (auto defaultProfile{ json[JsonKey(DEFAULTPROFILE_KEY)] }) { auto guid = Utils::GuidFromString(GetWstringFromJson(defaultProfile)); result._defaultProfile = guid; } - if (auto alwaysShowTabs{ json[ALWAYS_SHOW_TABS_KEY.data()] }) + if (auto alwaysShowTabs{ json[JsonKey(ALWAYS_SHOW_TABS_KEY)] }) { result._alwaysShowTabs = alwaysShowTabs.asBool(); } - if (auto initialRows{ json[INITIALROWS_KEY.data()] }) + if (auto initialRows{ json[JsonKey(INITIALROWS_KEY)] }) { result._initialRows = initialRows.asInt(); } - if (auto initialCols{ json[INITIALCOLS_KEY.data()] }) + if (auto initialCols{ json[JsonKey(INITIALCOLS_KEY)] }) { result._initialCols = initialCols.asInt(); } - if (auto showTitleInTitlebar{ json[SHOW_TITLE_IN_TITLEBAR_KEY.data()] }) + if (auto showTitleInTitlebar{ json[JsonKey(SHOW_TITLE_IN_TITLEBAR_KEY)] }) { result._showTitleInTitlebar = showTitleInTitlebar.asBool(); } - if (auto showTabsInTitlebar{ json[SHOW_TABS_IN_TITLEBAR_KEY.data()] }) + if (auto showTabsInTitlebar{ json[JsonKey(SHOW_TABS_IN_TITLEBAR_KEY)] }) { result._showTabsInTitlebar = showTabsInTitlebar.asBool(); } - if (auto requestedTheme{ json[REQUESTED_THEME_KEY.data()] }) + if (auto requestedTheme{ json[JsonKey(REQUESTED_THEME_KEY)] }) { result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme)); } - if (auto keybindings{ json[KEYBINDINGS_KEY.data()] }) + if (auto keybindings{ json[JsonKey(KEYBINDINGS_KEY)] }) { result._keybindings = AppKeyBindingsSerialization::FromJson(keybindings); } diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 464c996aaa6..cd83fcdf49b 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -51,7 +51,7 @@ class TerminalApp::GlobalAppSettings final winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept; Json::Value ToJson() const; - static GlobalAppSettings FromJson(Json::Value json); + static GlobalAppSettings FromJson(const Json::Value& json); void ApplyToSettings(winrt::Microsoft::Terminal::Settings::TerminalSettings& settings) const noexcept; diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index cbefcca0a30..0efa06eba4e 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -187,69 +187,66 @@ Json::Value Profile::ToJson() const Json::Value root; ///// Profile-specific settings ///// - root[GUID_KEY.data()] = winrt::to_string(Utils::GuidToString(_guid)); - root[NAME_KEY.data()] = winrt::to_string(_name); + root[JsonKey(GUID_KEY)] = winrt::to_string(Utils::GuidToString(_guid)); + root[JsonKey(NAME_KEY)] = winrt::to_string(_name); ///// Core Settings ///// if (_defaultForeground) { - const auto defaultForeground = winrt::to_string(Utils::ColorToHexString(_defaultForeground.value())); - root[FOREGROUND_KEY.data()] = defaultForeground; + root[JsonKey(FOREGROUND_KEY)] = Utils::ColorToHexString(_defaultForeground.value()); } if (_defaultBackground) { - const auto defaultBackground = winrt::to_string(Utils::ColorToHexString(_defaultBackground.value())); - root[BACKGROUND_KEY.data()] = defaultBackground; + root[JsonKey(BACKGROUND_KEY)] = Utils::ColorToHexString(_defaultBackground.value()); } if (_schemeName) { const auto scheme = winrt::to_string(_schemeName.value()); - root[COLORSCHEME_KEY.data()] = scheme; + root[JsonKey(COLORSCHEME_KEY)] = scheme; } else { Json::Value tableArray{}; for (auto& color : _colorTable) { - auto s = Utils::ColorToHexString(color); - tableArray.append(winrt::to_string(s)); + tableArray.append(Utils::ColorToHexString(color)); } - root[COLORTABLE_KEY.data()] = tableArray; + root[JsonKey(COLORTABLE_KEY)] = tableArray; } - root[HISTORYSIZE_KEY.data()] = _historySize; - root[SNAPONINPUT_KEY.data()] = _snapOnInput; - root[CURSORCOLOR_KEY.data()] = winrt::to_string(Utils::ColorToHexString(_cursorColor)); + root[JsonKey(HISTORYSIZE_KEY)] = _historySize; + root[JsonKey(SNAPONINPUT_KEY)] = _snapOnInput; + root[JsonKey(CURSORCOLOR_KEY)] = Utils::ColorToHexString(_cursorColor); // Only add the cursor height property if we're a legacy-style cursor. if (_cursorShape == CursorStyle::Vintage) { - root[CURSORHEIGHT_KEY.data()] = _cursorHeight; + root[JsonKey(CURSORHEIGHT_KEY)] = _cursorHeight; } - root[CURSORSHAPE_KEY.data()] = winrt::to_string(_SerializeCursorStyle(_cursorShape)); + root[JsonKey(CURSORSHAPE_KEY)] = winrt::to_string(_SerializeCursorStyle(_cursorShape)); ///// Control Settings ///// - root[COMMANDLINE_KEY.data()] = winrt::to_string(_commandline); - root[FONTFACE_KEY.data()] = winrt::to_string(_fontFace); - root[FONTSIZE_KEY.data()] = _fontSize; - root[ACRYLICTRANSPARENCY_KEY.data()] = _acrylicTransparency; - root[USEACRYLIC_KEY.data()] = _useAcrylic; - root[CLOSEONEXIT_KEY.data()] = _closeOnExit; - root[PADDING_KEY.data()] = winrt::to_string(_padding); + root[JsonKey(COMMANDLINE_KEY)] = winrt::to_string(_commandline); + root[JsonKey(FONTFACE_KEY)] = winrt::to_string(_fontFace); + root[JsonKey(FONTSIZE_KEY)] = _fontSize; + root[JsonKey(ACRYLICTRANSPARENCY_KEY)] = _acrylicTransparency; + root[JsonKey(USEACRYLIC_KEY)] = _useAcrylic; + root[JsonKey(CLOSEONEXIT_KEY)] = _closeOnExit; + root[JsonKey(PADDING_KEY)] = winrt::to_string(_padding); if (_scrollbarState) { const auto scrollbarState = winrt::to_string(_scrollbarState.value()); - root[SCROLLBARSTATE_KEY.data()] = scrollbarState; + root[JsonKey(SCROLLBARSTATE_KEY)] = scrollbarState; } if (_icon) { const auto icon = winrt::to_string(_icon.value()); - root[ICON_KEY.data()] = icon; + root[JsonKey(ICON_KEY)] = icon; } if (_startingDirectory) { - root[STARTINGDIRECTORY_KEY.data()] = winrt::to_string(_startingDirectory.value()); + root[JsonKey(STARTINGDIRECTORY_KEY)] = winrt::to_string(_startingDirectory.value()); } return root; @@ -261,112 +258,112 @@ Json::Value Profile::ToJson() const // - json: an object which should be a serialization of a Profile object. // Return Value: // - a new Profile instance created from the values in `json` -Profile Profile::FromJson(Json::Value json) +Profile Profile::FromJson(const Json::Value& json) { Profile result{}; // Profile-specific Settings - if (auto name{ json[NAME_KEY.data()] }) + if (auto name{ json[JsonKey(NAME_KEY)] }) { result._name = GetWstringFromJson(name); } - if (auto guid{ json[GUID_KEY.data()] }) + if (auto guid{ json[JsonKey(GUID_KEY)] }) { result._guid = Utils::GuidFromString(GetWstringFromJson(guid)); } // Core Settings - if (auto foreground{ json[FOREGROUND_KEY.data()] }) + if (auto foreground{ json[JsonKey(FOREGROUND_KEY)] }) { - const auto color = Utils::ColorFromHexString(GetWstringFromJson(foreground)); + const auto color = Utils::ColorFromHexString(foreground.asString()); result._defaultForeground = color; } - if (auto background{ json[BACKGROUND_KEY.data()] }) + if (auto background{ json[JsonKey(BACKGROUND_KEY)] }) { - const auto color = Utils::ColorFromHexString(GetWstringFromJson(background)); + const auto color = Utils::ColorFromHexString(background.asString()); result._defaultBackground = color; } - if (auto colorScheme{ json[COLORSCHEME_KEY.data()] }) + if (auto colorScheme{ json[JsonKey(COLORSCHEME_KEY)] }) { result._schemeName = GetWstringFromJson(colorScheme); } else { - if (auto colortable{ json[COLORTABLE_KEY.data()] }) + if (auto colortable{ json[JsonKey(COLORTABLE_KEY)] }) { int i = 0; for (auto tableEntry : colortable) { if (tableEntry.isString()) { - const auto color = Utils::ColorFromHexString(GetWstringFromJson(tableEntry)); + const auto color = Utils::ColorFromHexString(tableEntry.asString()); result._colorTable[i] = color; } i++; } } } - if (auto historySize{ json[HISTORYSIZE_KEY.data()] }) + if (auto historySize{ json[JsonKey(HISTORYSIZE_KEY)] }) { // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" result._historySize = historySize.asInt(); } - if (auto snapOnInput{ json[SNAPONINPUT_KEY.data()] }) + if (auto snapOnInput{ json[JsonKey(SNAPONINPUT_KEY)] }) { result._snapOnInput = snapOnInput.asBool(); } - if (auto cursorColor{ json[CURSORCOLOR_KEY.data()] }) + if (auto cursorColor{ json[JsonKey(CURSORCOLOR_KEY)] }) { - const auto color = Utils::ColorFromHexString(GetWstringFromJson(cursorColor)); + const auto color = Utils::ColorFromHexString(cursorColor.asString()); result._cursorColor = color; } - if (auto cursorHeight{ json[CURSORHEIGHT_KEY.data()] }) + if (auto cursorHeight{ json[JsonKey(CURSORHEIGHT_KEY)] }) { result._cursorHeight = cursorHeight.asUInt(); } - if (auto cursorShape{ json[CURSORSHAPE_KEY.data()] }) + if (auto cursorShape{ json[JsonKey(CURSORSHAPE_KEY)] }) { result._cursorShape = _ParseCursorShape(GetWstringFromJson(cursorShape)); } // Control Settings - if (auto commandline{ json[COMMANDLINE_KEY.data()] }) + if (auto commandline{ json[JsonKey(COMMANDLINE_KEY)] }) { result._commandline = GetWstringFromJson(commandline); } - if (auto fontFace{ json[FONTFACE_KEY.data()] }) + if (auto fontFace{ json[JsonKey(FONTFACE_KEY)] }) { result._fontFace = GetWstringFromJson(fontFace); } - if (auto fontSize{ json[FONTSIZE_KEY.data()] }) + if (auto fontSize{ json[JsonKey(FONTSIZE_KEY)] }) { result._fontSize = fontSize.asInt(); } - if (auto acrylicTransparency{ json[ACRYLICTRANSPARENCY_KEY.data()] }) + if (auto acrylicTransparency{ json[JsonKey(ACRYLICTRANSPARENCY_KEY)] }) { result._acrylicTransparency = acrylicTransparency.asFloat(); } - if (auto useAcrylic{ json[USEACRYLIC_KEY.data()] }) + if (auto useAcrylic{ json[JsonKey(USEACRYLIC_KEY)] }) { result._useAcrylic = useAcrylic.asBool(); } - if (auto closeOnExit{ json[CLOSEONEXIT_KEY.data()] }) + if (auto closeOnExit{ json[JsonKey(CLOSEONEXIT_KEY)] }) { result._closeOnExit = closeOnExit.asBool(); } - if (auto padding{ json[PADDING_KEY.data()] }) + if (auto padding{ json[JsonKey(PADDING_KEY)] }) { result._padding = GetWstringFromJson(padding); } - if (auto scrollbarState{ json[SCROLLBARSTATE_KEY.data()] }) + if (auto scrollbarState{ json[JsonKey(SCROLLBARSTATE_KEY)] }) { result._scrollbarState = GetWstringFromJson(scrollbarState); } - if (auto startingDirectory{ json[STARTINGDIRECTORY_KEY.data()] }) + if (auto startingDirectory{ json[JsonKey(STARTINGDIRECTORY_KEY)] }) { result._startingDirectory = GetWstringFromJson(startingDirectory); } - if (auto icon{ json[ICON_KEY.data()] }) + if (auto icon{ json[JsonKey(ICON_KEY)] }) { result._icon = GetWstringFromJson(icon); } diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index fbbec46b68a..af5272be871 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -33,7 +33,7 @@ class TerminalApp::Profile final winrt::Microsoft::Terminal::Settings::TerminalSettings CreateTerminalSettings(const std::vector<::TerminalApp::ColorScheme>& schemes) const; Json::Value ToJson() const; - static Profile FromJson(Json::Value json); + static Profile FromJson(const Json::Value& json); GUID GetGuid() const noexcept; std::wstring_view GetName() const noexcept; diff --git a/src/cascadia/TerminalApp/Utils.h b/src/cascadia/TerminalApp/Utils.h index 337e72f2b2e..4cdfc2f4b85 100644 --- a/src/cascadia/TerminalApp/Utils.h +++ b/src/cascadia/TerminalApp/Utils.h @@ -14,3 +14,17 @@ Author(s): #pragma once std::wstring GetWstringFromJson(const Json::Value& json); + +// Method Description: +// - Create a std::string from a string_view. We do this because we can't look +// up a key in a Json::Value with a string_view directly, so instead we'll use +// this helper. Should a string_view lookup ever be added to jsoncpp, we can +// remove this entirely. +// Arguments: +// - key: the string_view to build a string from +// Return Value: +// - a std::string to use for looking up a value from a Json::Value +inline std::string JsonKey(const std::string_view key) +{ + return static_cast(key); +} diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index 2e005f064d1..d2c3c2dd0ab 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -50,5 +50,4 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalWin32Provider); #include // JsonCpp -#include #include diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index e4e1d42a434..d8769aef34d 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -19,8 +19,10 @@ namespace Microsoft::Console::Utils GUID GuidFromString(const std::wstring wstr); GUID CreateGuid(); - std::wstring ColorToHexString(const COLORREF color); - COLORREF ColorFromHexString(const std::wstring wstr); + std::wstring ColorToHexStringW(const COLORREF color); + COLORREF ColorFromHexStringW(const std::wstring wstr); + std::string ColorToHexString(const COLORREF color); + COLORREF ColorFromHexString(const std::string wstr); void InitializeCampbellColorTable(gsl::span& table); void Initialize256ColorTable(gsl::span& table); diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 32ef395518b..bd46d3e80db 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -60,7 +60,7 @@ GUID Utils::CreateGuid() // - color: the COLORREF to create the string for // Return Value: // - a string representation of the color -std::wstring Utils::ColorToHexString(const COLORREF color) +std::wstring Utils::ColorToHexStringW(const COLORREF color) { std::wstringstream ss; ss << L"#" << std::uppercase << std::setfill(L'0') << std::hex; @@ -77,7 +77,7 @@ std::wstring Utils::ColorToHexString(const COLORREF color) // Return Value: // - A COLORREF if the string could successfully be parsed. If the string is not // the correct format, throws E_INVALIDARG -COLORREF Utils::ColorFromHexString(const std::wstring wstr) +COLORREF Utils::ColorFromHexStringW(const std::wstring wstr) { THROW_HR_IF(E_INVALIDARG, wstr.size() < 7 || wstr.size() >= 8); THROW_HR_IF(E_INVALIDARG, wstr[0] != L'#'); @@ -93,6 +93,45 @@ COLORREF Utils::ColorFromHexString(const std::wstring wstr) return RGB(r, g, b); } +// Function Description: +// - Creates a String representation of a color, in the format "#RRGGBB" +// Arguments: +// - color: the COLORREF to create the string for +// Return Value: +// - a string representation of the color +std::string Utils::ColorToHexString(const COLORREF color) +{ + std::stringstream ss; + ss << "#" << std::uppercase << std::setfill('0') << std::hex; + ss << std::setw(2) << GetRValue(color); + ss << std::setw(2) << GetGValue(color); + ss << std::setw(2) << GetBValue(color); + return ss.str(); +} + +// Function Description: +// - Parses a color from a string. The string should be in the format "#RRGGBB" +// Arguments: +// - str: a string representation of the COLORREF to parse +// Return Value: +// - A COLORREF if the string could successfully be parsed. If the string is not +// the correct format, throws E_INVALIDARG +COLORREF Utils::ColorFromHexString(const std::string str) +{ + THROW_HR_IF(E_INVALIDARG, str.size() < 7 || str.size() >= 8); + THROW_HR_IF(E_INVALIDARG, str[0] != '#'); + + std::string rStr{ &str[1], 2 }; + std::string gStr{ &str[3], 2 }; + std::string bStr{ &str[5], 2 }; + + BYTE r = static_cast(std::stoul(rStr, nullptr, 16)); + BYTE g = static_cast(std::stoul(gStr, nullptr, 16)); + BYTE b = static_cast(std::stoul(bStr, nullptr, 16)); + + return RGB(r, g, b); +} + // Routine Description: // - Shorthand check if a handle value is null or invalid. // Arguments: From c4b2eefc6a67d3633e7f7fd3cdec10dcef01568a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 30 May 2019 14:50:55 -0500 Subject: [PATCH 24/26] Remaining PR nits --- .../AppKeyBindingsSerialization.cpp | 21 +++++---- .../TerminalApp/AppKeyBindingsSerialization.h | 16 +++++++ src/cascadia/TerminalApp/CascadiaSettings.h | 4 +- .../CascadiaSettingsSerialization.cpp | 19 +++----- src/types/inc/utils.hpp | 2 - src/types/utils.cpp | 47 ++----------------- 6 files changed, 43 insertions(+), 66 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index d34381155ff..34d9e1d0a4e 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -50,7 +50,13 @@ static constexpr std::string_view OPENSETTINGS_KEY{ "openSettings" }; // Specifically use a map here over an unordered_map. We want to be able to // iterate over these entries in-order when we're serializing the keybindings. -static const std::map commandNames { +// HERE BE DRAGONS: +// These are string_views that are being used as keys. These string_views are +// just pointers to other strings. This could be dangerous, if the map outlived +// the actual strings being pointed to. However, since both these strings and +// the map are all const for the lifetime of the app, we have nothing to worry +// about here. +static const std::map> commandNames { { COPYTEXT_KEY, ShortcutAction::CopyText }, { PASTETEXT_KEY, ShortcutAction::PasteText }, { NEWTAB_KEY, ShortcutAction::NewTab }, @@ -98,7 +104,7 @@ static const std::map commandNames { // Return Value: // - a Json::Value which is an equivalent serialization of this object. static Json::Value _ShortcutAsJsonObject(const KeyChord& chord, - const std::string_view& actionName) + const std::string_view actionName) { const auto keyString = KeyChordSerialization::ToString(chord); if (keyString == L"") @@ -132,11 +138,10 @@ Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKey { const auto searchedForName = actionName.first; const auto searchedForAction = actionName.second; - const auto chord = bindings.GetKeyBinding(searchedForAction); - if (chord) + + if (const auto chord{ bindings.GetKeyBinding(searchedForAction) }) { - const auto serialization = _ShortcutAsJsonObject(chord, searchedForName); - if (serialization) + if (const auto serialization{ _ShortcutAsJsonObject(chord, searchedForName) }) { bindingsArray.append(serialization); } @@ -178,7 +183,7 @@ winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const J ShortcutAction action; // Try matching the command to one we have - auto found = commandNames.find(commandString.asString()); + const auto found = commandNames.find(commandString.asString()); if (found != commandNames.end()) { action = found->second; @@ -191,7 +196,7 @@ winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const J // Try parsing the chord try { - auto chord = KeyChordSerialization::FromString(keyChordString); + const auto chord = KeyChordSerialization::FromString(keyChordString); newBindings.SetKeyBinding(action, chord); } catch (...) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h index 54b3a9e862e..284de077026 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h @@ -1,5 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +// +// Module Name: +// - Profile.hpp +// +// Abstract: +// - A coupler helper functions for serializing/deserializing an AppKeyBindings +// to/from json. We need this to exist as external helper functions, rather +// than defining these as methods on the AppKeyBindings class, because +// AppKeyBindings is a winrt type. When we're working with a AppKeyBindings +// object, we only have access to methods defined on the winrt interface (in +// the idl). We don't have access to methods we define on the +// implementation. Since JsonValue is not a winrt type, we can't define any +// methods that operate on it in the idl. +// +// Author(s): +// - Mike Griese - May 2019 #pragma once #include "AppKeyBindings.h" diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 3c8f0b12d58..16e263a706e 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -62,8 +62,8 @@ class TerminalApp::CascadiaSettings final void _CreateDefaultProfiles(); static bool _IsPackaged(); - static void _SaveAsPackagedApp(const std::string content); - static void _SaveAsUnpackagedApp(const std::string content); + static void _SaveAsPackagedApp(const std::string& content); + static void _SaveAsUnpackagedApp(const std::string& content); static std::wstring _GetFullPathToUnpackagedSettingsFile(); static winrt::hstring _GetPackagedSettingsPath(); static std::optional _LoadAsPackagedApp(); diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 692bfa0803c..0475dc2f83b 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -51,7 +51,6 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa Json::Value root; std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() }; std::string errs; // This string will recieve any error text from failing to parse. - // `parse` will return false if it fails. if (!reader->parse(actualData.c_str(), actualData.c_str() + actualData.size(), &root, &errs)) { @@ -236,7 +235,7 @@ bool CascadiaSettings::_IsPackaged() // - content: the given string of content to write to the file. // Return Value: // - -void CascadiaSettings::_SaveAsPackagedApp(const std::string content) +void CascadiaSettings::_SaveAsPackagedApp(const std::string& content) { auto curr = ApplicationData::Current(); auto folder = curr.RoamingFolder(); @@ -249,9 +248,9 @@ void CascadiaSettings::_SaveAsPackagedApp(const std::string content) DataWriter dw = DataWriter(); const char* firstChar = content.c_str(); const char* lastChar = firstChar + content.size(); - // Manually cast to a uint8_t*, because the compiler is unhappy with a static_cast here. - const uint8_t* firstByte = (const uint8_t*)firstChar; - const uint8_t* lastByte = (const uint8_t*)lastChar; + + const uint8_t* firstByte = reinterpret_cast(firstChar); + const uint8_t* lastByte = reinterpret_cast(lastChar); winrt::array_view bytes{ firstByte, lastByte }; dw.WriteBytes(bytes); @@ -267,7 +266,7 @@ void CascadiaSettings::_SaveAsPackagedApp(const std::string content) // - // This can throw an exception if we fail to open the file for writing, or we // fail to write the file -void CascadiaSettings::_SaveAsUnpackagedApp(const std::string content) +void CascadiaSettings::_SaveAsUnpackagedApp(const std::string& content) { // Get path to output file // In this scenario, the settings file will end up under e.g. C:\Users\admin\AppData\Roaming\Microsoft\Windows Terminal\profiles.json @@ -336,12 +335,8 @@ std::optional CascadiaSettings::_LoadAsPackagedApp() // settings file is UTF-8 without BOM auto buffer = FileIO::ReadBufferAsync(storageFile).get(); auto bufferData = buffer.data(); - std::string resultString; - resultString.reserve(buffer.Length()); - for (size_t i = 0; i < buffer.Length(); i++) - { - resultString.push_back(bufferData[i]); - } + std::vector bytes{ bufferData, bufferData + buffer.Length() }; + std::string resultString{ bytes.begin(), bytes.end() }; return { resultString }; } diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index d8769aef34d..eb0d6c75249 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -19,8 +19,6 @@ namespace Microsoft::Console::Utils GUID GuidFromString(const std::wstring wstr); GUID CreateGuid(); - std::wstring ColorToHexStringW(const COLORREF color); - COLORREF ColorFromHexStringW(const std::wstring wstr); std::string ColorToHexString(const COLORREF color); COLORREF ColorFromHexString(const std::string wstr); diff --git a/src/types/utils.cpp b/src/types/utils.cpp index bd46d3e80db..8016491df7d 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -54,45 +54,6 @@ GUID Utils::CreateGuid() return result; } -// Function Description: -// - Creates a String representation of a color, in the format "#RRGGBB" -// Arguments: -// - color: the COLORREF to create the string for -// Return Value: -// - a string representation of the color -std::wstring Utils::ColorToHexStringW(const COLORREF color) -{ - std::wstringstream ss; - ss << L"#" << std::uppercase << std::setfill(L'0') << std::hex; - ss << std::setw(2) << GetRValue(color); - ss << std::setw(2) << GetGValue(color); - ss << std::setw(2) << GetBValue(color); - return ss.str(); -} - -// Function Description: -// - Parses a color from a string. The string should be in the format "#RRGGBB" -// Arguments: -// - wstr: a string representation of the COLORREF to parse -// Return Value: -// - A COLORREF if the string could successfully be parsed. If the string is not -// the correct format, throws E_INVALIDARG -COLORREF Utils::ColorFromHexStringW(const std::wstring wstr) -{ - THROW_HR_IF(E_INVALIDARG, wstr.size() < 7 || wstr.size() >= 8); - THROW_HR_IF(E_INVALIDARG, wstr[0] != L'#'); - - std::wstring rStr{ &wstr[1], 2 }; - std::wstring gStr{ &wstr[3], 2 }; - std::wstring bStr{ &wstr[5], 2 }; - - BYTE r = static_cast(std::stoul(rStr, nullptr, 16)); - BYTE g = static_cast(std::stoul(gStr, nullptr, 16)); - BYTE b = static_cast(std::stoul(bStr, nullptr, 16)); - - return RGB(r, g, b); -} - // Function Description: // - Creates a String representation of a color, in the format "#RRGGBB" // Arguments: @@ -103,9 +64,11 @@ std::string Utils::ColorToHexString(const COLORREF color) { std::stringstream ss; ss << "#" << std::uppercase << std::setfill('0') << std::hex; - ss << std::setw(2) << GetRValue(color); - ss << std::setw(2) << GetGValue(color); - ss << std::setw(2) << GetBValue(color); + // Force the compiler to promote from byte to int. Without it, the + // stringstream will try to write the components as chars + ss << std::setw(2) << static_cast(GetRValue(color)); + ss << std::setw(2) << static_cast(GetGValue(color)); + ss << std::setw(2) << static_cast(GetBValue(color)); return ss.str(); } From 63e0a23acf25cca275838956a4d9f2cc80855b07 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 31 May 2019 08:32:13 -0500 Subject: [PATCH 25/26] Change the case of all the keys to PascalCase and the remaining PR nits --- .../AppKeyBindingsSerialization.cpp | 146 ++++++------ .../TerminalApp/AppKeyBindingsSerialization.h | 2 +- .../CascadiaSettingsSerialization.cpp | 22 +- src/cascadia/TerminalApp/ColorScheme.cpp | 28 +-- .../TerminalApp/GlobalAppSettings.cpp | 68 +++--- src/cascadia/TerminalApp/Profile.cpp | 219 +++++++++--------- src/cascadia/TerminalApp/Profile.h | 2 +- 7 files changed, 242 insertions(+), 245 deletions(-) diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index 34d9e1d0a4e..e32d2adc15d 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -10,43 +10,43 @@ using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::TerminalApp; -static constexpr std::string_view KEYS_KEY{ "keys" }; -static constexpr std::string_view COMMAND_KEY{ "command" }; - -static constexpr std::string_view COPYTEXT_KEY{ "copy" }; -static constexpr std::string_view PASTETEXT_KEY{ "paste" }; -static constexpr std::string_view NEWTAB_KEY{ "newTab" }; -static constexpr std::string_view NEWTABWITHPROFILE0_KEY{ "newTabProfile0" }; -static constexpr std::string_view NEWTABWITHPROFILE1_KEY{ "newTabProfile1" }; -static constexpr std::string_view NEWTABWITHPROFILE2_KEY{ "newTabProfile2" }; -static constexpr std::string_view NEWTABWITHPROFILE3_KEY{ "newTabProfile3" }; -static constexpr std::string_view NEWTABWITHPROFILE4_KEY{ "newTabProfile4" }; -static constexpr std::string_view NEWTABWITHPROFILE5_KEY{ "newTabProfile5" }; -static constexpr std::string_view NEWTABWITHPROFILE6_KEY{ "newTabProfile6" }; -static constexpr std::string_view NEWTABWITHPROFILE7_KEY{ "newTabProfile7" }; -static constexpr std::string_view NEWTABWITHPROFILE8_KEY{ "newTabProfile8" }; -static constexpr std::string_view NEWWINDOW_KEY{ "newWindow" }; -static constexpr std::string_view CLOSEWINDOW_KEY{ "closeWindow" }; -static constexpr std::string_view CLOSETAB_KEY{ "closeTab" }; -static constexpr std::string_view SWITCHTOTAB_KEY{ "switchToTab" }; -static constexpr std::string_view NEXTTAB_KEY{ "nextTab" }; -static constexpr std::string_view PREVTAB_KEY{ "prevTab" }; -static constexpr std::string_view INCREASEFONTSIZE_KEY{ "increaseFontSize" }; -static constexpr std::string_view DECREASEFONTSIZE_KEY{ "decreaseFontSize" }; -static constexpr std::string_view SCROLLUP_KEY{ "scrollUp" }; -static constexpr std::string_view SCROLLDOWN_KEY{ "scrollDown" }; -static constexpr std::string_view SCROLLUPPAGE_KEY{ "scrollUpPage" }; -static constexpr std::string_view SCROLLDOWNPAGE_KEY{ "scrollDownPage" }; -static constexpr std::string_view SWITCHTOTAB0_KEY{ "switchToTab0" }; -static constexpr std::string_view SWITCHTOTAB1_KEY{ "switchToTab1" }; -static constexpr std::string_view SWITCHTOTAB2_KEY{ "switchToTab2" }; -static constexpr std::string_view SWITCHTOTAB3_KEY{ "switchToTab3" }; -static constexpr std::string_view SWITCHTOTAB4_KEY{ "switchToTab4" }; -static constexpr std::string_view SWITCHTOTAB5_KEY{ "switchToTab5" }; -static constexpr std::string_view SWITCHTOTAB6_KEY{ "switchToTab6" }; -static constexpr std::string_view SWITCHTOTAB7_KEY{ "switchToTab7" }; -static constexpr std::string_view SWITCHTOTAB8_KEY{ "switchToTab8" }; -static constexpr std::string_view OPENSETTINGS_KEY{ "openSettings" }; +static constexpr std::string_view KeysKey{ "keys" }; +static constexpr std::string_view CommandKey{ "command" }; + +static constexpr std::string_view CopyTextKey{ "copy" }; +static constexpr std::string_view PasteTextKey{ "paste" }; +static constexpr std::string_view NewTabKey{ "newTab" }; +static constexpr std::string_view NewTabWithProfile0Key{ "newTabProfile0" }; +static constexpr std::string_view NewTabWithProfile1Key{ "newTabProfile1" }; +static constexpr std::string_view NewTabWithProfile2Key{ "newTabProfile2" }; +static constexpr std::string_view NewTabWithProfile3Key{ "newTabProfile3" }; +static constexpr std::string_view NewTabWithProfile4Key{ "newTabProfile4" }; +static constexpr std::string_view NewTabWithProfile5Key{ "newTabProfile5" }; +static constexpr std::string_view NewTabWithProfile6Key{ "newTabProfile6" }; +static constexpr std::string_view NewTabWithProfile7Key{ "newTabProfile7" }; +static constexpr std::string_view NewTabWithProfile8Key{ "newTabProfile8" }; +static constexpr std::string_view NewWindowKey{ "newWindow" }; +static constexpr std::string_view CloseWindowKey{ "closeWindow" }; +static constexpr std::string_view CloseTabKey{ "closeTab" }; +static constexpr std::string_view SwitchtoTabKey{ "switchToTab" }; +static constexpr std::string_view NextTabKey{ "nextTab" }; +static constexpr std::string_view PrevTabKey{ "prevTab" }; +static constexpr std::string_view IncreaseFontSizeKey{ "increaseFontSize" }; +static constexpr std::string_view DecreaseFontSizeKey{ "decreaseFontSize" }; +static constexpr std::string_view ScrollupKey{ "scrollUp" }; +static constexpr std::string_view ScrolldownKey{ "scrollDown" }; +static constexpr std::string_view ScrolluppageKey{ "scrollUpPage" }; +static constexpr std::string_view ScrolldownpageKey{ "scrollDownPage" }; +static constexpr std::string_view SwitchToTab0Key{ "switchToTab0" }; +static constexpr std::string_view SwitchToTab1Key{ "switchToTab1" }; +static constexpr std::string_view SwitchToTab2Key{ "switchToTab2" }; +static constexpr std::string_view SwitchToTab3Key{ "switchToTab3" }; +static constexpr std::string_view SwitchToTab4Key{ "switchToTab4" }; +static constexpr std::string_view SwitchToTab5Key{ "switchToTab5" }; +static constexpr std::string_view SwitchToTab6Key{ "switchToTab6" }; +static constexpr std::string_view SwitchToTab7Key{ "switchToTab7" }; +static constexpr std::string_view SwitchToTab8Key{ "switchToTab8" }; +static constexpr std::string_view OpenSettingsKey{ "openSettings" }; // Specifically use a map here over an unordered_map. We want to be able to // iterate over these entries in-order when we're serializing the keybindings. @@ -57,38 +57,38 @@ static constexpr std::string_view OPENSETTINGS_KEY{ "openSettings" }; // the map are all const for the lifetime of the app, we have nothing to worry // about here. static const std::map> commandNames { - { COPYTEXT_KEY, ShortcutAction::CopyText }, - { PASTETEXT_KEY, ShortcutAction::PasteText }, - { NEWTAB_KEY, ShortcutAction::NewTab }, - { NEWTABWITHPROFILE0_KEY, ShortcutAction::NewTabProfile0 }, - { NEWTABWITHPROFILE1_KEY, ShortcutAction::NewTabProfile1 }, - { NEWTABWITHPROFILE2_KEY, ShortcutAction::NewTabProfile2 }, - { NEWTABWITHPROFILE3_KEY, ShortcutAction::NewTabProfile3 }, - { NEWTABWITHPROFILE4_KEY, ShortcutAction::NewTabProfile4 }, - { NEWTABWITHPROFILE5_KEY, ShortcutAction::NewTabProfile5 }, - { NEWTABWITHPROFILE6_KEY, ShortcutAction::NewTabProfile6 }, - { NEWTABWITHPROFILE7_KEY, ShortcutAction::NewTabProfile7 }, - { NEWTABWITHPROFILE8_KEY, ShortcutAction::NewTabProfile8 }, - { NEWWINDOW_KEY, ShortcutAction::NewWindow }, - { CLOSEWINDOW_KEY, ShortcutAction::CloseWindow }, - { CLOSETAB_KEY, ShortcutAction::CloseTab }, - { NEXTTAB_KEY, ShortcutAction::NextTab }, - { PREVTAB_KEY, ShortcutAction::PrevTab }, - { INCREASEFONTSIZE_KEY, ShortcutAction::IncreaseFontSize }, - { DECREASEFONTSIZE_KEY, ShortcutAction::DecreaseFontSize }, - { SCROLLUP_KEY, ShortcutAction::ScrollUp }, - { SCROLLDOWN_KEY, ShortcutAction::ScrollDown }, - { SCROLLUPPAGE_KEY, ShortcutAction::ScrollUpPage }, - { SCROLLDOWNPAGE_KEY, ShortcutAction::ScrollDownPage }, - { SWITCHTOTAB0_KEY, ShortcutAction::SwitchToTab0 }, - { SWITCHTOTAB1_KEY, ShortcutAction::SwitchToTab1 }, - { SWITCHTOTAB2_KEY, ShortcutAction::SwitchToTab2 }, - { SWITCHTOTAB3_KEY, ShortcutAction::SwitchToTab3 }, - { SWITCHTOTAB4_KEY, ShortcutAction::SwitchToTab4 }, - { SWITCHTOTAB5_KEY, ShortcutAction::SwitchToTab5 }, - { SWITCHTOTAB6_KEY, ShortcutAction::SwitchToTab6 }, - { SWITCHTOTAB7_KEY, ShortcutAction::SwitchToTab7 }, - { SWITCHTOTAB8_KEY, ShortcutAction::SwitchToTab8 }, + { CopyTextKey, ShortcutAction::CopyText }, + { PasteTextKey, ShortcutAction::PasteText }, + { NewTabKey, ShortcutAction::NewTab }, + { NewTabWithProfile0Key, ShortcutAction::NewTabProfile0 }, + { NewTabWithProfile1Key, ShortcutAction::NewTabProfile1 }, + { NewTabWithProfile2Key, ShortcutAction::NewTabProfile2 }, + { NewTabWithProfile3Key, ShortcutAction::NewTabProfile3 }, + { NewTabWithProfile4Key, ShortcutAction::NewTabProfile4 }, + { NewTabWithProfile5Key, ShortcutAction::NewTabProfile5 }, + { NewTabWithProfile6Key, ShortcutAction::NewTabProfile6 }, + { NewTabWithProfile7Key, ShortcutAction::NewTabProfile7 }, + { NewTabWithProfile8Key, ShortcutAction::NewTabProfile8 }, + { NewWindowKey, ShortcutAction::NewWindow }, + { CloseWindowKey, ShortcutAction::CloseWindow }, + { CloseTabKey, ShortcutAction::CloseTab }, + { NextTabKey, ShortcutAction::NextTab }, + { PrevTabKey, ShortcutAction::PrevTab }, + { IncreaseFontSizeKey, ShortcutAction::IncreaseFontSize }, + { DecreaseFontSizeKey, ShortcutAction::DecreaseFontSize }, + { ScrollupKey, ShortcutAction::ScrollUp }, + { ScrolldownKey, ShortcutAction::ScrollDown }, + { ScrolluppageKey, ShortcutAction::ScrollUpPage }, + { ScrolldownpageKey, ShortcutAction::ScrollDownPage }, + { SwitchToTab0Key, ShortcutAction::SwitchToTab0 }, + { SwitchToTab1Key, ShortcutAction::SwitchToTab1 }, + { SwitchToTab2Key, ShortcutAction::SwitchToTab2 }, + { SwitchToTab3Key, ShortcutAction::SwitchToTab3 }, + { SwitchToTab4Key, ShortcutAction::SwitchToTab4 }, + { SwitchToTab5Key, ShortcutAction::SwitchToTab5 }, + { SwitchToTab6Key, ShortcutAction::SwitchToTab6 }, + { SwitchToTab7Key, ShortcutAction::SwitchToTab7 }, + { SwitchToTab8Key, ShortcutAction::SwitchToTab8 }, }; // Function Description: @@ -116,8 +116,8 @@ static Json::Value _ShortcutAsJsonObject(const KeyChord& chord, Json::Value keysArray; keysArray.append(winrt::to_string(keyString)); - jsonObject[JsonKey(KEYS_KEY)] = keysArray; - jsonObject[JsonKey(COMMAND_KEY)] = actionName.data(); + jsonObject[JsonKey(KeysKey)] = keysArray; + jsonObject[JsonKey(CommandKey)] = actionName.data(); return jsonObject; } @@ -170,8 +170,8 @@ winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const J { if (value.isObject()) { - const auto commandString = value[JsonKey(COMMAND_KEY)]; - const auto keys = value[JsonKey(KEYS_KEY)]; + const auto commandString = value[JsonKey(CommandKey)]; + const auto keys = value[JsonKey(KeysKey)]; if (commandString && keys) { diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h index 284de077026..4b4cc778f90 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.h @@ -5,7 +5,7 @@ // - Profile.hpp // // Abstract: -// - A coupler helper functions for serializing/deserializing an AppKeyBindings +// - A couple helper functions for serializing/deserializing an AppKeyBindings // to/from json. We need this to exist as external helper functions, rather // than defining these as methods on the AppKeyBindings class, because // AppKeyBindings is a winrt type. When we're working with a AppKeyBindings diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 0475dc2f83b..1d51425761f 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -20,10 +20,10 @@ using namespace ::Microsoft::Console; static constexpr std::wstring_view FILENAME { L"profiles.json" }; static constexpr std::wstring_view SETTINGS_FOLDER_NAME{ L"\\Microsoft\\Windows Terminal\\" }; -static constexpr std::string_view PROFILES_KEY{ "profiles" }; -static constexpr std::string_view KEYBINDINGS_KEY{ "keybindings" }; -static constexpr std::string_view GLOBALS_KEY{ "globals" }; -static constexpr std::string_view SCHEMES_KEY{ "schemes" }; +static constexpr std::string_view ProfilesKey{ "profiles" }; +static constexpr std::string_view KeybindingsKey{ "keybindings" }; +static constexpr std::string_view GlobalsKey{ "globals" }; +static constexpr std::string_view SchemesKey{ "schemes" }; // Method Description: // - Creates a CascadiaSettings from whatever's saved on disk, or instantiates @@ -133,9 +133,9 @@ Json::Value CascadiaSettings::ToJson() const schemesArray.append(scheme.ToJson()); } - root[GLOBALS_KEY.data()] = _globals.ToJson(); - root[PROFILES_KEY.data()] = profilesArray; - root[SCHEMES_KEY.data()] = schemesArray; + root[GlobalsKey.data()] = _globals.ToJson(); + root[ProfilesKey.data()] = profilesArray; + root[SchemesKey.data()] = schemesArray; return root; } @@ -150,7 +150,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& { std::unique_ptr resultPtr = std::make_unique(); - if (auto globals{ json[GLOBALS_KEY.data()] }) + if (auto globals{ json[GlobalsKey.data()] }) { if (globals.isObject()) { @@ -167,7 +167,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& // If we didn't find keybindings in the legacy path, then they probably // don't exist in the file. Create the default keybindings if we // couldn't find any keybindings. - auto keybindings{ json[KEYBINDINGS_KEY.data()] }; + auto keybindings{ json[KeybindingsKey.data()] }; if (!keybindings) { resultPtr->_CreateDefaultKeybindings(); @@ -184,7 +184,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& // Or should we just recreate the default profiles? auto& resultSchemes = resultPtr->_globals.GetColorSchemes(); - if (auto schemes{ json[SCHEMES_KEY.data()] }) + if (auto schemes{ json[SchemesKey.data()] }) { for (auto schemeJson : schemes) { @@ -196,7 +196,7 @@ std::unique_ptr CascadiaSettings::FromJson(const Json::Value& } } - if (auto profiles{ json[PROFILES_KEY.data()] }) + if (auto profiles{ json[ProfilesKey.data()] }) { for (auto profileJson : profiles) { diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index 7f6e404581f..07ce50bb90f 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -13,11 +13,11 @@ using namespace winrt::Microsoft::Terminal::TerminalControl; using namespace winrt::TerminalApp; using namespace winrt::Windows::Data::Json; -static constexpr std::string_view NAME_KEY{ "name" }; -static constexpr std::string_view TABLE_KEY{ "colors" }; -static constexpr std::string_view FOREGROUND_KEY{ "foreground" }; -static constexpr std::string_view BACKGROUND_KEY{ "background" }; -static constexpr std::array TABLE_COLORS = +static constexpr std::string_view NameKey{ "name" }; +static constexpr std::string_view TableKey{ "colors" }; +static constexpr std::string_view ForegroundKey{ "foreground" }; +static constexpr std::string_view BackgroundKey{ "background" }; +static constexpr std::array TableColors = { "black", "red", @@ -87,12 +87,12 @@ void ColorScheme::ApplyScheme(TerminalSettings terminalSettings) const Json::Value ColorScheme::ToJson() const { Json::Value root; - root[JsonKey(NAME_KEY)] = winrt::to_string(_schemeName); - root[JsonKey(FOREGROUND_KEY)] = Utils::ColorToHexString(_defaultForeground); - root[JsonKey(BACKGROUND_KEY)] = Utils::ColorToHexString(_defaultBackground); + root[JsonKey(NameKey)] = winrt::to_string(_schemeName); + root[JsonKey(ForegroundKey)] = Utils::ColorToHexString(_defaultForeground); + root[JsonKey(BackgroundKey)] = Utils::ColorToHexString(_defaultBackground); int i = 0; - for (const auto& colorName : TABLE_COLORS) + for (const auto& colorName : TableColors) { auto& colorValue = _table.at(i); root[JsonKey(colorName)] = Utils::ColorToHexString(colorValue); @@ -112,23 +112,23 @@ ColorScheme ColorScheme::FromJson(const Json::Value& json) { ColorScheme result{}; - if (auto name{ json[JsonKey(NAME_KEY)] }) + if (auto name{ json[JsonKey(NameKey)] }) { result._schemeName = winrt::to_hstring(name.asString()); } - if (auto fgString{ json[JsonKey(FOREGROUND_KEY)] }) + if (auto fgString{ json[JsonKey(ForegroundKey)] }) { const auto color = Utils::ColorFromHexString(fgString.asString()); result._defaultForeground = color; } - if (auto bgString{ json[JsonKey(BACKGROUND_KEY)] }) + if (auto bgString{ json[JsonKey(BackgroundKey)] }) { const auto color = Utils::ColorFromHexString(bgString.asString()); result._defaultBackground = color; } // Legacy Deserialization. Leave in place to allow forward compatibility - if (auto table{ json[JsonKey(TABLE_KEY)] }) + if (auto table{ json[JsonKey(TableKey)] }) { int i = 0; @@ -144,7 +144,7 @@ ColorScheme ColorScheme::FromJson(const Json::Value& json) } int i = 0; - for (const auto& current : TABLE_COLORS) + for (const auto& current : TableColors) { if (auto str{ json[JsonKey(current)] }) { diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 543ab55d376..eb19218a471 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -15,18 +15,18 @@ using namespace winrt::Windows::Data::Json; using namespace winrt::Windows::UI::Xaml; using namespace ::Microsoft::Console; -static constexpr std::string_view KEYBINDINGS_KEY{ "keybindings" }; -static constexpr std::string_view DEFAULTPROFILE_KEY{ "defaultProfile" }; -static constexpr std::string_view ALWAYS_SHOW_TABS_KEY{ "alwaysShowTabs" }; -static constexpr std::string_view INITIALROWS_KEY{ "initialRows" }; -static constexpr std::string_view INITIALCOLS_KEY{ "initialCols" }; -static constexpr std::string_view SHOW_TITLE_IN_TITLEBAR_KEY{ "showTerminalTitleInTitlebar" }; -static constexpr std::string_view REQUESTED_THEME_KEY{ "requestedTheme" }; -static constexpr std::string_view SHOW_TABS_IN_TITLEBAR_KEY{ "showTabsInTitlebar" }; - -static constexpr std::wstring_view LIGHT_THEME_VALUE{ L"light" }; -static constexpr std::wstring_view DARK_THEME_VALUE{ L"dark" }; -static constexpr std::wstring_view SYSTEM_THEME_VALUE{ L"system" }; +static constexpr std::string_view KeybindingsKey{ "keybindings" }; +static constexpr std::string_view DefaultProfileKey{ "defaultProfile" }; +static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" }; +static constexpr std::string_view InitialRowsKey{ "initialRows" }; +static constexpr std::string_view InitialColsKey{ "initialCols" }; +static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" }; +static constexpr std::string_view RequestedThemeKey{ "requestedTheme" }; +static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; + +static constexpr std::wstring_view LightThemeValue{ L"light" }; +static constexpr std::wstring_view DarkThemeValue{ L"dark" }; +static constexpr std::wstring_view SystemThemeValue{ L"system" }; GlobalAppSettings::GlobalAppSettings() : _keybindings{}, @@ -143,14 +143,14 @@ Json::Value GlobalAppSettings::ToJson() const { Json::Value jsonObject; - jsonObject[JsonKey(DEFAULTPROFILE_KEY)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); - jsonObject[JsonKey(INITIALROWS_KEY)] = _initialRows; - jsonObject[JsonKey(INITIALCOLS_KEY)] = _initialCols; - jsonObject[JsonKey(ALWAYS_SHOW_TABS_KEY)] = _alwaysShowTabs; - jsonObject[JsonKey(SHOW_TITLE_IN_TITLEBAR_KEY)] = _showTitleInTitlebar; - jsonObject[JsonKey(SHOW_TABS_IN_TITLEBAR_KEY)] = _showTabsInTitlebar; - jsonObject[JsonKey(REQUESTED_THEME_KEY)] = winrt::to_string(_SerializeTheme(_requestedTheme)); - jsonObject[JsonKey(KEYBINDINGS_KEY)] = AppKeyBindingsSerialization::ToJson(_keybindings); + jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); + jsonObject[JsonKey(InitialRowsKey)] = _initialRows; + jsonObject[JsonKey(InitialColsKey)] = _initialCols; + jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs; + jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; + jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; + jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme)); + jsonObject[JsonKey(KeybindingsKey)] = AppKeyBindingsSerialization::ToJson(_keybindings); return jsonObject; } @@ -165,41 +165,41 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) { GlobalAppSettings result{}; - if (auto defaultProfile{ json[JsonKey(DEFAULTPROFILE_KEY)] }) + if (auto defaultProfile{ json[JsonKey(DefaultProfileKey)] }) { auto guid = Utils::GuidFromString(GetWstringFromJson(defaultProfile)); result._defaultProfile = guid; } - if (auto alwaysShowTabs{ json[JsonKey(ALWAYS_SHOW_TABS_KEY)] }) + if (auto alwaysShowTabs{ json[JsonKey(AlwaysShowTabsKey)] }) { result._alwaysShowTabs = alwaysShowTabs.asBool(); } - if (auto initialRows{ json[JsonKey(INITIALROWS_KEY)] }) + if (auto initialRows{ json[JsonKey(InitialRowsKey)] }) { result._initialRows = initialRows.asInt(); } - if (auto initialCols{ json[JsonKey(INITIALCOLS_KEY)] }) + if (auto initialCols{ json[JsonKey(InitialColsKey)] }) { result._initialCols = initialCols.asInt(); } - if (auto showTitleInTitlebar{ json[JsonKey(SHOW_TITLE_IN_TITLEBAR_KEY)] }) + if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] }) { result._showTitleInTitlebar = showTitleInTitlebar.asBool(); } - if (auto showTabsInTitlebar{ json[JsonKey(SHOW_TABS_IN_TITLEBAR_KEY)] }) + if (auto showTabsInTitlebar{ json[JsonKey(ShowTabsInTitlebarKey)] }) { result._showTabsInTitlebar = showTabsInTitlebar.asBool(); } - if (auto requestedTheme{ json[JsonKey(REQUESTED_THEME_KEY)] }) + if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] }) { result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme)); } - if (auto keybindings{ json[JsonKey(KEYBINDINGS_KEY)] }) + if (auto keybindings{ json[JsonKey(KeybindingsKey)] }) { result._keybindings = AppKeyBindingsSerialization::FromJson(keybindings); } @@ -217,15 +217,15 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) // - The corresponding enum value which maps to the string provided by the user ElementTheme GlobalAppSettings::_ParseTheme(const std::wstring& themeString) noexcept { - if (themeString == LIGHT_THEME_VALUE) + if (themeString == LightThemeValue) { return ElementTheme::Light; } - else if (themeString == DARK_THEME_VALUE) + else if (themeString == DarkThemeValue) { return ElementTheme::Dark; } - // default behavior for invalid data or SYSTEM_THEME_VALUE + // default behavior for invalid data or SystemThemeValue return ElementTheme::Default; } @@ -241,10 +241,10 @@ std::wstring_view GlobalAppSettings::_SerializeTheme(const ElementTheme theme) n switch (theme) { case ElementTheme::Light: - return LIGHT_THEME_VALUE; + return LightThemeValue; case ElementTheme::Dark: - return DARK_THEME_VALUE; + return DarkThemeValue; default: - return SYSTEM_THEME_VALUE; + return SystemThemeValue; } } diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 9605c8d680e..47c50aab064 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -13,50 +13,50 @@ using namespace winrt::TerminalApp; using namespace winrt::Windows::Data::Json; using namespace ::Microsoft::Console; -static constexpr std::string_view NAME_KEY{ "name" }; -static constexpr std::string_view GUID_KEY{ "guid" }; -static constexpr std::string_view COLORSCHEME_KEY{ "colorScheme" }; -static constexpr std::string_view COLORSCHEME_KEY_OLD{ "colorscheme" }; - -static constexpr std::string_view FOREGROUND_KEY{ "foreground" }; -static constexpr std::string_view BACKGROUND_KEY{ "background" }; -static constexpr std::string_view COLORTABLE_KEY{ "colorTable" }; -static constexpr std::string_view HISTORYSIZE_KEY{ "historySize" }; -static constexpr std::string_view SNAPONINPUT_KEY{ "snapOnInput" }; -static constexpr std::string_view CURSORCOLOR_KEY{ "cursorColor" }; -static constexpr std::string_view CURSORSHAPE_KEY{ "cursorShape" }; -static constexpr std::string_view CURSORHEIGHT_KEY{ "cursorHeight" }; - -static constexpr std::string_view COMMANDLINE_KEY{ "commandline" }; -static constexpr std::string_view FONTFACE_KEY{ "fontFace" }; -static constexpr std::string_view FONTSIZE_KEY{ "fontSize" }; -static constexpr std::string_view ACRYLICTRANSPARENCY_KEY{ "acrylicOpacity" }; -static constexpr std::string_view USEACRYLIC_KEY{ "useAcrylic" }; -static constexpr std::string_view SCROLLBARSTATE_KEY{ "scrollbarState" }; -static constexpr std::string_view CLOSEONEXIT_KEY{ "closeOnExit" }; -static constexpr std::string_view PADDING_KEY{ "padding" }; -static constexpr std::string_view STARTINGDIRECTORY_KEY{ "startingDirectory" }; -static constexpr std::string_view ICON_KEY{ "icon" }; -static constexpr std::string_view BACKGROUNDIMAGE_KEY{ "backgroundImage" }; -static constexpr std::string_view BACKGROUNDIMAGEOPACITY_KEY{ "backgroundImageOpacity" }; -static constexpr std::string_view BACKGROUNDIMAGESTRETCHMODE_KEY{ "backgroundImageStretchMode" }; +static constexpr std::string_view NameKey{ "name" }; +static constexpr std::string_view GuidKey{ "guid" }; +static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; +static constexpr std::string_view ColorSchemeKeyOld{ "colorscheme" }; + +static constexpr std::string_view ForegroundKey{ "foreground" }; +static constexpr std::string_view BackgroundKey{ "background" }; +static constexpr std::string_view ColorTableKey{ "colorTable" }; +static constexpr std::string_view HistorySizeKey{ "historySize" }; +static constexpr std::string_view SnapOnInputKey{ "snapOnInput" }; +static constexpr std::string_view CursorColorKey{ "cursorColor" }; +static constexpr std::string_view CursorShapeKey{ "cursorShape" }; +static constexpr std::string_view CursorHeightKey{ "cursorHeight" }; + +static constexpr std::string_view CommandlineKey{ "commandline" }; +static constexpr std::string_view FontFaceKey{ "fontFace" }; +static constexpr std::string_view FontSizeKey{ "fontSize" }; +static constexpr std::string_view AcrylicTransparencyKey{ "acrylicOpacity" }; +static constexpr std::string_view UseAcrylicKey{ "useAcrylic" }; +static constexpr std::string_view ScrollbarStateKey{ "scrollbarState" }; +static constexpr std::string_view CloseOnExitKey{ "closeOnExit" }; +static constexpr std::string_view PaddingKey{ "padding" }; +static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" }; +static constexpr std::string_view IconKey{ "icon" }; +static constexpr std::string_view BackgroundImageKey{ "backgroundImage" }; +static constexpr std::string_view BackgroundImageOpacityKey{ "backgroundImageOpacity" }; +static constexpr std::string_view BackgroundimageStretchModeKey{ "backgroundImageStretchMode" }; // Possible values for Scrollbar state -static constexpr std::wstring_view ALWAYS_VISIBLE{ L"visible" }; -static constexpr std::wstring_view ALWAYS_HIDE{ L"hidden" }; +static constexpr std::wstring_view AlwaysVisible{ L"visible" }; +static constexpr std::wstring_view AlwaysHide{ L"hidden" }; // Possible values for Cursor Shape -static constexpr std::wstring_view CURSORSHAPE_VINTAGE{ L"vintage" }; -static constexpr std::wstring_view CURSORSHAPE_BAR{ L"bar" }; -static constexpr std::wstring_view CURSORSHAPE_UNDERSCORE{ L"underscore" }; -static constexpr std::wstring_view CURSORSHAPE_FILLEDBOX{ L"filledBox" }; -static constexpr std::wstring_view CURSORSHAPE_EMPTYBOX{ L"emptyBox" }; +static constexpr std::wstring_view CursorShapeVintage{ L"vintage" }; +static constexpr std::wstring_view CursorShapeBar{ L"bar" }; +static constexpr std::wstring_view CursorShapeUnderscore{ L"underscore" }; +static constexpr std::wstring_view CursorShapeFilledbox{ L"filledBox" }; +static constexpr std::wstring_view CursorShapeEmptybox{ L"emptyBox" }; // Possible values for Image Stretch Mode -static constexpr std::string_view IMAGESTRETCHMODE_NONE{ "none" }; -static constexpr std::string_view IMAGESTRETCHMODE_FILL{ "fill" }; -static constexpr std::string_view IMAGESTRETCHMODE_UNIFORM{ "uniform" }; -static constexpr std::string_view IMAGESTRETCHMODE_UNIFORMTOFILL{ "uniformToFill" }; +static constexpr std::string_view ImageStretchModeNone{ "none" }; +static constexpr std::string_view ImageStretchModeFill{ "fill" }; +static constexpr std::string_view ImageStretchModeUniform{ "uniform" }; +static constexpr std::string_view ImageStretchModeUniformTofill{ "uniformToFill" }; Profile::Profile() : Profile(Utils::CreateGuid()) @@ -215,22 +215,22 @@ Json::Value Profile::ToJson() const Json::Value root; ///// Profile-specific settings ///// - root[JsonKey(GUID_KEY)] = winrt::to_string(Utils::GuidToString(_guid)); - root[JsonKey(NAME_KEY)] = winrt::to_string(_name); + root[JsonKey(GuidKey)] = winrt::to_string(Utils::GuidToString(_guid)); + root[JsonKey(NameKey)] = winrt::to_string(_name); ///// Core Settings ///// if (_defaultForeground) { - root[JsonKey(FOREGROUND_KEY)] = Utils::ColorToHexString(_defaultForeground.value()); + root[JsonKey(ForegroundKey)] = Utils::ColorToHexString(_defaultForeground.value()); } if (_defaultBackground) { - root[JsonKey(BACKGROUND_KEY)] = Utils::ColorToHexString(_defaultBackground.value()); + root[JsonKey(BackgroundKey)] = Utils::ColorToHexString(_defaultBackground.value()); } if (_schemeName) { const auto scheme = winrt::to_string(_schemeName.value()); - root[JsonKey(COLORSCHEME_KEY)] = scheme; + root[JsonKey(ColorSchemeKey)] = scheme; } else { @@ -239,57 +239,57 @@ Json::Value Profile::ToJson() const { tableArray.append(Utils::ColorToHexString(color)); } - root[JsonKey(COLORTABLE_KEY)] = tableArray; + root[JsonKey(ColorTableKey)] = tableArray; } - root[JsonKey(HISTORYSIZE_KEY)] = _historySize; - root[JsonKey(SNAPONINPUT_KEY)] = _snapOnInput; - root[JsonKey(CURSORCOLOR_KEY)] = Utils::ColorToHexString(_cursorColor); + root[JsonKey(HistorySizeKey)] = _historySize; + root[JsonKey(SnapOnInputKey)] = _snapOnInput; + root[JsonKey(CursorColorKey)] = Utils::ColorToHexString(_cursorColor); // Only add the cursor height property if we're a legacy-style cursor. if (_cursorShape == CursorStyle::Vintage) { - root[JsonKey(CURSORHEIGHT_KEY)] = _cursorHeight; + root[JsonKey(CursorHeightKey)] = _cursorHeight; } - root[JsonKey(CURSORSHAPE_KEY)] = winrt::to_string(_SerializeCursorStyle(_cursorShape)); + root[JsonKey(CursorShapeKey)] = winrt::to_string(_SerializeCursorStyle(_cursorShape)); ///// Control Settings ///// - root[JsonKey(COMMANDLINE_KEY)] = winrt::to_string(_commandline); - root[JsonKey(FONTFACE_KEY)] = winrt::to_string(_fontFace); - root[JsonKey(FONTSIZE_KEY)] = _fontSize; - root[JsonKey(ACRYLICTRANSPARENCY_KEY)] = _acrylicTransparency; - root[JsonKey(USEACRYLIC_KEY)] = _useAcrylic; - root[JsonKey(CLOSEONEXIT_KEY)] = _closeOnExit; - root[JsonKey(PADDING_KEY)] = winrt::to_string(_padding); + root[JsonKey(CommandlineKey)] = winrt::to_string(_commandline); + root[JsonKey(FontFaceKey)] = winrt::to_string(_fontFace); + root[JsonKey(FontSizeKey)] = _fontSize; + root[JsonKey(AcrylicTransparencyKey)] = _acrylicTransparency; + root[JsonKey(UseAcrylicKey)] = _useAcrylic; + root[JsonKey(CloseOnExitKey)] = _closeOnExit; + root[JsonKey(PaddingKey)] = winrt::to_string(_padding); if (_scrollbarState) { const auto scrollbarState = winrt::to_string(_scrollbarState.value()); - root[JsonKey(SCROLLBARSTATE_KEY)] = scrollbarState; + root[JsonKey(ScrollbarStateKey)] = scrollbarState; } if (_icon) { const auto icon = winrt::to_string(_icon.value()); - root[JsonKey(ICON_KEY)] = icon; + root[JsonKey(IconKey)] = icon; } if (_startingDirectory) { - root[JsonKey(STARTINGDIRECTORY_KEY)] = winrt::to_string(_startingDirectory.value()); + root[JsonKey(StartingDirectoryKey)] = winrt::to_string(_startingDirectory.value()); } if (_backgroundImage) { - root[JsonKey(BACKGROUNDIMAGE_KEY)] = winrt::to_string(_backgroundImage.value()); + root[JsonKey(BackgroundImageKey)] = winrt::to_string(_backgroundImage.value()); } if (_backgroundImageOpacity) { - root[JsonKey(BACKGROUNDIMAGEOPACITY_KEY)] = _backgroundImageOpacity.value(); + root[JsonKey(BackgroundImageOpacityKey)] = _backgroundImageOpacity.value(); } if (_backgroundImageStretchMode) { - root[JsonKey(BACKGROUNDIMAGESTRETCHMODE_KEY)] = SerializeImageStretchMode(_backgroundImageStretchMode.value()).data(); + root[JsonKey(BackgroundimageStretchModeKey)] = SerializeImageStretchMode(_backgroundImageStretchMode.value()).data(); } return root; @@ -306,39 +306,39 @@ Profile Profile::FromJson(const Json::Value& json) Profile result{}; // Profile-specific Settings - if (auto name{ json[JsonKey(NAME_KEY)] }) + if (auto name{ json[JsonKey(NameKey)] }) { result._name = GetWstringFromJson(name); } - if (auto guid{ json[JsonKey(GUID_KEY)] }) + if (auto guid{ json[JsonKey(GuidKey)] }) { result._guid = Utils::GuidFromString(GetWstringFromJson(guid)); } // Core Settings - if (auto foreground{ json[JsonKey(FOREGROUND_KEY)] }) + if (auto foreground{ json[JsonKey(ForegroundKey)] }) { const auto color = Utils::ColorFromHexString(foreground.asString()); result._defaultForeground = color; } - if (auto background{ json[JsonKey(BACKGROUND_KEY)] }) + if (auto background{ json[JsonKey(BackgroundKey)] }) { const auto color = Utils::ColorFromHexString(background.asString()); result._defaultBackground = color; } - if (auto colorScheme{ json[JsonKey(COLORSCHEME_KEY)] }) + if (auto colorScheme{ json[JsonKey(ColorSchemeKey)] }) { result._schemeName = GetWstringFromJson(colorScheme); } - else if (auto colorScheme{ json[JsonKey(COLORSCHEME_KEY_OLD)] }) + else if (auto colorScheme{ json[JsonKey(ColorSchemeKeyOld)] }) { // TODO:GH#1069 deprecate old settings key result._schemeName = GetWstringFromJson(colorScheme); } - else if (auto colortable{ json[JsonKey(COLORTABLE_KEY)] }) + else if (auto colortable{ json[JsonKey(ColorTableKey)] }) { int i = 0; - for (auto tableEntry : colortable) + for (const auto& tableEntry : colortable) { if (tableEntry.isString()) { @@ -348,79 +348,79 @@ Profile Profile::FromJson(const Json::Value& json) i++; } } - if (auto historySize{ json[JsonKey(HISTORYSIZE_KEY)] }) + if (auto historySize{ json[JsonKey(HistorySizeKey)] }) { // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" result._historySize = historySize.asInt(); } - if (auto snapOnInput{ json[JsonKey(SNAPONINPUT_KEY)] }) + if (auto snapOnInput{ json[JsonKey(SnapOnInputKey)] }) { result._snapOnInput = snapOnInput.asBool(); } - if (auto cursorColor{ json[JsonKey(CURSORCOLOR_KEY)] }) + if (auto cursorColor{ json[JsonKey(CursorColorKey)] }) { const auto color = Utils::ColorFromHexString(cursorColor.asString()); result._cursorColor = color; } - if (auto cursorHeight{ json[JsonKey(CURSORHEIGHT_KEY)] }) + if (auto cursorHeight{ json[JsonKey(CursorHeightKey)] }) { result._cursorHeight = cursorHeight.asUInt(); } - if (auto cursorShape{ json[JsonKey(CURSORSHAPE_KEY)] }) + if (auto cursorShape{ json[JsonKey(CursorShapeKey)] }) { result._cursorShape = _ParseCursorShape(GetWstringFromJson(cursorShape)); } // Control Settings - if (auto commandline{ json[JsonKey(COMMANDLINE_KEY)] }) + if (auto commandline{ json[JsonKey(CommandlineKey)] }) { result._commandline = GetWstringFromJson(commandline); } - if (auto fontFace{ json[JsonKey(FONTFACE_KEY)] }) + if (auto fontFace{ json[JsonKey(FontFaceKey)] }) { result._fontFace = GetWstringFromJson(fontFace); } - if (auto fontSize{ json[JsonKey(FONTSIZE_KEY)] }) + if (auto fontSize{ json[JsonKey(FontSizeKey)] }) { result._fontSize = fontSize.asInt(); } - if (auto acrylicTransparency{ json[JsonKey(ACRYLICTRANSPARENCY_KEY)] }) + if (auto acrylicTransparency{ json[JsonKey(AcrylicTransparencyKey)] }) { result._acrylicTransparency = acrylicTransparency.asFloat(); } - if (auto useAcrylic{ json[JsonKey(USEACRYLIC_KEY)] }) + if (auto useAcrylic{ json[JsonKey(UseAcrylicKey)] }) { result._useAcrylic = useAcrylic.asBool(); } - if (auto closeOnExit{ json[JsonKey(CLOSEONEXIT_KEY)] }) + if (auto closeOnExit{ json[JsonKey(CloseOnExitKey)] }) { result._closeOnExit = closeOnExit.asBool(); } - if (auto padding{ json[JsonKey(PADDING_KEY)] }) + if (auto padding{ json[JsonKey(PaddingKey)] }) { result._padding = GetWstringFromJson(padding); } - if (auto scrollbarState{ json[JsonKey(SCROLLBARSTATE_KEY)] }) + if (auto scrollbarState{ json[JsonKey(ScrollbarStateKey)] }) { result._scrollbarState = GetWstringFromJson(scrollbarState); } - if (auto startingDirectory{ json[JsonKey(STARTINGDIRECTORY_KEY)] }) + if (auto startingDirectory{ json[JsonKey(StartingDirectoryKey)] }) { result._startingDirectory = GetWstringFromJson(startingDirectory); } - if (auto icon{ json[JsonKey(ICON_KEY)] }) + if (auto icon{ json[JsonKey(IconKey)] }) { result._icon = GetWstringFromJson(icon); } - if (auto backgroundImage{ json[JsonKey(BACKGROUNDIMAGE_KEY)] }) + if (auto backgroundImage{ json[JsonKey(BackgroundImageKey)] }) { result._backgroundImage = GetWstringFromJson(backgroundImage); } - if (auto backgroundImageOpacity{ json[JsonKey(BACKGROUNDIMAGEOPACITY_KEY)] }) + if (auto backgroundImageOpacity{ json[JsonKey(BackgroundImageOpacityKey)] }) { result._backgroundImageOpacity = backgroundImageOpacity.asFloat(); } - if (auto backgroundImageStretchMode{ json[JsonKey(BACKGROUNDIMAGESTRETCHMODE_KEY)] }) + if (auto backgroundImageStretchMode{ json[JsonKey(BackgroundimageStretchModeKey)] }) { result._backgroundImageStretchMode = ParseImageStretchMode(backgroundImageStretchMode.asString()); } @@ -553,17 +553,16 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory) // - The corresponding enum value which maps to the string provided by the user ScrollbarState Profile::ParseScrollbarState(const std::wstring& scrollbarState) { - if (scrollbarState == ALWAYS_VISIBLE) + if (scrollbarState == AlwaysVisible) { return ScrollbarState::Visible; } - else if (scrollbarState == ALWAYS_HIDE) + else if (scrollbarState == AlwaysHide) { return ScrollbarState::Hidden; } else { - // default behavior for invalid data return ScrollbarState::Visible; } } @@ -575,17 +574,17 @@ ScrollbarState Profile::ParseScrollbarState(const std::wstring& scrollbarState) // - The value from the profiles.json file // Return Value: // - The corresponding enum value which maps to the string provided by the user -winrt::Windows::UI::Xaml::Media::Stretch Profile::ParseImageStretchMode(const std::string& imageStretchMode) +winrt::Windows::UI::Xaml::Media::Stretch Profile::ParseImageStretchMode(const std::string_view imageStretchMode) { - if (imageStretchMode == IMAGESTRETCHMODE_NONE) + if (imageStretchMode == ImageStretchModeNone) { return winrt::Windows::UI::Xaml::Media::Stretch::None; } - else if (imageStretchMode == IMAGESTRETCHMODE_FILL) + else if (imageStretchMode == ImageStretchModeFill) { return winrt::Windows::UI::Xaml::Media::Stretch::Fill; } - else if (imageStretchMode == IMAGESTRETCHMODE_UNIFORM) + else if (imageStretchMode == ImageStretchModeUniform) { return winrt::Windows::UI::Xaml::Media::Stretch::Uniform; } @@ -607,19 +606,17 @@ std::string_view Profile::SerializeImageStretchMode(const winrt::Windows::UI::Xa switch (imageStretchMode) { case winrt::Windows::UI::Xaml::Media::Stretch::None: - return IMAGESTRETCHMODE_NONE; + return ImageStretchModeNone; case winrt::Windows::UI::Xaml::Media::Stretch::Fill: - return IMAGESTRETCHMODE_FILL; + return ImageStretchModeFill; case winrt::Windows::UI::Xaml::Media::Stretch::Uniform: - return IMAGESTRETCHMODE_UNIFORM; + return ImageStretchModeUniform; default: case winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill: - return IMAGESTRETCHMODE_UNIFORMTOFILL; + return ImageStretchModeUniformTofill; } } - - // Method Description: // - Helper function for converting a user-specified cursor style corresponding // CursorStyle enum value @@ -629,23 +626,23 @@ std::string_view Profile::SerializeImageStretchMode(const winrt::Windows::UI::Xa // - The corresponding enum value which maps to the string provided by the user CursorStyle Profile::_ParseCursorShape(const std::wstring& cursorShapeString) { - if (cursorShapeString == CURSORSHAPE_VINTAGE) + if (cursorShapeString == CursorShapeVintage) { return CursorStyle::Vintage; } - else if (cursorShapeString == CURSORSHAPE_BAR) + else if (cursorShapeString == CursorShapeBar) { return CursorStyle::Bar; } - else if (cursorShapeString == CURSORSHAPE_UNDERSCORE) + else if (cursorShapeString == CursorShapeUnderscore) { return CursorStyle::Underscore; } - else if (cursorShapeString == CURSORSHAPE_FILLEDBOX) + else if (cursorShapeString == CursorShapeFilledbox) { return CursorStyle::FilledBox; } - else if (cursorShapeString == CURSORSHAPE_EMPTYBOX) + else if (cursorShapeString == CursorShapeEmptybox) { return CursorStyle::EmptyBox; } @@ -665,15 +662,15 @@ std::wstring_view Profile::_SerializeCursorStyle(const CursorStyle cursorShape) switch (cursorShape) { case CursorStyle::Underscore: - return CURSORSHAPE_UNDERSCORE; + return CursorShapeUnderscore; case CursorStyle::FilledBox: - return CURSORSHAPE_FILLEDBOX; + return CursorShapeFilledbox; case CursorStyle::EmptyBox: - return CURSORSHAPE_EMPTYBOX; + return CursorShapeEmptybox; case CursorStyle::Vintage: - return CURSORSHAPE_VINTAGE; + return CursorShapeVintage; default: case CursorStyle::Bar: - return CURSORSHAPE_BAR; + return CursorShapeBar; } } diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 7cf3863dfd1..b2fc7d96407 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -59,7 +59,7 @@ class TerminalApp::Profile final static std::wstring EvaluateStartingDirectory(const std::wstring& directory); static winrt::Microsoft::Terminal::Settings::ScrollbarState ParseScrollbarState(const std::wstring& scrollbarState); - static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::string& imageStretchMode); + static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::string_view imageStretchMode); static std::string_view SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode); static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString); static std::wstring_view _SerializeCursorStyle(const winrt::Microsoft::Terminal::Settings::CursorStyle cursorShape); From a68a9bba02b162328e48771c62531b71647f6382 Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Fri, 31 May 2019 14:43:51 -0700 Subject: [PATCH 26/26] Add a NOTICES file --- NOTICE.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 NOTICE.md diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 00000000000..623e3fb70d4 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,49 @@ +# NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. Microsoft makes certain +open source code available at http://3rdpartysource.microsoft.com, or you may +send a check or money order for US $5.00, including the product name, the open +source component name, and version number, to: + +``` +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA +``` + +Notwithstanding any other terms, you may reverse engineer this software to the +extent required to debug changes to any libraries licensed under the GNU Lesser +General Public License. + +## jsoncpp + +**Source**: https://github.com/open-source-parsers/jsoncpp + +### License + +``` +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +```