2020-11-16 16:37:19 -08:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
|
|
|
|
#include "../TerminalSettingsModel/ColorScheme.h"
|
|
|
|
|
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
2024-03-14 13:50:07 -07:00
|
|
|
#include "../TerminalSettingsModel/resource.h"
|
2020-11-16 16:37:19 -08:00
|
|
|
#include "JsonTestClass.h"
|
|
|
|
|
#include "TestUtils.h"
|
|
|
|
|
|
|
|
|
|
using namespace Microsoft::Console;
|
|
|
|
|
using namespace WEX::Logging;
|
|
|
|
|
using namespace WEX::TestExecution;
|
|
|
|
|
using namespace WEX::Common;
|
|
|
|
|
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
2021-03-17 15:47:24 -05:00
|
|
|
using namespace winrt::Microsoft::Terminal::Control;
|
2020-11-16 16:37:19 -08:00
|
|
|
|
2024-04-17 17:30:25 -07:00
|
|
|
// Different architectures will hash the same SendInput command to a different ID
|
|
|
|
|
// Check for the correct ID based on the architecture
|
|
|
|
|
#if defined(_M_IX86)
|
|
|
|
|
#define SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH "56911147"
|
|
|
|
|
#else
|
|
|
|
|
#define SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH "A020D2"
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-06-03 17:23:51 -07:00
|
|
|
#if defined(_M_IX86)
|
|
|
|
|
#define SEND_INPUT2_ARCH_SPECIFIC_ACTION_HASH "35488AA6"
|
|
|
|
|
#else
|
|
|
|
|
#define SEND_INPUT2_ARCH_SPECIFIC_ACTION_HASH "58D1971"
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-02-29 11:00:04 -06:00
|
|
|
namespace SettingsModelUnitTests
|
2020-11-16 16:37:19 -08:00
|
|
|
{
|
|
|
|
|
class SerializationTests : public JsonTestClass
|
|
|
|
|
{
|
2024-02-29 11:00:04 -06:00
|
|
|
TEST_CLASS(SerializationTests);
|
2020-11-16 16:37:19 -08:00
|
|
|
|
|
|
|
|
TEST_METHOD(GlobalSettings);
|
|
|
|
|
TEST_METHOD(Profile);
|
|
|
|
|
TEST_METHOD(ColorScheme);
|
2021-05-20 14:44:04 -04:00
|
|
|
TEST_METHOD(Actions);
|
2020-11-16 16:37:19 -08:00
|
|
|
TEST_METHOD(CascadiaSettings);
|
2021-07-01 10:08:46 -07:00
|
|
|
TEST_METHOD(LegacyFontSettings);
|
2020-11-16 16:37:19 -08:00
|
|
|
|
Allow inheriting env vars from `wt` again and other env var changes too (#15897)
This PR is a few things:
* part the first: Convert the `compatibility.reloadEnvironmentVariables`
setting to a per-profile one.
* The settings should migrate it from the user's old global place to the
new one.
* We also added it to "Profile>Advanced" while I was here.
* Adds a new pair of commandline flags to `new-tab` and `split-pane`:
`--inheritEnvironment` / `--reloadEnvironment`
* On `wt` launch, bundle the entire environment that `wt` was spawned
with, and put it into the `Remoting.CommandlineArgs`, and give them to
the monarch (and ultimately, down to `TerminalPage` with the
`AppCommandlineArgs`). DO THIS ALWAYS.
* As a part of this, we’ll default to _reloading_ if there’s no explicit
commandline set, and _inheriting_ if there is.
* For example, `wt -- cmd` would inherit, and `wt -p “Command Prompt”`
would reload.[^1]
* This is a little wacky, but we’re trying to separate out the
intentions here:
* `wt -- cmd` feels like “I want to run cmd.exe (in a terminal tab)”.
That feels like the user would _like_ environment variables from the
calling process. They’re doing something more manual, so they get more
refined control over it.
* `wt` (or `wt -p “Command Prompt”`) is more like, “I want to run the
Terminal (or, my Command Prompt profile) using whatever the Terminal
would normally do”. So that feels more like a situation where it should
just reload by default. (Of course, this will respect their settings
here)
## References and Relevant Issues
https://github.com/microsoft/terminal/issues/15496#issuecomment-1692450231
has more notes.
## Detailed Description of the Pull Request / Additional comments
This is so VERY much plumbing. I'll try to leave comments in the
interesting parts.
## PR Checklist
- [x] This is not _all_ of #15496. We're also going to do a `-E foo=bar`
arg on top of this.
- [x] Tests added/passed
- [x] Schema updated
[^1]: In both these cases, plus the `environment` setting, of course.
2023-09-19 15:03:24 -05:00
|
|
|
TEST_METHOD(RoundtripReloadEnvVars);
|
|
|
|
|
TEST_METHOD(DontRoundtripNoReloadEnvVars);
|
|
|
|
|
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
TEST_METHOD(RoundtripUserModifiedColorSchemeCollision);
|
|
|
|
|
TEST_METHOD(RoundtripUserModifiedColorSchemeCollisionUnusedByProfiles);
|
|
|
|
|
TEST_METHOD(RoundtripUserDeletedColorSchemeCollision);
|
|
|
|
|
|
2024-04-17 17:30:25 -07:00
|
|
|
TEST_METHOD(RoundtripGenerateActionID);
|
|
|
|
|
TEST_METHOD(NoGeneratedIDsForIterableAndNestedCommands);
|
|
|
|
|
TEST_METHOD(GeneratedActionIDsEqualForIdenticalCommands);
|
2024-06-03 17:23:51 -07:00
|
|
|
TEST_METHOD(RoundtripLegacyToModernActions);
|
|
|
|
|
TEST_METHOD(RoundtripUserActionsSameAsInBoxAreRemoved);
|
|
|
|
|
TEST_METHOD(RoundtripActionsSameNameDifferentCommandsAreRetained);
|
|
|
|
|
TEST_METHOD(MultipleActionsAreCollapsed);
|
2024-04-17 17:30:25 -07:00
|
|
|
|
Rewrite media resource handling (relative path icons, web URLs) (#19143)
This pull request broadly rewrites how we handle all media resources in
the Terminal settings model.
## What is a media resource?
A media resource is any JSON property that refers to a file on disk,
including:
- `icon` on profile
- `backgroundImage` on profile (appearance)
- `pixelShaderPath` and `pixelShaderImagePath` on profile (appearance)
- `icon` on command and the new tab menu entries
The last two bullet points were newly discovered during the course of
this work.
## Description of Changes
In every place the settings model used to store a string for a media
path, it now stores an `IMediaResource`.
A media resource must be _resolved_ before it's used. When resolved, it
can report whether it is `Ok` (found, valid) and what the final
normalized path was.
This allows the settings model to apply some new behaviors.
One of those new behaviors is resolving media paths _relative to the
JSON file that referred to them._ This means fragments and user settings
can now contain _local_ images, pixel shaders and more and refer to them
by filename.
Relative path support requires us to track the path from which every
media resource "container" was read[^2]. For "big" objects like Profile,
we track it directly in the object and for each layer. This means that
fragments **updating** a profile pass their relative base path into the
mix. For some of the entries such as those in `newTabMenu`, we just wing
it (#19191). For everything that is recursively owned by a parent that
has a path (say each Command inside an ActionMap), we pass it in from
the parent during media resolution.
During resolution, we now track _exactly which layer_ an icon,
background image, or pixel shader path came from and read the "base
path" from only that layer. The base path is not inherited.
Another new behavior is in the handling of web and other URLs.
Canonical and a few other WSL distributors had to resort to web URLs for
icons because we did not support loading them from the package. Julia
tried to use `ms-appx://JuliaPackageNameHere/path/to/icon` for the same
reason. Neither was intended, and of the two the second _should_ have
worked but never could[^1].
For both `http(s?)` URLs and `ms-appx://` URLs which specify a package
name, we now strip everything except the filename. As an example...
If my fragment specifies `https://example.net/assets/foo.ico`, and my
fragment was loaded from `C:\Fragments`, Terminal will look *only* at
`C:\Fragments\foo.ico`.
This works today for Julia (they put their icon in the fragment folder
hoping that one day we would support this.) It will require some work
from existing WSL distributors.
I'm told that this is similar to how XML schema documents work.
Now, icons are special. They support _Emoji_ and _Segoe Icons_. This PR
adds an early pass to avoid resolving anything that looks like an
emoji.
This PR intentionally expands the heuristic definition of an emoji. It
used to only cover 1-2 code unit emoji, which prevented the use of any
emoji more complicated than "man in business suite levitating."
An icon path will now be considered an emoji or symbol icon if it is
composed of a single grapheme cluster (as measured by ICU.)
This is not perfect, as it errs on the side of allowing too many
things... but each of those things is technically a single grapheme
cluster and is a perfectly legal FontIcon ;)
Profile icons are _even more special_ than icons. They have an
additional fallback behavior which we had to preserve. When a profile
icon fails validation, or is expressly set to `null`, we fall back to
the EXE specified in the command line.
Because we do this fallback during resolution, _and the icon may be
inherited by any higher profile,_ we can only resolve it against the
commandline at the same level as the failed or nulled icon.
Therefore, if you specify `icon: null` in your `defaults` profile, it
will only ever resolve to `cmd.exe` for any profile that inherits it
(unless you change `defaults.commandline`).
This change expands support for the magic keywords `desktopWallpaper`
and `none` to all media paths (yes, even `pixelShaderPath`... but also,
`pixelShaderImagePath`!) It also expands support for _environment
variables_ to all of those places. Yes, we had like forty different
handlers for different types of string path. They are now uniform.
## Resource Validation
Media resources which are not found are "rejected". If a rejected
resource lives in _user_ settings, we will generate a warning and
display it.
In the future, we could detect this in the Settings UI and display a
warning inline.
## Surprises
I learned that `Windows.Foundation.Uri` parses file paths into `file://`
URIs, but does not offer you a way to get the original file path back
out. If you pass `C:\hello world`, _`Uri.Path`_ will return
`/C:/hello%20world`. I kid you not.
As a workaround, we bail out of URL handling if the `:` is too close to
the start (indicating an absolute file path).
## Testing
I added a narow test hook in the media resource resolver, which is
removed completely by link-time code generation. It is a real joy.
The test cases are all new and hopefully comprehensive.
Closes #19075
Closes #16295
Closes #10359 (except it doesn't support fonts)
Supersedes #16949 somewhat (`WT_SETTINGS_DIR`)
Refs #18679
Refs #19215 (future work)
Refs #19201 (future work)
Refs #19191 (future work)
[^1]: Handling a `ms-appx` path requires us to _add their package to our
dependency graph_ for the entire duration during which the resource will
be used. For us, that could be any time (like opening the command
palette for the first time!)
[^2]: We don't bother tracking where the defaults came from, because we
control everything about them.
2025-08-05 15:47:50 -05:00
|
|
|
TEST_METHOD(ProfileWithInvalidIcon);
|
|
|
|
|
|
2026-04-29 10:16:30 -07:00
|
|
|
TEST_METHOD(ModifyProfileSettingAndRoundtrip);
|
|
|
|
|
TEST_METHOD(ModifyGlobalSettingAndRoundtrip);
|
|
|
|
|
TEST_METHOD(ModifyColorSchemeAndRoundtrip);
|
|
|
|
|
TEST_METHOD(FixupUserSettingsDetectsChanges);
|
|
|
|
|
TEST_METHOD(FixupCommandlinePatching);
|
|
|
|
|
|
2020-11-16 16:37:19 -08:00
|
|
|
private:
|
|
|
|
|
// Method Description:
|
|
|
|
|
// - deserializes and reserializes a json string representing a settings object model of type T
|
|
|
|
|
// - verifies that the generated json string matches the provided one
|
|
|
|
|
// Template Types:
|
|
|
|
|
// - <T>: The type of Settings Model object to generate (must be impl type)
|
|
|
|
|
// Arguments:
|
|
|
|
|
// - jsonString - JSON string we're performing the test on
|
|
|
|
|
// Return Value:
|
|
|
|
|
// - the JsonObject representing this instance
|
|
|
|
|
template<typename T>
|
2021-10-05 20:21:03 +02:00
|
|
|
void RoundtripTest(const std::string_view& jsonString)
|
2020-11-16 16:37:19 -08:00
|
|
|
{
|
|
|
|
|
const auto json{ VerifyParseSucceeded(jsonString) };
|
|
|
|
|
const auto settings{ T::FromJson(json) };
|
|
|
|
|
const auto result{ settings->ToJson() };
|
|
|
|
|
|
|
|
|
|
// Compare toString(json) instead of jsonString here.
|
|
|
|
|
// The toString writes the json out alphabetically.
|
|
|
|
|
// This trick allows jsonString to _not_ have to be
|
|
|
|
|
// written alphabetically.
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(json), toString(result));
|
|
|
|
|
}
|
2024-02-26 12:27:57 -08:00
|
|
|
|
|
|
|
|
// Helper to remove the `$schema` property from a json object. We
|
|
|
|
|
// populate that based off the local path to the settings file. Of
|
|
|
|
|
// course, that's entirely unpredictable in tests. So cut it out before
|
|
|
|
|
// we do any sort of roundtrip testing.
|
|
|
|
|
static Json::Value removeSchema(Json::Value json)
|
|
|
|
|
{
|
|
|
|
|
if (json.isMember("$schema"))
|
|
|
|
|
{
|
|
|
|
|
json.removeMember("$schema");
|
|
|
|
|
}
|
|
|
|
|
return json;
|
|
|
|
|
}
|
2020-11-16 16:37:19 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void SerializationTests::GlobalSettings()
|
|
|
|
|
{
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view globalsString{ R"(
|
2020-11-16 16:37:19 -08:00
|
|
|
{
|
|
|
|
|
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
|
|
|
|
|
|
|
|
|
"initialRows": 30,
|
|
|
|
|
"initialCols": 120,
|
|
|
|
|
"initialPosition": ",",
|
|
|
|
|
"launchMode": "default",
|
|
|
|
|
"alwaysOnTop": false,
|
|
|
|
|
"copyOnSelect": false,
|
|
|
|
|
"copyFormatting": "all",
|
|
|
|
|
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
|
|
|
|
|
|
|
|
|
|
"alwaysShowTabs": true,
|
|
|
|
|
"showTabsInTitlebar": true,
|
|
|
|
|
"showTerminalTitleInTitlebar": true,
|
|
|
|
|
"tabWidthMode": "equal",
|
|
|
|
|
"tabSwitcherMode": "mru",
|
|
|
|
|
|
|
|
|
|
"theme": "system",
|
|
|
|
|
"snapToGridOnResize": true,
|
|
|
|
|
"disableAnimations": false,
|
|
|
|
|
|
2021-10-28 17:38:23 +02:00
|
|
|
"trimPaste": true,
|
2020-11-16 16:37:19 -08:00
|
|
|
|
2026-05-04 10:43:34 -07:00
|
|
|
"warning.confirmOnClose": "automatic",
|
2024-09-25 10:10:24 -07:00
|
|
|
"warning.inputService" : true,
|
|
|
|
|
"warning.largePaste" : true,
|
2025-08-08 22:53:42 +02:00
|
|
|
"warning.multiLinePaste" : "automatic",
|
2024-09-25 10:10:24 -07:00
|
|
|
|
2024-06-03 17:23:51 -07:00
|
|
|
"actions": [],
|
|
|
|
|
"keybindings": []
|
2020-11-16 16:37:19 -08:00
|
|
|
})" };
|
|
|
|
|
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view smallGlobalsString{ R"(
|
2020-11-16 16:37:19 -08:00
|
|
|
{
|
2021-05-20 14:44:04 -04:00
|
|
|
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
2024-06-03 17:23:51 -07:00
|
|
|
"actions": [],
|
|
|
|
|
"keybindings": []
|
2020-11-16 16:37:19 -08:00
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
RoundtripTest<implementation::GlobalAppSettings>(globalsString);
|
|
|
|
|
RoundtripTest<implementation::GlobalAppSettings>(smallGlobalsString);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::Profile()
|
|
|
|
|
{
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view profileString{ R"(
|
2020-11-16 16:37:19 -08:00
|
|
|
{
|
|
|
|
|
"name": "Windows PowerShell",
|
|
|
|
|
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
|
|
|
|
|
|
|
|
|
"commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
|
|
|
|
"startingDirectory": "%USERPROFILE%",
|
|
|
|
|
|
|
|
|
|
"icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
|
|
|
|
|
"hidden": false,
|
|
|
|
|
|
|
|
|
|
"tabTitle": "Cool Tab",
|
|
|
|
|
"suppressApplicationTitle": false,
|
|
|
|
|
|
2021-07-01 10:08:46 -07:00
|
|
|
"font": {
|
|
|
|
|
"face": "Cascadia Mono",
|
2024-05-01 19:09:20 +02:00
|
|
|
"size": 12,
|
2021-07-01 10:08:46 -07:00
|
|
|
"weight": "normal"
|
|
|
|
|
},
|
2020-11-16 16:37:19 -08:00
|
|
|
"padding": "8, 8, 8, 8",
|
|
|
|
|
"antialiasingMode": "grayscale",
|
|
|
|
|
|
|
|
|
|
"cursorShape": "bar",
|
|
|
|
|
"cursorColor": "#CCBBAA",
|
|
|
|
|
"cursorHeight": 10,
|
|
|
|
|
|
|
|
|
|
"altGrAliasing": true,
|
|
|
|
|
|
|
|
|
|
"colorScheme": "Campbell",
|
|
|
|
|
"tabColor": "#0C0C0C",
|
|
|
|
|
"foreground": "#AABBCC",
|
|
|
|
|
"background": "#BBCCAA",
|
|
|
|
|
"selectionBackground": "#CCAABB",
|
|
|
|
|
|
|
|
|
|
"useAcrylic": false,
|
2021-10-05 20:21:03 +02:00
|
|
|
"opacity": 50,
|
2020-11-16 16:37:19 -08:00
|
|
|
|
|
|
|
|
"backgroundImage": "made_you_look.jpeg",
|
|
|
|
|
"backgroundImageStretchMode": "uniformToFill",
|
|
|
|
|
"backgroundImageAlignment": "center",
|
2024-05-01 19:09:20 +02:00
|
|
|
"backgroundImageOpacity": 1,
|
2020-11-16 16:37:19 -08:00
|
|
|
|
|
|
|
|
"scrollbarState": "visible",
|
|
|
|
|
"snapOnInput": true,
|
|
|
|
|
"historySize": 9001,
|
|
|
|
|
|
|
|
|
|
"closeOnExit": "graceful",
|
2023-04-12 00:01:11 +01:00
|
|
|
"experimental.retroTerminalEffect": false,
|
|
|
|
|
"environment":
|
|
|
|
|
{
|
|
|
|
|
"KEY_1": "VALUE_1",
|
|
|
|
|
"KEY_2": "%KEY_1%",
|
|
|
|
|
"KEY_3": "%PATH%"
|
|
|
|
|
}
|
2020-11-16 16:37:19 -08:00
|
|
|
})" };
|
|
|
|
|
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view smallProfileString{ R"(
|
2020-11-16 16:37:19 -08:00
|
|
|
{
|
|
|
|
|
"name": "Custom Profile"
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// Setting "tabColor" to null tests two things:
|
|
|
|
|
// - null should count as an explicit user-set value, not falling back to the parent's value
|
|
|
|
|
// - null should be acceptable even though we're working with colors
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view weirdProfileString{ R"(
|
2020-11-16 16:37:19 -08:00
|
|
|
{
|
2021-05-20 14:44:04 -04:00
|
|
|
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
|
2020-11-16 16:37:19 -08:00
|
|
|
"name": "Weird Profile",
|
2021-05-20 14:44:04 -04:00
|
|
|
"hidden": false,
|
2020-11-16 16:37:19 -08:00
|
|
|
"tabColor": null,
|
|
|
|
|
"foreground": null,
|
|
|
|
|
"source": "local"
|
|
|
|
|
})" };
|
|
|
|
|
|
2024-02-29 11:00:04 -06:00
|
|
|
static constexpr std::string_view profileWithIcon{ R"(
|
|
|
|
|
{
|
|
|
|
|
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
|
|
|
|
|
"name": "profileWithIcon",
|
|
|
|
|
"hidden": false,
|
|
|
|
|
"icon": "foo.png"
|
|
|
|
|
})" };
|
|
|
|
|
static constexpr std::string_view profileWithNullIcon{ R"(
|
|
|
|
|
{
|
|
|
|
|
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
|
|
|
|
|
"name": "profileWithNullIcon",
|
|
|
|
|
"hidden": false,
|
|
|
|
|
"icon": null
|
|
|
|
|
})" };
|
|
|
|
|
static constexpr std::string_view profileWithNoIcon{ R"(
|
|
|
|
|
{
|
|
|
|
|
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
|
|
|
|
|
"name": "profileWithNoIcon",
|
|
|
|
|
"hidden": false,
|
|
|
|
|
"icon": "none"
|
|
|
|
|
})" };
|
|
|
|
|
|
2020-11-16 16:37:19 -08:00
|
|
|
RoundtripTest<implementation::Profile>(profileString);
|
|
|
|
|
RoundtripTest<implementation::Profile>(smallProfileString);
|
|
|
|
|
RoundtripTest<implementation::Profile>(weirdProfileString);
|
2024-02-29 11:00:04 -06:00
|
|
|
RoundtripTest<implementation::Profile>(profileWithIcon);
|
|
|
|
|
RoundtripTest<implementation::Profile>(profileWithNullIcon);
|
|
|
|
|
RoundtripTest<implementation::Profile>(profileWithNoIcon);
|
2020-11-16 16:37:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::ColorScheme()
|
|
|
|
|
{
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view schemeString{ R"({
|
2020-11-16 16:37:19 -08:00
|
|
|
"name": "Campbell",
|
|
|
|
|
|
|
|
|
|
"cursorColor": "#FFFFFF",
|
|
|
|
|
"selectionBackground": "#131313",
|
|
|
|
|
|
|
|
|
|
"background": "#0C0C0C",
|
|
|
|
|
"foreground": "#F2F2F2",
|
|
|
|
|
|
|
|
|
|
"black": "#0C0C0C",
|
|
|
|
|
"blue": "#0037DA",
|
|
|
|
|
"cyan": "#3A96DD",
|
|
|
|
|
"green": "#13A10E",
|
|
|
|
|
"purple": "#881798",
|
|
|
|
|
"red": "#C50F1F",
|
|
|
|
|
"white": "#CCCCCC",
|
|
|
|
|
"yellow": "#C19C00",
|
|
|
|
|
"brightBlack": "#767676",
|
|
|
|
|
"brightBlue": "#3B78FF",
|
|
|
|
|
"brightCyan": "#61D6D6",
|
|
|
|
|
"brightGreen": "#16C60C",
|
|
|
|
|
"brightPurple": "#B4009E",
|
|
|
|
|
"brightRed": "#E74856",
|
|
|
|
|
"brightWhite": "#F2F2F2",
|
|
|
|
|
"brightYellow": "#F9F1A5"
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
RoundtripTest<implementation::ColorScheme>(schemeString);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-20 14:44:04 -04:00
|
|
|
void SerializationTests::Actions()
|
|
|
|
|
{
|
2021-06-10 14:25:27 -04:00
|
|
|
// simple command
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString1{ R"([
|
2024-06-03 17:23:51 -07:00
|
|
|
{ "command": "paste", "id": "Test.Paste" }
|
2021-05-20 14:44:04 -04:00
|
|
|
])" };
|
|
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// complex command
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString2A{ R"([
|
2024-06-03 17:23:51 -07:00
|
|
|
{ "command": { "action": "setTabColor" }, "id": "Test.SetTabColor" }
|
2021-05-20 14:44:04 -04:00
|
|
|
])" };
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString2B{ R"([
|
2024-06-03 17:23:51 -07:00
|
|
|
{ "command": { "action": "setTabColor", "color": "#112233" }, "id": "Test.SetTabColor112233" }
|
2021-05-20 14:44:04 -04:00
|
|
|
])" };
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString2C{ R"([
|
2024-06-03 17:23:51 -07:00
|
|
|
{ "command": { "action": "copy" }, "id": "Test.Copy" },
|
|
|
|
|
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" }, "id": "Test.CopyWithArgs" }
|
2021-05-20 14:44:04 -04:00
|
|
|
])" };
|
|
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// simple command with key chords
|
2024-06-03 17:23:51 -07:00
|
|
|
static constexpr std::string_view actionsString3{ R"({ "actions": [
|
|
|
|
|
{ "command": "toggleAlwaysOnTop", "id": "Test.ToggleAlwaysOnTop" } ],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{ "keys": "ctrl+a", "id": "Test.ToggleAlwaysOnTop" },
|
|
|
|
|
{ "keys": "ctrl+b", "id": "Test.ToggleAlwaysOnTop" } ]})" };
|
2021-05-20 14:44:04 -04:00
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// complex command with key chords
|
2024-06-03 17:23:51 -07:00
|
|
|
static constexpr std::string_view actionsString4A{ R"({ "actions":[
|
|
|
|
|
{ "command": { "action": "adjustFontSize", "delta": 1 }, "id": "Test.EnlargeFont" } ],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{ "keys": "ctrl+c", "id": "Test.EnlargeFont" },
|
|
|
|
|
{ "keys": "ctrl+d", "id": "Test.EnlargeFont" } ]})" };
|
2021-05-20 14:44:04 -04:00
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// command with name and icon and multiple key chords
|
2024-06-03 17:23:51 -07:00
|
|
|
static constexpr std::string_view actionsString5{ R"({ "actions":[
|
|
|
|
|
{ "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "id": "Test.ScrollToTop" } ],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{ "id": "Test.ScrollToTop", "keys": "ctrl+f" },
|
|
|
|
|
{ "id": "Test.ScrollToTop", "keys": "ctrl+e" } ]})" };
|
2021-05-20 14:44:04 -04:00
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// complex command with new terminal args
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString6{ R"([
|
2024-06-03 17:23:51 -07:00
|
|
|
{ "command": { "action": "newTab", "index": 0 }, "id": "Test.NewTerminal" },
|
2021-05-20 14:44:04 -04:00
|
|
|
])" };
|
|
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// complex command with meaningful null arg
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString7{ R"([
|
2024-06-03 17:23:51 -07:00
|
|
|
{ "command": { "action": "renameWindow", "name": null }, "id": "Test.MeaningfulNull" }
|
2021-05-20 14:44:04 -04:00
|
|
|
])" };
|
|
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// nested command
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString8{ R"([
|
2021-05-20 14:44:04 -04:00
|
|
|
{
|
|
|
|
|
"name": "Change font size...",
|
|
|
|
|
"commands": [
|
2024-05-01 19:09:20 +02:00
|
|
|
{ "command": { "action": "adjustFontSize", "delta": 1 } },
|
|
|
|
|
{ "command": { "action": "adjustFontSize", "delta": -1 } },
|
2021-05-20 14:44:04 -04:00
|
|
|
{ "command": "resetFontSize" },
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
])" };
|
|
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// iterable command
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString9A{ R"([
|
2021-05-20 14:44:04 -04:00
|
|
|
{
|
|
|
|
|
"name": "New tab",
|
|
|
|
|
"commands": [
|
|
|
|
|
{
|
|
|
|
|
"iterateOn": "profiles",
|
|
|
|
|
"icon": "${profile.icon}",
|
|
|
|
|
"name": "${profile.name}",
|
|
|
|
|
"command": { "action": "newTab", "profile": "${profile.name}" }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
])" };
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString9B{ R"([
|
2021-05-20 14:44:04 -04:00
|
|
|
{
|
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
|
|
|
"commands":
|
2021-05-20 14:44:04 -04:00
|
|
|
[
|
|
|
|
|
{
|
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
|
|
|
"command":
|
2021-05-20 14:44:04 -04:00
|
|
|
{
|
|
|
|
|
"action": "sendInput",
|
|
|
|
|
"input": "${profile.name}"
|
|
|
|
|
},
|
|
|
|
|
"iterateOn": "profiles"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"name": "Send Input ..."
|
|
|
|
|
}
|
|
|
|
|
])" };
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString9C{ R""([
|
2021-05-20 14:44:04 -04:00
|
|
|
{
|
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
|
|
|
"commands":
|
2021-05-20 14:44:04 -04:00
|
|
|
[
|
|
|
|
|
{
|
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
|
|
|
"commands":
|
2021-05-20 14:44:04 -04:00
|
|
|
[
|
|
|
|
|
{
|
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
|
|
|
"command":
|
2021-05-20 14:44:04 -04:00
|
|
|
{
|
|
|
|
|
"action": "sendInput",
|
|
|
|
|
"input": "${profile.name} ${scheme.name}"
|
|
|
|
|
},
|
|
|
|
|
"iterateOn": "schemes"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"iterateOn": "profiles",
|
|
|
|
|
"name": "nest level (${profile.name})"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"name": "Send Input (Evil) ..."
|
|
|
|
|
}
|
|
|
|
|
])"" };
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view actionsString9D{ R""([
|
2021-06-10 14:25:27 -04:00
|
|
|
{
|
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
|
|
|
"command":
|
2021-06-10 14:25:27 -04:00
|
|
|
{
|
|
|
|
|
"action": "newTab",
|
|
|
|
|
"profile": "${profile.name}"
|
|
|
|
|
},
|
|
|
|
|
"icon": "${profile.icon}",
|
|
|
|
|
"iterateOn": "profiles",
|
|
|
|
|
"name": "${profile.name}: New tab"
|
|
|
|
|
}
|
|
|
|
|
])"" };
|
2021-05-20 14:44:04 -04:00
|
|
|
|
2021-06-10 14:25:27 -04:00
|
|
|
// unbound command
|
2024-06-03 17:23:51 -07:00
|
|
|
static constexpr std::string_view actionsString10{ R"({ "actions": [],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{ "id": null, "keys": "ctrl+c" } ]})" };
|
2021-05-20 14:44:04 -04:00
|
|
|
|
|
|
|
|
Log::Comment(L"simple command");
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString1);
|
|
|
|
|
|
|
|
|
|
Log::Comment(L"complex commands");
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString2A);
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString2B);
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString2C);
|
|
|
|
|
|
2024-06-03 17:23:51 -07:00
|
|
|
// ActionMap has effectively 2 "to json" calls we need to make, one for the actions and one for the keybindings
|
|
|
|
|
// So we cannot use RoundtripTest<ActionMap> for actions + keychords, just use RoundTripTest<GlobalAppSettings>
|
2021-05-20 14:44:04 -04:00
|
|
|
Log::Comment(L"simple command with key chords");
|
2024-06-03 17:23:51 -07:00
|
|
|
RoundtripTest<implementation::GlobalAppSettings>(actionsString3);
|
2021-05-20 14:44:04 -04:00
|
|
|
|
|
|
|
|
Log::Comment(L"complex commands with key chords");
|
2024-06-03 17:23:51 -07:00
|
|
|
RoundtripTest<implementation::GlobalAppSettings>(actionsString4A);
|
2021-05-20 14:44:04 -04:00
|
|
|
|
|
|
|
|
Log::Comment(L"command with name and icon and multiple key chords");
|
2024-06-03 17:23:51 -07:00
|
|
|
RoundtripTest<implementation::GlobalAppSettings>(actionsString5);
|
2021-05-20 14:44:04 -04:00
|
|
|
|
|
|
|
|
Log::Comment(L"complex command with new terminal args");
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString6);
|
|
|
|
|
|
|
|
|
|
Log::Comment(L"complex command with meaningful null arg");
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString7);
|
|
|
|
|
|
|
|
|
|
Log::Comment(L"nested command");
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString8);
|
|
|
|
|
|
|
|
|
|
Log::Comment(L"iterable command");
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString9A);
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString9B);
|
|
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString9C);
|
2021-06-10 14:25:27 -04:00
|
|
|
RoundtripTest<implementation::ActionMap>(actionsString9D);
|
2021-05-20 14:44:04 -04:00
|
|
|
|
|
|
|
|
Log::Comment(L"unbound command");
|
2024-06-03 17:23:51 -07:00
|
|
|
RoundtripTest<implementation::GlobalAppSettings>(actionsString10);
|
2021-05-20 14:44:04 -04:00
|
|
|
}
|
|
|
|
|
|
2020-11-16 16:37:19 -08:00
|
|
|
void SerializationTests::CascadiaSettings()
|
|
|
|
|
{
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view settingsString{ R"({
|
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
|
|
|
"$help" : "https://aka.ms/terminal-documentation",
|
|
|
|
|
"$schema" : "https://aka.ms/terminal-profiles-schema",
|
|
|
|
|
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
|
|
|
|
|
"disabledProfileSources": [ "Windows.Terminal.Wsl" ],
|
2023-01-19 13:12:40 -06:00
|
|
|
"newTabMenu":
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"type": "remainingProfiles"
|
|
|
|
|
}
|
|
|
|
|
],
|
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
|
|
|
"profiles": {
|
|
|
|
|
"defaults": {
|
|
|
|
|
"font": {
|
|
|
|
|
"face": "Zamora Code"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"list": [
|
|
|
|
|
{
|
|
|
|
|
"font": { "face": "Cascadia Code" },
|
|
|
|
|
"guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
|
|
|
|
|
"name": "HowettShell"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"hidden": true,
|
|
|
|
|
"guid": "{c08b0496-e71c-5503-b84e-3af7a7a6d2a7}",
|
|
|
|
|
"name": "BhojwaniShell"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"antialiasingMode": "aliased",
|
|
|
|
|
"guid": "{fe9df758-ac22-5c20-922d-c7766cdd13af}",
|
|
|
|
|
"name": "NiksaShell"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"schemes": [
|
|
|
|
|
{
|
|
|
|
|
"name": "Cinnamon Roll",
|
|
|
|
|
|
|
|
|
|
"cursorColor": "#FFFFFD",
|
|
|
|
|
"selectionBackground": "#FFFFFF",
|
|
|
|
|
|
|
|
|
|
"background": "#3C0315",
|
|
|
|
|
"foreground": "#FFFFFD",
|
|
|
|
|
|
|
|
|
|
"black": "#282A2E",
|
|
|
|
|
"blue": "#0170C5",
|
|
|
|
|
"cyan": "#3F8D83",
|
|
|
|
|
"green": "#76AB23",
|
|
|
|
|
"purple": "#7D498F",
|
|
|
|
|
"red": "#BD0940",
|
|
|
|
|
"white": "#FFFFFD",
|
|
|
|
|
"yellow": "#E0DE48",
|
|
|
|
|
"brightBlack": "#676E7A",
|
|
|
|
|
"brightBlue": "#5C98C5",
|
|
|
|
|
"brightCyan": "#8ABEB7",
|
|
|
|
|
"brightGreen": "#B5D680",
|
|
|
|
|
"brightPurple": "#AC79BB",
|
|
|
|
|
"brightRed": "#BD6D85",
|
|
|
|
|
"brightWhite": "#FFFFFD",
|
|
|
|
|
"brightYellow": "#FFFD76"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"actions": [
|
2024-06-03 17:23:51 -07:00
|
|
|
{ "command": { "action": "sendInput", "input": "VT Griese Mode" }, "id": "Test.SendInput" }
|
|
|
|
|
],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{ "id": "Test.SendInput", "keys": "ctrl+k" }
|
2022-07-27 14:39:51 -05:00
|
|
|
],
|
|
|
|
|
"theme": "system",
|
|
|
|
|
"themes": []
|
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
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsString) };
|
2020-11-16 16:37:19 -08:00
|
|
|
|
|
|
|
|
const auto result{ settings->ToJson() };
|
2024-02-26 12:27:57 -08:00
|
|
|
VERIFY_ARE_EQUAL(toString(removeSchema(VerifyParseSucceeded(settingsString))),
|
|
|
|
|
toString(removeSchema(result)));
|
2020-11-16 16:37:19 -08:00
|
|
|
}
|
2021-07-01 10:08:46 -07:00
|
|
|
|
|
|
|
|
void SerializationTests::LegacyFontSettings()
|
|
|
|
|
{
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view profileString{ R"(
|
2021-07-01 10:08:46 -07:00
|
|
|
{
|
|
|
|
|
"name": "Profile with legacy font settings",
|
|
|
|
|
|
|
|
|
|
"fontFace": "Cascadia Mono",
|
2024-05-01 19:09:20 +02:00
|
|
|
"fontSize": 12,
|
2021-07-01 10:08:46 -07:00
|
|
|
"fontWeight": "normal"
|
|
|
|
|
})" };
|
|
|
|
|
|
2021-10-05 20:21:03 +02:00
|
|
|
static constexpr std::string_view expectedOutput{ R"(
|
2021-07-01 10:08:46 -07:00
|
|
|
{
|
|
|
|
|
"name": "Profile with legacy font settings",
|
|
|
|
|
|
|
|
|
|
"font": {
|
|
|
|
|
"face": "Cascadia Mono",
|
2024-05-01 19:09:20 +02:00
|
|
|
"size": 12,
|
2021-07-01 10:08:46 -07:00
|
|
|
"weight": "normal"
|
|
|
|
|
}
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
const auto json{ VerifyParseSucceeded(profileString) };
|
|
|
|
|
const auto settings{ implementation::Profile::FromJson(json) };
|
|
|
|
|
const auto result{ settings->ToJson() };
|
|
|
|
|
|
|
|
|
|
const auto jsonOutput{ VerifyParseSucceeded(expectedOutput) };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(jsonOutput), toString(result));
|
|
|
|
|
}
|
Allow inheriting env vars from `wt` again and other env var changes too (#15897)
This PR is a few things:
* part the first: Convert the `compatibility.reloadEnvironmentVariables`
setting to a per-profile one.
* The settings should migrate it from the user's old global place to the
new one.
* We also added it to "Profile>Advanced" while I was here.
* Adds a new pair of commandline flags to `new-tab` and `split-pane`:
`--inheritEnvironment` / `--reloadEnvironment`
* On `wt` launch, bundle the entire environment that `wt` was spawned
with, and put it into the `Remoting.CommandlineArgs`, and give them to
the monarch (and ultimately, down to `TerminalPage` with the
`AppCommandlineArgs`). DO THIS ALWAYS.
* As a part of this, we’ll default to _reloading_ if there’s no explicit
commandline set, and _inheriting_ if there is.
* For example, `wt -- cmd` would inherit, and `wt -p “Command Prompt”`
would reload.[^1]
* This is a little wacky, but we’re trying to separate out the
intentions here:
* `wt -- cmd` feels like “I want to run cmd.exe (in a terminal tab)”.
That feels like the user would _like_ environment variables from the
calling process. They’re doing something more manual, so they get more
refined control over it.
* `wt` (or `wt -p “Command Prompt”`) is more like, “I want to run the
Terminal (or, my Command Prompt profile) using whatever the Terminal
would normally do”. So that feels more like a situation where it should
just reload by default. (Of course, this will respect their settings
here)
## References and Relevant Issues
https://github.com/microsoft/terminal/issues/15496#issuecomment-1692450231
has more notes.
## Detailed Description of the Pull Request / Additional comments
This is so VERY much plumbing. I'll try to leave comments in the
interesting parts.
## PR Checklist
- [x] This is not _all_ of #15496. We're also going to do a `-E foo=bar`
arg on top of this.
- [x] Tests added/passed
- [x] Schema updated
[^1]: In both these cases, plus the `environment` setting, of course.
2023-09-19 15:03:24 -05:00
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripReloadEnvVars()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"compatibility.reloadEnvironmentVariables": false,
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"historySize": 1,
|
|
|
|
|
"commandline": "cmd.exe"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": "closePane",
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles":
|
|
|
|
|
{
|
|
|
|
|
"defaults":
|
|
|
|
|
{
|
|
|
|
|
"compatibility.reloadEnvironmentVariables": false
|
|
|
|
|
},
|
|
|
|
|
"list":
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"historySize": 1,
|
|
|
|
|
"commandline": "cmd.exe"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": "closePane",
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Allow inheriting env vars from `wt` again and other env var changes too (#15897)
This PR is a few things:
* part the first: Convert the `compatibility.reloadEnvironmentVariables`
setting to a per-profile one.
* The settings should migrate it from the user's old global place to the
new one.
* We also added it to "Profile>Advanced" while I was here.
* Adds a new pair of commandline flags to `new-tab` and `split-pane`:
`--inheritEnvironment` / `--reloadEnvironment`
* On `wt` launch, bundle the entire environment that `wt` was spawned
with, and put it into the `Remoting.CommandlineArgs`, and give them to
the monarch (and ultimately, down to `TerminalPage` with the
`AppCommandlineArgs`). DO THIS ALWAYS.
* As a part of this, we’ll default to _reloading_ if there’s no explicit
commandline set, and _inheriting_ if there is.
* For example, `wt -- cmd` would inherit, and `wt -p “Command Prompt”`
would reload.[^1]
* This is a little wacky, but we’re trying to separate out the
intentions here:
* `wt -- cmd` feels like “I want to run cmd.exe (in a terminal tab)”.
That feels like the user would _like_ environment variables from the
calling process. They’re doing something more manual, so they get more
refined control over it.
* `wt` (or `wt -p “Command Prompt”`) is more like, “I want to run the
Terminal (or, my Command Prompt profile) using whatever the Terminal
would normally do”. So that feels more like a situation where it should
just reload by default. (Of course, this will respect their settings
here)
## References and Relevant Issues
https://github.com/microsoft/terminal/issues/15496#issuecomment-1692450231
has more notes.
## Detailed Description of the Pull Request / Additional comments
This is so VERY much plumbing. I'll try to leave comments in the
interesting parts.
## PR Checklist
- [x] This is not _all_ of #15496. We're also going to do a `-E foo=bar`
arg on top of this.
- [x] Tests added/passed
- [x] Schema updated
[^1]: In both these cases, plus the `environment` setting, of course.
2023-09-19 15:03:24 -05:00
|
|
|
oldLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
oldLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(oldLoader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto oldSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(oldLoader));
|
|
|
|
|
const auto oldResult{ oldSettings->ToJson() };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Allow inheriting env vars from `wt` again and other env var changes too (#15897)
This PR is a few things:
* part the first: Convert the `compatibility.reloadEnvironmentVariables`
setting to a per-profile one.
* The settings should migrate it from the user's old global place to the
new one.
* We also added it to "Profile>Advanced" while I was here.
* Adds a new pair of commandline flags to `new-tab` and `split-pane`:
`--inheritEnvironment` / `--reloadEnvironment`
* On `wt` launch, bundle the entire environment that `wt` was spawned
with, and put it into the `Remoting.CommandlineArgs`, and give them to
the monarch (and ultimately, down to `TerminalPage` with the
`AppCommandlineArgs`). DO THIS ALWAYS.
* As a part of this, we’ll default to _reloading_ if there’s no explicit
commandline set, and _inheriting_ if there is.
* For example, `wt -- cmd` would inherit, and `wt -p “Command Prompt”`
would reload.[^1]
* This is a little wacky, but we’re trying to separate out the
intentions here:
* `wt -- cmd` feels like “I want to run cmd.exe (in a terminal tab)”.
That feels like the user would _like_ environment variables from the
calling process. They’re doing something more manual, so they get more
refined control over it.
* `wt` (or `wt -p “Command Prompt”`) is more like, “I want to run the
Terminal (or, my Command Prompt profile) using whatever the Terminal
would normally do”. So that feels more like a situation where it should
just reload by default. (Of course, this will respect their settings
here)
## References and Relevant Issues
https://github.com/microsoft/terminal/issues/15496#issuecomment-1692450231
has more notes.
## Detailed Description of the Pull Request / Additional comments
This is so VERY much plumbing. I'll try to leave comments in the
interesting parts.
## PR Checklist
- [x] This is not _all_ of #15496. We're also going to do a `-E foo=bar`
arg on top of this.
- [x] Tests added/passed
- [x] Schema updated
[^1]: In both these cases, plus the `environment` setting, of course.
2023-09-19 15:03:24 -05:00
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
newLoader.FixupUserSettings();
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::DontRoundtripNoReloadEnvVars()
|
|
|
|
|
{
|
|
|
|
|
// Kinda like the above test, but confirming that _nothing_ happens if
|
|
|
|
|
// we don't have a setting to migrate.
|
|
|
|
|
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"historySize": 1,
|
|
|
|
|
"commandline": "cmd.exe"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": "closePane",
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Allow inheriting env vars from `wt` again and other env var changes too (#15897)
This PR is a few things:
* part the first: Convert the `compatibility.reloadEnvironmentVariables`
setting to a per-profile one.
* The settings should migrate it from the user's old global place to the
new one.
* We also added it to "Profile>Advanced" while I was here.
* Adds a new pair of commandline flags to `new-tab` and `split-pane`:
`--inheritEnvironment` / `--reloadEnvironment`
* On `wt` launch, bundle the entire environment that `wt` was spawned
with, and put it into the `Remoting.CommandlineArgs`, and give them to
the monarch (and ultimately, down to `TerminalPage` with the
`AppCommandlineArgs`). DO THIS ALWAYS.
* As a part of this, we’ll default to _reloading_ if there’s no explicit
commandline set, and _inheriting_ if there is.
* For example, `wt -- cmd` would inherit, and `wt -p “Command Prompt”`
would reload.[^1]
* This is a little wacky, but we’re trying to separate out the
intentions here:
* `wt -- cmd` feels like “I want to run cmd.exe (in a terminal tab)”.
That feels like the user would _like_ environment variables from the
calling process. They’re doing something more manual, so they get more
refined control over it.
* `wt` (or `wt -p “Command Prompt”`) is more like, “I want to run the
Terminal (or, my Command Prompt profile) using whatever the Terminal
would normally do”. So that feels more like a situation where it should
just reload by default. (Of course, this will respect their settings
here)
## References and Relevant Issues
https://github.com/microsoft/terminal/issues/15496#issuecomment-1692450231
has more notes.
## Detailed Description of the Pull Request / Additional comments
This is so VERY much plumbing. I'll try to leave comments in the
interesting parts.
## PR Checklist
- [x] This is not _all_ of #15496. We're also going to do a `-E foo=bar`
arg on top of this.
- [x] Tests added/passed
- [x] Schema updated
[^1]: In both these cases, plus the `environment` setting, of course.
2023-09-19 15:03:24 -05:00
|
|
|
oldLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
oldLoader.FinalizeLayering();
|
|
|
|
|
oldLoader.FixupUserSettings();
|
|
|
|
|
const auto oldSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(oldLoader));
|
|
|
|
|
const auto oldResult{ oldSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
Log::Comment(L"Now, create a _new_ settings object from the re-serialization of the first");
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader newLoader{ toString(oldResult), implementation::LoadStringResource(IDR_DEFAULTS) };
|
Allow inheriting env vars from `wt` again and other env var changes too (#15897)
This PR is a few things:
* part the first: Convert the `compatibility.reloadEnvironmentVariables`
setting to a per-profile one.
* The settings should migrate it from the user's old global place to the
new one.
* We also added it to "Profile>Advanced" while I was here.
* Adds a new pair of commandline flags to `new-tab` and `split-pane`:
`--inheritEnvironment` / `--reloadEnvironment`
* On `wt` launch, bundle the entire environment that `wt` was spawned
with, and put it into the `Remoting.CommandlineArgs`, and give them to
the monarch (and ultimately, down to `TerminalPage` with the
`AppCommandlineArgs`). DO THIS ALWAYS.
* As a part of this, we’ll default to _reloading_ if there’s no explicit
commandline set, and _inheriting_ if there is.
* For example, `wt -- cmd` would inherit, and `wt -p “Command Prompt”`
would reload.[^1]
* This is a little wacky, but we’re trying to separate out the
intentions here:
* `wt -- cmd` feels like “I want to run cmd.exe (in a terminal tab)”.
That feels like the user would _like_ environment variables from the
calling process. They’re doing something more manual, so they get more
refined control over it.
* `wt` (or `wt -p “Command Prompt”`) is more like, “I want to run the
Terminal (or, my Command Prompt profile) using whatever the Terminal
would normally do”. So that feels more like a situation where it should
just reload by default. (Of course, this will respect their settings
here)
## References and Relevant Issues
https://github.com/microsoft/terminal/issues/15496#issuecomment-1692450231
has more notes.
## Detailed Description of the Pull Request / Additional comments
This is so VERY much plumbing. I'll try to leave comments in the
interesting parts.
## PR Checklist
- [x] This is not _all_ of #15496. We're also going to do a `-E foo=bar`
arg on top of this.
- [x] Tests added/passed
- [x] Schema updated
[^1]: In both these cases, plus the `environment` setting, of course.
2023-09-19 15:03:24 -05:00
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
newLoader.FixupUserSettings();
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
VERIFY_IS_FALSE(newSettings->ProfileDefaults().HasReloadEnvironmentVariables(),
|
|
|
|
|
L"Ensure that the new settings object didn't find a reloadEnvironmentVariables");
|
|
|
|
|
}
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripUserModifiedColorSchemeCollision()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "profile1",
|
|
|
|
|
"colorScheme": "Tango Dark",
|
|
|
|
|
"guid": "{d0a65a9d-8665-4128-97a4-a581aa747aa7}"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"schemes": [
|
|
|
|
|
{
|
|
|
|
|
"background": "#121314",
|
|
|
|
|
"black": "#121314",
|
|
|
|
|
"blue": "#121314",
|
|
|
|
|
"brightBlack": "#121314",
|
|
|
|
|
"brightBlue": "#121314",
|
|
|
|
|
"brightCyan": "#121314",
|
|
|
|
|
"brightGreen": "#121314",
|
|
|
|
|
"brightPurple": "#121314",
|
|
|
|
|
"brightRed": "#121314",
|
|
|
|
|
"brightWhite": "#121314",
|
|
|
|
|
"brightYellow": "#121314",
|
|
|
|
|
"cursorColor": "#121314",
|
|
|
|
|
"cyan": "#121314",
|
|
|
|
|
"foreground": "#121314",
|
|
|
|
|
"green": "#121314",
|
|
|
|
|
"name": "Campbell",
|
|
|
|
|
"purple": "#121314",
|
|
|
|
|
"red": "#121314",
|
|
|
|
|
"selectionBackground": "#121314",
|
|
|
|
|
"white": "#121314",
|
|
|
|
|
"yellow": "#121314"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"background": "#000000",
|
|
|
|
|
"black": "#000000",
|
|
|
|
|
"blue": "#3465A4",
|
|
|
|
|
"brightBlack": "#555753",
|
|
|
|
|
"brightBlue": "#729FCF",
|
|
|
|
|
"brightCyan": "#34E2E2",
|
|
|
|
|
"brightGreen": "#8AE234",
|
|
|
|
|
"brightPurple": "#AD7FA8",
|
|
|
|
|
"brightRed": "#EF2929",
|
|
|
|
|
"brightWhite": "#EEEEEC",
|
|
|
|
|
"brightYellow": "#FCE94F",
|
|
|
|
|
"cursorColor": "#FFFFFF",
|
|
|
|
|
"cyan": "#06989A",
|
|
|
|
|
"foreground": "#D3D7CF",
|
|
|
|
|
"green": "#4E9A06",
|
|
|
|
|
"name": "Tango Dark",
|
|
|
|
|
"purple": "#75507B",
|
|
|
|
|
"red": "#CC0000",
|
|
|
|
|
"selectionBackground": "#FFFFFF",
|
|
|
|
|
"white": "#D3D7CF",
|
|
|
|
|
"yellow": "#C4A000"
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// Key differences: one fewer color scheme (Tango Dark has been deleted) and defaults.colorScheme is set.
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"-(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles":
|
|
|
|
|
{
|
|
|
|
|
"defaults": {
|
|
|
|
|
"colorScheme": "Campbell (modified)"
|
|
|
|
|
},
|
|
|
|
|
"list":
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "profile1",
|
|
|
|
|
"colorScheme": "Tango Dark",
|
|
|
|
|
"guid": "{d0a65a9d-8665-4128-97a4-a581aa747aa7}"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"actions": [ ],
|
|
|
|
|
"schemes": [
|
|
|
|
|
{
|
|
|
|
|
"background": "#121314",
|
|
|
|
|
"black": "#121314",
|
|
|
|
|
"blue": "#121314",
|
|
|
|
|
"brightBlack": "#121314",
|
|
|
|
|
"brightBlue": "#121314",
|
|
|
|
|
"brightCyan": "#121314",
|
|
|
|
|
"brightGreen": "#121314",
|
|
|
|
|
"brightPurple": "#121314",
|
|
|
|
|
"brightRed": "#121314",
|
|
|
|
|
"brightWhite": "#121314",
|
|
|
|
|
"brightYellow": "#121314",
|
|
|
|
|
"cursorColor": "#121314",
|
|
|
|
|
"cyan": "#121314",
|
|
|
|
|
"foreground": "#121314",
|
|
|
|
|
"green": "#121314",
|
|
|
|
|
"name": "Campbell",
|
|
|
|
|
"purple": "#121314",
|
|
|
|
|
"red": "#121314",
|
|
|
|
|
"selectionBackground": "#121314",
|
|
|
|
|
"white": "#121314",
|
|
|
|
|
"yellow": "#121314"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})-" };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
oldLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
oldLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(oldLoader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto oldSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(oldLoader));
|
|
|
|
|
const auto oldResult{ oldSettings->ToJson() };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
newLoader.FixupUserSettings();
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripUserModifiedColorSchemeCollisionUnusedByProfiles()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"schemes": [
|
|
|
|
|
{
|
|
|
|
|
"background": "#111111",
|
|
|
|
|
"black": "#111111",
|
|
|
|
|
"blue": "#111111",
|
|
|
|
|
"brightBlack": "#111111",
|
|
|
|
|
"brightBlue": "#111111",
|
|
|
|
|
"brightCyan": "#111111",
|
|
|
|
|
"brightGreen": "#111111",
|
|
|
|
|
"brightPurple": "#111111",
|
|
|
|
|
"brightRed": "#111111",
|
|
|
|
|
"brightWhite": "#111111",
|
|
|
|
|
"brightYellow": "#111111",
|
|
|
|
|
"cursorColor": "#111111",
|
|
|
|
|
"cyan": "#111111",
|
|
|
|
|
"foreground": "#111111",
|
|
|
|
|
"green": "#111111",
|
|
|
|
|
"name": "Tango Dark",
|
|
|
|
|
"purple": "#111111",
|
|
|
|
|
"red": "#111111",
|
|
|
|
|
"selectionBackground": "#111111",
|
|
|
|
|
"white": "#111111",
|
|
|
|
|
"yellow": "#111111"
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// Key differences: Tango Dark has been renamed; nothing else has changed
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"-(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles":
|
|
|
|
|
{
|
|
|
|
|
"list":
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"actions": [ ],
|
|
|
|
|
"schemes": [
|
|
|
|
|
{
|
|
|
|
|
"background": "#111111",
|
|
|
|
|
"black": "#111111",
|
|
|
|
|
"blue": "#111111",
|
|
|
|
|
"brightBlack": "#111111",
|
|
|
|
|
"brightBlue": "#111111",
|
|
|
|
|
"brightCyan": "#111111",
|
|
|
|
|
"brightGreen": "#111111",
|
|
|
|
|
"brightPurple": "#111111",
|
|
|
|
|
"brightRed": "#111111",
|
|
|
|
|
"brightWhite": "#111111",
|
|
|
|
|
"brightYellow": "#111111",
|
|
|
|
|
"cursorColor": "#111111",
|
|
|
|
|
"cyan": "#111111",
|
|
|
|
|
"foreground": "#111111",
|
|
|
|
|
"green": "#111111",
|
|
|
|
|
"name": "Tango Dark (modified)",
|
|
|
|
|
"purple": "#111111",
|
|
|
|
|
"red": "#111111",
|
|
|
|
|
"selectionBackground": "#111111",
|
|
|
|
|
"white": "#111111",
|
|
|
|
|
"yellow": "#111111"
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
})-" };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
oldLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
oldLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(oldLoader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto oldSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(oldLoader));
|
|
|
|
|
const auto oldResult{ oldSettings->ToJson() };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
newLoader.FixupUserSettings();
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripUserDeletedColorSchemeCollision()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"schemes": [
|
|
|
|
|
{
|
|
|
|
|
"name": "Tango Dark",
|
|
|
|
|
"foreground": "#D3D7CF",
|
|
|
|
|
"background": "#000000",
|
|
|
|
|
"cursorColor": "#FFFFFF",
|
|
|
|
|
"black": "#000000",
|
|
|
|
|
"red": "#CC0000",
|
|
|
|
|
"green": "#4E9A06",
|
|
|
|
|
"yellow": "#C4A000",
|
|
|
|
|
"blue": "#3465A4",
|
|
|
|
|
"purple": "#75507B",
|
|
|
|
|
"cyan": "#06989A",
|
|
|
|
|
"white": "#D3D7CF",
|
|
|
|
|
"brightBlack": "#555753",
|
|
|
|
|
"brightRed": "#EF2929",
|
|
|
|
|
"brightGreen": "#8AE234",
|
|
|
|
|
"brightYellow": "#FCE94F",
|
|
|
|
|
"brightBlue": "#729FCF",
|
|
|
|
|
"brightPurple": "#AD7FA8",
|
|
|
|
|
"brightCyan": "#34E2E2",
|
|
|
|
|
"brightWhite": "#EEEEEC"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// Key differences: Tango Dark has been deleted, as it was identical to the inbox one.
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"-(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles":
|
|
|
|
|
{
|
|
|
|
|
"list":
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"actions": [ ],
|
|
|
|
|
"schemes": [ ]
|
|
|
|
|
})-" };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
oldLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
oldLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(oldLoader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto oldSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(oldLoader));
|
|
|
|
|
const auto oldResult{ oldSettings->ToJson() };
|
|
|
|
|
|
2024-03-14 13:50:07 -07:00
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
Deduplicate identical inbox color schemes to heal user settings (#12800)
Up until now, we have treated inbox, fragment and user color schemes the
same: we load them all into one big map and when we save the settings
file we write them *all* out. It's been a big annoyance pretty much
forever.
In addition to cluttering the user's settings file, it prevents us from
making changes to the stock color schemes (like to change the cursor
color, or to adjust the colors in Tango Dark, or what have you) because
they're already copied in full in the user settings. It also means that
we need some special UI affordances for color schemes that you are
allowed to view but not to delete or rename.
We also have a funny hardcoded list of color scheme names and we use
that to determine whether they're "inbox" for UI purposes.
Because of all that, we are hesitant to add *more* color schemes to the
default set.
This pull request resolves all of those issues at once.
It:
- Adds an "origin" to color schemes indicating where they're from
(Inbox, Fragment, User, ...)
- Replaces the Edit UI with a much simpler version that pretty much only
has a "duplicate this color scheme to start editing it" button
- Deletes color schemes that we consider to be equivalent to inbox ones;
this allows us to finally disentangle the user's preferences from the
terminal's.
- Migrates all user settings that referred to schemes they may have
modified (even implicitly!) to their modified versions.
The equivalence check intentionally leaves out the cursor and selection
colors, so that we have the freedom to change them in the future.
The Origin is part of a new interface, `ISettingsModelObject`, which we
can use in the future for things like Themes and Actions.
2024-02-27 13:07:08 -06:00
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
newLoader.FixupUserSettings();
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
2024-04-17 17:30:25 -07:00
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripGenerateActionID()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
2024-04-29 07:57:47 -07:00
|
|
|
implementation::SettingsLoader loader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
loader.FixupUserSettings();
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto oldResult{ settings->ToJson() };
|
|
|
|
|
const auto sendInputCmd = settings->ActionMap().GetActionByKeyChord(KeyChord{ true, false, true, false, 87, 0 });
|
2024-04-17 17:30:25 -07:00
|
|
|
|
2024-04-29 07:57:47 -07:00
|
|
|
std::string_view expectedID{ R"(User.sendInput.)" SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH };
|
2024-04-17 17:30:25 -07:00
|
|
|
|
2024-04-29 07:57:47 -07:00
|
|
|
VERIFY_ARE_EQUAL(sendInputCmd.ID(), winrt::to_hstring(expectedID));
|
2024-04-17 17:30:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::NoGeneratedIDsForIterableAndNestedCommands()
|
|
|
|
|
{
|
|
|
|
|
// for iterable commands, nested commands, and user-defined actions that already have
|
|
|
|
|
// an ID, we do not need to generate an ID
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": "closePane",
|
|
|
|
|
"id": "thisIsMyClosePane"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"iterateOn": "profiles",
|
|
|
|
|
"icon": "${profile.icon}",
|
|
|
|
|
"name": "${profile.name}",
|
|
|
|
|
"command": { "action": "newTab", "profile": "${profile.name}" }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "Change font size...",
|
|
|
|
|
"commands": [
|
2024-05-01 19:09:20 +02:00
|
|
|
{ "command": { "action": "adjustFontSize", "delta": 1 } },
|
|
|
|
|
{ "command": { "action": "adjustFontSize", "delta": -1 } },
|
2024-04-17 17:30:25 -07:00
|
|
|
{ "command": "resetFontSize" },
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
oldLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
oldLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_FALSE(oldLoader.FixupUserSettings(), L"Validate that there is no need to write back to disk");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::GeneratedActionIDsEqualForIdenticalCommands()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view settingsJson1{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": { "action": "sendInput", "input": "this is some other input string" },
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// Both settings files define the same action, so the generated ID should be the same for both
|
|
|
|
|
static constexpr std::string_view settingsJson2{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": { "action": "sendInput", "input": "this is some other input string" },
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader loader1{ settingsJson1, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader1.MergeInboxIntoUserSettings();
|
|
|
|
|
loader1.FinalizeLayering();
|
2024-04-29 07:57:47 -07:00
|
|
|
loader1.FixupUserSettings();
|
2024-04-17 17:30:25 -07:00
|
|
|
const auto settings1 = winrt::make_self<implementation::CascadiaSettings>(std::move(loader1));
|
|
|
|
|
const auto result1{ settings1->ToJson() };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader loader2{ settingsJson2, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader2.MergeInboxIntoUserSettings();
|
|
|
|
|
loader2.FinalizeLayering();
|
2024-04-29 07:57:47 -07:00
|
|
|
loader2.FixupUserSettings();
|
2024-04-17 17:30:25 -07:00
|
|
|
const auto settings2 = winrt::make_self<implementation::CascadiaSettings>(std::move(loader2));
|
|
|
|
|
const auto result2{ settings2->ToJson() };
|
|
|
|
|
|
2024-04-29 07:57:47 -07:00
|
|
|
const auto sendInputCmd1 = settings1->ActionMap().GetActionByKeyChord(KeyChord{ true, false, true, false, 87, 0 });
|
|
|
|
|
const auto sendInputCmd2 = settings2->ActionMap().GetActionByKeyChord(KeyChord{ true, false, true, false, 87, 0 });
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(sendInputCmd1.ID(), sendInputCmd1.ID());
|
2024-04-17 17:30:25 -07:00
|
|
|
}
|
2024-06-03 17:23:51 -07:00
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripLegacyToModernActions()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"id": "Test.SendInput",
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"command": "unbound",
|
|
|
|
|
"keys": "ctrl+shift+x"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// modern style:
|
|
|
|
|
// - no "unbound" actions, these are just keybindings that have no id
|
|
|
|
|
// - no keys in actions, these are keybindings with an id
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"id": "Test.SendInput"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{
|
|
|
|
|
"id": "Test.SendInput",
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"id": null,
|
|
|
|
|
"keys": "ctrl+shift+x"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader loader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(loader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto oldResult{ settings->ToJson() };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_FALSE(newLoader.FixupUserSettings(), L"Validate that there is no need to write back to disk");
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripUserActionsSameAsInBoxAreRemoved()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"command": "paste",
|
|
|
|
|
"keys": "ctrl+shift+x"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// this action is the same as in inbox one,
|
|
|
|
|
// so we will delete this action from the user's file but retain the keybinding
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{
|
|
|
|
|
"id": "Terminal.PasteFromClipboard",
|
|
|
|
|
"keys": "ctrl+shift+x"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader loader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(loader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto oldResult{ settings->ToJson() };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_FALSE(newLoader.FixupUserSettings(), L"Validate that there is no need to write back to disk");
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::RoundtripActionsSameNameDifferentCommandsAreRetained()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"name": "mySendInput"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input 2" },
|
|
|
|
|
"name": "mySendInput"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// There are two different actions with the same name,
|
|
|
|
|
// ensure that both are kept but have different IDs generated for them
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "mySendInput",
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"id": "User.sendInput.)" SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH R"("
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "mySendInput",
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input 2" },
|
|
|
|
|
"id": "User.sendInput.)" SEND_INPUT2_ARCH_SPECIFIC_ACTION_HASH R"("
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader loader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(loader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto oldResult{ settings->ToJson() };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_FALSE(newLoader.FixupUserSettings(), L"Validate that there is no need to write back to disk");
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::MultipleActionsAreCollapsed()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view oldSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"icon": "myCoolIconPath.png",
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"keys": "ctrl+shift+w"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"keys": "ctrl+shift+x"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// modern style:
|
|
|
|
|
// - multiple action blocks whose purpose is simply to define more keybindings for the same action
|
|
|
|
|
// get collapsed into one action block, with the name and icon path preserved and have multiple keybindings instead
|
|
|
|
|
static constexpr std::string_view newSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"name": "foo",
|
|
|
|
|
"icon": "myCoolIconPath.png",
|
|
|
|
|
"command": { "action": "sendInput", "input": "just some input" },
|
|
|
|
|
"id": "User.sendInput.)" SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH R"("
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"keybindings": [
|
|
|
|
|
{
|
|
|
|
|
"keys": "ctrl+shift+w",
|
|
|
|
|
"id": "User.sendInput.)" SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH R"("
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"keys": "ctrl+shift+x",
|
|
|
|
|
"id": "User.sendInput.)" SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH R"("
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader loader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_TRUE(loader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk");
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto oldResult{ settings->ToJson() };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
newLoader.MergeInboxIntoUserSettings();
|
|
|
|
|
newLoader.FinalizeLayering();
|
|
|
|
|
VERIFY_IS_FALSE(newLoader.FixupUserSettings(), L"Validate that there is no need to write back to disk");
|
|
|
|
|
const auto newSettings = winrt::make_self<implementation::CascadiaSettings>(std::move(newLoader));
|
|
|
|
|
const auto newResult{ newSettings->ToJson() };
|
|
|
|
|
|
|
|
|
|
VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult));
|
|
|
|
|
}
|
Rewrite media resource handling (relative path icons, web URLs) (#19143)
This pull request broadly rewrites how we handle all media resources in
the Terminal settings model.
## What is a media resource?
A media resource is any JSON property that refers to a file on disk,
including:
- `icon` on profile
- `backgroundImage` on profile (appearance)
- `pixelShaderPath` and `pixelShaderImagePath` on profile (appearance)
- `icon` on command and the new tab menu entries
The last two bullet points were newly discovered during the course of
this work.
## Description of Changes
In every place the settings model used to store a string for a media
path, it now stores an `IMediaResource`.
A media resource must be _resolved_ before it's used. When resolved, it
can report whether it is `Ok` (found, valid) and what the final
normalized path was.
This allows the settings model to apply some new behaviors.
One of those new behaviors is resolving media paths _relative to the
JSON file that referred to them._ This means fragments and user settings
can now contain _local_ images, pixel shaders and more and refer to them
by filename.
Relative path support requires us to track the path from which every
media resource "container" was read[^2]. For "big" objects like Profile,
we track it directly in the object and for each layer. This means that
fragments **updating** a profile pass their relative base path into the
mix. For some of the entries such as those in `newTabMenu`, we just wing
it (#19191). For everything that is recursively owned by a parent that
has a path (say each Command inside an ActionMap), we pass it in from
the parent during media resolution.
During resolution, we now track _exactly which layer_ an icon,
background image, or pixel shader path came from and read the "base
path" from only that layer. The base path is not inherited.
Another new behavior is in the handling of web and other URLs.
Canonical and a few other WSL distributors had to resort to web URLs for
icons because we did not support loading them from the package. Julia
tried to use `ms-appx://JuliaPackageNameHere/path/to/icon` for the same
reason. Neither was intended, and of the two the second _should_ have
worked but never could[^1].
For both `http(s?)` URLs and `ms-appx://` URLs which specify a package
name, we now strip everything except the filename. As an example...
If my fragment specifies `https://example.net/assets/foo.ico`, and my
fragment was loaded from `C:\Fragments`, Terminal will look *only* at
`C:\Fragments\foo.ico`.
This works today for Julia (they put their icon in the fragment folder
hoping that one day we would support this.) It will require some work
from existing WSL distributors.
I'm told that this is similar to how XML schema documents work.
Now, icons are special. They support _Emoji_ and _Segoe Icons_. This PR
adds an early pass to avoid resolving anything that looks like an
emoji.
This PR intentionally expands the heuristic definition of an emoji. It
used to only cover 1-2 code unit emoji, which prevented the use of any
emoji more complicated than "man in business suite levitating."
An icon path will now be considered an emoji or symbol icon if it is
composed of a single grapheme cluster (as measured by ICU.)
This is not perfect, as it errs on the side of allowing too many
things... but each of those things is technically a single grapheme
cluster and is a perfectly legal FontIcon ;)
Profile icons are _even more special_ than icons. They have an
additional fallback behavior which we had to preserve. When a profile
icon fails validation, or is expressly set to `null`, we fall back to
the EXE specified in the command line.
Because we do this fallback during resolution, _and the icon may be
inherited by any higher profile,_ we can only resolve it against the
commandline at the same level as the failed or nulled icon.
Therefore, if you specify `icon: null` in your `defaults` profile, it
will only ever resolve to `cmd.exe` for any profile that inherits it
(unless you change `defaults.commandline`).
This change expands support for the magic keywords `desktopWallpaper`
and `none` to all media paths (yes, even `pixelShaderPath`... but also,
`pixelShaderImagePath`!) It also expands support for _environment
variables_ to all of those places. Yes, we had like forty different
handlers for different types of string path. They are now uniform.
## Resource Validation
Media resources which are not found are "rejected". If a rejected
resource lives in _user_ settings, we will generate a warning and
display it.
In the future, we could detect this in the Settings UI and display a
warning inline.
## Surprises
I learned that `Windows.Foundation.Uri` parses file paths into `file://`
URIs, but does not offer you a way to get the original file path back
out. If you pass `C:\hello world`, _`Uri.Path`_ will return
`/C:/hello%20world`. I kid you not.
As a workaround, we bail out of URL handling if the `:` is too close to
the start (indicating an absolute file path).
## Testing
I added a narow test hook in the media resource resolver, which is
removed completely by link-time code generation. It is a real joy.
The test cases are all new and hopefully comprehensive.
Closes #19075
Closes #16295
Closes #10359 (except it doesn't support fonts)
Supersedes #16949 somewhat (`WT_SETTINGS_DIR`)
Refs #18679
Refs #19215 (future work)
Refs #19201 (future work)
Refs #19191 (future work)
[^1]: Handling a `ms-appx` path requires us to _add their package to our
dependency graph_ for the entire duration during which the resource will
be used. For us, that could be any time (like opening the command
palette for the first time!)
[^2]: We don't bother tracking where the defaults came from, because we
control everything about them.
2025-08-05 15:47:50 -05:00
|
|
|
|
|
|
|
|
void SerializationTests::ProfileWithInvalidIcon()
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::string_view settingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"historySize": 1,
|
|
|
|
|
"commandline": "cmd.exe",
|
|
|
|
|
"icon": "c:\\this_icon_had_better_not_exist.tiff"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
implementation::SettingsLoader loader{ settingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto newResult{ settings->ToJson() };
|
|
|
|
|
|
|
|
|
|
// A profile that specifies an invalid icon will fall back to the commandline on load, but that should
|
|
|
|
|
// not be reflected back in settings.json as null *or* as the commandline. The value should be exactly
|
|
|
|
|
// what was written in the settings file.
|
|
|
|
|
VERIFY_ARE_EQUAL(R"(c:\this_icon_had_better_not_exist.tiff)", newResult["profiles"]["list"][0]["icon"].asString());
|
|
|
|
|
}
|
2026-04-29 10:16:30 -07:00
|
|
|
|
|
|
|
|
void SerializationTests::ModifyProfileSettingAndRoundtrip()
|
|
|
|
|
{
|
|
|
|
|
// Load settings, modify a profile setting via setter, serialize,
|
|
|
|
|
// and verify the JSON output reflects the change.
|
|
|
|
|
static constexpr std::string_view settingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"historySize": 1000,
|
|
|
|
|
"commandline": "cmd.exe"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsJson) };
|
|
|
|
|
|
|
|
|
|
// Verify initial value
|
|
|
|
|
VERIFY_ARE_EQUAL(1000, settings->AllProfiles().GetAt(0).HistorySize());
|
|
|
|
|
|
|
|
|
|
// Modify the setting
|
|
|
|
|
settings->AllProfiles().GetAt(0).HistorySize(5000);
|
|
|
|
|
VERIFY_ARE_EQUAL(5000, settings->AllProfiles().GetAt(0).HistorySize());
|
|
|
|
|
|
|
|
|
|
// Serialize and verify the change is reflected in JSON
|
|
|
|
|
const auto result{ settings->ToJson() };
|
|
|
|
|
VERIFY_ARE_EQUAL(5000, result["profiles"]["list"][0]["historySize"].asInt());
|
|
|
|
|
|
|
|
|
|
// Verify other settings are preserved
|
|
|
|
|
VERIFY_ARE_EQUAL("cmd.exe", result["profiles"]["list"][0]["commandline"].asString());
|
|
|
|
|
|
|
|
|
|
// Also verify: modify a setting that wasn't previously set
|
|
|
|
|
settings->AllProfiles().GetAt(0).TabTitle(L"NewTitle");
|
|
|
|
|
const auto result2{ settings->ToJson() };
|
|
|
|
|
VERIFY_ARE_EQUAL("NewTitle", result2["profiles"]["list"][0]["tabTitle"].asString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::ModifyGlobalSettingAndRoundtrip()
|
|
|
|
|
{
|
|
|
|
|
// Load settings, modify a global setting, serialize, verify JSON.
|
|
|
|
|
static constexpr std::string_view settingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"initialRows": 30,
|
|
|
|
|
"alwaysOnTop": false,
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsJson) };
|
|
|
|
|
|
|
|
|
|
// Verify initial values
|
|
|
|
|
VERIFY_ARE_EQUAL(30, settings->GlobalSettings().InitialRows());
|
|
|
|
|
VERIFY_ARE_EQUAL(false, settings->GlobalSettings().AlwaysOnTop());
|
|
|
|
|
|
|
|
|
|
// Modify global settings
|
|
|
|
|
settings->GlobalSettings().InitialRows(50);
|
|
|
|
|
settings->GlobalSettings().AlwaysOnTop(true);
|
|
|
|
|
|
|
|
|
|
// Verify in-memory changes
|
|
|
|
|
VERIFY_ARE_EQUAL(50, settings->GlobalSettings().InitialRows());
|
|
|
|
|
VERIFY_ARE_EQUAL(true, settings->GlobalSettings().AlwaysOnTop());
|
|
|
|
|
|
|
|
|
|
// Serialize and verify
|
|
|
|
|
const auto result{ settings->ToJson() };
|
|
|
|
|
VERIFY_ARE_EQUAL(50, result["initialRows"].asInt());
|
|
|
|
|
VERIFY_ARE_EQUAL(true, result["alwaysOnTop"].asBool());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::ModifyColorSchemeAndRoundtrip()
|
|
|
|
|
{
|
|
|
|
|
// Load settings with a user color scheme, modify it, serialize, verify.
|
|
|
|
|
static constexpr std::string_view settingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"schemes": [
|
|
|
|
|
{
|
|
|
|
|
"name": "MyScheme",
|
|
|
|
|
"foreground": "#CCCCCC",
|
|
|
|
|
"background": "#0C0C0C",
|
|
|
|
|
"cursorColor": "#FFFFFF",
|
|
|
|
|
"black": "#0C0C0C",
|
|
|
|
|
"red": "#C50F1F",
|
|
|
|
|
"green": "#13A10E",
|
|
|
|
|
"yellow": "#C19C00",
|
|
|
|
|
"blue": "#0037DA",
|
|
|
|
|
"purple": "#881798",
|
|
|
|
|
"cyan": "#3A96DD",
|
|
|
|
|
"white": "#CCCCCC",
|
|
|
|
|
"brightBlack": "#767676",
|
|
|
|
|
"brightRed": "#E74856",
|
|
|
|
|
"brightGreen": "#16C60C",
|
|
|
|
|
"brightYellow": "#F9F1A5",
|
|
|
|
|
"brightBlue": "#3B78FF",
|
|
|
|
|
"brightPurple": "#B4009E",
|
|
|
|
|
"brightCyan": "#61D6D6",
|
|
|
|
|
"brightWhite": "#F2F2F2"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsJson) };
|
|
|
|
|
|
|
|
|
|
// Find and modify the color scheme
|
|
|
|
|
const auto schemes = settings->GlobalSettings().ColorSchemes();
|
|
|
|
|
VERIFY_IS_TRUE(schemes.HasKey(L"MyScheme"));
|
|
|
|
|
auto myScheme = schemes.Lookup(L"MyScheme");
|
|
|
|
|
|
|
|
|
|
const auto origForeground = myScheme.Foreground();
|
|
|
|
|
myScheme.Foreground(til::color{ 0xAA, 0xBB, 0xCC });
|
|
|
|
|
|
|
|
|
|
// Serialize and verify the change persists
|
|
|
|
|
const auto result{ settings->ToJson() };
|
|
|
|
|
const auto& schemesJson = result["schemes"];
|
|
|
|
|
bool found = false;
|
|
|
|
|
for (const auto& scheme : schemesJson)
|
|
|
|
|
{
|
|
|
|
|
if (scheme["name"].asString() == "MyScheme")
|
|
|
|
|
{
|
|
|
|
|
VERIFY_ARE_EQUAL("#AABBCC", scheme["foreground"].asString());
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VERIFY_IS_TRUE(found, L"MyScheme should be present in serialized output");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::FixupUserSettingsDetectsChanges()
|
|
|
|
|
{
|
|
|
|
|
// Verify that FixupUserSettings returns true when settings need
|
|
|
|
|
// to be written back (e.g., migration), and false when clean.
|
|
|
|
|
static constexpr std::string_view cleanSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "profile0",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"commandline": "cmd.exe"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
// Load, fixup, serialize. Reload and verify fixup returns false.
|
|
|
|
|
implementation::SettingsLoader loader1{ cleanSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader1.MergeInboxIntoUserSettings();
|
|
|
|
|
loader1.FinalizeLayering();
|
|
|
|
|
loader1.FixupUserSettings();
|
|
|
|
|
const auto settings1 = winrt::make_self<implementation::CascadiaSettings>(std::move(loader1));
|
|
|
|
|
const auto result1{ settings1->ToJson() };
|
|
|
|
|
|
|
|
|
|
// Reload from the serialized output (should be stable)
|
|
|
|
|
implementation::SettingsLoader loader2{ toString(result1), implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader2.MergeInboxIntoUserSettings();
|
|
|
|
|
loader2.FinalizeLayering();
|
|
|
|
|
const auto fixupNeeded = loader2.FixupUserSettings();
|
|
|
|
|
|
|
|
|
|
// After a clean roundtrip, no further fixups should be needed
|
|
|
|
|
VERIFY_IS_FALSE(fixupNeeded, L"A clean roundtrip should not require further fixups");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerializationTests::FixupCommandlinePatching()
|
|
|
|
|
{
|
|
|
|
|
// Verify that FixupUserSettings patches "cmd.exe" to the full path
|
|
|
|
|
// for the Command Prompt profile, and "powershell.exe" for the
|
|
|
|
|
// Windows PowerShell profile, and returns true to indicate changes.
|
|
|
|
|
|
|
|
|
|
// Case 1: CMD profile with short commandline should be patched
|
|
|
|
|
static constexpr std::string_view cmdSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "Command Prompt",
|
|
|
|
|
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
|
|
|
|
"commandline": "cmd.exe"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
implementation::SettingsLoader loader{ cmdSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
const auto fixupNeeded = loader.FixupUserSettings();
|
|
|
|
|
VERIFY_IS_TRUE(fixupNeeded, L"FixupUserSettings should return true when cmd.exe is patched");
|
|
|
|
|
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto cmdProfile = settings->FindProfile(Utils::GuidFromString(L"{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"));
|
|
|
|
|
VERIFY_IS_NOT_NULL(cmdProfile);
|
|
|
|
|
VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\cmd.exe", cmdProfile.Commandline());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Case 2: PowerShell profile with short commandline should be patched
|
|
|
|
|
static constexpr std::string_view psSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "Windows PowerShell",
|
|
|
|
|
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
|
|
|
|
"commandline": "powershell.exe"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
implementation::SettingsLoader loader{ psSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
const auto fixupNeeded = loader.FixupUserSettings();
|
|
|
|
|
VERIFY_IS_TRUE(fixupNeeded, L"FixupUserSettings should return true when powershell.exe is patched");
|
|
|
|
|
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto psProfile = settings->FindProfile(Utils::GuidFromString(L"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"));
|
|
|
|
|
VERIFY_IS_NOT_NULL(psProfile);
|
|
|
|
|
VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", psProfile.Commandline());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Case 3: CMD profile with the full path should NOT trigger fixup
|
|
|
|
|
static constexpr std::string_view cleanCmdSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "Command Prompt",
|
|
|
|
|
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
implementation::SettingsLoader loader{ cleanCmdSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
const auto fixupNeeded = loader.FixupUserSettings();
|
|
|
|
|
VERIFY_IS_FALSE(fixupNeeded, L"FixupUserSettings should return false when no patching is needed");
|
|
|
|
|
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto cmdProfile = settings->FindProfile(Utils::GuidFromString(L"{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"));
|
|
|
|
|
VERIFY_IS_NOT_NULL(cmdProfile);
|
|
|
|
|
// Should still resolve to the full path via inbox defaults
|
|
|
|
|
VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\cmd.exe", cmdProfile.Commandline());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Case 4: A non-builtin profile with "cmd.exe" should NOT be patched
|
|
|
|
|
static constexpr std::string_view customCmdSettingsJson{ R"(
|
|
|
|
|
{
|
|
|
|
|
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"profiles": [
|
|
|
|
|
{
|
|
|
|
|
"name": "My Custom CMD",
|
|
|
|
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
|
|
|
|
"commandline": "cmd.exe"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})" };
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
implementation::SettingsLoader loader{ customCmdSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) };
|
|
|
|
|
loader.MergeInboxIntoUserSettings();
|
|
|
|
|
loader.FinalizeLayering();
|
|
|
|
|
loader.FixupUserSettings();
|
|
|
|
|
|
|
|
|
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(std::move(loader));
|
|
|
|
|
const auto customProfile = settings->FindProfile(Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}"));
|
|
|
|
|
VERIFY_IS_NOT_NULL(customProfile);
|
|
|
|
|
// Custom profile should keep "cmd.exe" unchanged
|
|
|
|
|
VERIFY_ARE_EQUAL(L"cmd.exe", customProfile.Commandline());
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-16 16:37:19 -08:00
|
|
|
}
|