Skip to content

Commit 06ab6f3

Browse files
Add IDs to Commands (microsoft#16904)
As laid out in microsoft#16816, adds an `ID` field to `Command`. **This first PR only adds IDs for built-in commands in defaults, and generates IDs for user-created commands that don't define an ID.** Also note that for now we **will not** be allowing IDs for iterable/nested commands. The follow-up PR is where we will actually use the IDs by referring to commands with them. Refs microsoft#16816 ## Validation Steps Performed User-created commands in the settings file get rewritten with generated IDs
1 parent 643f716 commit 06ab6f3

File tree

10 files changed

+352
-127
lines changed

10 files changed

+352
-127
lines changed

src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,36 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
450450
return found != GeneratedActionNames.end() ? found->second : L"";
451451
}
452452

453+
// Function Description:
454+
// - This will generate an ID for this ActionAndArgs, based on the ShortcutAction and the Args
455+
// - It will always create the same ID if the ShortcutAction and the Args are the same
456+
// - Note: this should only be called for User-created actions
457+
// - Example: The "SendInput 'abc'" action will have the generated ID "User.sendInput.<hash of 'abc'>"
458+
// Return Value:
459+
// - The ID, based on the ShortcutAction and the Args
460+
winrt::hstring ActionAndArgs::GenerateID() const
461+
{
462+
if (_Action != ShortcutAction::Invalid)
463+
{
464+
auto actionKeyString = ActionToStringMap.find(_Action)->second;
465+
auto result = fmt::format(FMT_COMPILE(L"User.{}"), actionKeyString);
466+
if (_Args)
467+
{
468+
// If there are args, we need to append the hash of the args
469+
// However, to make it a little more presentable we
470+
// 1. truncate the hash to 32 bits
471+
// 2. convert it to a hex string
472+
// there is a _tiny_ chance of collision because of the truncate but unlikely for
473+
// the number of commands a user is expected to have
474+
const auto argsHash32 = static_cast<uint32_t>(_Args.Hash() & 0xFFFFFFFF);
475+
// {0:X} formats the truncated hash to an uppercase hex string
476+
fmt::format_to(std::back_inserter(result), FMT_COMPILE(L".{:X}"), argsHash32);
477+
}
478+
return winrt::hstring{ result };
479+
}
480+
return L"";
481+
}
482+
453483
winrt::hstring ActionAndArgs::Serialize(const winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs>& args)
454484
{
455485
Json::Value json{ Json::objectValue };

src/cascadia/TerminalSettingsModel/ActionAndArgs.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
2727
com_ptr<ActionAndArgs> Copy() const;
2828

2929
hstring GenerateName() const;
30+
hstring GenerateID() const;
3031

3132
WINRT_PROPERTY(ShortcutAction, Action, ShortcutAction::Invalid);
3233
WINRT_PROPERTY(IActionArgs, Args, nullptr);

src/cascadia/TerminalSettingsModel/ActionMap.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
795795
return nullptr;
796796
}
797797

798+
bool ActionMap::GenerateIDsForActions()
799+
{
800+
bool fixedUp{ false };
801+
for (auto actionPair : _ActionMap)
802+
{
803+
auto cmdImpl{ winrt::get_self<Command>(actionPair.second) };
804+
805+
// Note: this function should ONLY be called for the action map in the user's settings file
806+
// this debug assert should verify that for debug builds
807+
assert(cmdImpl->Origin() == OriginTag::User);
808+
809+
if (cmdImpl->ID().empty())
810+
{
811+
fixedUp = cmdImpl->GenerateID() || fixedUp;
812+
}
813+
}
814+
return fixedUp;
815+
}
816+
798817
// Method Description:
799818
// - Rebinds a key binding to a new key chord
800819
// Arguments:

src/cascadia/TerminalSettingsModel/ActionMap.h

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
7171
Json::Value ToJson() const;
7272

7373
// modification
74+
bool GenerateIDsForActions();
7475
bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys);
7576
void DeleteKeyBinding(const Control::KeyChord& keys);
7677
void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action);

src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,10 @@ bool SettingsLoader::FixupUserSettings()
504504
fixedUp = true;
505505
}
506506

507+
// we need to generate an ID for a command in the user settings if it doesn't already have one
508+
auto actionMap{ winrt::get_self<ActionMap>(userSettings.globals->ActionMap()) };
509+
fixedUp = actionMap->GenerateIDsForActions() || fixedUp;
510+
507511
return fixedUp;
508512
}
509513

src/cascadia/TerminalSettingsModel/Command.cpp

+31-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace winrt
2121
}
2222

2323
static constexpr std::string_view NameKey{ "name" };
24+
static constexpr std::string_view IDKey{ "id" };
2425
static constexpr std::string_view IconKey{ "icon" };
2526
static constexpr std::string_view ActionKey{ "command" };
2627
static constexpr std::string_view IterateOnKey{ "iterateOn" };
@@ -39,7 +40,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
3940
{
4041
auto command{ winrt::make_self<Command>() };
4142
command->_name = _name;
42-
command->_Origin = OriginTag::User;
43+
command->_Origin = _Origin;
44+
command->_ID = _ID;
4345
command->_ActionAndArgs = *get_self<implementation::ActionAndArgs>(_ActionAndArgs)->Copy();
4446
command->_keyMappings = _keyMappings;
4547
command->_iconPath = _iconPath;
@@ -114,6 +116,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
114116
}
115117
}
116118

119+
hstring Command::ID() const noexcept
120+
{
121+
return hstring{ _ID };
122+
}
123+
124+
bool Command::GenerateID()
125+
{
126+
if (_ActionAndArgs)
127+
{
128+
auto actionAndArgsImpl{ winrt::get_self<implementation::ActionAndArgs>(_ActionAndArgs) };
129+
if (const auto generatedID = actionAndArgsImpl->GenerateID(); !generatedID.empty())
130+
{
131+
_ID = generatedID;
132+
return true;
133+
}
134+
}
135+
return false;
136+
}
137+
117138
void Command::Name(const hstring& value)
118139
{
119140
if (!_name.has_value() || _name.value() != value)
@@ -264,6 +285,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
264285
{
265286
auto result = winrt::make_self<Command>();
266287
result->_Origin = origin;
288+
JsonUtils::GetValueForKey(json, IDKey, result->_ID);
267289

268290
auto nested = false;
269291
JsonUtils::GetValueForKey(json, IterateOnKey, result->_IterateOn);
@@ -423,6 +445,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
423445
Json::Value cmdJson{ Json::ValueType::objectValue };
424446
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
425447
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
448+
if (!_ID.empty())
449+
{
450+
JsonUtils::SetValueForKey(cmdJson, IDKey, _ID);
451+
}
426452

427453
if (_ActionAndArgs)
428454
{
@@ -443,6 +469,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
443469
// First iteration also writes icon and name
444470
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
445471
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
472+
if (!_ID.empty())
473+
{
474+
JsonUtils::SetValueForKey(cmdJson, IDKey, _ID);
475+
}
446476
}
447477

448478
if (_ActionAndArgs)

src/cascadia/TerminalSettingsModel/Command.h

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
6161
hstring Name() const noexcept;
6262
void Name(const hstring& name);
6363

64+
hstring ID() const noexcept;
65+
bool GenerateID();
66+
6467
Control::KeyChord Keys() const noexcept;
6568
hstring KeyChordText() const noexcept;
6669
std::vector<Control::KeyChord> KeyMappings() const noexcept;
@@ -84,6 +87,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
8487
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command> _subcommands{ nullptr };
8588
std::vector<Control::KeyChord> _keyMappings;
8689
std::optional<std::wstring> _name;
90+
std::wstring _ID;
8791
std::optional<std::wstring> _iconPath;
8892
bool _nestedCommand{ false };
8993

src/cascadia/TerminalSettingsModel/Command.idl

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace Microsoft.Terminal.Settings.Model
3636
Command();
3737

3838
String Name { get; };
39+
String ID { get; };
3940
ActionAndArgs ActionAndArgs { get; };
4041
Microsoft.Terminal.Control.KeyChord Keys { get; };
4142
void RegisterKey(Microsoft.Terminal.Control.KeyChord keys);

0 commit comments

Comments
 (0)