Skip to content

Commit

Permalink
Introduce serialization for actions (#9926)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request

This PR builds on the `ActionMap` PR (#6900) by leveraging `ActionMap` to serialize actions. From the top down, the process is now as follows:
- `CascadiaSettings`: remove the hack of copying whatever we had for actions before.
- `GlobalAppSettings`: serialize the `ActionMap` to `"actions": []`
- `ActionMap`: iterate over the internal `_ActionMap` (list of actions) and serialize each `Command`
- `Command`: **THIS IS WHERE THE MAGIC HAPPENS!** For _each_ key mapping, serialize an action. Only the first one needs to include the name and icon.
- `ActionAndArgs`: Find the relevant `IActionArgs` parser and serialize the `ActionAndArgs`.
- `ActionArgs`: **ANNOYING CHANGE** Serialize any args that are set. We _need_ each setting to be saved as a `std::optional`. As with inheritance, this allows us to distinguish an explicit setting to the default value (sometimes `null`) vs an implicit "give me the default value". This allows us to serialize only the relevant details of each action, rather than writing _all_ of the args.

## References
- #8100: Inheritance/Layering for lists
   - This tracks layering and better serialization for color schemes _and_ actions. This PR resolves half of that issue. The next step is to apply the concepts used in this PR (and #9621) to solve the similar problem for color schemes.
- #6900: Actions page

## Validation Steps Performed
Tests added!
  • Loading branch information
carlos-zamora authored May 20, 2021
1 parent 13f0b8e commit ff8fdbd
Show file tree
Hide file tree
Showing 14 changed files with 847 additions and 277 deletions.
2 changes: 2 additions & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,8 @@ selectany
SELECTEDFONT
SELECTSTRING
Selfhosters
serializer
serializers
SERVERDLL
SETACTIVE
SETBUDDYINT
Expand Down
160 changes: 154 additions & 6 deletions src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace SettingsModelLocalTests
TEST_METHOD(GlobalSettings);
TEST_METHOD(Profile);
TEST_METHOD(ColorScheme);
TEST_METHOD(Actions);
TEST_METHOD(CascadiaSettings);

TEST_CLASS_SETUP(ClassSetup)
Expand Down Expand Up @@ -106,12 +107,15 @@ namespace SettingsModelLocalTests
"experimental.input.forceVT": false,
"experimental.rendering.forceFullRepaint": false,
"experimental.rendering.software": false
"experimental.rendering.software": false,
"actions": []
})" };

const std::string smallGlobalsString{ R"(
{
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"actions": []
})" };

RoundtripTest<implementation::GlobalAppSettings>(globalsString);
Expand Down Expand Up @@ -178,7 +182,9 @@ namespace SettingsModelLocalTests
// - null should be acceptable even though we're working with colors
const std::string weirdProfileString{ R"(
{
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
"name": "Weird Profile",
"hidden": false,
"tabColor": null,
"foreground": null,
"source": "local"
Expand Down Expand Up @@ -221,6 +227,149 @@ namespace SettingsModelLocalTests
RoundtripTest<implementation::ColorScheme>(schemeString);
}

void SerializationTests::Actions()
{
const std::string actionsString1{ R"([
{ "command": "paste" }
])" };

const std::string actionsString2A{ R"([
{ "command": { "action": "setTabColor" } }
])" };
const std::string actionsString2B{ R"([
{ "command": { "action": "setTabColor", "color": "#112233" } }
])" };
const std::string actionsString2C{ R"([
{ "command": { "action": "copy" } },
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } }
])" };

const std::string actionsString3{ R"([
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+a" },
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+b" }
])" };

const std::string actionsString4{ R"([
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" },
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" }
])" };

const std::string actionsString5{ R"([
{ "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" },
{ "command": "scrollToTop", "keys": "ctrl+f" }
])" };

const std::string actionsString6{ R"([
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" },
])" };

const std::string actionsString7{ R"([
{ "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" }
])" };

const std::string actionsString8{ R"([
{
"name": "Change font size...",
"commands": [
{ "command": { "action": "adjustFontSize", "delta": 1 } },
{ "command": { "action": "adjustFontSize", "delta": -1 } },
{ "command": "resetFontSize" },
]
}
])" };

const std::string actionsString9A{ R"([
{
"name": "New tab",
"commands": [
{
"iterateOn": "profiles",
"icon": "${profile.icon}",
"name": "${profile.name}",
"command": { "action": "newTab", "profile": "${profile.name}" }
}
]
}
])" };
const std::string actionsString9B{ R"([
{
"commands":
[
{
"command":
{
"action": "sendInput",
"input": "${profile.name}"
},
"iterateOn": "profiles"
}
],
"name": "Send Input ..."
}
])" };
const std::string actionsString9C{ R""([
{
"commands":
[
{
"commands":
[
{
"command":
{
"action": "sendInput",
"input": "${profile.name} ${scheme.name}"
},
"iterateOn": "schemes"
}
],
"iterateOn": "profiles",
"name": "nest level (${profile.name})"
}
],
"name": "Send Input (Evil) ..."
}
])"" };

const std::string actionsString10{ R"([
{ "command": "unbound", "keys": "ctrl+c" }
])" };

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);

Log::Comment(L"simple command with key chords");
RoundtripTest<implementation::ActionMap>(actionsString3);

Log::Comment(L"complex commands with key chords");
RoundtripTest<implementation::ActionMap>(actionsString4);

Log::Comment(L"command with name and icon and multiple key chords");
RoundtripTest<implementation::ActionMap>(actionsString5);

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);

Log::Comment(L"unbound command");
RoundtripTest<implementation::ActionMap>(actionsString10);
}

void SerializationTests::CascadiaSettings()
{
const std::string settingsString{ R"({
Expand Down Expand Up @@ -277,10 +426,9 @@ namespace SettingsModelLocalTests
}
],
"actions": [
{"command": { "action": "renameTab","input": "Liang Tab" },"keys": "ctrl+t" }
],
"keybindings": [
{ "command": { "action": "sendInput","input": "VT Griese Mode" },"keys": "ctrl+k" }
{ "command": { "action": "renameTab", "title": "Liang Tab" }, "keys": "ctrl+t" },
{ "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" },
{ "command": { "action": "renameWindow", "name": "Hecker Window" }, "keys": "ctrl+l" }
]
})" };

Expand Down
Loading

0 comments on commit ff8fdbd

Please sign in to comment.