Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Licensed under the MIT license.
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
- JsonUtils.h
|
|
|
|
|
|
|
|
Abstract:
|
2020-10-06 18:56:59 +02:00
|
|
|
- Helpers for the Terminal Settings Model project
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
Author(s):
|
|
|
|
- Mike Griese - August 2019
|
2020-07-17 03:31:09 +02:00
|
|
|
- Dustin Howett - January 2020
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
--*/
|
2020-07-17 03:31:09 +02:00
|
|
|
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
#pragma once
|
|
|
|
|
2020-07-17 03:31:09 +02:00
|
|
|
#include <json.h>
|
|
|
|
|
|
|
|
#include "../types/inc/utils.hpp"
|
|
|
|
|
|
|
|
namespace winrt
|
|
|
|
{
|
|
|
|
// If we don't use winrt, nobody will include the ConversionTraits for winrt stuff.
|
|
|
|
// If nobody includes it, these forward declarations will suffice.
|
|
|
|
struct guid;
|
|
|
|
struct hstring;
|
|
|
|
namespace Windows::Foundation
|
|
|
|
{
|
|
|
|
template<typename T>
|
|
|
|
struct IReference;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 18:56:59 +02:00
|
|
|
// 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<std::string>(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
{
|
2020-07-17 03:31:09 +02:00
|
|
|
namespace Detail
|
|
|
|
{
|
|
|
|
// Function Description:
|
|
|
|
// - Returns a string_view to a Json::Value's internal string storage,
|
|
|
|
// hopefully without copying it.
|
|
|
|
__declspec(noinline) inline const std::string_view GetStringView(const Json::Value& json)
|
|
|
|
{
|
|
|
|
const char* begin{ nullptr };
|
|
|
|
const char* end{ nullptr };
|
|
|
|
json.getString(&begin, &end);
|
|
|
|
const std::string_view zeroCopyString{ begin, gsl::narrow_cast<size_t>(end - begin) };
|
|
|
|
return zeroCopyString;
|
|
|
|
}
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
}
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
template<typename T>
|
|
|
|
struct OptionOracle
|
|
|
|
{
|
|
|
|
template<typename U> // universal parameter
|
|
|
|
static constexpr bool HasValue(U&&)
|
2020-07-17 03:31:09 +02:00
|
|
|
{
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
template<typename T>
|
|
|
|
struct OptionOracle<std::optional<T>>
|
|
|
|
{
|
|
|
|
static constexpr std::optional<T> EmptyV() { return std::nullopt; }
|
|
|
|
static constexpr bool HasValue(const std::optional<T>& o) { return o.has_value(); }
|
2021-05-20 20:44:04 +02:00
|
|
|
// We can return a reference here because the original value is stored inside an std::optional
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
static constexpr auto&& Value(const std::optional<T>& o) { return *o; }
|
|
|
|
};
|
2020-08-28 03:09:22 +02:00
|
|
|
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
template<typename T>
|
|
|
|
struct OptionOracle<::winrt::Windows::Foundation::IReference<T>>
|
|
|
|
{
|
|
|
|
static constexpr ::winrt::Windows::Foundation::IReference<T> EmptyV() { return nullptr; }
|
|
|
|
static constexpr bool HasValue(const ::winrt::Windows::Foundation::IReference<T>& o) { return static_cast<bool>(o); }
|
2021-05-20 20:44:04 +02:00
|
|
|
// We CANNOT return a reference here because IReference does NOT return a reference to its internal memory
|
|
|
|
static constexpr auto Value(const ::winrt::Windows::Foundation::IReference<T>& o) { return o.Value(); }
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
};
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
|
2020-11-17 01:37:19 +01:00
|
|
|
class SerializationError : public std::runtime_error
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SerializationError() :
|
|
|
|
runtime_error("failed to serialize") {}
|
|
|
|
};
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
class DeserializationError : public std::runtime_error
|
2020-07-17 03:31:09 +02:00
|
|
|
{
|
|
|
|
public:
|
2020-08-11 21:50:13 +02:00
|
|
|
DeserializationError(const Json::Value& value) :
|
2021-09-29 12:24:46 +02:00
|
|
|
runtime_error(std::string("failed to deserialize ") + (value.isNull() ? "" : value.asString())),
|
2020-08-11 21:50:13 +02:00
|
|
|
jsonValue{ value } {}
|
2020-07-17 03:31:09 +02:00
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
void SetKey(std::string_view newKey)
|
2020-07-17 03:31:09 +02:00
|
|
|
{
|
2020-08-11 21:50:13 +02:00
|
|
|
if (!key)
|
|
|
|
{
|
|
|
|
key = newKey;
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::optional<std::string> key;
|
|
|
|
Json::Value jsonValue;
|
|
|
|
std::string expectedType;
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
Persist window layout on window close (#10972)
This commit adds initial support for saving window layout on application
close.
Done:
- Add user setting for if tabs should be maintained.
- Added events to track the number of open windows for the monarch, and
then save if you are the last window closing.
- Saves layout when the user explicitly hits the "Close Window" button.
- If the user manually closed all of their tabs (through the tab x
button or through closing all panes on the tab) then remove any saved
state.
- Saves in the ApplicationState file a list of actions the terminal can
perform to restore its layout and the window size/position
information.
- This saves an action to focus the correct pane, but this won't
actually work without #10978. Note that if you have a pane zoomed, it
does still zoom the correct pane, but when you unzoom it will have a
different pane selected.
Todo:
- multiple windows? Right now it can only handle loading/saving one
window.
- PR #11083 will save multiple windows.
- This also sometimes runs into the existing bug where multiple tabs
appear to be focused on opening.
Next Steps:
- The business logic of when the save is triggered can be adjusted as
necessary.
- Right now I am taking the pragmatic approach and just saving the state
as an array of objects, but only ever populate it with 1, that way
saving multiple windows in the future could be added without breaking
schema compatibility. Selfishly I'm hoping that handling multiple
windows could be spun off into another pr/feature for now.
- One possible thing that can maybe be done is that the commandline can
be augmented with a "--saved ##" attribute that would load from the
nth saved state if it exists. e.g. if there are 3 saved windows, on
first load it can spawn three wt --saved {0,1,2} that would reopen the
windows? This way there also exists a way to load a copy of a previous
window (if it is in the saved state).
- Is the application state something that is planned to be public/user
editable? In theory the user could since it is just json, but I don't
know what it buys them over just modifying their settings and
startupActions.
Validation Steps Performed:
- The happy path: open terminal -> set setting to true -> close terminal
-> reopen and see tabs. Tested with powershell/cmd/wsl windows.
- That closing all panes/tabs on their own will remove the saved
session.
- Open multiple windows, close windows and confirm that the last window
closed saves its state.
The generated file stores a sequence of actions that will be executed to
restore the terminal to its saved form.
References #8324
This is also one of the items on microsoft/terminal#5000
Closes #766
2021-09-09 00:44:53 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Helper that will populate a reference with a value converted from a json object.
|
|
|
|
// Arguments:
|
|
|
|
// - json: the json object to convert
|
|
|
|
// - target: the value to populate with the converted result
|
|
|
|
// Return Value:
|
|
|
|
// - a boolean indicating whether the value existed (in this case, was non-null)
|
|
|
|
//
|
|
|
|
// GetValue, type-deduced, manual converter
|
|
|
|
template<typename T, typename Converter>
|
|
|
|
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
|
|
|
|
{
|
|
|
|
if (!conv.CanConvert(json))
|
|
|
|
{
|
|
|
|
DeserializationError e{ json };
|
|
|
|
e.expectedType = conv.TypeDescription();
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
|
|
|
|
target = conv.FromJson(json);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValue, forced return type, manual converter
|
|
|
|
template<typename T, typename Converter>
|
|
|
|
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
|
|
|
|
{
|
|
|
|
std::decay_t<T> local{};
|
|
|
|
GetValue(json, local, std::forward<Converter>(conv));
|
|
|
|
return local; // returns zero-initialized or value
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValueForKey, type-deduced, manual converter
|
|
|
|
template<typename T, typename Converter>
|
|
|
|
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
|
|
|
|
{
|
|
|
|
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
return GetValue(*found, target, std::forward<Converter>(conv));
|
|
|
|
}
|
|
|
|
catch (DeserializationError& e)
|
|
|
|
{
|
|
|
|
e.SetKey(key);
|
|
|
|
throw; // rethrow now that it has a key
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValueForKey, forced return type, manual converter
|
|
|
|
template<typename T, typename Converter>
|
|
|
|
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
|
|
|
|
{
|
|
|
|
std::decay_t<T> local{};
|
|
|
|
GetValueForKey(json, key, local, std::forward<Converter>(conv));
|
|
|
|
return local; // returns zero-initialized?
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValue, type-deduced, with automatic converter
|
|
|
|
template<typename T>
|
|
|
|
bool GetValue(const Json::Value& json, T& target)
|
|
|
|
{
|
|
|
|
return GetValue(json, target, ConversionTrait<typename std::decay<T>::type>{});
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValue, forced return type, with automatic converter
|
|
|
|
template<typename T>
|
|
|
|
std::decay_t<T> GetValue(const Json::Value& json)
|
|
|
|
{
|
|
|
|
std::decay_t<T> local{};
|
|
|
|
GetValue(json, local, ConversionTrait<typename std::decay<T>::type>{});
|
|
|
|
return local; // returns zero-initialized or value
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValueForKey, type-deduced, with automatic converter
|
|
|
|
template<typename T>
|
|
|
|
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
|
|
|
|
{
|
|
|
|
return GetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValueForKey, forced return type, with automatic converter
|
|
|
|
template<typename T>
|
|
|
|
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
|
|
|
|
{
|
|
|
|
return GetValueForKey<T>(json, key, ConversionTrait<typename std::decay<T>::type>{});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
|
|
|
|
// Uses the default converter for each v.
|
|
|
|
// Careful: this can cause a template explosion.
|
|
|
|
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
|
|
|
|
|
|
|
|
template<typename T, typename... Args>
|
|
|
|
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
|
|
|
|
{
|
|
|
|
GetValueForKey(json, key1, val1);
|
|
|
|
GetValuesForKeys(json, std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetValueForKey, type-deduced, manual converter
|
|
|
|
template<typename T, typename Converter>
|
|
|
|
void SetValueForKey(Json::Value& json, std::string_view key, const T& target, Converter&& conv)
|
|
|
|
{
|
|
|
|
// We don't want to write any empty optionals into JSON (right now).
|
|
|
|
if (OptionOracle<T>::HasValue(target))
|
|
|
|
{
|
|
|
|
// demand guarantees that it will return a value or throw an exception
|
|
|
|
*json.demand(&*key.cbegin(), (&*key.cbegin()) + key.size()) = conv.ToJson(target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetValueForKey, type-deduced, with automatic converter
|
|
|
|
template<typename T>
|
|
|
|
void SetValueForKey(Json::Value& json, std::string_view key, const T& target)
|
|
|
|
{
|
|
|
|
SetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
|
|
|
|
}
|
|
|
|
|
2020-07-17 03:31:09 +02:00
|
|
|
template<typename T>
|
|
|
|
struct ConversionTrait
|
|
|
|
{
|
|
|
|
// Forward-declare these so the linker can pick up specializations from elsewhere!
|
|
|
|
T FromJson(const Json::Value&);
|
|
|
|
bool CanConvert(const Json::Value& json);
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const T& val);
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const { return "<unknown>"; }
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<std::string>
|
|
|
|
{
|
|
|
|
std::string FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.asString();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isString();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const std::string& val)
|
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "string";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<std::wstring>
|
|
|
|
{
|
|
|
|
std::wstring FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return til::u8u16(Detail::GetStringView(json));
|
|
|
|
}
|
|
|
|
|
2021-08-26 21:04:35 +02:00
|
|
|
bool CanConvert(const Json::Value& json) const
|
2020-07-17 03:31:09 +02:00
|
|
|
{
|
|
|
|
return json.isString();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const std::wstring& val)
|
|
|
|
{
|
|
|
|
return til::u16u8(val);
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "string";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
Add action to run multiple actions. (#11045)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Add a new action that can contain multiple other actions.
<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #3992
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [x] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
Creates a shortcut action that allows a list of actions to be specified as arguments. Steals a bunch of the serialization code from my other pr. Overall, because I had the serialization code written already, this was remarkably easy.
I can't think of any combined action to be added to the defaults, so I think this is just a thing for the documentation unless someone else has a good example. I know there are lot of times when the recommended workaround is "make an action with commandline wt.exe ..." and this could be a good replacement for that, but that is all personalized.
I didn't add this to the command line parsing, since the command line is already a way to run multiple actions.
<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Created a new command, confirmed that "Move right->down" showed up in the command palette, and that running it did the correct behavior (moving right one pane, then down one pane).
```
{
"command": {
"action": "multipleActions",
"name": "Move right->down",
"actions": [
{"action": "moveFocus", "direction": "right" },
{"action": "moveFocus", "direction": "down" },
]
}
}
```
2021-08-31 21:35:51 +02:00
|
|
|
template<typename T>
|
|
|
|
struct ConversionTrait<std::vector<T>>
|
|
|
|
{
|
|
|
|
std::vector<T> FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
std::vector<T> val;
|
|
|
|
val.reserve(json.size());
|
|
|
|
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
for (const auto& element : json)
|
|
|
|
{
|
|
|
|
val.push_back(trait.FromJson(element));
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) mutable -> bool { return trait.CanConvert(json); });
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const std::vector<T>& val)
|
|
|
|
{
|
|
|
|
Json::Value json{ Json::arrayValue };
|
|
|
|
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
for (const auto& v : val)
|
|
|
|
{
|
|
|
|
json.append(trait.ToJson(v));
|
|
|
|
}
|
|
|
|
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return fmt::format("{}[]", ConversionTrait<T>{}.TypeDescription());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-23 01:15:44 +02:00
|
|
|
template<typename T>
|
Persist window layout on window close (#10972)
This commit adds initial support for saving window layout on application
close.
Done:
- Add user setting for if tabs should be maintained.
- Added events to track the number of open windows for the monarch, and
then save if you are the last window closing.
- Saves layout when the user explicitly hits the "Close Window" button.
- If the user manually closed all of their tabs (through the tab x
button or through closing all panes on the tab) then remove any saved
state.
- Saves in the ApplicationState file a list of actions the terminal can
perform to restore its layout and the window size/position
information.
- This saves an action to focus the correct pane, but this won't
actually work without #10978. Note that if you have a pane zoomed, it
does still zoom the correct pane, but when you unzoom it will have a
different pane selected.
Todo:
- multiple windows? Right now it can only handle loading/saving one
window.
- PR #11083 will save multiple windows.
- This also sometimes runs into the existing bug where multiple tabs
appear to be focused on opening.
Next Steps:
- The business logic of when the save is triggered can be adjusted as
necessary.
- Right now I am taking the pragmatic approach and just saving the state
as an array of objects, but only ever populate it with 1, that way
saving multiple windows in the future could be added without breaking
schema compatibility. Selfishly I'm hoping that handling multiple
windows could be spun off into another pr/feature for now.
- One possible thing that can maybe be done is that the commandline can
be augmented with a "--saved ##" attribute that would load from the
nth saved state if it exists. e.g. if there are 3 saved windows, on
first load it can spawn three wt --saved {0,1,2} that would reopen the
windows? This way there also exists a way to load a copy of a previous
window (if it is in the saved state).
- Is the application state something that is planned to be public/user
editable? In theory the user could since it is just json, but I don't
know what it buys them over just modifying their settings and
startupActions.
Validation Steps Performed:
- The happy path: open terminal -> set setting to true -> close terminal
-> reopen and see tabs. Tested with powershell/cmd/wsl windows.
- That closing all panes/tabs on their own will remove the saved
session.
- Open multiple windows, close windows and confirm that the last window
closed saves its state.
The generated file stores a sequence of actions that will be executed to
restore the terminal to its saved form.
References #8324
This is also one of the items on microsoft/terminal#5000
Closes #766
2021-09-09 00:44:53 +02:00
|
|
|
struct ConversionTrait<std::unordered_set<T>>
|
|
|
|
{
|
|
|
|
std::unordered_set<T> FromJson(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
std::unordered_set<T> val;
|
|
|
|
val.reserve(json.size());
|
|
|
|
|
|
|
|
for (const auto& element : json)
|
|
|
|
{
|
|
|
|
val.emplace(trait.FromJson(element));
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) mutable -> bool { return trait.CanConvert(json); });
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const std::unordered_set<T>& val)
|
|
|
|
{
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
Json::Value json{ Json::arrayValue };
|
|
|
|
|
|
|
|
for (const auto& key : val)
|
|
|
|
{
|
|
|
|
json.append(trait.ToJson(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return fmt::format("{}[]", ConversionTrait<GUID>{}.TypeDescription());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
2021-07-23 01:15:44 +02:00
|
|
|
struct ConversionTrait<std::unordered_map<std::string, T>>
|
|
|
|
{
|
|
|
|
std::unordered_map<std::string, T> FromJson(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
std::unordered_map<std::string, T> val;
|
|
|
|
val.reserve(json.size());
|
|
|
|
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
for (auto it = json.begin(), end = json.end(); it != end; ++it)
|
|
|
|
{
|
|
|
|
GetValue(*it, val[it.name()], trait);
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
if (!json.isObject())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
for (const auto& v : json)
|
|
|
|
{
|
|
|
|
if (!trait.CanConvert(v))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const std::unordered_map<std::string, T>& val)
|
|
|
|
{
|
|
|
|
Json::Value json{ Json::objectValue };
|
|
|
|
|
|
|
|
for (const auto& [k, v] : val)
|
|
|
|
{
|
|
|
|
SetValueForKey(json, k, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return fmt::format("map (string, {})", ConversionTrait<T>{}.TypeDescription());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-17 03:31:09 +02:00
|
|
|
#ifdef WINRT_BASE_H
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring>
|
|
|
|
{
|
|
|
|
// Leverage the wstring converter's validation
|
|
|
|
winrt::hstring FromJson(const Json::Value& json)
|
|
|
|
{
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
if (json.isNull())
|
|
|
|
{
|
|
|
|
return winrt::hstring{};
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
return winrt::hstring{ til::u8u16(Detail::GetStringView(json)) };
|
|
|
|
}
|
2020-09-17 20:27:46 +02:00
|
|
|
|
|
|
|
Json::Value ToJson(const winrt::hstring& val)
|
|
|
|
{
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
if (val == winrt::hstring{})
|
|
|
|
{
|
|
|
|
return Json::Value::nullSingleton();
|
|
|
|
}
|
2020-09-17 20:27:46 +02:00
|
|
|
return til::u16u8(val);
|
|
|
|
}
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
|
2021-08-26 21:04:35 +02:00
|
|
|
bool CanConvert(const Json::Value& json) const
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
{
|
|
|
|
// hstring has a specific behavior for null, so it can convert it
|
|
|
|
return ConversionTrait<std::wstring>::CanConvert(json) || json.isNull();
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
2021-07-23 01:15:44 +02:00
|
|
|
|
Add action to run multiple actions. (#11045)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Add a new action that can contain multiple other actions.
<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #3992
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [x] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
Creates a shortcut action that allows a list of actions to be specified as arguments. Steals a bunch of the serialization code from my other pr. Overall, because I had the serialization code written already, this was remarkably easy.
I can't think of any combined action to be added to the defaults, so I think this is just a thing for the documentation unless someone else has a good example. I know there are lot of times when the recommended workaround is "make an action with commandline wt.exe ..." and this could be a good replacement for that, but that is all personalized.
I didn't add this to the command line parsing, since the command line is already a way to run multiple actions.
<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Created a new command, confirmed that "Move right->down" showed up in the command palette, and that running it did the correct behavior (moving right one pane, then down one pane).
```
{
"command": {
"action": "multipleActions",
"name": "Move right->down",
"actions": [
{"action": "moveFocus", "direction": "right" },
{"action": "moveFocus", "direction": "down" },
]
}
}
```
2021-08-31 21:35:51 +02:00
|
|
|
template<typename T>
|
|
|
|
struct ConversionTrait<winrt::Windows::Foundation::Collections::IVector<T>>
|
|
|
|
{
|
|
|
|
winrt::Windows::Foundation::Collections::IVector<T> FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
ConversionTrait<std::vector<T>> trait;
|
|
|
|
return winrt::single_threaded_vector<T>(std::move(trait.FromJson(json)));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
ConversionTrait<std::vector<T>> trait;
|
|
|
|
return trait.CanConvert(json);
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IVector<T>& val)
|
|
|
|
{
|
|
|
|
Json::Value json{ Json::arrayValue };
|
|
|
|
|
|
|
|
if (val)
|
|
|
|
{
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
for (const auto& v : val)
|
|
|
|
{
|
|
|
|
json.append(trait.ToJson(v));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return fmt::format("{}[]", ConversionTrait<T>{}.TypeDescription());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-23 01:15:44 +02:00
|
|
|
template<typename T>
|
|
|
|
struct ConversionTrait<winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>>
|
|
|
|
{
|
|
|
|
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T> FromJson(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
std::unordered_map<winrt::hstring, T> val;
|
|
|
|
val.reserve(json.size());
|
|
|
|
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
for (auto it = json.begin(), end = json.end(); it != end; ++it)
|
|
|
|
{
|
|
|
|
GetValue(*it, val[winrt::to_hstring(it.name())], trait);
|
|
|
|
}
|
|
|
|
|
|
|
|
return winrt::single_threaded_map<winrt::hstring, T>(std::move(val));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
if (!json.isObject())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ConversionTrait<T> trait;
|
|
|
|
for (const auto& v : json)
|
|
|
|
{
|
|
|
|
if (!trait.CanConvert(v))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>& val)
|
|
|
|
{
|
|
|
|
Json::Value json{ Json::objectValue };
|
|
|
|
|
|
|
|
for (const auto& [k, v] : val)
|
|
|
|
{
|
|
|
|
SetValueForKey(json, til::u16u8(k), v);
|
|
|
|
}
|
|
|
|
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return fmt::format("map (string, {})", ConversionTrait<T>{}.TypeDescription());
|
|
|
|
}
|
|
|
|
};
|
2020-07-17 03:31:09 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<bool>
|
|
|
|
{
|
|
|
|
bool FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.asBool();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isBool();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const bool val)
|
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "true | false";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<int>
|
|
|
|
{
|
|
|
|
int FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.asInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isInt();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const int& val)
|
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "number";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<unsigned int>
|
|
|
|
{
|
|
|
|
unsigned int FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.asUInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isUInt();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const unsigned int& val)
|
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "number (>= 0)";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<float>
|
|
|
|
{
|
|
|
|
float FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.asFloat();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isNumeric();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const float& val)
|
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "number";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<double>
|
|
|
|
{
|
|
|
|
double FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.asDouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isNumeric();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const double& val)
|
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "number";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<GUID>
|
|
|
|
{
|
|
|
|
GUID FromJson(const Json::Value& json)
|
|
|
|
{
|
Reduce usage of Json::Value throughout Terminal.Settings.Model (#11184)
This commit reduces the code surface that interacts with raw JSON data,
reducing code complexity and improving maintainability.
Files that needed to be changed drastically were additionally
cleaned up to remove any code cruft that has accrued over time.
In order to facility this the following changes were made:
* Move JSON handling from `CascadiaSettings` into `SettingsLoader`
This allows us to use STL containers for data model instances.
For instance profiles are now added to a hashmap for O(1) lookup.
* JSON parsing within `SettingsLoader` doesn't differentiate between user,
inbox and fragment JSON data, reducing code complexity and size.
It also centralizes common concerns, like profile deduplication and
ensuring that all profiles are assigned a GUID.
* Direct JSON modification, like the insertion of dynamic profiles into
settings.json were removed. This vastly reduces code complexity,
but unfortunately removes support for comments in JSON on first start.
* `ColorScheme`s cannot be layered. As such its `LayerJson` API was replaced
with `FromJson`, allowing us to remove JSON-based color scheme validation.
* `Profile`s used to test their wish to layer using `ShouldBeLayered`, which
was replaced with a GUID-based hashmap lookup on previously parsed profiles.
Further changes were made as improvements upon the previous changes:
* Compact the JSON files embedded binary, saving 28kB
* Prevent double-initialization of the color table in `ColorScheme`
* Making `til::color` getters `constexpr`, allow better optimizations
The result is a reduction of:
* 48kB binary size for the Settings.Model.dll
* 5-10% startup duration
* 26% code for the `CascadiaSettings` class
* 1% overall code in this project
Furthermore this results in the following breaking changes:
* The long deprecated "globals" settings object will not be detected and no
warning will be created during load.
* The initial creation of a new settings.json will not produce helpful comments.
Both cases are caused by the removal of manual JSON handling and the
move to representing the settings file with model objects instead
## PR Checklist
* [x] Closes #5276
* [x] Closes #7421
* [x] I work here
* [x] Tests added/passed
## Validation Steps Performed
* Out-of-box-experience is identical to before ✔️
(Except for the settings.json file lacking comments.)
* Existing user settings load correctly ✔️
* New WSL instances are added to user settings ✔️
* New fragments are added to user settings ✔️
* All profiles are assigned GUIDs ✔️
2021-09-22 18:27:31 +02:00
|
|
|
return ::Microsoft::Console::Utils::GuidFromString(til::u8u16(Detail::GetStringView(json)).c_str());
|
2020-07-17 03:31:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
if (!json.isString())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto string{ Detail::GetStringView(json) };
|
|
|
|
return string.length() == 38 && string.front() == '{' && string.back() == '}';
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const GUID& val)
|
|
|
|
{
|
|
|
|
return til::u16u8(::Microsoft::Console::Utils::GuidToString(val));
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "guid";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
2020-08-28 03:09:22 +02:00
|
|
|
// GUID and winrt::guid are mutually convertible,
|
|
|
|
// but IReference<winrt::guid> throws some of this off
|
2020-07-17 03:31:09 +02:00
|
|
|
template<>
|
2020-08-28 03:09:22 +02:00
|
|
|
struct ConversionTrait<winrt::guid>
|
2020-07-17 03:31:09 +02:00
|
|
|
{
|
2020-08-28 03:09:22 +02:00
|
|
|
winrt::guid FromJson(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
return static_cast<winrt::guid>(ConversionTrait<GUID>{}.FromJson(json));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
return ConversionTrait<GUID>{}.CanConvert(json);
|
|
|
|
}
|
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const winrt::guid& val)
|
|
|
|
{
|
|
|
|
return ConversionTrait<GUID>{}.ToJson(val);
|
|
|
|
}
|
|
|
|
|
2020-08-28 03:09:22 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return ConversionTrait<GUID>{}.TypeDescription();
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<til::color>
|
|
|
|
{
|
|
|
|
til::color FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return ::Microsoft::Console::Utils::ColorFromHexString(Detail::GetStringView(json));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
if (!json.isString())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto string{ Detail::GetStringView(json) };
|
|
|
|
return (string.length() == 7 || string.length() == 4) && string.front() == '#';
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const til::color& val)
|
|
|
|
{
|
|
|
|
return til::u16u8(val.ToHexString(true));
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "color (#rrggbb, #rgb)";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
Persist window layout on window close (#10972)
This commit adds initial support for saving window layout on application
close.
Done:
- Add user setting for if tabs should be maintained.
- Added events to track the number of open windows for the monarch, and
then save if you are the last window closing.
- Saves layout when the user explicitly hits the "Close Window" button.
- If the user manually closed all of their tabs (through the tab x
button or through closing all panes on the tab) then remove any saved
state.
- Saves in the ApplicationState file a list of actions the terminal can
perform to restore its layout and the window size/position
information.
- This saves an action to focus the correct pane, but this won't
actually work without #10978. Note that if you have a pane zoomed, it
does still zoom the correct pane, but when you unzoom it will have a
different pane selected.
Todo:
- multiple windows? Right now it can only handle loading/saving one
window.
- PR #11083 will save multiple windows.
- This also sometimes runs into the existing bug where multiple tabs
appear to be focused on opening.
Next Steps:
- The business logic of when the save is triggered can be adjusted as
necessary.
- Right now I am taking the pragmatic approach and just saving the state
as an array of objects, but only ever populate it with 1, that way
saving multiple windows in the future could be added without breaking
schema compatibility. Selfishly I'm hoping that handling multiple
windows could be spun off into another pr/feature for now.
- One possible thing that can maybe be done is that the commandline can
be augmented with a "--saved ##" attribute that would load from the
nth saved state if it exists. e.g. if there are 3 saved windows, on
first load it can spawn three wt --saved {0,1,2} that would reopen the
windows? This way there also exists a way to load a copy of a previous
window (if it is in the saved state).
- Is the application state something that is planned to be public/user
editable? In theory the user could since it is just json, but I don't
know what it buys them over just modifying their settings and
startupActions.
Validation Steps Performed:
- The happy path: open terminal -> set setting to true -> close terminal
-> reopen and see tabs. Tested with powershell/cmd/wsl windows.
- That closing all panes/tabs on their own will remove the saved
session.
- Open multiple windows, close windows and confirm that the last window
closed saves its state.
The generated file stores a sequence of actions that will be executed to
restore the terminal to its saved form.
References #8324
This is also one of the items on microsoft/terminal#5000
Closes #766
2021-09-09 00:44:53 +02:00
|
|
|
#ifdef WINRT_Windows_Foundation_H
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<winrt::Windows::Foundation::Size>
|
|
|
|
{
|
|
|
|
winrt::Windows::Foundation::Size FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
winrt::Windows::Foundation::Size size{};
|
|
|
|
GetValueForKey(json, std::string_view("width"), size.Width);
|
|
|
|
GetValueForKey(json, std::string_view("height"), size.Height);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
if (!json.isObject())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return json.isMember("width") && json.isMember("height");
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const winrt::Windows::Foundation::Size& val)
|
|
|
|
{
|
|
|
|
Json::Value json{ Json::objectValue };
|
|
|
|
|
|
|
|
SetValueForKey(json, std::string_view("width"), val.Width);
|
|
|
|
SetValueForKey(json, std::string_view("height"), val.Height);
|
|
|
|
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "size { width, height }";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2020-08-28 03:09:22 +02:00
|
|
|
#ifdef WINRT_Windows_UI_H
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<winrt::Windows::UI::Color>
|
|
|
|
{
|
|
|
|
winrt::Windows::UI::Color FromJson(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
return static_cast<winrt::Windows::UI::Color>(ConversionTrait<til::color>{}.FromJson(json));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
return ConversionTrait<til::color>{}.CanConvert(json);
|
|
|
|
}
|
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const winrt::Windows::UI::Color& val)
|
|
|
|
{
|
|
|
|
return ConversionTrait<til::color>{}.ToJson(val);
|
|
|
|
}
|
|
|
|
|
2020-08-28 03:09:22 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return ConversionTrait<til::color>{}.TypeDescription();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
Introduce MS.Term.Core.Color to replace W.U.Color for Core/Control/TSM (#9658)
This pull request introduces Microsoft.Terminal.Core.Color as an
alternative to both Windows.UI.Color and uint32_t/COLORREF in the
TerminalCore, ...Control, ...SettingsModel and ...SettingsEditor layers.
M.T.C.Color is trivially convertible to/from til::color and therefore
to/from COLORREF, W.U.Color, and any other color representation we might
need².
I've replaced almost every use of W.U.Color and uint32_t-as-color in the
above layers, with minor exception¹.
The need for this work is twofold.
First: We cannot bear a dependency from TerminalCore (which should,
on paper, be Windows 7 compatible) on Windows.UI or any other WinRT
namespace.
This work removes one big dependency on Windows.UI, but it does not go
all the way.
Second: TerminalCore chose to communicate mostly in packed uint32s
(COLORREF), which was inherently lossy and dangerous.
¹ The UI layers (TerminalControl, TerminalApp) still use
Windows.UI.Color as they are intimately connected to the UWP XAML UI.
² In the future, we might even be able to *use* the alpha channel...
## PR Checklist
* [x] I ran into the need for this when I introduced cursor inversion
* [X] Fixes a longstanding itch
## Validation Steps Performed
Built and ran all tests for the impacted layers, even the local ones!
2021-03-30 22:15:49 +02:00
|
|
|
#ifdef WINRT_Microsoft_Terminal_Core_H
|
|
|
|
template<>
|
|
|
|
struct ConversionTrait<winrt::Microsoft::Terminal::Core::Color>
|
|
|
|
{
|
|
|
|
winrt::Microsoft::Terminal::Core::Color FromJson(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
return static_cast<winrt::Microsoft::Terminal::Core::Color>(ConversionTrait<til::color>{}.FromJson(json));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json) const
|
|
|
|
{
|
|
|
|
return ConversionTrait<til::color>{}.CanConvert(json);
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const winrt::Microsoft::Terminal::Core::Color& val)
|
|
|
|
{
|
|
|
|
return ConversionTrait<til::color>{}.ToJson(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return ConversionTrait<til::color>{}.TypeDescription();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
template<typename T, typename TDelegatedConverter = ConversionTrait<typename std::decay<T>::type>, typename TOpt = std::optional<typename std::decay<T>::type>>
|
|
|
|
struct OptionalConverter
|
|
|
|
{
|
|
|
|
using Oracle = OptionOracle<TOpt>;
|
|
|
|
TDelegatedConverter delegatedConverter{};
|
|
|
|
|
|
|
|
TOpt FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
if (!json && !delegatedConverter.CanConvert(json))
|
|
|
|
{
|
|
|
|
// If the nested converter can't deal with null, emit an empty optional
|
|
|
|
// If it can, it probably has specific null behavior that it wants to use.
|
|
|
|
return Oracle::EmptyV();
|
|
|
|
}
|
|
|
|
TOpt val{ delegatedConverter.FromJson(json) };
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isNull() || delegatedConverter.CanConvert(json);
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value ToJson(const TOpt& val)
|
|
|
|
{
|
|
|
|
if (!Oracle::HasValue(val))
|
|
|
|
{
|
|
|
|
return Json::Value::nullSingleton();
|
|
|
|
}
|
|
|
|
return delegatedConverter.ToJson(Oracle::Value(val));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return delegatedConverter.TypeDescription();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct ConversionTrait<std::optional<T>> : public OptionalConverter<T, ConversionTrait<T>, std::optional<T>>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef WINRT_Windows_Foundation_H
|
|
|
|
template<typename T>
|
|
|
|
struct ConversionTrait<::winrt::Windows::Foundation::IReference<T>> : public OptionalConverter<T, ConversionTrait<T>, ::winrt::Windows::Foundation::IReference<T>>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2020-07-17 03:31:09 +02:00
|
|
|
template<typename T, typename TBase>
|
|
|
|
struct EnumMapper
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
{
|
2020-07-17 03:31:09 +02:00
|
|
|
using BaseEnumMapper = EnumMapper<T, TBase>;
|
|
|
|
using ValueType = T;
|
|
|
|
using pair_type = std::pair<std::string_view, T>;
|
|
|
|
T FromJson(const Json::Value& json)
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
{
|
2020-07-17 03:31:09 +02:00
|
|
|
const auto name{ Detail::GetStringView(json) };
|
|
|
|
for (const auto& pair : TBase::mappings)
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
{
|
2020-07-17 03:31:09 +02:00
|
|
|
if (pair.first == name)
|
2020-03-20 21:35:51 +01:00
|
|
|
{
|
2020-07-17 03:31:09 +02:00
|
|
|
return pair.second;
|
2020-03-20 21:35:51 +01:00
|
|
|
}
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
DeserializationError e{ json };
|
2020-12-15 20:33:52 +01:00
|
|
|
e.expectedType = static_cast<const TBase&>(*this).TypeDescription();
|
2020-08-11 21:50:13 +02:00
|
|
|
throw e;
|
2020-07-17 03:31:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return json.isString();
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const T& val)
|
|
|
|
{
|
|
|
|
for (const auto& pair : TBase::mappings)
|
|
|
|
{
|
|
|
|
if (pair.second == val)
|
|
|
|
{
|
|
|
|
return { pair.first.data() };
|
|
|
|
}
|
|
|
|
}
|
2020-11-17 01:37:19 +01:00
|
|
|
throw SerializationError{};
|
2020-09-17 20:27:46 +02:00
|
|
|
}
|
|
|
|
|
2020-08-11 21:50:13 +02:00
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
std::vector<std::string_view> names;
|
|
|
|
std::transform(TBase::mappings.cbegin(), TBase::mappings.cend(), std::back_inserter(names), [](auto&& p) { return p.first; });
|
|
|
|
return fmt::format("{}", fmt::join(names, " | "));
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// FlagMapper is EnumMapper, but it works for bitfields.
|
|
|
|
// It supports a string (single flag) or an array of strings.
|
|
|
|
// Does an O(n*m) search; meant for small search spaces!
|
|
|
|
//
|
|
|
|
// Cleverly leverage EnumMapper to do the heavy lifting.
|
|
|
|
template<typename T, typename TBase>
|
|
|
|
struct FlagMapper : public EnumMapper<T, TBase>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
// Hide BaseEnumMapper so FlagMapper's consumers cannot see
|
|
|
|
// it.
|
|
|
|
using BaseEnumMapper = EnumMapper<T, TBase>::BaseEnumMapper;
|
|
|
|
|
|
|
|
public:
|
|
|
|
using BaseFlagMapper = FlagMapper<T, TBase>;
|
|
|
|
static constexpr T AllSet{ static_cast<T>(~0u) };
|
|
|
|
static constexpr T AllClear{ static_cast<T>(0u) };
|
|
|
|
|
|
|
|
T FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
if (json.isString())
|
|
|
|
{
|
|
|
|
return BaseEnumMapper::FromJson(json);
|
|
|
|
}
|
|
|
|
else if (json.isArray())
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
{
|
2020-07-17 03:31:09 +02:00
|
|
|
unsigned int seen{ 0 };
|
|
|
|
T value{};
|
|
|
|
for (const auto& element : json)
|
|
|
|
{
|
|
|
|
const auto newFlag{ BaseEnumMapper::FromJson(element) };
|
|
|
|
if (++seen > 1 &&
|
|
|
|
((newFlag == AllClear && value != AllClear) ||
|
|
|
|
(value == AllClear && newFlag != AllClear)))
|
|
|
|
{
|
|
|
|
// attempt to combine AllClear (explicitly) with anything else
|
2020-08-11 21:50:13 +02:00
|
|
|
DeserializationError e{ element };
|
2020-12-15 20:33:52 +01:00
|
|
|
e.expectedType = static_cast<const TBase&>(*this).TypeDescription();
|
2020-08-11 21:50:13 +02:00
|
|
|
throw e;
|
2020-07-17 03:31:09 +02:00
|
|
|
}
|
|
|
|
value |= newFlag;
|
|
|
|
}
|
|
|
|
return value;
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
|
|
|
|
// We'll only get here if CanConvert has failed us.
|
|
|
|
return AllClear;
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
|
2020-09-17 20:27:46 +02:00
|
|
|
Json::Value ToJson(const T& val)
|
|
|
|
{
|
|
|
|
if (val == AllClear)
|
|
|
|
{
|
|
|
|
return BaseEnumMapper::ToJson(AllClear);
|
|
|
|
}
|
|
|
|
else if (val == AllSet)
|
|
|
|
{
|
|
|
|
return BaseEnumMapper::ToJson(AllSet);
|
|
|
|
}
|
|
|
|
else if (WI_IsSingleFlagSet(val))
|
|
|
|
{
|
|
|
|
return BaseEnumMapper::ToJson(val);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Json::Value json{ Json::ValueType::arrayValue };
|
|
|
|
for (const auto& pair : TBase::mappings)
|
|
|
|
{
|
|
|
|
if (pair.second != AllClear &&
|
2021-05-25 00:51:03 +02:00
|
|
|
(val & pair.second) == pair.second &&
|
|
|
|
WI_IsSingleFlagSet(pair.second))
|
2020-09-17 20:27:46 +02:00
|
|
|
{
|
|
|
|
json.append(BaseEnumMapper::ToJson(pair.second));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-17 03:31:09 +02:00
|
|
|
bool CanConvert(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return BaseEnumMapper::CanConvert(json) || json.isArray();
|
|
|
|
}
|
2020-08-10 21:48:33 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct PermissiveStringConverter
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct PermissiveStringConverter<std::wstring>
|
|
|
|
{
|
|
|
|
std::wstring FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
return til::u8u16(json.asString());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CanConvert(const Json::Value& /*unused*/)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2020-08-11 21:50:13 +02:00
|
|
|
|
|
|
|
std::string TypeDescription() const
|
|
|
|
{
|
|
|
|
return "any";
|
|
|
|
}
|
2020-07-17 03:31:09 +02:00
|
|
|
};
|
Add Cascading User + Default Settings (#2515)
This PR represents the start of the work on Cascading User + default settings, #754.
Cascading settings will be done in two parts:
* [ ] Layered Default+User settings (this PR)
* [ ] Dynamic Profile Generation (#2603).
Until _both_ are done, _neither are going in. The dynamic profiles PR will target this PR when it's ready, but will go in as a separate commit into master.
This PR covers adding one primary feature: the settings are now in two separate files:
* a static `defaults.json` that ships with the package (the "default settings")
* a `profiles.json` with the user's customizations (the "user settings)
User settings are _layered_ upon the settings in the defaults settings.
## References
Other things that might be related here:
* #1378 - This seems like it's definitely fixed. The default keybindings are _much_ cleaner, and without the save-on-load behavior, the user's keybindings will be left in a good state
* #1398 - This might have honestly been solved by #2475
## PR Checklist
* [x] Closes #754
* [x] Closes #1378
* [x] Closes #2566
* [x] I work here
* [x] Tests added/passed
* [x] Requires documentation to be updated - it **ABSOLUTELY DOES**
## Detailed Description of the Pull Request / Additional comments
1. We start by taking all of the `FromJson` functions in Profile, ColorScheme, Globals, etc, and converting them to `LayerJson` methods. These are effectively the same, with the change that instead of building a new object, they are simply layering the values on top of `this` object.
2. Next, we add tests for layering properties like that.
3. Now, we add a `defaults.json` to the package. This is the file the users can refer to as our default settings.
4. We then take that `defaults.json` and stamp it into an auto generated `.h` file, so we can use it's data without having to worry about reading it from disk.
5. We then change the `LoadAll` function in `CascadiaSettings`. Now, the function does two loads - one from the defaults, and then a second load from the `profiles.json` file, layering the settings from each source upon the previous values.
6. If the `profiles.json` file doesn't exist, we'll create it from a hardcoded `userDefaults.json`, which is stamped in similar to how `defaults.json` is.
7. We also add support for _unbinding_ keybindings that might exist in the `defaults.json`, but the user doesn't want to be bound to anything.
8. We add support for _hiding_ a profile, which is useful if a user doesn't want one of the default profiles to appear in the list of profiles.
## TODO:
* [x] Still need to make Alt+Click work on the settings button
* [x] Need to write some user documentation on how the new settings model works
* [x] Fix the pair of tests I broke (re: Duplicate profiles)
<hr>
* Create profiles by layering them
* Update test to layer multiple times on the same profile
* Add support for layering an array of profiles, but break a couple tests
* Add a defaults.json to the package
* Layer colorschemes
* Moves tests into individual classes
* adds support for layering a colorscheme on top of another
* Layer an array of color schemes
* oh no, this was missed with #2481
must have committed without staging this change, uh oh. Not like those tests actually work so nbd
* Layer keybindings
* Read settings from defaults.json + profiles.json, layer appropriately
This is like 80% of #754. Needs tests.
* Add tests for keybindings
* add support to unbind a key with `null` or `"unbound"` or `"garbage"`
* Layer or clear optional properties
* Add a helper to get an optional variable for a bunch of different types
In the end, I think we need to ask _was this worth it_
* Do this with the stretch mode too
* Add back in the GUID check for profiles
* Add some tests for global settings layering
* M A D W I T H P O W E R
Add a MsBuild target to auto-generate a header with the defaults.json as a
string in the file. That way, we can _always_ load the defaults. Literally impossible to not.
* When the user's profile.json doesn't exist, create it from a template
* Re-order profiles to match the order set in the user's profiles.json
* Add tests for re-ordering profiles to match user ordering
* Add support for hiding profiles using `"hidden": true`
* Use the hardcoded defaults.json for the exception->"use defaults" case
* Somehow I messed up the git submodules?
* woo documentation
* Fix a Terminal.App.Unit.Tests failure
* signed/unsigned is hard
* Use Alt+Settings button to open the default settings
* Missed a signed/unsigned
* Some very preliminary PR feedback
* More PR feedback
Use the wil helper for the exe path
Move jsonutils into their own file
kill some dead code
* Add templates to these bois
* remove some code for generating defaults, reorder defaults.json a tad
* Make guid a std::optional
* Large block of PR feedback
* Remove some dead code
* add some comments
* tag some todos
* stl is love, stl is life
* add `-noprofile`
* Fix the crash that dustin found
* -Encoding ASCII
* Set a profile's default scheme to Campbell
* Fix the tests I regressed
* Update UsingJsonSetting.md to reflect that changes from these PRs
* Change how GenerateGuidForProfile works
* Make AppKeyBindings do its own serialization
* Remove leftover dead code from the previous commit
* Fix up an enormous number of PR nits
* Fix a typo; Update the defaults to match #2378
* Tiny nits
* Some typos, PR nits
* Fix this broken defaults case
2019-09-16 21:57:10 +02:00
|
|
|
};
|
2020-07-17 03:31:09 +02:00
|
|
|
|
2020-10-06 18:56:59 +02:00
|
|
|
#define JSON_ENUM_MAPPER(...) \
|
|
|
|
template<> \
|
|
|
|
struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
|
|
|
public ::Microsoft::Terminal::Settings::Model::JsonUtils::EnumMapper<__VA_ARGS__, ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
2020-07-17 03:31:09 +02:00
|
|
|
|
2020-10-06 18:56:59 +02:00
|
|
|
#define JSON_FLAG_MAPPER(...) \
|
|
|
|
template<> \
|
|
|
|
struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
|
|
|
public ::Microsoft::Terminal::Settings::Model::JsonUtils::FlagMapper<__VA_ARGS__, ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
2020-07-17 03:31:09 +02:00
|
|
|
|
|
|
|
#define JSON_MAPPINGS(Count) \
|
|
|
|
static constexpr std::array<pair_type, Count> mappings
|