36 KiB
author | created on | last updated | issue id |
---|---|---|---|
Mike Griese @zadjii-msft | 2019-05-31 | 2019-07-31 | 754 |
Cascading Default + User Settings
Abstract
This spec outlines adding support for a cascading settings model. In this model, there are two settings files, instead of one.
- The default settings file
- The user's settings file
The default settings file would be a static, read-only file shipped with the terminal. The user settings file would then contain all the user's chosen customizations to the settings. These two files would then be composed together when the app is launched, so that the runtime settings are the union of both the defaults and whatever modifications the user has chosen. This will enable the app to always use a default schema that it knows will be valid, and minimize the settings that the user needs to customize.
Should the settings schema ever change, the defaults file will change, without needing to re-write the user's settings file.
It also outlines a mechanism by which profiles could be dynamically added or hidden from the profiles list, based on some external source.
Inspiration
Largely inspired by the settings model that both VS Code (and Sublime Text) use.
Goal: Minimize Re-Serializing profiles.json
We want to re-serialize the user settings file, profiles.json
, as little as
possible. Each time we serialize the file, there's the possibility that we've
re-ordered the keys, as jsoncpp
provides no ordering guarantee of the keys.
This isn't great, as each write of the file will randomly re-order the file.
One of our overarching goals with this change should be to re-serialize the user settings file as little as possible.
Goal: Minimize Content in profiles.json
We want the user to only have to make the minimal number of changes possible to
the user settings file. Additionally, the user should only have to have the
settings that they've changed in that file. If the user wants to change only the
cursorColor
of a profile, they should only need to set that property in the
user settings file, and not need an entire copy of the Profile
object in their
user settings file. That would create additional noise that's not relevant to
the user.
Goal: Remove the Need to Reset Settings Entirely to get New Settings
One problem with the current settings design is that we only generate "default" settings for the user when there's no settings file present at all. So, when we want to do things like update the default profiles to have an icon, or add support for generating WSL profiles, it will only apply to users for fresh installs. Otherwise, a user needs to completely delete the settings file to have the terminal re-generate the default settings.
This is fairly annoying to the end-user, so ideally we'll find a way to be able to prevent this scenario.
Goal: Prevent Roaming Settings from Failing
Another problem currently is that when settings roam to another machine, it's possible that the second machine doesn't have the same applications installed as the first, and some profiles might be totally invalid on the second machine. Take for example, profiles for WSL distros. If you have and Ubuntu profile on your first machine, and roam that profile to a second machine without Ubuntu installed, then the Ubuntu profile would be totally broken on the second machine.
While we won't be able to non-destructively prevent all failures of this case, we should be able to catch it in certain scenarios.
Solution Design
The settings are now composed from two files: a "Default" settings file, and a "User" settings file.
When we load the settings, we'll perform the following steps, each mentioned in greater detail below:
- Load from disk the
defaults.json
(the default settings) -> DefaultsJson - Load from disk the
profiles.json
(the user settings) -> UserJson - Parse DefaultsJson to create all the default profiles, schemes, keybindings.
- [Not covered in this spec] Check the UserJson to find the list of dynamic profile sources that should run.
- Run all the enabled dynamic profile generators. Those profiles will be
added to the set of profiles.
- During this step, check if any of the profiles added here don't exist in UserJson. If they don't, the generator created a profile that didn't exist before. Return a value indicating the user settings should be re-saved (with the new profiles added).
- [Not covered in this spec] Layer the UserJson.globals.defaults settings to every profile in the set, both the defaults, and generated profiles.
- Apply the user settings from UserJson. Layer the profiles on top of the
existing profiles if possible (if both
guid
andsource
match). If a profile from the user settings does not already exist, make sure to apply the UserJson.globals.defaults settings first. Also layer Color schemes and keybindings.- If a profile has a
source
key, but there is not an existing profile with a matchingguid
andsource
, don't create a new Profile object for it. Either that generator didn't run, or the generator wanted to delete that profile, so we'll effectively hide the profile.
- If a profile has a
- Re-order the list of profiles, to match the ordering in the UserJson. If a profile doesn't exist in UserJson, it should follow all the profiles in the UserJson. If a profile listed in UserJson doesn't exist, we can skip it safely in this step (the profile will be a dynamic profile that didn't get populated.)
- Validate the settings.
- If requested in step 5, write the modified settings back to
profiles.json
.
Default Settings
We'll have a static version of the "Default" file hard-coded within the
application package. This defaults.json
file will live within the
application's package, which will prevent users from being able to edit it.
// This is an auto-generated file. Place any modifications to your settings in "profiles.json"
This disclaimer will help identify that the file shouldn't be modified. The file won't actually be generated, but because it's shipped with our app, it'll be overridden each time the app is updated. "Auto-generated" should be good enough to indicate to users that it should not be modified.
Because the defaults.json
file is hard-coded within our application, we can use
its text directly, without loading the file from disk. This should help save
some startup time, as we'll only need to load the user settings from disk.
When we make changes to the default settings, or we make changes to the settings
schema, we should make sure that we update the hard-coded defaults.json
with
the new values. That way, the defaults.json
file will always have the complete
set of settings in it.
Layering settings
When we load the settings, we'll do it in three stages. First, we'll deserialize the default settings that we've hard-coded. We'll then generate any profiles that might come from dynamic profile sources. Then, we'll intelligently layer the user's setting upon those we've already loaded. If a user wants to make changes to some objects, like the default profiles, we'll need to make sure to load from the user settings into the existing objects we created from the default settings.
- We'll need to make sure that any profile in the user settings that has a GUID matching a default profile loads the user settings into the object created from the defaults.
- We'll need to make sure that there's only one action bound to each key chord for a keybinding. If there are any key chords in the user settings that match a default key chord, we should bind them to the action from the user settings instead.
- For any color schemes whose name matches the name of a default color scheme,
we'll need to apply the user settings to the existing color scheme. For
example, a user could override the
red
entry of the "Campbell" scheme to be#ff9900
if they want. This would then apply to all profiles using the "Campbell" scheme. - For profiles that were created from a dynamic profile source, they'll have
both a
guid
andsource
guid that must both match. If a user profile with asource
set does not find a matching profile at load time, the profile will be ignored. See more details in the Dynamic Profiles section.
Hiding Default Profiles
What if a user doesn't want to see one of the profiles that we've included in the default profiles?
We will add a hidden
key to each profile, which defaults to false. When we
want to mark a profile as hidden, we'd just set that value to true
, instead of
trying to look up the profile's guid.
So, if someone wanted to hide the default cmd.exe profile, all they'd have to do
is add "hidden": true
to the cmd.exe entry in their user settings, like so:
{
"profiles": [
{
// Make changes here to the cmd.exe profile
"guid": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}",
"hidden": true
}
],
Hidden Profiles and the Open New Tab shortcuts
Currently, there are keyboard shortcuts for "Open New Tab With Profile <N>". These shortcuts will open up the Nth profile in the new tab dropdown. Considering we're adding the ability to remove profiles from that list, but keep them in the overall list of profiles, we'll need to make sure that the handler for that event still opens the Nth visible profile.
Serializing User Settings
How can we tell that a setting should be written back to the user settings file?
If the value of the setting isn't the same as the defaults, then it could easily
be added to the user's profiles.json
. We'll have to do a smart serialization
of the various settings models. We'll pass in the default version of that
model during the serialization. If that object finds that a particular setting
is the same as a default setting, then we'll skip serializing it.
What happens if a user has chosen to set the value to coincidentally the same value as the default value? We should keep that key in the user's settings file, even though it is the same.
In order to facilitate this, we'll need to keep the originally parsed user settings around in memory. When we go to serialize the settings, we'll check if either the setting exists already in the user settings file, or the setting has changed. If either is true, then we'll make sure to write that setting back out.
For serializing settings for the default profiles, we'll check if the setting is
in the user settings file, or if the value of the setting is different from the
version of that Profile
from the default settings. For user-created profiles,
we'll compare the value of the setting with the value of the default
constructed Profile
object. This will help ensure that each profile in the
user's settings file maintains the minimal amount of info necessary.
When we're adding profiles due to their generation in a dynamic profile generator, we'll need to serialize them, then insert them back into the originally parsed json object to be serialized. We don't want the automatic creation of a new profile to automatically trigger re-writing the entire user settings file, but we do want newly created dynamic profiles to have an entry the user can easily edit.
Dynamic Profiles
Sometimes, we may want to auto-generate a profile on the user's behalf. Consider the case of WSL distros on their machine, or VMs running in Azure they may want to auto-connect to. These dynamic profiles have a source that might be added or removed after the app is installed, and they will be different from user to user.
Currently, these profiles are only generated when a user first launches the
Terminal. If they already have a profiles.json
file, then we won't run the
auto-generation behavior. This is obviously not great - if any new types of
dynamic profiles are added, then users that already have the Terminal installed
won't get any of these dynamic profiles. Furthermore, if any of the sources of
these dynamic profiles are removed, then the app won't auto-remove the
associated profile.
In the new model, with a combined defaults & user settings, how should these dynamic profiles work?
I propose we add functionality to automatically search for these profile sources and add/remove them on every Terminal launch. To make this functionality work appropriately, we'll need to introduce a constraint on dynamic profiles.
For any dynamic profiles, they must be able to be generated using a stable GUID. For example, any time we try adding the "Ubuntu" profile, we must be able to generate the same GUID every time. This way, when a dynamic profile generator runs, it can check if that profile source already has a profile associated with it, and do nothing (as to not create many duplicate "Ubuntu" profiles, for example).
Additionally, each dynamic profile generator must have a unique source guid to associate with the profile. When a dynamic profile is generated, the source's guid will be added to the profile, to make sure the profile is correlated with the source it came from.
We'll generate these dynamic profiles immediately after parsing the default profiles and settings. When a generator runs, it'll be able to create unique profile GUIDs for each source it wants to generate a profile for. It'll hand back a list of Profile objects, with settings set up how the generator likes, with GUIDs set.
After a dynamic profile generator runs, we will determine what new profiles need
to be added to the user settings, so we can append those to the list of
profiles. The deserializer will look at the list of generated profiles and check
if each and every one already has a entry in the user settings. The generator
will just blind hand back a list of profiles, and the deserializer will figure
out if any of them need to be added to the user settings. We'll store some sort
of result indicating that we want a save operation to occur. After the rest of
the deserializing is done, the app will then save the profiles.json
file,
including these new profiles.
When we're serializing the settings, instead of comparing a dynamic profile to
the default-constructed Profile
, we'll compare it to the state of the
Profile
after the dynamic profile generator created it. It'd then only
serialize settings that are different from the auto-generated version. It will
also always make sure that the guid
of the dynamic profile is included in the
user settings file, as a point for the user to add customizations to the dynamic
profile to. Additionally, we'll also make sure the source
is always serialized
as well, to keep the profile correlated with the generator that created it.
We'll need to keep the state of these dynamically generated profiles around in memory during runtime to be able to ensure the only state we're serializing is that which is different from the initially generated dynamic profile.
When the generator is run, and determines that a new profile has been added,
we'll need to make sure to add the profile to the user's settings file. This
will create an easy point for users to customize the dynamic profiles. When
added to the user settings, all that will be added is the name
, guid
, and
source
.
Additionally, a user might not want a dynamic profile generator to always run.
They might want to keep their Azure connections visible in the list of profiles,
even if its no longer a valid target. Or they might want to not automatically
connect to Azure to find new instances every time they launch the terminal. To
enable scenarios like this, we'll add an additional setting,
disabledProfileSources
. This is an array of guids. If any guids are in that
list, then those dynamic profile generators won't be run, suppressing those
profiles from appearing in the profiles list.
If a dynamic profile generator needs to "delete" a profile, this will also work
naturally with the above rules. Lets examine the case where the user has
uninstalled the Ubuntu distro. When the WSL generator runs, it won't create the
Ubuntu profile. When we get to the Ubuntu profile in the user's settings, it'll
have a source
, but we won't already have a profile with that guid
and
source
. So we'll just ignore it, because whatever source for that profile
doesn't want it anymore. Effectively, this will act like it was "deleted",
though the artifacts still remain untouched in the user's json.
What if a dynamic profile is removed, but it's the default?
I'll direct our attention to #1348 - Display a specific error for not finding the default profile. When we're done loading, and we determine that the default profile doesn't exist in the finalized list of profiles, we'll display a dialog to the user. This includes both hidden profiles and dynamic profiles that have been "deleted". We'll temporarily use the first profile instead.
Dynamic profile GUID generation
In order to help facilitate the generation of stable, unique GUIDs for dynamically generated profiles, we'll enforce a few methods on each generator. The Generator should implement a method that returns its unique namespace for profiles it generates:
class IDynamicProfileGenerator
{
...
virtual std::wstring GetNamespace() = 0;
...
}
For example, the WSL generator would return Microsoft.Terminal.WSL
. The
Powershell Core generator would return Microsoft.Terminal.PowershellCore
.
We'll use these names to be able to generate uuidv5 GUIDs that will be unique
(so long as the names are unique).
The generator should also be able to ask the app for two other pieces of functionality:
- The generator should be able to ask the app for the generator's own namespace GUID
- The generator should be able to ask the app for a uuidv5 in the generator's namespace, given a specific name key.
These two functions will be exposed to the generator like so:
GUID GetNamespaceGuid(IDynamicProfileGenerator& generator);
GUID GetGuidForName(IDynamicProfileGenerator& generator, std::wstring& name);
The generator does not need to use GetGuidForName
to generate guids for it's
profiles. If the generator can determine another way to generate stable GUIDs
for its profiles, it's free to use whatever method it wants. GetGuidForName
is
provided as a convenience.
It's not the responsibility of the dynamic profile generator to fill in the
source
of the profiles it generates. The deserializer will make sure to go
through and fill in the guid for the generated profiles given the generator's
namespace GUID.
Powershell Core & the Defaults
How do we handle the potential existence of Powershell Core in this model?
Powershell core is unique as far as the default profiles goes - it may or may
not exist on the user's system. Not only that, but depending on the user's
install of Powershell Core, it might have a path in either Program Files
or
Program Files(x86)
.
Additionally, if it is installed, we set it as the default profile instead of Windows Powershell.
Powershell core acts much like a dynamic profile. It has an installation source that may or not be there. So we'll add a dynamic profile generator for Powershell Core. This will automatically create a profile for Powershell Core if necessary.
Unlike the other dynamic profiles, if Powershell Core is present on first launch of the terminal, we set that as the default profile. This can still be done - we'll need to do some special-case work when we're loading the user settings and we don't find any existing settings. When that happens, we'll generate all the default user settings. Before we commit them, we'll check if the Powershell Core profile exists, and if it does, we'll set that as the default profile before writing the settings to disk.
Unbinding a Keybinding
How can a user unbind a key that's part of the default keybindings? What if a user really wants ctrl+t to fall through to the commandline application attached to the shell, instead of opening a new tab?
We'll need to introduce a new keybinding command that should indicate that the
key is unbound. We'll load the user keybindings and layer them on the defaults
as described above. If during the deserializing we find an entry that's bound to
the command "unbound"
or any other string that we don't understand, instead of
trying to set the keybinding, we'll clear the keybinding with a new method
AppKeyBindings::ClearKeyBinding(chord)
.
Removing the Globals Object
As a part of #1005, all the
global settings were moved to their own object within the serialized settings.
This was to try and make the file easier to parse as a user, considering global
settings would be intermingled with profiles, keybindings, color schemes, etc.
Since this change will make the user settings dramatically easier to navigate,
we should probably remove the globals
object, and have globals at the root
level again.
Default profiles.json
Below is an example of what the default user settings file might look like when it's first generated, taking all the above points into consideration.
// To view the default settings, open <path-to-app-package>\defaults.json
{
"defaultProfile" : "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
"profiles": [
{
// Make changes here to the cmd.exe profile
"guid": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}"
},
{
// Make changes here to the Windows Powershell profile
"guid": "{086a83cd-e4ef-418b-89b1-3f6523ff9195}",
},
{
"guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
"name" : "Powershell Core",
"source": "{2bde4a90-d05f-401c-9492-e40884ead1d8}",
}
],
// Add custom color schemes to this array
"schemes": [],
// Add any keybinding overrides to this array.
// To unbind a default keybinding, set the command to "unbound"
"keybindings": []
}
Note the following:
- cmd.exe and powershell.exe are both in the file, as to give users an easy point to extend the settings for those default profiles.
- Powershell Core is included in the file, and the default profile has been set
to its GUID. The
source
has been set, indicating that it came from a dynamic profile source. - There are a few helpful comments scattered throughout the file to help point the user in the right direction.
Re-ordering profiles
Since there are shortcuts to open the Nth profile in the list of profiles, we need to expose a way for the user to change the order of the profiles. This was not a problem when there was only a single list of profiles, but if the defaults are applied first to the list of profiles, then the user wouldn't be able to change the order of the default profiles. Additionally, any profiles they add would always show up after the defaults.
To remedy this, we could scan the user profiles in the user settings first, and
create Profile
objects for each of those profiles first. These Profile
s
would only be initialized with their GUID temporarily, but they'd be placed into
the list of profiles in the order they appear in the user's settings. Then, we'd
load all the default settings, overlaying any default profiles on the Profile
objects that might already exist in the list of profiles. If there are any
default profiles that don't appear in the user's settings, they'll appear
after any profiles in the user's settings. Then, we'll overlay the full user
settings on top of the defaults.
UI/UX Design
Opening defaults.json
How do we open both these files to show to the user (for the interim period
before a proper Settings UI is created)? Currently, the "Settings" button only
opens a single json file, profiles.json
. We could keep that button doing the
same thing, though we want the user to be able to also view the default settings
file, to be able to inspect what settings they wish to change.
We could have the "Settings" button open both files at the same
time. I'm not sure that ShellExecute
(which is used to open these files)
provides any ordering guarantees, so it's possible that the defaults.json
would open in the foreground of the default json editor, while making in unclear
that there's another file they should be opening instead. Additionally, if
there's no .json
editor for the user, I believe the shell will attempt
twice to ask the user to select a program to open the file with, and it might
not be clear that they need to select a program in both dialogs.
Alternatively, we could make the defaults file totally inaccessible from the
Terminal UI, and instead leave a comment in the auto-generated profiles.json
like so:
// To view the default settings, open the defaults.json file in this directory
The "Settings" button would then only open the file the user needs to edit, and provide them instructions on how to open the defaults file.
There could alternatively be a hidden option for the "Open Settings" button,
where holding Alt while clicking on the button would open the
defaults.json
instead.
We could additionally add a ShortcutAction
(to be bound to a keybinding) that
would openDefaultSettings
, and we could bind that to
ctrl+alt+`, similar to openSettings
on
ctrl+`.
How does this work with the settings UI?
If we only have one version of the settings models (Globals, Profiles, ColorSchemes, Keybindings) at runtime, and the user changes one of the settings with the settings UI, how can we tell that settings changed?
Fortunately, this should be handled cleanly by the algorithm proposed above, in the "Serializing User Settings" section. We'll only be serializing settings that have changed from the defaults, so only the actual changes they've made will be persisted back to the user settings file.
Capabilities
Security
I don't think this will introduce any new security issues that weren't already present
Reliability
I don't think this will introduce any new reliability concerns that weren't already present. We will likely improve our reliability, as dynamic profiles that no longer exist will not cause the terminal to crash on startup anymore.
Performance, Power, and Efficiency
By not writing the defaults to disk, we'll theoretically marginally improve the
load and save times for the profiles.json
file, by simply having a smaller
file to load. However we'll also be doing more work to process the layering of
defaults and user settings, which will likely slightly increase the load times.
Overall, I expect the difference to be negligible due to these factors.
One potential concern is long-running dynamic profile generators. Because they'll need to run on startup, they could negatively impact startup time. You can read more below, in "Dynamic Profile Generators Need to be Enabled".
Accessibility
N/A
Potential Issues
Profiles with the same guid
as a dynamic profile but not the same source
What happens if the User settings has a profile with a guid
that matches a
dynamic or default profile, but the user profile doesn't have a matching source?
This could happen trivially easily if the user deletes the source
key from a
profile that has dynamically generated.
We could:
- Treat the profile as an entirely separate profile
- There's lots of other code that assumes each profile has only a unique GUID, so we'd have to change the GUID of this profile. This would mean writing out the user settings, which we'd like to avoid.
- We'll still end up generating the entry for the dynamic profile in the user's settings, so we'll need to write out the user settings anyways.
- This other profile will likely not have a commandline set, so it might not work at all.
- Ignore the profile entirely.
- When the dynamic profile generator runs, we're not going to find another
entry in the user profiles with both a matching
guid
and a matchingsource
. So we'll end up creating another entry in the user profiles for the dynamic profile. - How could the user know that the profile is being ignored? There's nothing in the file itself that indicates obviously that this profile is now invalid.
- When the dynamic profile generator runs, we're not going to find another
entry in the user profiles with both a matching
- Treat the user settings as part of the dynamic profile
- In this scenario, the user profile continues to exist as part of the dynamic profile.
- When the dynamic profile generator runs, we're not going to find another
entry in the user profiles with both a matching
guid
and a matchingsource
. So we'll end up creating another entry in the user profiles for the dynamic profile.- These two entries will each be layered upon the dynamically generated profile, so the settings in the second profile entry will override settings from the first.
- If the user disables the generator, or the profile source is removed, the
dynamic profile will cease to exist. However, the profile without the
source
entry will remain, though likely will not work. - How do we order these profiles for the user? When we're parsing the user profiles list to build an ordering of profiles, do we use the first entry as the index for that profile?
- (Variant of the above) Treat the profile as part of the dynamic profile, and
re-insert the
source
key.- This will re-connect the user profile to the dynamic one.
- We'll need to make sure to do this before determining the new dynamic profiles to add to the user settings.
- Given all the scenarios are going to cause a user settings write anyways, this isn't terrible.
- If the user really wants to split the profile in their user settings from
the dynamic one, they're free to always generate a new guid and delete the
source
key.
Given the drawbacks associated with options 1-3, I propose we choose option 4 as our solution to this case.
Migrating Existing Settings
I believe that existing profiles.json
files will smoothly update to this
model, without breaking. While in the new model, the profiles.json
file can be
much more sparse, users who have existing profiles.json
files will have full
settings in their user settings. We'll leave their files largely untouched, as
we won't touch keys that have the same values as defaults that are currently in
the profiles.json
file. Fortunately though, users should be able to remove
much of the boilerplate from their profiles.json
files, and trim it down just
to their modifications.
Migrating Powershell Core
Right now, default-generated Powershell Core profiles exist with a stable guid
we've generated for them. However, when we move Powershell Core to being a
dynamically generated profile, we'll have to ensure that we don't create a
duplicated "dynamic" entry for that profile. If we want to convert the existing
Powershell Core profiles into a dynamic profile, we'll need to make sure to add
a source
key to the profile. Everything else in the profile can remain the
same. Once the source
is added, we'll know to treat it as a dynamic profile,
and it'll respond dynamically.
This is actually something that will automatically be covered by the scenario
mentioned above in "Profiles with the same guid
as a dynamic profile but not
the same source
". When we encounter the existing Powershell Core profiles that
don't have a source
, we'll automatically think they're the dynamically
generated ones, and auto-migrate them.
Migrating Existing WSL Profiles
Similar to the above, so long as we ensure the WSL dynamic profile generator generates the same GUIDs as it does currently, all the existing WSL profiles will automatically be migrated to dynamic profiles.
Dynamic Profile Generators Need to be Enabled
With the current proposal, profiles that are generated by a dynamic profile generator need that generator to be enabled for the profile to appear in the list of profiles. If the generator isn't enabled, then the important parts of the profile (name, commandline) will never be set, and the profile's settings from the user settings will be ignored at runtime.
For generators where the generation of profiles might be a lengthy process, this could negatively impact startup time. Take for example, some hypothetical generator that needs to make web requests to generate dynamic profiles. Because we need the finalized settings to be able to launch the terminal, we'll be stuck loading until that generator is complete.
However, if the user disables that generator entirely, we'll never display that profile to the user, even if they've done that setup before.
So the trade-off with this design is that nonexistent dynamic profiles will never roam to machines where they don't exist and aren't valid, but the generators must be enabled to use the dynamic profiles.
Future considerations
- It's possible that a very similar layering loading mechanism could be used to layer per-machine settings with roaming settings. Currently, there's only one settings file, and it roams to all your devices. This could be problematic, for example, if one of your machines has a font installed, but another doesn't. A proposed solution to that problem was to have both roaming settings and per-machine settings. The code to layer settings from the defaults and the user settings could be re-used to handle layer the roaming and per-machine settings.
- What if an extension wants to generate their own dynamic profiles? We've already outlined a contract that profile generators would have to follow to behave correctly. It's possible that we could abstract our implementation into a WinRT interface that extensions could implement, and be triggered just like other dynamic profile generators.
- Multiple settings files - This could enable us to place color schemes into
a separate file (like
colorschemes.json
) and put keybindings into their own file as well, and reduce the number of settings in the user'sprofiles.json
. It's unclear if this is something that we need quite yet, but the same layering functionality that enables this scenario could also enable more than two sources for settings. - Global Default Profile Settings - Say a user wants to override what the
defaults for a profile are, so that they can set settings for all their
profiles at once? We could maybe introduce a profile in the user settings file
with a special guid set to
"default
, that we look for first, and treat specially. We wouldn't include it in the list of profiles. When we're creating profiles, we'll start with that profile as our prototype, instead of using the default-constructedProfile
. When we're serializing profiles, we'd again use that as the point of comparison to check if a setting's value has changed. There may be more unknowns with this proposal, so I leave it for a future feature spec.- We'll also want to make sure that when we're serializing default/dynamic profiles, we take into account the state from the global defaults, and we don't duplicate that information into the entries for those types of profiles in the user profiles.
- Re-ordering profiles - Under "Solution Design", we provide an algorithm for decoding the settings. One of the steps mentioned is parsing the user settings to determine the ordering of the profiles. It's possible in the future we may want to give the user more control over this ordering. Maybe we'll want to allow the user to manually index the profiles. Or, as discussed in issues like #1571, we may want to allow the user to further customize the new tab dropdown, beyond just the order of profiles. The re-ordering step would be a great place to add code to support this re-ordering, with whatever algorithm we eventually land on. Determining such an algorithm is outside the scope of this spec, however.
Resources
N/A