mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-20 21:22:47 +00:00
Compare commits
94 Commits
dev/lhecke
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa27423485 | ||
|
|
c80f3f5f38 | ||
|
|
040ba0f9bd | ||
|
|
54aaed8dcf | ||
|
|
dc56e983d1 | ||
|
|
ac18f8147c | ||
|
|
4fedf06f7c | ||
|
|
c1c483ee36 | ||
|
|
73afe48041 | ||
|
|
0dff336556 | ||
|
|
51528e9cfd | ||
|
|
40b4aa2c94 | ||
|
|
d4d216ca33 | ||
|
|
7d00b25fbf | ||
|
|
ba375ec2a2 | ||
|
|
5e48a45ba2 | ||
|
|
14d83b5f5c | ||
|
|
3e31bda6f2 | ||
|
|
4d35c14966 | ||
|
|
abef25d29c | ||
|
|
7793c5c5bc | ||
|
|
ccf1cc9e83 | ||
|
|
ebc03e98f4 | ||
|
|
80fc299c10 | ||
|
|
02a1e37aae | ||
|
|
0480d651cb | ||
|
|
193e5733bf | ||
|
|
2b16acd4cf | ||
|
|
f35bf206e6 | ||
|
|
cdb907d94d | ||
|
|
3e601f5b66 | ||
|
|
c2c75c80ed | ||
|
|
3c6015d97b | ||
|
|
45cfcd6eca | ||
|
|
ca4015f5f9 | ||
|
|
4c744e6ab3 | ||
|
|
6437b9f508 | ||
|
|
428821b40c | ||
|
|
3d92f27de7 | ||
|
|
db00b90306 | ||
|
|
e725f1e936 | ||
|
|
e62dfa2177 | ||
|
|
2b4aeb2b11 | ||
|
|
2f1d8d2dca | ||
|
|
754bf04ab3 | ||
|
|
c51558ff4c | ||
|
|
5a1b822833 | ||
|
|
b3e9c267f5 | ||
|
|
936afd6b01 | ||
|
|
dc874c3b3f | ||
|
|
ae16a5e0e1 | ||
|
|
3e7ab3861a | ||
|
|
ddfac907f4 | ||
|
|
f1633e0360 | ||
|
|
12a61c595e | ||
|
|
d0938e2a24 | ||
|
|
f425746169 | ||
|
|
e28d47888c | ||
|
|
0a3e17eebb | ||
|
|
22ab9363ef | ||
|
|
c134402507 | ||
|
|
85933e2231 | ||
|
|
ca3eb87301 | ||
|
|
5e70911a68 | ||
|
|
360b92e567 | ||
|
|
5ee630ec82 | ||
|
|
aa4921268e | ||
|
|
12f3aa9d06 | ||
|
|
bdf42c2d9c | ||
|
|
af2d22f343 | ||
|
|
6e293a5ee8 | ||
|
|
dd25ed762f | ||
|
|
dca7df50c8 | ||
|
|
9fc69721c9 | ||
|
|
5c2307c531 | ||
|
|
d57c7a1f03 | ||
|
|
71bf90f295 | ||
|
|
10d1fc8d60 | ||
|
|
44510dce1b | ||
|
|
eccd87f303 | ||
|
|
6c3253968f | ||
|
|
2093660ac1 | ||
|
|
b43191d2c5 | ||
|
|
7c907fed6e | ||
|
|
db528c94fc | ||
|
|
be193b21eb | ||
|
|
2bb1b6c6ad | ||
|
|
66fe08f964 | ||
|
|
642d0ab2b7 | ||
|
|
8cc82de489 | ||
|
|
052d063686 | ||
|
|
8bcbd0bd42 | ||
|
|
9dff28f23d | ||
|
|
90627b3ae5 |
28
.wt.json
Normal file
28
.wt.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"actions":
|
||||
[
|
||||
{
|
||||
"command": { "action": "sendInput", "input": "bx\r" },
|
||||
"name": "Build projectttttttttttttttttt",
|
||||
"description": "Build the project in the CWD"
|
||||
},
|
||||
{
|
||||
"command": { "action": "sendInput", "input": "bz\r" },
|
||||
"name": "Build solution, incremental",
|
||||
"description": "Just build changes to the solution"
|
||||
},
|
||||
{
|
||||
"command": { "action": "sendInput", "input": "bcz\r" },
|
||||
"name": "Clean & build solution",
|
||||
"icon": "\uE8e6",
|
||||
"description": "Start over. Go get your coffee. "
|
||||
},
|
||||
{
|
||||
"command": { "action": "sendInput", "input": "nuget push -apikey az -source TerminalDependencies %userprofile%\\Downloads" },
|
||||
"name": "Upload package to nuget feed",
|
||||
"icon": "\uE898",
|
||||
"description": "Go download a .nupkg, put it in ~/Downloads, and use this to push to our private feed."
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
@@ -629,7 +629,8 @@
|
||||
"folder",
|
||||
"separator",
|
||||
"remainingProfiles",
|
||||
"matchProfiles"
|
||||
"matchProfiles",
|
||||
"action"
|
||||
]
|
||||
},
|
||||
"NewTabMenuEntry": {
|
||||
@@ -781,6 +782,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ActionEntry": {
|
||||
"description": "An action in the new tab dropdown",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "action"
|
||||
},
|
||||
"profile": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The ID of the action to show in this entry"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SwitchToAdjacentTabArgs": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -2054,11 +2077,14 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/RemainingProfilesEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/ActionEntry"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Keybinding": {
|
||||
"FullCommand": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"command": {
|
||||
@@ -2186,21 +2212,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"keys": {
|
||||
"description": "Defines the key combinations used to call the command. It must be composed of...\n -any number of modifiers (ctrl/alt/shift)\n -a non-modifier key",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/KeyChordSegment"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"$ref": "#/$defs/KeyChordSegment"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
}
|
||||
]
|
||||
},
|
||||
"icon": {
|
||||
"$ref": "#/$defs/Icon"
|
||||
},
|
||||
@@ -2235,10 +2246,10 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {
|
||||
"$ref": "#/$defs/Keybinding/properties/command"
|
||||
"$ref": "#/$defs/FullCommand/properties/command"
|
||||
},
|
||||
"name": {
|
||||
"$ref": "#/$defs/Keybinding/properties/name"
|
||||
"$ref": "#/$defs/FullCommand/properties/name"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2261,6 +2272,44 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The ID of the command this keybinding should execute.",
|
||||
"type": "string"
|
||||
},
|
||||
"keys": {
|
||||
"description": "Defines the key combinations used to call the command. It must be composed of...\n -any number of modifiers (ctrl/alt/shift)\n -a non-modifier key",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/KeyChordSegment"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"$ref": "#/$defs/KeyChordSegment"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"keys",
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"keys"
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Globals": {
|
||||
"additionalProperties": true,
|
||||
"description": "Properties that affect the entire window, regardless of the profile settings.",
|
||||
@@ -2464,12 +2513,12 @@
|
||||
"actions": {
|
||||
"description": "Properties are specific to each custom action.",
|
||||
"items": {
|
||||
"$ref": "#/$defs/Keybinding"
|
||||
"$ref": "#/$defs/FullCommand"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"keybindings": {
|
||||
"description": "[deprecated] Use actions instead.",
|
||||
"description": "A list of keychords bound to action IDs",
|
||||
"deprecated": true,
|
||||
"items": {
|
||||
"$ref": "#/$defs/Keybinding"
|
||||
|
||||
@@ -18,11 +18,11 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
ActionPaletteItem::ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command) :
|
||||
ActionPaletteItem::ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command, const winrt::hstring keyChordText) :
|
||||
_Command(command)
|
||||
{
|
||||
Name(command.Name());
|
||||
KeyChordText(command.KeyChordText());
|
||||
KeyChordText(keyChordText);
|
||||
Icon(command.IconPath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace winrt::TerminalApp::implementation
|
||||
struct ActionPaletteItem : ActionPaletteItemT<ActionPaletteItem, PaletteItem>
|
||||
{
|
||||
ActionPaletteItem() = default;
|
||||
ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command);
|
||||
ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command, const winrt::hstring keyChordText);
|
||||
|
||||
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass ActionPaletteItem : PaletteItem
|
||||
{
|
||||
ActionPaletteItem(Microsoft.Terminal.Settings.Model.Command command);
|
||||
ActionPaletteItem(Microsoft.Terminal.Settings.Model.Command command, String keyChordText);
|
||||
|
||||
Microsoft.Terminal.Settings.Model.Command Command { get; };
|
||||
}
|
||||
|
||||
@@ -1330,66 +1330,77 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SuggestionsArgs>())
|
||||
{
|
||||
const auto source = realArgs.Source();
|
||||
std::vector<Command> commandsCollection;
|
||||
Control::CommandHistoryContext context{ nullptr };
|
||||
winrt::hstring currentCommandline = L"";
|
||||
_doHandleSuggestions(realArgs);
|
||||
|
||||
// If the user wanted to use the current commandline to filter results,
|
||||
// OR they wanted command history (or some other source that
|
||||
// requires context from the control)
|
||||
// then get that here.
|
||||
const bool shouldGetContext = realArgs.UseCommandline() ||
|
||||
WI_IsFlagSet(source, SuggestionsSource::CommandHistory);
|
||||
if (shouldGetContext)
|
||||
{
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
context = control.CommandHistory();
|
||||
if (context)
|
||||
{
|
||||
currentCommandline = context.CurrentCommandline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate all the commands from the different sources that
|
||||
// the user selected.
|
||||
|
||||
// Tasks are all the sendInput commands the user has saved in
|
||||
// their settings file. Ask the ActionMap for those.
|
||||
if (WI_IsFlagSet(source, SuggestionsSource::Tasks))
|
||||
{
|
||||
const auto tasks = _settings.GlobalSettings().ActionMap().FilterToSendInput(currentCommandline);
|
||||
for (const auto& t : tasks)
|
||||
{
|
||||
commandsCollection.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
// Command History comes from the commands in the buffer,
|
||||
// assuming the user has enabled shell integration. Get those
|
||||
// from the active control.
|
||||
if (WI_IsFlagSet(source, SuggestionsSource::CommandHistory) &&
|
||||
context != nullptr)
|
||||
{
|
||||
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false);
|
||||
for (const auto& t : recentCommands)
|
||||
{
|
||||
commandsCollection.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
// Open the palette with all these commands in it.
|
||||
_OpenSuggestions(_GetActiveControl(),
|
||||
winrt::single_threaded_vector<Command>(std::move(commandsCollection)),
|
||||
SuggestionsMode::Palette,
|
||||
currentCommandline);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::_doHandleSuggestions(SuggestionsArgs realArgs)
|
||||
{
|
||||
const auto source = realArgs.Source();
|
||||
std::vector<Command> commandsCollection;
|
||||
Control::CommandHistoryContext context{ nullptr };
|
||||
winrt::hstring currentCommandline = L"";
|
||||
winrt::hstring currentWorkingDirectory = L"";
|
||||
|
||||
// If the user wanted to use the current commandline to filter results,
|
||||
// OR they wanted command history (or some other source that
|
||||
// requires context from the control)
|
||||
// then get that here.
|
||||
const bool shouldGetContext = realArgs.UseCommandline() ||
|
||||
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory);
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
currentWorkingDirectory = control.CurrentWorkingDirectory();
|
||||
|
||||
if (shouldGetContext)
|
||||
{
|
||||
context = control.CommandHistory();
|
||||
if (context)
|
||||
{
|
||||
currentCommandline = context.CurrentCommandline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate all the commands from the different sources that
|
||||
// the user selected.
|
||||
|
||||
// Tasks are all the sendInput commands the user has saved in
|
||||
// their settings file. Ask the ActionMap for those.
|
||||
if (WI_IsFlagSet(source, SuggestionsSource::Tasks))
|
||||
{
|
||||
const auto tasks = co_await _settings.GlobalSettings().ActionMap().FilterToSnippets(currentCommandline, currentWorkingDirectory);
|
||||
for (const auto& t : tasks)
|
||||
{
|
||||
commandsCollection.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
// Command History comes from the commands in the buffer,
|
||||
// assuming the user has enabled shell integration. Get those
|
||||
// from the active control.
|
||||
if (WI_IsFlagSet(source, SuggestionsSource::CommandHistory) &&
|
||||
context != nullptr)
|
||||
{
|
||||
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false);
|
||||
for (const auto& t : recentCommands)
|
||||
{
|
||||
commandsCollection.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
co_await wil::resume_foreground(Dispatcher());
|
||||
|
||||
// Open the palette with all these commands in it.
|
||||
_OpenSuggestions(_GetActiveControl(),
|
||||
winrt::single_threaded_vector<Command>(std::move(commandsCollection)),
|
||||
SuggestionsMode::Palette,
|
||||
currentCommandline);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleColorSelection(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
|
||||
@@ -950,21 +950,27 @@ namespace winrt::TerminalApp::implementation
|
||||
void CommandPalette::SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap)
|
||||
{
|
||||
_actionMap = actionMap;
|
||||
_setCommands();
|
||||
}
|
||||
|
||||
void CommandPalette::SetCommands(const Collections::IVector<Command>& actions)
|
||||
void CommandPalette::_setCommands()
|
||||
{
|
||||
_allCommands.Clear();
|
||||
for (const auto& action : actions)
|
||||
if (_actionMap)
|
||||
{
|
||||
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
|
||||
auto filteredCommand{ winrt::make<FilteredCommand>(actionPaletteItem) };
|
||||
_allCommands.Append(filteredCommand);
|
||||
}
|
||||
_allCommands.Clear();
|
||||
const auto expandedCommands{ _actionMap.ExpandedCommands() };
|
||||
for (const auto& action : expandedCommands)
|
||||
{
|
||||
const auto keyChordText{ KeyChordSerialization::ToString(_actionMap.GetKeyBindingForAction(action.ID())) };
|
||||
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, keyChordText) };
|
||||
auto filteredCommand{ winrt::make<FilteredCommand>(actionPaletteItem) };
|
||||
_allCommands.Append(filteredCommand);
|
||||
}
|
||||
|
||||
if (Visibility() == Visibility::Visible && _currentMode == CommandPaletteMode::ActionMode)
|
||||
{
|
||||
_updateFilteredActions();
|
||||
if (Visibility() == Visibility::Visible && _currentMode == CommandPaletteMode::ActionMode)
|
||||
{
|
||||
_updateFilteredActions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1178,7 +1184,8 @@ namespace winrt::TerminalApp::implementation
|
||||
for (const auto& nameAndCommand : parentCommand.NestedCommands())
|
||||
{
|
||||
const auto action = nameAndCommand.Value();
|
||||
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
|
||||
// nested commands cannot have keys bound to them, so just pass in the command and no keys
|
||||
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, L"") };
|
||||
auto nestedFilteredCommand{ winrt::make<FilteredCommand>(nestedActionPaletteItem) };
|
||||
_currentNestedCommands.Append(nestedFilteredCommand);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> FilteredActions();
|
||||
|
||||
void SetCommands(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& actions);
|
||||
void SetTabs(const Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::TabBase>& tabs, const Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::TabBase>& mruTabs);
|
||||
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
|
||||
|
||||
@@ -81,6 +80,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
bool _lastFilterTextWasEmpty{ true };
|
||||
|
||||
void _setCommands();
|
||||
|
||||
void _filterTextChanged(const Windows::Foundation::IInspectable& sender,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender,
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace TerminalApp
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<FilteredCommand> FilteredActions { get; };
|
||||
|
||||
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
|
||||
|
||||
void SetTabs(Windows.Foundation.Collections.IObservableVector<TabBase> tabs, Windows.Foundation.Collections.IObservableVector<TabBase> mruTabs);
|
||||
|
||||
void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap);
|
||||
|
||||
@@ -508,7 +508,7 @@ namespace winrt::TerminalApp::implementation
|
||||
automationPeer.RaiseNotificationEvent(
|
||||
Automation::Peers::AutomationNotificationKind::ItemAdded,
|
||||
Automation::Peers::AutomationNotificationProcessing::MostRecent,
|
||||
paletteItem.Name() + L" " + paletteItem.KeyChordText(),
|
||||
paletteItem.Name(),
|
||||
L"SuggestionsControlSelectedItemChanged" /* unique name for this notification category */);
|
||||
}
|
||||
}
|
||||
@@ -751,17 +751,13 @@ namespace winrt::TerminalApp::implementation
|
||||
return _filteredActions;
|
||||
}
|
||||
|
||||
void SuggestionsControl::SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap)
|
||||
{
|
||||
_actionMap = actionMap;
|
||||
}
|
||||
|
||||
void SuggestionsControl::SetCommands(const Collections::IVector<Command>& actions)
|
||||
{
|
||||
_allCommands.Clear();
|
||||
for (const auto& action : actions)
|
||||
{
|
||||
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
|
||||
// key chords aren't relevant in the suggestions control, so make the palette item with just the command and no keys
|
||||
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, L"") };
|
||||
auto filteredCommand{ winrt::make<FilteredCommand>(actionPaletteItem) };
|
||||
_allCommands.Append(filteredCommand);
|
||||
}
|
||||
@@ -915,7 +911,7 @@ namespace winrt::TerminalApp::implementation
|
||||
for (const auto& nameAndCommand : parentCommand.NestedCommands())
|
||||
{
|
||||
const auto action = nameAndCommand.Value();
|
||||
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
|
||||
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, L"") };
|
||||
auto nestedFilteredCommand{ winrt::make<FilteredCommand>(nestedActionPaletteItem) };
|
||||
_currentNestedCommands.Append(nestedFilteredCommand);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace winrt::TerminalApp::implementation
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> FilteredActions();
|
||||
|
||||
void SetCommands(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& actions);
|
||||
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace TerminalApp
|
||||
SuggestionsMode Mode { get; set; };
|
||||
|
||||
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
|
||||
void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap);
|
||||
void SelectNextItem(Boolean moveDown);
|
||||
|
||||
void Open(SuggestionsMode mode, IVector<Microsoft.Terminal.Settings.Model.Command> commands, String filterText, Windows.Foundation.Point anchor, Windows.Foundation.Size space, Single characterHeight);
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
MinHeight="0"
|
||||
Padding="16,0,12,0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}"
|
||||
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
|
||||
FontSize="12" />
|
||||
</DataTemplate>
|
||||
|
||||
@@ -192,7 +192,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TabBase::_UpdateSwitchToTabKeyChord()
|
||||
{
|
||||
const auto keyChord = _actionMap ? _actionMap.GetKeyBindingForAction(ShortcutAction::SwitchToTab, SwitchToTabArgs{ _TabViewIndex }) : nullptr;
|
||||
const auto id = fmt::format(FMT_COMPILE(L"Terminal.SwitchToTab{}"), _TabViewIndex);
|
||||
const auto keyChord{ _actionMap.GetKeyBindingForAction(id) };
|
||||
const auto keyChordText = keyChord ? KeyChordSerialization::ToString(keyChord) : L"";
|
||||
|
||||
if (_keyChord == keyChordText)
|
||||
|
||||
@@ -123,7 +123,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// to happen before the Settings UI is reloaded and tries to re-read those values.
|
||||
if (const auto p = CommandPaletteElement())
|
||||
{
|
||||
p.SetCommands(_settings.GlobalSettings().ActionMap().ExpandedCommands());
|
||||
p.SetActionMap(_settings.ActionMap());
|
||||
}
|
||||
|
||||
@@ -826,7 +825,7 @@ namespace winrt::TerminalApp::implementation
|
||||
newTabFlyout.Items().Append(settingsItem);
|
||||
|
||||
auto actionMap = _settings.ActionMap();
|
||||
const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
|
||||
const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(L"Terminal.OpenSettingsUI") };
|
||||
if (settingsKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
|
||||
@@ -848,7 +847,7 @@ namespace winrt::TerminalApp::implementation
|
||||
commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick });
|
||||
newTabFlyout.Items().Append(commandPaletteFlyout);
|
||||
|
||||
const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
|
||||
const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(L"Terminal.ToggleCommandPalette") };
|
||||
if (commandPaletteKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord);
|
||||
@@ -1005,6 +1004,18 @@ namespace winrt::TerminalApp::implementation
|
||||
items.push_back(profileItem);
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Action:
|
||||
{
|
||||
const auto actionEntry = entry.as<ActionEntry>();
|
||||
const auto actionId = actionEntry.ActionId();
|
||||
if (_settings.ActionMap().GetActionById(actionId))
|
||||
{
|
||||
auto actionItem = _CreateNewTabFlyoutAction(actionId);
|
||||
items.push_back(actionItem);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1023,7 +1034,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// NewTab(ProfileIndex=N) action
|
||||
NewTerminalArgs newTerminalArgs{ profileIndex };
|
||||
NewTabArgs newTabArgs{ newTerminalArgs };
|
||||
auto profileKeyChord{ _settings.ActionMap().GetKeyBindingForAction(ShortcutAction::NewTab, newTabArgs) };
|
||||
const auto id = fmt::format(FMT_COMPILE(L"Terminal.OpenNewTabProfile{}"), profileIndex);
|
||||
const auto profileKeyChord{ _settings.ActionMap().GetKeyBindingForAction(id) };
|
||||
|
||||
// make sure we find one to display
|
||||
if (profileKeyChord)
|
||||
@@ -1094,6 +1106,42 @@ namespace winrt::TerminalApp::implementation
|
||||
return profileMenuItem;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method creates a flyout menu item for a given action
|
||||
// It makes sure to set the correct icon, keybinding, and click-action.
|
||||
WUX::Controls::MenuFlyoutItem TerminalPage::_CreateNewTabFlyoutAction(const winrt::hstring& actionId)
|
||||
{
|
||||
auto actionMenuItem = WUX::Controls::MenuFlyoutItem{};
|
||||
const auto action{ _settings.ActionMap().GetActionById(actionId) };
|
||||
const auto actionKeyChord{ _settings.ActionMap().GetKeyBindingForAction(actionId) };
|
||||
|
||||
if (actionKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(actionMenuItem, actionKeyChord);
|
||||
}
|
||||
|
||||
const auto actionName = action.Name();
|
||||
actionMenuItem.Text(actionName);
|
||||
|
||||
// If there's an icon set for this action, set it as the icon for
|
||||
// this flyout item
|
||||
const auto& iconPath = action.IconPath();
|
||||
if (!iconPath.empty())
|
||||
{
|
||||
const auto icon = _CreateNewTabFlyoutIcon(iconPath);
|
||||
actionMenuItem.Icon(icon);
|
||||
}
|
||||
|
||||
actionMenuItem.Click([action, weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->_actionDispatch->DoAction(action.ActionAndArgs());
|
||||
}
|
||||
});
|
||||
|
||||
return actionMenuItem;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method to create an IconElement that can be passed to MenuFlyoutItems and
|
||||
// MenuFlyoutSubItems
|
||||
@@ -1827,7 +1875,6 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
const auto p = FindName(L"CommandPaletteElement").as<CommandPalette>();
|
||||
|
||||
p.SetCommands(_settings.GlobalSettings().ActionMap().ExpandedCommands());
|
||||
p.SetActionMap(_settings.ActionMap());
|
||||
|
||||
// When the visibility of the command palette changes to "collapsed",
|
||||
|
||||
@@ -300,6 +300,7 @@ namespace winrt::TerminalApp::implementation
|
||||
std::vector<winrt::Windows::UI::Xaml::Controls::MenuFlyoutItemBase> _CreateNewTabFlyoutItems(winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::NewTabMenuEntry> entries);
|
||||
winrt::Windows::UI::Xaml::Controls::IconElement _CreateNewTabFlyoutIcon(const winrt::hstring& icon);
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutProfile(const Microsoft::Terminal::Settings::Model::Profile profile, int profileIndex);
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutAction(const winrt::hstring& actionId);
|
||||
|
||||
void _OpenNewTabDropdown();
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::INewContentArgs& newContentArgs);
|
||||
@@ -543,6 +544,7 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::com_ptr<TerminalTab> _senderOrFocusedTab(const IInspectable& sender);
|
||||
|
||||
void _activePaneChanged(winrt::TerminalApp::TerminalTab tab, Windows::Foundation::IInspectable args);
|
||||
winrt::fire_and_forget _doHandleSuggestions(Microsoft::Terminal::Settings::Model::SuggestionsArgs realArgs);
|
||||
|
||||
#pragma region ActionHandlers
|
||||
// These are all defined in AppActionHandlers.cpp
|
||||
|
||||
@@ -2256,7 +2256,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
return winrt::hstring{ L"" };
|
||||
};
|
||||
|
||||
const auto currentCommand = _terminal->CurrentCommand();
|
||||
const auto trimmedCurrentCommand = trimToHstring(currentCommand);
|
||||
|
||||
@@ -2279,9 +2278,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
auto context = winrt::make_self<CommandHistoryContext>(std::move(commands));
|
||||
context->CurrentCommandline(trimmedCurrentCommand);
|
||||
|
||||
return *context;
|
||||
}
|
||||
|
||||
winrt::hstring ControlCore::CurrentWorkingDirectory() const
|
||||
{
|
||||
return winrt::hstring{ _terminal->GetWorkingDirectory() };
|
||||
}
|
||||
|
||||
Core::Scheme ControlCore::ColorScheme() const noexcept
|
||||
{
|
||||
Core::Scheme s;
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> History;
|
||||
til::property<winrt::hstring> CurrentCommandline;
|
||||
til::property<winrt::hstring> CurrentWorkingDirectory;
|
||||
|
||||
CommandHistoryContext(std::vector<winrt::hstring>&& history)
|
||||
{
|
||||
@@ -184,6 +185,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ContextMenuSelectCommand();
|
||||
void ContextMenuSelectOutput();
|
||||
|
||||
winrt::hstring CurrentWorkingDirectory() const;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ITerminalInput
|
||||
|
||||
@@ -62,5 +62,7 @@ namespace Microsoft.Terminal.Control
|
||||
void SelectOutput(Boolean goUp);
|
||||
IVector<ScrollMark> ScrollMarks { get; };
|
||||
|
||||
String CurrentWorkingDirectory { get; };
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3555,6 +3555,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
return _core.CommandHistory();
|
||||
}
|
||||
winrt::hstring TermControl::CurrentWorkingDirectory() const
|
||||
{
|
||||
return _core.CurrentWorkingDirectory();
|
||||
}
|
||||
|
||||
Core::Scheme TermControl::ColorScheme() const noexcept
|
||||
{
|
||||
|
||||
@@ -113,6 +113,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void SelectCommand(const bool goUp);
|
||||
void SelectOutput(const bool goUp);
|
||||
|
||||
winrt::hstring CurrentWorkingDirectory() const;
|
||||
#pragma endregion
|
||||
|
||||
void ScrollViewport(int viewTop);
|
||||
|
||||
36
src/cascadia/TerminalSettingsModel/ActionEntry.cpp
Normal file
36
src/cascadia/TerminalSettingsModel/ActionEntry.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ActionEntry.h"
|
||||
#include "JsonUtils.h"
|
||||
|
||||
#include "ActionEntry.g.cpp"
|
||||
|
||||
using namespace Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
|
||||
static constexpr std::string_view ActionIdKey{ "actionId" };
|
||||
|
||||
ActionEntry::ActionEntry() noexcept :
|
||||
ActionEntryT<ActionEntry, NewTabMenuEntry>(NewTabMenuEntryType::Action)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Value ActionEntry::ToJson() const
|
||||
{
|
||||
auto json = NewTabMenuEntry::ToJson();
|
||||
|
||||
JsonUtils::SetValueForKey(json, ActionIdKey, _ActionId);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
winrt::com_ptr<NewTabMenuEntry> ActionEntry::FromJson(const Json::Value& json)
|
||||
{
|
||||
auto entry = winrt::make_self<ActionEntry>();
|
||||
|
||||
JsonUtils::GetValueForKey(json, ActionIdKey, entry->_ActionId);
|
||||
|
||||
return entry;
|
||||
}
|
||||
37
src/cascadia/TerminalSettingsModel/ActionEntry.h
Normal file
37
src/cascadia/TerminalSettingsModel/ActionEntry.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- ActionEntry.h
|
||||
|
||||
Abstract:
|
||||
- An action entry in the "new tab" dropdown menu
|
||||
|
||||
Author(s):
|
||||
- Pankaj Bhojwani - May 2024
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "NewTabMenuEntry.h"
|
||||
#include "ActionEntry.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
struct ActionEntry : ActionEntryT<ActionEntry, NewTabMenuEntry>
|
||||
{
|
||||
public:
|
||||
ActionEntry() noexcept;
|
||||
|
||||
Json::Value ToJson() const override;
|
||||
static com_ptr<NewTabMenuEntry> FromJson(const Json::Value& json);
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, ActionId);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ActionEntry);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
struct ActionMap : ActionMapT<ActionMap>, IInheritable<ActionMap>
|
||||
{
|
||||
void _FinalizeInheritance() override;
|
||||
|
||||
// views
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::ActionAndArgs> AvailableActions();
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::Command> NameMap();
|
||||
@@ -58,20 +60,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
// queries
|
||||
Model::Command GetActionByKeyChord(const Control::KeyChord& keys) const;
|
||||
Model::Command GetActionById(const winrt::hstring& cmdID) const;
|
||||
bool IsKeyChordExplicitlyUnbound(const Control::KeyChord& keys) const;
|
||||
Control::KeyChord GetKeyBindingForAction(const ShortcutAction& action) const;
|
||||
Control::KeyChord GetKeyBindingForAction(const ShortcutAction& action, const IActionArgs& actionArgs) const;
|
||||
Control::KeyChord GetKeyBindingForAction(winrt::hstring cmdID);
|
||||
|
||||
// population
|
||||
void AddAction(const Model::Command& cmd);
|
||||
void AddAction(const Model::Command& cmd, const Control::KeyChord& keys);
|
||||
|
||||
// JSON
|
||||
static com_ptr<ActionMap> FromJson(const Json::Value& json, const OriginTag origin = OriginTag::None);
|
||||
std::vector<SettingsLoadWarnings> LayerJson(const Json::Value& json, const OriginTag origin, const bool withKeybindings = true);
|
||||
Json::Value ToJson() const;
|
||||
Json::Value KeyBindingsToJson() const;
|
||||
bool FixUpsAppliedDuringLoad() const;
|
||||
|
||||
// modification
|
||||
bool GenerateIDsForActions();
|
||||
bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys);
|
||||
void DeleteKeyBinding(const Control::KeyChord& keys);
|
||||
void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action);
|
||||
@@ -80,49 +83,54 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
void ExpandCommands(const Windows::Foundation::Collections::IVectorView<Model::Profile>& profiles,
|
||||
const Windows::Foundation::Collections::IMapView<winrt::hstring, Model::ColorScheme>& schemes);
|
||||
|
||||
winrt::Windows::Foundation::Collections::IVector<Model::Command> FilterToSendInput(winrt::hstring currentCommandline);
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Foundation::Collections::IVector<Model::Command>> FilterToSnippets(winrt::hstring currentCommandline, winrt::hstring currentWorkingDirectory);
|
||||
|
||||
private:
|
||||
std::optional<Model::Command> _GetActionByID(const InternalActionID actionID) const;
|
||||
Model::Command _GetActionByID(const winrt::hstring actionID) const;
|
||||
std::optional<winrt::hstring> _GetActionIdByKeyChordInternal(const Control::KeyChord& keys) const;
|
||||
std::optional<Model::Command> _GetActionByKeyChordInternal(const Control::KeyChord& keys) const;
|
||||
|
||||
void _RefreshKeyBindingCaches();
|
||||
void _PopulateAvailableActionsWithStandardCommands(std::unordered_map<hstring, Model::ActionAndArgs>& availableActions, std::unordered_set<InternalActionID>& visitedActionIDs) const;
|
||||
void _PopulateNameMapWithSpecialCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
|
||||
void _PopulateNameMapWithStandardCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
|
||||
void _PopulateKeyBindingMapWithStandardCommands(std::unordered_map<Control::KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<Control::KeyChord, KeyChordHash, KeyChordEquality>& unboundKeys) const;
|
||||
std::vector<Model::Command> _GetCumulativeActions() const noexcept;
|
||||
|
||||
void _TryUpdateActionMap(const Model::Command& cmd, Model::Command& oldCmd, Model::Command& consolidatedCmd);
|
||||
void _TryUpdateName(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd);
|
||||
void _TryUpdateKeyChord(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd);
|
||||
void _PopulateCumulativeKeyMap(std::unordered_map<Control::KeyChord, winrt::hstring, KeyChordHash, KeyChordEquality>& keyBindingsMap);
|
||||
void _PopulateCumulativeActionMap(std::unordered_map<hstring, Model::Command>& actionMap);
|
||||
|
||||
void _recursiveUpdateCommandKeybindingLabels();
|
||||
void _TryUpdateActionMap(const Model::Command& cmd);
|
||||
void _TryUpdateKeyChord(const Model::Command& cmd, const Control::KeyChord& keys);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction _updateLocalSnippetCache(winrt::hstring currentWorkingDirectory);
|
||||
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionsCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NameMapCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _GlobalHotkeysCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _KeyBindingMapCache{ nullptr };
|
||||
|
||||
Windows::Foundation::Collections::IVector<Model::Command> _ExpandedCommandsCache{ nullptr };
|
||||
|
||||
std::unordered_map<winrt::hstring, Model::Command> _NestedCommands;
|
||||
std::vector<Model::Command> _IterableCommands;
|
||||
std::unordered_map<Control::KeyChord, InternalActionID, KeyChordHash, KeyChordEquality> _KeyMap;
|
||||
std::unordered_map<InternalActionID, Model::Command> _ActionMap;
|
||||
|
||||
// Masking Actions:
|
||||
// These are actions that were introduced in an ancestor,
|
||||
// but were edited (or unbound) in the current layer.
|
||||
// _ActionMap shows a Command with keys that were added in this layer,
|
||||
// whereas _MaskingActions provides a view that encompasses all of
|
||||
// the valid associated key chords.
|
||||
// Maintaining this map allows us to return a valid Command
|
||||
// in GetKeyBindingForAction.
|
||||
// Additionally, these commands to not need to be serialized,
|
||||
// whereas those in _ActionMap do. These actions provide more data
|
||||
// than is necessary to be serialized.
|
||||
std::unordered_map<InternalActionID, Model::Command> _MaskingActions;
|
||||
bool _fixUpsAppliedDuringLoad;
|
||||
|
||||
// _KeyMap is the map of key chords -> action IDs defined in this layer
|
||||
// _ActionMap is the map of action IDs -> commands defined in this layer
|
||||
// These maps are the ones that we deserialize into when parsing the user json and vice-versa
|
||||
std::unordered_map<Control::KeyChord, winrt::hstring, KeyChordHash, KeyChordEquality> _KeyMap;
|
||||
std::unordered_map<winrt::hstring, Model::Command> _ActionMap;
|
||||
|
||||
// _CumulativeKeyMapCache is the map of key chords -> action IDs defined in all layers, with child layers overriding parent layers
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, winrt::hstring> _CumulativeKeyMapCache{ nullptr };
|
||||
// _CumulativeActionMapCache is the map of action IDs -> commands defined in all layers, with child layers overriding parent layers
|
||||
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command> _CumulativeActionMapCache{ nullptr };
|
||||
|
||||
// _ResolvedKeyActionMapCache is the map of key chords -> commands defined in all layers, with child layers overriding parent layers
|
||||
// This is effectively a combination of _CumulativeKeyMapCache and _CumulativeActionMapCache and its purpose is so that
|
||||
// we can give the SUI a view of the key chords and the commands they map to
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _ResolvedKeyActionMapCache{ nullptr };
|
||||
|
||||
std::unordered_map<hstring, std::vector<Model::Command>> _cwdLocalSnippetsCache{};
|
||||
|
||||
friend class SettingsModelUnitTests::KeyBindingsTests;
|
||||
friend class SettingsModelUnitTests::DeserializationTests;
|
||||
|
||||
@@ -11,9 +11,8 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Boolean IsKeyChordExplicitlyUnbound(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
Command GetActionByKeyChord(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
[method_name("GetKeyBindingForActionWithArgs")] Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action, IActionArgs actionArgs);
|
||||
Command GetActionById(String cmdID);
|
||||
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(String cmdID);
|
||||
|
||||
Windows.Foundation.Collections.IMapView<String, ActionAndArgs> AvailableActions { get; };
|
||||
|
||||
@@ -23,7 +22,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
IVector<Command> ExpandedCommands { get; };
|
||||
|
||||
IVector<Command> FilterToSendInput(String CurrentCommandline);
|
||||
Windows.Foundation.IAsyncOperation<IVector<Command> > FilterToSnippets(String CurrentCommandline, String CurrentWorkingDirectory);
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ActionMap : IActionMapView
|
||||
|
||||
@@ -27,12 +27,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Deserialize an ActionMap from the array `json`. The json array should contain
|
||||
// an array of serialized `Command` objects.
|
||||
// - These actions are added to the `ActionMap`, where we automatically handle
|
||||
// overwriting and unbinding actions.
|
||||
// - Deserialize an ActionMap from the array `json`
|
||||
// - The json array either contains an array of serialized `Command` objects,
|
||||
// or an array of keybindings
|
||||
// - The actions are added to _ActionMap and the keybindings are added to _KeyMap
|
||||
// Arguments:
|
||||
// - json: an array of Json::Value's to deserialize into our ActionMap.
|
||||
// - json: an array of Json::Value's to deserialize into our _ActionMap and _KeyMap
|
||||
// Return value:
|
||||
// - a list of warnings encountered while deserializing the json
|
||||
std::vector<SettingsLoadWarnings> ActionMap::LayerJson(const Json::Value& json, const OriginTag origin, const bool withKeybindings)
|
||||
@@ -43,14 +43,69 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// settings phase, so we'll collect them now.
|
||||
std::vector<SettingsLoadWarnings> warnings;
|
||||
|
||||
for (const auto& cmdJson : json)
|
||||
for (const auto& jsonBlock : json)
|
||||
{
|
||||
if (!cmdJson.isObject())
|
||||
if (!jsonBlock.isObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddAction(*Command::FromJson(cmdJson, warnings, origin, withKeybindings));
|
||||
// the json block may be 1 of 3 things:
|
||||
// - the legacy style command block, that has the action, args and keys in it
|
||||
// - the modern style command block, that has the action, args and an ID
|
||||
// - the modern style keys block, that has the keys and an ID
|
||||
|
||||
// if the block contains a "command" field, it is either a legacy or modern style command block
|
||||
// and we can call Command::FromJson on it (Command::FromJson can handle parsing both legacy or modern)
|
||||
|
||||
// if there is no "command" field, then it is a modern style keys block
|
||||
|
||||
// if there are keys, extract them first
|
||||
Control::KeyChord keys{ nullptr };
|
||||
if (withKeybindings && jsonBlock.isMember(JsonKey(KeysKey)))
|
||||
{
|
||||
const auto keysJson{ jsonBlock[JsonKey(KeysKey)] };
|
||||
if (keysJson.isArray() && keysJson.size() > 1)
|
||||
{
|
||||
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonUtils::GetValueForKey(jsonBlock, KeysKey, keys);
|
||||
}
|
||||
}
|
||||
|
||||
// Now check if this is a command block
|
||||
if (jsonBlock.isMember(JsonKey(CommandsKey)) || jsonBlock.isMember(JsonKey(ActionKey)))
|
||||
{
|
||||
AddAction(*Command::FromJson(jsonBlock, warnings, origin), keys);
|
||||
|
||||
if (jsonBlock.isMember(JsonKey(KeysKey)))
|
||||
{
|
||||
// there are keys in this command block meaning this is the legacy style -
|
||||
// inform the loader that fixups are needed
|
||||
_fixUpsAppliedDuringLoad = true;
|
||||
}
|
||||
|
||||
if (jsonBlock.isMember(JsonKey(ActionKey)) && !jsonBlock.isMember(JsonKey(IterateOnKey)) && origin == OriginTag::User && !jsonBlock.isMember(JsonKey(IDKey)))
|
||||
{
|
||||
// for non-nested non-iterable commands,
|
||||
// if there's no ID in the command block we will generate one for the user -
|
||||
// inform the loader that the ID needs to be written into the json
|
||||
_fixUpsAppliedDuringLoad = true;
|
||||
}
|
||||
}
|
||||
else if (keys)
|
||||
{
|
||||
// this is not a command block, so it is a keybinding block
|
||||
|
||||
// if the "id" field doesn't exist in the json, then idJson will be an empty string which is fine
|
||||
winrt::hstring idJson;
|
||||
JsonUtils::GetValueForKey(jsonBlock, IDKey, idJson);
|
||||
|
||||
// any existing keybinding with the same keychord in this layer will get overwritten
|
||||
_KeyMap.insert_or_assign(keys, idJson);
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
@@ -60,23 +115,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
Json::Value actionList{ Json::ValueType::arrayValue };
|
||||
|
||||
// Command serializes to an array of JSON objects.
|
||||
// This is because a Command may have multiple key chords associated with it.
|
||||
// The name and icon are only serialized in the first object.
|
||||
// Example:
|
||||
// { "name": "Custom Copy", "command": "copy", "keys": "ctrl+c" }
|
||||
// { "command": "copy", "keys": "ctrl+shift+c" }
|
||||
// { "command": "copy", "keys": "ctrl+ins" }
|
||||
auto toJson = [&actionList](const Model::Command& cmd) {
|
||||
const auto cmdImpl{ winrt::get_self<implementation::Command>(cmd) };
|
||||
const auto& cmdJsonArray{ cmdImpl->ToJson() };
|
||||
for (const auto& cmdJson : cmdJsonArray)
|
||||
{
|
||||
actionList.append(cmdJson);
|
||||
}
|
||||
const auto& cmdJson{ cmdImpl->ToJson() };
|
||||
actionList.append(cmdJson);
|
||||
};
|
||||
|
||||
// Serialize all standard Command objects in the current layer
|
||||
for (const auto& [_, cmd] : _ActionMap)
|
||||
{
|
||||
toJson(cmd);
|
||||
@@ -96,4 +140,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
return actionList;
|
||||
}
|
||||
|
||||
Json::Value ActionMap::KeyBindingsToJson() const
|
||||
{
|
||||
Json::Value keybindingsList{ Json::ValueType::arrayValue };
|
||||
|
||||
auto toJson = [&keybindingsList](const KeyChord kc, const winrt::hstring cmdID) {
|
||||
Json::Value keyIDPair{ Json::ValueType::objectValue };
|
||||
JsonUtils::SetValueForKey(keyIDPair, KeysKey, kc);
|
||||
JsonUtils::SetValueForKey(keyIDPair, IDKey, cmdID);
|
||||
keybindingsList.append(keyIDPair);
|
||||
};
|
||||
|
||||
// Serialize all standard keybinding objects in the current layer
|
||||
for (const auto& [keys, cmdID] : _KeyMap)
|
||||
{
|
||||
toJson(keys, cmdID);
|
||||
}
|
||||
|
||||
return keybindingsList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1095,6 +1095,20 @@ void CascadiaSettings::ExportFile(winrt::hstring path, winrt::hstring content)
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
winrt::hstring CascadiaSettings::ReadFile(winrt::hstring path)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto maybeContents = ReadUTF8FileIfExists({ path.c_str() });
|
||||
if (maybeContents.has_value())
|
||||
{
|
||||
return winrt::hstring{ til::u8u16(*maybeContents) };
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
void CascadiaSettings::_validateThemeExists()
|
||||
{
|
||||
|
||||
@@ -113,6 +113,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
static winrt::hstring ApplicationVersion();
|
||||
static bool IsPortableMode();
|
||||
static void ExportFile(winrt::hstring path, winrt::hstring content);
|
||||
static winrt::hstring ReadFile(winrt::hstring path);
|
||||
|
||||
CascadiaSettings() noexcept = default;
|
||||
CascadiaSettings(const winrt::hstring& userJSON, const winrt::hstring& inboxJSON);
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
static String ApplicationVersion { get; };
|
||||
|
||||
static void ExportFile(String path, String content);
|
||||
static String ReadFile(String path);
|
||||
|
||||
CascadiaSettings(String userJSON, String inboxJSON);
|
||||
|
||||
|
||||
@@ -461,6 +461,7 @@ bool SettingsLoader::FixupUserSettings()
|
||||
};
|
||||
|
||||
auto fixedUp = userSettings.fixupsAppliedDuringLoad;
|
||||
fixedUp = userSettings.globals->FixUpsAppliedDuringLoad() || fixedUp;
|
||||
|
||||
fixedUp = RemapColorSchemeForProfile(userSettings.baseLayerProfile) || fixedUp;
|
||||
for (const auto& profile : userSettings.profiles)
|
||||
@@ -504,10 +505,6 @@ bool SettingsLoader::FixupUserSettings()
|
||||
fixedUp = true;
|
||||
}
|
||||
|
||||
// we need to generate an ID for a command in the user settings if it doesn't already have one
|
||||
auto actionMap{ winrt::get_self<ActionMap>(userSettings.globals->ActionMap()) };
|
||||
actionMap->GenerateIDsForActions();
|
||||
|
||||
return fixedUp;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,6 @@ namespace winrt
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
}
|
||||
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view IDKey{ "id" };
|
||||
static constexpr std::string_view IconKey{ "icon" };
|
||||
static constexpr std::string_view ActionKey{ "command" };
|
||||
static constexpr std::string_view IterateOnKey{ "iterateOn" };
|
||||
static constexpr std::string_view CommandsKey{ "commands" };
|
||||
static constexpr std::string_view KeysKey{ "keys" };
|
||||
|
||||
static constexpr std::string_view ProfileNameToken{ "${profile.name}" };
|
||||
static constexpr std::string_view ProfileIconToken{ "${profile.icon}" };
|
||||
static constexpr std::string_view SchemeNameToken{ "${scheme.name}" };
|
||||
@@ -43,7 +35,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
command->_Origin = _Origin;
|
||||
command->_ID = _ID;
|
||||
command->_ActionAndArgs = *get_self<implementation::ActionAndArgs>(_ActionAndArgs)->Copy();
|
||||
command->_keyMappings = _keyMappings;
|
||||
command->_iconPath = _iconPath;
|
||||
command->_IterateOn = _IterateOn;
|
||||
|
||||
@@ -121,7 +112,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return hstring{ _ID };
|
||||
}
|
||||
|
||||
bool Command::GenerateID()
|
||||
void Command::GenerateID()
|
||||
{
|
||||
if (_ActionAndArgs)
|
||||
{
|
||||
@@ -129,11 +120,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
if (const auto generatedID = actionAndArgsImpl->GenerateID(); !generatedID.empty())
|
||||
{
|
||||
_ID = generatedID;
|
||||
_IDWasGenerated = true;
|
||||
return true;
|
||||
_IdWasGenerated = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Command::IdWasGenerated()
|
||||
{
|
||||
return _IdWasGenerated;
|
||||
}
|
||||
|
||||
void Command::Name(const hstring& value)
|
||||
@@ -144,70 +138,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Control::KeyChord> Command::KeyMappings() const noexcept
|
||||
{
|
||||
return _keyMappings;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Add the key chord to the command's list of key mappings.
|
||||
// - If the key chord was already registered, move it to the back
|
||||
// of the line, and dispatch a notification that Command::Keys changed.
|
||||
// Arguments:
|
||||
// - keys: the new key chord that we are registering this command to
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Command::RegisterKey(const Control::KeyChord& keys)
|
||||
{
|
||||
if (!keys)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the KeyChord and add it to the back of the line.
|
||||
// This makes it so that the main key chord associated with this
|
||||
// command is updated.
|
||||
EraseKey(keys);
|
||||
_keyMappings.push_back(keys);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Remove the key chord from the command's list of key mappings.
|
||||
// Arguments:
|
||||
// - keys: the key chord that we are unregistering
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Command::EraseKey(const Control::KeyChord& keys)
|
||||
{
|
||||
_keyMappings.erase(std::remove_if(_keyMappings.begin(), _keyMappings.end(), [&keys](const Control::KeyChord& iterKey) {
|
||||
return keys.Modifiers() == iterKey.Modifiers() && keys.Vkey() == iterKey.Vkey();
|
||||
}),
|
||||
_keyMappings.end());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Keys is the Command's identifying KeyChord. The command may have multiple keys associated
|
||||
// with it, but we'll only ever display the most recently added one externally. To do this,
|
||||
// _keyMappings stores all of the associated key chords, but ensures that the last entry
|
||||
// is the most recently added one.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the primary key chord associated with this Command
|
||||
Control::KeyChord Command::Keys() const noexcept
|
||||
{
|
||||
if (_keyMappings.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return _keyMappings.back();
|
||||
}
|
||||
|
||||
hstring Command::KeyChordText() const noexcept
|
||||
{
|
||||
return KeyChordSerialization::ToString(Keys());
|
||||
}
|
||||
|
||||
hstring Command::IconPath() const noexcept
|
||||
{
|
||||
if (_iconPath.has_value())
|
||||
@@ -281,8 +211,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// - the newly constructed Command object.
|
||||
winrt::com_ptr<Command> Command::FromJson(const Json::Value& json,
|
||||
std::vector<SettingsLoadWarnings>& warnings,
|
||||
const OriginTag origin,
|
||||
const bool parseKeys)
|
||||
const OriginTag origin)
|
||||
{
|
||||
auto result = winrt::make_self<Command>();
|
||||
result->_Origin = origin;
|
||||
@@ -338,26 +267,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// create an "invalid" ActionAndArgs
|
||||
result->_ActionAndArgs = make<implementation::ActionAndArgs>();
|
||||
}
|
||||
|
||||
if (parseKeys)
|
||||
{
|
||||
// GH#4239 - If the user provided more than one key
|
||||
// chord to a "keys" array, warn the user here.
|
||||
// TODO: GH#1334 - remove this check.
|
||||
const auto keysJson{ json[JsonKey(KeysKey)] };
|
||||
if (keysJson.isArray() && keysJson.size() > 1)
|
||||
{
|
||||
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
|
||||
}
|
||||
else
|
||||
{
|
||||
Control::KeyChord keys{ nullptr };
|
||||
if (JsonUtils::GetValueForKey(json, KeysKey, keys))
|
||||
{
|
||||
result->RegisterKey(keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If an iterable command doesn't have a name set, we'll still just
|
||||
@@ -423,14 +332,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Serialize the Command into an array of json actions
|
||||
// - Serialize the Command into a json value
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - an array of serialized actions
|
||||
// - a serialized command
|
||||
Json::Value Command::ToJson() const
|
||||
{
|
||||
Json::Value cmdList{ Json::ValueType::arrayValue };
|
||||
Json::Value cmdJson{ Json::ValueType::objectValue };
|
||||
|
||||
if (_nestedCommand || _IterateOn != ExpandCommandType::None)
|
||||
{
|
||||
@@ -438,15 +347,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// For these, we can trust _originalJson to be correct.
|
||||
// In fact, we _need_ to use it here because we don't actually deserialize `iterateOn`
|
||||
// until we expand the command.
|
||||
cmdList.append(_originalJson);
|
||||
cmdJson = _originalJson;
|
||||
}
|
||||
else if (_keyMappings.empty())
|
||||
else
|
||||
{
|
||||
// only write out one command
|
||||
Json::Value cmdJson{ Json::ValueType::objectValue };
|
||||
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
|
||||
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
|
||||
if (!_ID.empty() && !_IDWasGenerated)
|
||||
if (!_ID.empty())
|
||||
{
|
||||
JsonUtils::SetValueForKey(cmdJson, IDKey, _ID);
|
||||
}
|
||||
@@ -455,38 +362,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs);
|
||||
}
|
||||
|
||||
cmdList.append(cmdJson);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we'll write out one command per key mapping
|
||||
for (auto keys{ _keyMappings.begin() }; keys != _keyMappings.end(); ++keys)
|
||||
{
|
||||
Json::Value cmdJson{ Json::ValueType::objectValue };
|
||||
|
||||
if (keys == _keyMappings.begin())
|
||||
{
|
||||
// First iteration also writes icon and name
|
||||
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
|
||||
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
|
||||
if (!_ID.empty())
|
||||
{
|
||||
JsonUtils::SetValueForKey(cmdJson, IDKey, _ID);
|
||||
}
|
||||
}
|
||||
|
||||
if (_ActionAndArgs)
|
||||
{
|
||||
cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs);
|
||||
}
|
||||
|
||||
JsonUtils::SetValueForKey(cmdJson, KeysKey, *keys);
|
||||
cmdList.append(cmdJson);
|
||||
}
|
||||
}
|
||||
|
||||
return cmdList;
|
||||
return cmdJson;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -828,4 +706,33 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
return winrt::single_threaded_vector<Model::Command>(std::move(result));
|
||||
}
|
||||
|
||||
IVector<Model::Command> Command::ParseLocalCommands(winrt::hstring localTasksFileContents)
|
||||
{
|
||||
auto data = winrt::to_string(localTasksFileContents);
|
||||
std::string errs;
|
||||
static std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
|
||||
Json::Value root;
|
||||
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
|
||||
{
|
||||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
||||
}
|
||||
|
||||
auto result = std::vector<Model::Command>();
|
||||
if (auto actions{ root[JsonKey("actions")] })
|
||||
{
|
||||
std::vector<SettingsLoadWarnings> warnings;
|
||||
for (const auto& json : actions)
|
||||
{
|
||||
auto parsed = Command::FromJson(json, warnings, OriginTag::Generated);
|
||||
if (parsed->ActionAndArgs().Action() != ShortcutAction::SendInput)
|
||||
continue;
|
||||
// commands.Append(*parsed);
|
||||
result.push_back(*parsed);
|
||||
}
|
||||
}
|
||||
|
||||
return winrt::single_threaded_vector<Model::Command>(std::move(result));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,14 @@ namespace SettingsModelUnitTests
|
||||
class CommandTests;
|
||||
};
|
||||
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view IDKey{ "id" };
|
||||
static constexpr std::string_view IconKey{ "icon" };
|
||||
static constexpr std::string_view ActionKey{ "command" };
|
||||
static constexpr std::string_view IterateOnKey{ "iterateOn" };
|
||||
static constexpr std::string_view CommandsKey{ "commands" };
|
||||
static constexpr std::string_view KeysKey{ "keys" };
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
struct Command : CommandT<Command>
|
||||
@@ -40,8 +48,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
static winrt::com_ptr<Command> FromJson(const Json::Value& json,
|
||||
std::vector<SettingsLoadWarnings>& warnings,
|
||||
const OriginTag origin,
|
||||
const bool parseKeys = true);
|
||||
const OriginTag origin);
|
||||
|
||||
static void ExpandCommands(Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command>& commands,
|
||||
Windows::Foundation::Collections::IVectorView<Model::Profile> profiles,
|
||||
@@ -62,13 +69,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
void Name(const hstring& name);
|
||||
|
||||
hstring ID() const noexcept;
|
||||
bool GenerateID();
|
||||
|
||||
Control::KeyChord Keys() const noexcept;
|
||||
hstring KeyChordText() const noexcept;
|
||||
std::vector<Control::KeyChord> KeyMappings() const noexcept;
|
||||
void RegisterKey(const Control::KeyChord& keys);
|
||||
void EraseKey(const Control::KeyChord& keys);
|
||||
void GenerateID();
|
||||
bool IdWasGenerated();
|
||||
|
||||
hstring IconPath() const noexcept;
|
||||
void IconPath(const hstring& val);
|
||||
@@ -77,6 +79,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
static Windows::Foundation::Collections::IVector<Model::Command> HistoryToCommands(Windows::Foundation::Collections::IVector<winrt::hstring> history,
|
||||
winrt::hstring currentCommandline,
|
||||
bool directories);
|
||||
static Windows::Foundation::Collections::IVector<Model::Command> ParseLocalCommands(winrt::hstring localTasksFileContents);
|
||||
|
||||
WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
|
||||
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);
|
||||
@@ -85,10 +88,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
private:
|
||||
Json::Value _originalJson;
|
||||
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command> _subcommands{ nullptr };
|
||||
std::vector<Control::KeyChord> _keyMappings;
|
||||
std::optional<std::wstring> _name;
|
||||
std::wstring _ID;
|
||||
bool _IDWasGenerated{ false };
|
||||
bool _IdWasGenerated{ false };
|
||||
std::optional<std::wstring> _iconPath;
|
||||
bool _nestedCommand{ false };
|
||||
|
||||
|
||||
@@ -38,9 +38,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
String Name { get; };
|
||||
String ID { get; };
|
||||
ActionAndArgs ActionAndArgs { get; };
|
||||
Microsoft.Terminal.Control.KeyChord Keys { get; };
|
||||
void RegisterKey(Microsoft.Terminal.Control.KeyChord keys);
|
||||
String KeyChordText { get; };
|
||||
|
||||
String IconPath;
|
||||
|
||||
@@ -49,6 +46,5 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
static IVector<Command> ParsePowerShellMenuComplete(String json, Int32 replaceLength);
|
||||
static IVector<Command> HistoryToCommands(IVector<String> commandHistory, String commandline, Boolean directories);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ void GlobalAppSettings::_FinalizeInheritance()
|
||||
}
|
||||
}
|
||||
}
|
||||
_actionMap->_FinalizeInheritance();
|
||||
}
|
||||
|
||||
winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
|
||||
@@ -155,7 +156,9 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi
|
||||
|
||||
void GlobalAppSettings::LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings)
|
||||
{
|
||||
static constexpr std::array bindingsKeys{ LegacyKeybindingsKey, ActionsKey };
|
||||
// we want to do the keybindings map after the actions map so that we overwrite any leftover keybindings
|
||||
// that might have existed in the first pass, in case the user did a partial update from legacy to modern
|
||||
static constexpr std::array bindingsKeys{ ActionsKey, LegacyKeybindingsKey };
|
||||
for (const auto& jsonKey : bindingsKeys)
|
||||
{
|
||||
if (auto bindings{ json[JsonKey(jsonKey)] })
|
||||
@@ -259,9 +262,16 @@ Json::Value GlobalAppSettings::ToJson()
|
||||
#undef GLOBAL_SETTINGS_TO_JSON
|
||||
|
||||
json[JsonKey(ActionsKey)] = _actionMap->ToJson();
|
||||
json[JsonKey(LegacyKeybindingsKey)] = _actionMap->KeyBindingsToJson();
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool GlobalAppSettings::FixUpsAppliedDuringLoad()
|
||||
{
|
||||
return _actionMap->FixUpsAppliedDuringLoad();
|
||||
}
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentTheme() noexcept
|
||||
{
|
||||
auto requestedTheme = Model::Theme::IsSystemInDarkTheme() ?
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
void LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings = true);
|
||||
|
||||
Json::Value ToJson();
|
||||
bool FixUpsAppliedDuringLoad();
|
||||
|
||||
const std::vector<SettingsLoadWarnings>& KeybindingsWarnings() const;
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
<ClInclude Include="SeparatorEntry.h">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ActionEntry.h">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FolderEntry.h">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@@ -185,6 +188,9 @@
|
||||
<ClCompile Include="SeparatorEntry.cpp">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ActionEntry.cpp">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FolderEntry.cpp">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "SeparatorEntry.h"
|
||||
#include "FolderEntry.h"
|
||||
#include "ProfileEntry.h"
|
||||
#include "ActionEntry.h"
|
||||
#include "RemainingProfilesEntry.h"
|
||||
#include "MatchProfilesEntry.h"
|
||||
|
||||
@@ -52,6 +53,8 @@ winrt::com_ptr<NewTabMenuEntry> NewTabMenuEntry::FromJson(const Json::Value& jso
|
||||
return RemainingProfilesEntry::FromJson(json);
|
||||
case NewTabMenuEntryType::MatchProfiles:
|
||||
return MatchProfilesEntry::FromJson(json);
|
||||
case NewTabMenuEntryType::Action:
|
||||
return ActionEntry::FromJson(json);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Separator,
|
||||
Folder,
|
||||
RemainingProfiles,
|
||||
MatchProfiles
|
||||
MatchProfiles,
|
||||
Action
|
||||
};
|
||||
|
||||
[default_interface] unsealed runtimeclass NewTabMenuEntry
|
||||
@@ -34,6 +35,13 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Int32 ProfileIndex;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ActionEntry : NewTabMenuEntry
|
||||
{
|
||||
ActionEntry();
|
||||
|
||||
String ActionId;
|
||||
}
|
||||
|
||||
enum FolderEntryInlining
|
||||
{
|
||||
Never = 0,
|
||||
|
||||
@@ -501,9 +501,10 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FindMatchDirecti
|
||||
|
||||
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SuggestionsSource)
|
||||
{
|
||||
static constexpr std::array<pair_type, 5> mappings = {
|
||||
static constexpr std::array<pair_type, 6> mappings = {
|
||||
pair_type{ "none", AllClear },
|
||||
pair_type{ "tasks", ValueType::Tasks },
|
||||
pair_type{ "snippets", ValueType::Tasks },
|
||||
pair_type{ "commandHistory", ValueType::CommandHistory },
|
||||
pair_type{ "directoryHistory", ValueType::DirectoryHistory },
|
||||
pair_type{ "all", AllSet },
|
||||
@@ -678,8 +679,9 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollToMarkDirection)
|
||||
// Possible NewTabMenuEntryType values
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::NewTabMenuEntryType)
|
||||
{
|
||||
JSON_MAPPINGS(5) = {
|
||||
JSON_MAPPINGS(6) = {
|
||||
pair_type{ "profile", ValueType::Profile },
|
||||
pair_type{ "action", ValueType::Action },
|
||||
pair_type{ "separator", ValueType::Separator },
|
||||
pair_type{ "folder", ValueType::Folder },
|
||||
pair_type{ "remainingProfiles", ValueType::RemainingProfiles },
|
||||
|
||||
@@ -422,28 +422,27 @@
|
||||
],
|
||||
"actions":
|
||||
[
|
||||
// Application-level Keys
|
||||
{ "command": "closeWindow", "keys": "alt+f4", "id": "Terminal.CloseWindow" },
|
||||
{ "command": "toggleFullscreen", "keys": "alt+enter", "id": "Terminal.ToggleFullscreen" },
|
||||
{ "command": "toggleFullscreen", "keys": "f11", "id": "Terminal.ToggleFullscreen" },
|
||||
// Application-level Commands
|
||||
{ "command": "closeWindow", "id": "Terminal.CloseWindow" },
|
||||
{ "command": "toggleFullscreen", "id": "Terminal.ToggleFullscreen" },
|
||||
{ "command": "toggleFocusMode", "id": "Terminal.ToggleFocusMode" },
|
||||
{ "command": "toggleAlwaysOnTop", "id": "Terminal.ToggleAlwaysOnTop" },
|
||||
{ "command": "openNewTabDropdown", "keys": "ctrl+shift+space", "id": "Terminal.OpenNewTabDropdown" },
|
||||
{ "command": { "action": "openSettings", "target": "settingsUI" }, "keys": "ctrl+,", "id": "Terminal.OpenSettingsUI" },
|
||||
{ "command": { "action": "openSettings", "target": "settingsFile" }, "keys": "ctrl+shift+,", "id": "Terminal.OpenSettingsFile" },
|
||||
{ "command": { "action": "openSettings", "target": "defaultsFile" }, "keys": "ctrl+alt+,", "id": "Terminal.OpenDefaultSettingsFile" },
|
||||
{ "command": "find", "keys": "ctrl+shift+f", "id": "Terminal.FindText" },
|
||||
{ "command": "openNewTabDropdown", "id": "Terminal.OpenNewTabDropdown" },
|
||||
{ "command": { "action": "openSettings", "target": "settingsUI" }, "id": "Terminal.OpenSettingsUI" },
|
||||
{ "command": { "action": "openSettings", "target": "settingsFile" }, "id": "Terminal.OpenSettingsFile" },
|
||||
{ "command": { "action": "openSettings", "target": "defaultsFile" }, "id": "Terminal.OpenDefaultSettingsFile" },
|
||||
{ "command": "find", "id": "Terminal.FindText" },
|
||||
{ "command": { "action": "findMatch", "direction": "next" }, "id": "Terminal.FindNextMatch" },
|
||||
{ "command": { "action": "findMatch", "direction": "prev" }, "id": "Terminal.FindPrevMatch" },
|
||||
{ "command": "toggleShaderEffects", "id": "Terminal.ToggleShaderEffects" },
|
||||
{ "command": "openTabColorPicker", "id": "Terminal.OpenTabColorPicker" },
|
||||
{ "command": "renameTab", "id": "Terminal.RenameTab" },
|
||||
{ "command": "openTabRenamer", "id": "Terminal.OpenTabRenamer" },
|
||||
{ "command": "commandPalette", "keys":"ctrl+shift+p", "id": "Terminal.ToggleCommandPalette" },
|
||||
{ "command": "commandPalette", "id": "Terminal.ToggleCommandPalette" },
|
||||
{ "command": "identifyWindow", "id": "Terminal.IdentifyWindow" },
|
||||
{ "command": "openWindowRenamer", "id": "Terminal.OpenWindowRenamer" },
|
||||
{ "command": "quakeMode", "keys":"win+sc(41)", "id": "Terminal.QuakeMode" },
|
||||
{ "command": "openSystemMenu", "keys": "alt+space", "id": "Terminal.OpenSystemMenu" },
|
||||
{ "command": "quakeMode", "id": "Terminal.QuakeMode" },
|
||||
{ "command": "openSystemMenu", "id": "Terminal.OpenSystemMenu" },
|
||||
{ "command": "quit", "id": "Terminal.Quit" },
|
||||
{ "command": "restoreLastClosed", "id": "Terminal.RestoreLastClosed" },
|
||||
{ "command": "openAbout", "id": "Terminal.OpenAboutDialog" },
|
||||
@@ -455,49 +454,50 @@
|
||||
{ "command": "closeTabsAfter", "id": "Terminal.CloseTabsAfter" },
|
||||
{ "command": { "action" : "moveTab", "direction": "forward" }, "id": "Terminal.MoveTabForward" },
|
||||
{ "command": { "action" : "moveTab", "direction": "backward" }, "id": "Terminal.MoveTabBackward" },
|
||||
{ "command": "newTab", "keys": "ctrl+shift+t", "id": "Terminal.OpenNewTab" },
|
||||
{ "command": "newWindow", "keys": "ctrl+shift+n", "id": "Terminal.OpenNewWindow" },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+shift+1", "id": "Terminal.OpenNewTabProfile0" },
|
||||
{ "command": { "action": "newTab", "index": 1 }, "keys": "ctrl+shift+2", "id": "Terminal.OpenNewTabProfile1" },
|
||||
{ "command": { "action": "newTab", "index": 2 }, "keys": "ctrl+shift+3", "id": "Terminal.OpenNewTabProfile2" },
|
||||
{ "command": { "action": "newTab", "index": 3 }, "keys": "ctrl+shift+4", "id": "Terminal.OpenNewTabProfile3" },
|
||||
{ "command": { "action": "newTab", "index": 4 }, "keys": "ctrl+shift+5", "id": "Terminal.OpenNewTabProfile4" },
|
||||
{ "command": { "action": "newTab", "index": 5 }, "keys": "ctrl+shift+6", "id": "Terminal.OpenNewTabProfile5" },
|
||||
{ "command": { "action": "newTab", "index": 6 }, "keys": "ctrl+shift+7", "id": "Terminal.OpenNewTabProfile6" },
|
||||
{ "command": { "action": "newTab", "index": 7 }, "keys": "ctrl+shift+8", "id": "Terminal.OpenNewTabProfile7" },
|
||||
{ "command": { "action": "newTab", "index": 8 }, "keys": "ctrl+shift+9", "id": "Terminal.OpenNewTabProfile8" },
|
||||
{ "command": "duplicateTab", "keys": "ctrl+shift+d", "id": "Terminal.DuplicateTab" },
|
||||
{ "command": "nextTab", "keys": "ctrl+tab", "id": "Terminal.NextTab" },
|
||||
{ "command": "prevTab", "keys": "ctrl+shift+tab", "id": "Terminal.PrevTab" },
|
||||
{ "command": { "action": "switchToTab", "index": 0 }, "keys": "ctrl+alt+1", "id": "Terminal.SwitchToTab0" },
|
||||
{ "command": { "action": "switchToTab", "index": 1 }, "keys": "ctrl+alt+2", "id": "Terminal.SwitchToTab1" },
|
||||
{ "command": { "action": "switchToTab", "index": 2 }, "keys": "ctrl+alt+3", "id": "Terminal.SwitchToTab2" },
|
||||
{ "command": { "action": "switchToTab", "index": 3 }, "keys": "ctrl+alt+4", "id": "Terminal.SwitchToTab3" },
|
||||
{ "command": { "action": "switchToTab", "index": 4 }, "keys": "ctrl+alt+5", "id": "Terminal.SwitchToTab4" },
|
||||
{ "command": { "action": "switchToTab", "index": 5 }, "keys": "ctrl+alt+6", "id": "Terminal.SwitchToTab5" },
|
||||
{ "command": { "action": "switchToTab", "index": 6 }, "keys": "ctrl+alt+7", "id": "Terminal.SwitchToTab6" },
|
||||
{ "command": { "action": "switchToTab", "index": 7 }, "keys": "ctrl+alt+8", "id": "Terminal.SwitchToTab7" },
|
||||
{ "command": { "action": "switchToTab", "index": 4294967295 }, "keys": "ctrl+alt+9", "id": "Terminal.SwitchToLastTab" },
|
||||
{ "command": "newTab", "id": "Terminal.OpenNewTab" },
|
||||
{ "command": "newWindow", "id": "Terminal.OpenNewWindow" },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "id": "Terminal.OpenNewTabProfile0" },
|
||||
{ "command": { "action": "newTab", "index": 1 }, "id": "Terminal.OpenNewTabProfile1" },
|
||||
{ "command": { "action": "newTab", "index": 2 }, "id": "Terminal.OpenNewTabProfile2" },
|
||||
{ "command": { "action": "newTab", "index": 3 }, "id": "Terminal.OpenNewTabProfile3" },
|
||||
{ "command": { "action": "newTab", "index": 4 }, "id": "Terminal.OpenNewTabProfile4" },
|
||||
{ "command": { "action": "newTab", "index": 5 }, "id": "Terminal.OpenNewTabProfile5" },
|
||||
{ "command": { "action": "newTab", "index": 6 }, "id": "Terminal.OpenNewTabProfile6" },
|
||||
{ "command": { "action": "newTab", "index": 7 }, "id": "Terminal.OpenNewTabProfile7" },
|
||||
{ "command": { "action": "newTab", "index": 8 }, "id": "Terminal.OpenNewTabProfile8" },
|
||||
{ "command": "duplicateTab", "id": "Terminal.DuplicateTab" },
|
||||
{ "command": "nextTab", "id": "Terminal.NextTab" },
|
||||
{ "command": "prevTab", "id": "Terminal.PrevTab" },
|
||||
{ "command": { "action": "switchToTab", "index": 0 }, "id": "Terminal.SwitchToTab0" },
|
||||
{ "command": { "action": "switchToTab", "index": 1 }, "id": "Terminal.SwitchToTab1" },
|
||||
{ "command": { "action": "switchToTab", "index": 2 }, "id": "Terminal.SwitchToTab2" },
|
||||
{ "command": { "action": "switchToTab", "index": 3 }, "id": "Terminal.SwitchToTab3" },
|
||||
{ "command": { "action": "switchToTab", "index": 4 }, "id": "Terminal.SwitchToTab4" },
|
||||
{ "command": { "action": "switchToTab", "index": 5 }, "id": "Terminal.SwitchToTab5" },
|
||||
{ "command": { "action": "switchToTab", "index": 6 }, "id": "Terminal.SwitchToTab6" },
|
||||
{ "command": { "action": "switchToTab", "index": 7 }, "id": "Terminal.SwitchToTab7" },
|
||||
{ "command": { "action": "switchToTab", "index": 4294967295 }, "id": "Terminal.SwitchToLastTab" },
|
||||
{ "command": { "action": "moveTab", "window": "new" }, "id": "Terminal.MoveTabToNewWindow" },
|
||||
|
||||
// Pane Management
|
||||
{ "command": "closeOtherPanes", "id": "Terminal.CloseOtherPanes" },
|
||||
{ "command": "closePane", "keys": "ctrl+shift+w", "id": "Terminal.ClosePane" },
|
||||
{ "command": "closePane", "id": "Terminal.ClosePane" },
|
||||
{ "command": { "action": "splitPane", "split": "up" }, "id": "Terminal.SplitPaneUp" },
|
||||
{ "command": { "action": "splitPane", "split": "down" }, "id": "Terminal.SplitPaneDown" },
|
||||
{ "command": { "action": "splitPane", "split": "left" }, "id": "Terminal.SplitPaneLeft" },
|
||||
{ "command": { "action": "splitPane", "split": "right" }, "id": "Terminal.SplitPaneRight" },
|
||||
{ "command": { "action": "splitPane", "splitMode": "duplicate", "split": "down" }, "keys": "alt+shift+-", "id": "Terminal.SplitPaneDuplicateDown" },
|
||||
{ "command": { "action": "splitPane", "splitMode": "duplicate", "split": "right" }, "keys": "alt+shift+plus", "id": "Terminal.SplitPaneDuplicateRight" },
|
||||
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down", "id": "Terminal.ResizePaneDown" },
|
||||
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left", "id": "Terminal.ResizePaneLeft" },
|
||||
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right", "id": "Terminal.ResizePaneRight" },
|
||||
{ "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up", "id": "Terminal.ResizePaneUp" },
|
||||
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down", "id": "Terminal.MoveFocusDown" },
|
||||
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left", "id": "Terminal.MoveFocusLeft" },
|
||||
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right", "id": "Terminal.MoveFocusRight" },
|
||||
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up", "id": "Terminal.MoveFocusUp" },
|
||||
{ "command": { "action": "moveFocus", "direction": "previous" }, "keys": "ctrl+alt+left", "id": "Terminal.MoveFocusPrevious" },
|
||||
{ "command": { "action": "splitPane", "splitMode": "duplicate", "split": "down" }, "id": "Terminal.DuplicatePaneDown" },
|
||||
{ "command": { "action": "splitPane", "splitMode": "duplicate", "split": "right" }, "id": "Terminal.DuplicatePaneRight" },
|
||||
{ "command": { "action": "splitPane", "splitMode": "duplicate", "split": "auto" }, "id": "Terminal.DuplicatePaneAuto" },
|
||||
{ "command": { "action": "resizePane", "direction": "down" }, "id": "Terminal.ResizePaneDown" },
|
||||
{ "command": { "action": "resizePane", "direction": "left" }, "id": "Terminal.ResizePaneLeft" },
|
||||
{ "command": { "action": "resizePane", "direction": "right" }, "id": "Terminal.ResizePaneRight" },
|
||||
{ "command": { "action": "resizePane", "direction": "up" }, "id": "Terminal.ResizePaneUp" },
|
||||
{ "command": { "action": "moveFocus", "direction": "down" }, "id": "Terminal.MoveFocusDown" },
|
||||
{ "command": { "action": "moveFocus", "direction": "left" }, "id": "Terminal.MoveFocusLeft" },
|
||||
{ "command": { "action": "moveFocus", "direction": "right" }, "id": "Terminal.MoveFocusRight" },
|
||||
{ "command": { "action": "moveFocus", "direction": "up" }, "id": "Terminal.MoveFocusUp" },
|
||||
{ "command": { "action": "moveFocus", "direction": "previous" }, "id": "Terminal.MoveFocusPrevious" },
|
||||
{ "command": { "action": "moveFocus", "direction": "previousInOrder" }, "id": "Terminal.MoveFocusPreviousInOrder" },
|
||||
{ "command": { "action": "moveFocus", "direction": "nextInOrder" }, "id": "Terminal.MoveFocusNextInOrder" },
|
||||
{ "command": { "action": "moveFocus", "direction": "first" }, "id": "Terminal.MoveFocusFirst" },
|
||||
@@ -530,38 +530,35 @@
|
||||
{ "command": "restartConnection", "id": "Terminal.RestartConnection" },
|
||||
|
||||
// Clipboard Integration
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+shift+c", "id": "Terminal.CopySelectedText" },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+insert", "id": "Terminal.CopySelectedText" },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": "enter", "id": "Terminal.CopySelectedText" },
|
||||
{ "command": "paste", "keys": "ctrl+shift+v", "id": "Terminal.PasteFromClipboard" },
|
||||
{ "command": "paste", "keys": "shift+insert", "id": "Terminal.PasteFromClipboard" },
|
||||
{ "command": "selectAll", "keys": "ctrl+shift+a", "id": "Terminal.SelectAll" },
|
||||
{ "command": "markMode", "keys": "ctrl+shift+m", "id": "Terminal.ToggleMarkMode" },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "id": "Terminal.CopySelectedText" },
|
||||
{ "command": "paste", "id": "Terminal.PasteFromClipboard" },
|
||||
{ "command": "selectAll", "id": "Terminal.SelectAll" },
|
||||
{ "command": "markMode", "id": "Terminal.ToggleMarkMode" },
|
||||
{ "command": "toggleBlockSelection", "id": "Terminal.ToggleBlockSelection" },
|
||||
{ "command": "switchSelectionEndpoint", "id": "Terminal.SwitchSelectionEndpoint" },
|
||||
{ "command": "expandSelectionToWord", "id": "Terminal.ExpandSelectionToWord" },
|
||||
{ "command": "showContextMenu", "keys": "menu", "id": "Terminal.ShowContextMenu" },
|
||||
{ "command": "showContextMenu", "id": "Terminal.ShowContextMenu" },
|
||||
|
||||
// Web Search
|
||||
{ "command": { "action": "searchWeb" }, "name": { "key": "SearchWebCommandKey" }, "id": "Terminal.SearchWeb" },
|
||||
|
||||
// Scrollback
|
||||
{ "command": "scrollDown", "keys": "ctrl+shift+down", "id": "Terminal.ScrollDown" },
|
||||
{ "command": "scrollDownPage", "keys": "ctrl+shift+pgdn", "id": "Terminal.ScrollDownPage" },
|
||||
{ "command": "scrollUp", "keys": "ctrl+shift+up", "id": "Terminal.ScrollUp" },
|
||||
{ "command": "scrollUpPage", "keys": "ctrl+shift+pgup", "id": "Terminal.ScrollUpPage" },
|
||||
{ "command": "scrollToTop", "keys": "ctrl+shift+home", "id": "Terminal.ScrollToTop" },
|
||||
{ "command": "scrollToBottom", "keys": "ctrl+shift+end", "id": "Terminal.ScrollToBottom" },
|
||||
{ "command": "scrollDown", "id": "Terminal.ScrollDown" },
|
||||
{ "command": "scrollDownPage", "id": "Terminal.ScrollDownPage" },
|
||||
{ "command": "scrollUp", "id": "Terminal.ScrollUp" },
|
||||
{ "command": "scrollUpPage", "id": "Terminal.ScrollUpPage" },
|
||||
{ "command": "scrollToTop", "id": "Terminal.ScrollToTop" },
|
||||
{ "command": "scrollToBottom", "id": "Terminal.ScrollToBottom" },
|
||||
{ "command": { "action": "clearBuffer", "clear": "all" }, "id": "Terminal.ClearBuffer" },
|
||||
{ "command": "exportBuffer", "id": "Terminal.ExportBuffer" },
|
||||
|
||||
// Visual Adjustments
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+plus", "id": "Terminal.IncreaseFontSize" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+minus", "id": "Terminal.DecreaseFontSize" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+numpad_plus", "id": "Terminal.IncreaseFontSize" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+numpad_minus", "id": "Terminal.DecreaseFontSize" },
|
||||
{ "command": "resetFontSize", "keys": "ctrl+0", "id": "Terminal.ResetFontSize" },
|
||||
{ "command": "resetFontSize", "keys": "ctrl+numpad_0", "id": "Terminal.ResetFontSize" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "id": "Terminal.IncreaseFontSize" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "id": "Terminal.DecreaseFontSize" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "id": "Terminal.IncreaseFontSize" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "id": "Terminal.DecreaseFontSize" },
|
||||
{ "command": "resetFontSize", "id": "Terminal.ResetFontSize" },
|
||||
{ "command": "resetFontSize", "id": "Terminal.ResetFontSize" },
|
||||
|
||||
// Other commands
|
||||
{
|
||||
@@ -626,5 +623,86 @@
|
||||
{ "command": { "action": "adjustOpacity", "opacity": 100, "relative": false } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
// Application-level Keys
|
||||
{ "keys": "alt+f4", "id": "Terminal.CloseWindow" },
|
||||
{ "keys": "alt+enter", "id": "Terminal.ToggleFullscreen" },
|
||||
{ "keys": "f11", "id": "Terminal.ToggleFullscreen" },
|
||||
{ "keys": "ctrl+shift+space", "id": "Terminal.OpenNewTabDropdown" },
|
||||
{ "keys": "ctrl+,", "id": "Terminal.OpenSettingsUI" },
|
||||
{ "keys": "ctrl+shift+,", "id": "Terminal.OpenSettingsFile" },
|
||||
{ "keys": "ctrl+alt+,", "id": "Terminal.OpenDefaultSettingsFile" },
|
||||
{ "keys": "ctrl+shift+f", "id": "Terminal.FindText" },
|
||||
{ "keys":"ctrl+shift+p", "id": "Terminal.ToggleCommandPalette" },
|
||||
{ "keys":"win+sc(41)", "id": "Terminal.QuakeMode" },
|
||||
{ "keys": "alt+space", "id": "Terminal.OpenSystemMenu" },
|
||||
|
||||
// Tab Management
|
||||
// "command": "closeTab" is unbound by default.
|
||||
// The closeTab command closes a tab without confirmation, even if it has multiple panes.
|
||||
{ "keys": "ctrl+shift+t", "id": "Terminal.OpenNewTab" },
|
||||
{ "keys": "ctrl+shift+n", "id": "Terminal.OpenNewWindow" },
|
||||
{ "keys": "ctrl+shift+1", "id": "Terminal.OpenNewTabProfile0" },
|
||||
{ "keys": "ctrl+shift+2", "id": "Terminal.OpenNewTabProfile1" },
|
||||
{ "keys": "ctrl+shift+3", "id": "Terminal.OpenNewTabProfile2" },
|
||||
{ "keys": "ctrl+shift+4", "id": "Terminal.OpenNewTabProfile3" },
|
||||
{ "keys": "ctrl+shift+5", "id": "Terminal.OpenNewTabProfile4" },
|
||||
{ "keys": "ctrl+shift+6", "id": "Terminal.OpenNewTabProfile5" },
|
||||
{ "keys": "ctrl+shift+7", "id": "Terminal.OpenNewTabProfile6" },
|
||||
{ "keys": "ctrl+shift+8", "id": "Terminal.OpenNewTabProfile7" },
|
||||
{ "keys": "ctrl+shift+9", "id": "Terminal.OpenNewTabProfile8" },
|
||||
{ "keys": "ctrl+shift+d", "id": "Terminal.DuplicateTab" },
|
||||
{ "keys": "ctrl+tab", "id": "Terminal.NextTab" },
|
||||
{ "keys": "ctrl+shift+tab", "id": "Terminal.PrevTab" },
|
||||
{ "keys": "ctrl+alt+1", "id": "Terminal.SwitchToTab0" },
|
||||
{ "keys": "ctrl+alt+2", "id": "Terminal.SwitchToTab1" },
|
||||
{ "keys": "ctrl+alt+3", "id": "Terminal.SwitchToTab2" },
|
||||
{ "keys": "ctrl+alt+4", "id": "Terminal.SwitchToTab3" },
|
||||
{ "keys": "ctrl+alt+5", "id": "Terminal.SwitchToTab4" },
|
||||
{ "keys": "ctrl+alt+6", "id": "Terminal.SwitchToTab5" },
|
||||
{ "keys": "ctrl+alt+7", "id": "Terminal.SwitchToTab6" },
|
||||
{ "keys": "ctrl+alt+8", "id": "Terminal.SwitchToTab7" },
|
||||
{ "keys": "ctrl+alt+9", "id": "Terminal.SwitchToLastTab" },
|
||||
|
||||
// Pane Management
|
||||
{ "keys": "ctrl+shift+w", "id": "Terminal.ClosePane" },
|
||||
{ "keys": "alt+shift+-", "id": "Terminal.DuplicatePaneDown" },
|
||||
{ "keys": "alt+shift+plus", "id": "Terminal.DuplicatePaneRight" },
|
||||
{ "keys": "alt+shift+down", "id": "Terminal.ResizePaneDown" },
|
||||
{ "keys": "alt+shift+left", "id": "Terminal.ResizePaneLeft" },
|
||||
{ "keys": "alt+shift+right", "id": "Terminal.ResizePaneRight" },
|
||||
{ "keys": "alt+shift+up", "id": "Terminal.ResizePaneUp" },
|
||||
{ "keys": "alt+down", "id": "Terminal.MoveFocusDown" },
|
||||
{ "keys": "alt+left", "id": "Terminal.MoveFocusLeft" },
|
||||
{ "keys": "alt+right", "id": "Terminal.MoveFocusRight" },
|
||||
{ "keys": "alt+up", "id": "Terminal.MoveFocusUp" },
|
||||
{ "keys": "ctrl+alt+left", "id": "Terminal.MoveFocusPrevious" },
|
||||
|
||||
// Clipboard Integration
|
||||
{ "keys": "ctrl+shift+c", "id": "Terminal.CopySelectedText" },
|
||||
{ "keys": "ctrl+insert", "id": "Terminal.CopySelectedText" },
|
||||
{ "keys": "enter", "id": "Terminal.CopySelectedText" },
|
||||
{ "keys": "ctrl+shift+v", "id": "Terminal.PasteFromClipboard" },
|
||||
{ "keys": "shift+insert", "id": "Terminal.PasteFromClipboard" },
|
||||
{ "keys": "ctrl+shift+a", "id": "Terminal.SelectAll" },
|
||||
{ "keys": "ctrl+shift+m", "id": "Terminal.ToggleMarkMode" },
|
||||
{ "keys": "menu", "id": "Terminal.ShowContextMenu" },
|
||||
|
||||
// Scrollback
|
||||
{ "keys": "ctrl+shift+down", "id": "Terminal.ScrollDown" },
|
||||
{ "keys": "ctrl+shift+pgdn", "id": "Terminal.ScrollDownPage" },
|
||||
{ "keys": "ctrl+shift+up", "id": "Terminal.ScrollUp" },
|
||||
{ "keys": "ctrl+shift+pgup", "id": "Terminal.ScrollUpPage" },
|
||||
{ "keys": "ctrl+shift+home", "id": "Terminal.ScrollToTop" },
|
||||
{ "keys": "ctrl+shift+end", "id": "Terminal.ScrollToBottom" },
|
||||
|
||||
// Visual Adjustments
|
||||
{ "keys": "ctrl+plus", "id": "Terminal.IncreaseFontSize" },
|
||||
{ "keys": "ctrl+minus", "id": "Terminal.DecreaseFontSize" },
|
||||
{ "keys": "ctrl+numpad_plus", "id": "Terminal.IncreaseFontSize" },
|
||||
{ "keys": "ctrl+numpad_minus", "id": "Terminal.DecreaseFontSize" },
|
||||
{ "keys": "ctrl+0", "id": "Terminal.ResetFontSize" },
|
||||
{ "keys": "ctrl+numpad_0", "id": "Terminal.ResetFontSize" },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
<ClInclude Include="../Profile.h" />
|
||||
<ClInclude Include="../TerminalWarnings.h" />
|
||||
<ClInclude Include="../NewTabMenuEntry.h" />
|
||||
<ClInclude Include="../ActionEntry.h">
|
||||
<DependentUpon>../NewTabMenuEntry.h</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../SeparatorEntry.h">
|
||||
<DependentUpon>../NewTabMenuEntry.h</DependentUpon>
|
||||
</ClInclude>
|
||||
|
||||
@@ -21,11 +21,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"actions":
|
||||
"keybindings":
|
||||
[
|
||||
{ "command": {"action": "copy", "singleLine": false }, "keys": "ctrl+c" },
|
||||
{ "command": "paste", "keys": "ctrl+v" },
|
||||
{ "command": "find", "keys": "ctrl+shift+f" },
|
||||
{ "command": { "action": "splitPane", "split": "auto", "splitMode": "duplicate" }, "keys": "alt+shift+d" }
|
||||
{ "id": "Terminal.CopySelectedText", "keys": "ctrl+c" },
|
||||
{ "id": "Terminal.PasteFromClipboard", "keys": "ctrl+v" },
|
||||
{ "id": "Terminal.DuplicatePaneAuto", "keys": "alt+shift+d" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace SettingsModelUnitTests
|
||||
TEST_METHOD(TestCloneInheritanceTree);
|
||||
TEST_METHOD(TestValidDefaults);
|
||||
TEST_METHOD(TestInheritedCommand);
|
||||
TEST_METHOD(TestOverwriteParentCommandAndKeybinding);
|
||||
TEST_METHOD(LoadFragmentsWithMultipleUpdates);
|
||||
|
||||
TEST_METHOD(FragmentActionSimple);
|
||||
@@ -1235,11 +1236,11 @@ namespace SettingsModelUnitTests
|
||||
const auto settings = createSettings(badSettings);
|
||||
|
||||
// KeyMap: ctrl+a/b are mapped to "invalid"
|
||||
// ActionMap: "splitPane" and "invalid" are the only deserialized actions
|
||||
// ActionMap: "splitPane" is the only deserialized action
|
||||
// NameMap: "splitPane" has no key binding, but it is still added to the name map
|
||||
const auto actionMap = winrt::get_self<implementation::ActionMap>(settings->GlobalSettings().ActionMap());
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_ActionMap.size());
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_ActionMap.size());
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->NameMap().Size());
|
||||
VERIFY_ARE_EQUAL(5u, settings->Warnings().Size());
|
||||
|
||||
@@ -1981,7 +1982,8 @@ namespace SettingsModelUnitTests
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"command": "closePane"
|
||||
"command": "closePane",
|
||||
"id": "Test.ClosePane"
|
||||
},
|
||||
],
|
||||
})" };
|
||||
@@ -2005,11 +2007,108 @@ namespace SettingsModelUnitTests
|
||||
}
|
||||
{
|
||||
// Verify ActionMap::GetKeyBindingForAction API
|
||||
const auto& actualKeyChord{ settings->ActionMap().GetKeyBindingForAction(ShortcutAction::ClosePane) };
|
||||
const auto& actualKeyChord{ settings->ActionMap().GetKeyBindingForAction(L"Test.ClosePane") };
|
||||
VERIFY_IS_NULL(actualKeyChord);
|
||||
}
|
||||
}
|
||||
|
||||
void DeserializationTests::TestOverwriteParentCommandAndKeybinding()
|
||||
{
|
||||
// Tests:
|
||||
// - Redefine an action whose ID was originally defined in another layer
|
||||
// - Redefine a keychord that exists in another layer
|
||||
// - Define a keychord that points to an action in another layer
|
||||
|
||||
static constexpr std::string_view settings1Json{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"commandline": "cmd.exe"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"command": "closePane",
|
||||
"id": "Parent.ClosePane"
|
||||
},
|
||||
{
|
||||
"command": "closePane",
|
||||
"id": "Parent.ClosePane2"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
{
|
||||
"keys": "ctrl+shift+w",
|
||||
"id": "Parent.ClosePane"
|
||||
},
|
||||
{
|
||||
"keys": "ctrl+shift+x",
|
||||
"id": "Parent.ClosePane2"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
// this child actions and keybindings list
|
||||
// - redefines Parent.ClosePane to perform a newTab action instead of a closePane action
|
||||
// - redefines ctrl+shift+x to point to Child.ClosePane instead of Parent.ClosePane2
|
||||
// - defines ctrl+shift+y to point to Parent.ClosePane2 (an action that does not exist in this child layer)
|
||||
static constexpr std::string_view settings2Json{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"actions": [
|
||||
{
|
||||
"command": "newTab",
|
||||
"id": "Parent.ClosePane"
|
||||
},
|
||||
{
|
||||
"command": "closePane",
|
||||
"id": "Child.ClosePane"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
{
|
||||
"id": "Child.ClosePane",
|
||||
"keys": "ctrl+shift+x"
|
||||
},
|
||||
{
|
||||
"id": "Parent.ClosePane2",
|
||||
"keys": "ctrl+shift+y"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settings2Json, settings1Json);
|
||||
const KeyChord ctrlShiftW{ true, false, true, false, static_cast<int>('W'), 0 };
|
||||
const KeyChord ctrlShiftX{ true, false, true, false, static_cast<int>('X'), 0 };
|
||||
const KeyChord ctrlShiftY{ true, false, true, false, static_cast<int>('Y'), 0 };
|
||||
|
||||
{
|
||||
// ctrl+shift+w should point to Parent.ClosePane, however Parent.ClosePane should be a newTab action
|
||||
const auto& cmd{ settings->ActionMap().GetActionByKeyChord(ctrlShiftW) };
|
||||
VERIFY_IS_NOT_NULL(cmd);
|
||||
VERIFY_ARE_EQUAL(cmd.ID(), L"Parent.ClosePane");
|
||||
VERIFY_ARE_EQUAL(cmd.ActionAndArgs().Action(), ShortcutAction::NewTab);
|
||||
}
|
||||
{
|
||||
// ctrl+shift+x should point to Child.ClosePane
|
||||
const auto& cmd{ settings->ActionMap().GetActionByKeyChord(ctrlShiftX) };
|
||||
VERIFY_IS_NOT_NULL(cmd);
|
||||
VERIFY_ARE_EQUAL(cmd.ID(), L"Child.ClosePane");
|
||||
VERIFY_ARE_EQUAL(cmd.ActionAndArgs().Action(), ShortcutAction::ClosePane);
|
||||
}
|
||||
{
|
||||
// ctrl+shift+y should point to Parent.ClosePane2
|
||||
const auto& cmd{ settings->ActionMap().GetActionByKeyChord(ctrlShiftY) };
|
||||
VERIFY_IS_NOT_NULL(cmd);
|
||||
VERIFY_ARE_EQUAL(cmd.ID(), L"Parent.ClosePane2");
|
||||
VERIFY_ARE_EQUAL(cmd.ActionAndArgs().Action(), ShortcutAction::ClosePane);
|
||||
}
|
||||
}
|
||||
|
||||
// This test ensures GH#11597, GH#12520 don't regress.
|
||||
void DeserializationTests::LoadFragmentsWithMultipleUpdates()
|
||||
{
|
||||
@@ -2049,7 +2148,8 @@ namespace SettingsModelUnitTests
|
||||
"actions": [
|
||||
{
|
||||
"command": { "action": "addMark" },
|
||||
"name": "Test Action"
|
||||
"name": "Test Action",
|
||||
"id": "Test.FragmentAction"
|
||||
},
|
||||
]
|
||||
})" };
|
||||
@@ -2074,6 +2174,7 @@ namespace SettingsModelUnitTests
|
||||
{
|
||||
"command": { "action": "addMark" },
|
||||
"keys": "ctrl+f",
|
||||
"id": "Test.FragmentAction",
|
||||
"name": "Test Action"
|
||||
},
|
||||
]
|
||||
@@ -2195,7 +2296,8 @@ namespace SettingsModelUnitTests
|
||||
"actions": [
|
||||
{
|
||||
"command": { "action": "addMark" },
|
||||
"name": "Test Action"
|
||||
"name": "Test Action",
|
||||
"id": "Test.FragmentAction"
|
||||
},
|
||||
]
|
||||
})" };
|
||||
|
||||
@@ -157,17 +157,17 @@ namespace SettingsModelUnitTests
|
||||
void KeyBindingsTests::HashDeduplication()
|
||||
{
|
||||
const auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])"), OriginTag::None);
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])"), OriginTag::None);
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])"), OriginTag::User);
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])"), OriginTag::User);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_ActionMap.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::HashContentArgs()
|
||||
{
|
||||
Log::Comment(L"These are two actions with different content args. They should have different hashes for their terminal args.");
|
||||
Log::Comment(L"These are two actions with different content args. They should have different generated IDs for their terminal args.");
|
||||
const auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", } , "keys": ["ctrl+c"] } ])"), OriginTag::None);
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", "index": 0 } , "keys": ["ctrl+shift+c"] } ])"), OriginTag::None);
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", } , "keys": ["ctrl+c"] } ])"), OriginTag::User);
|
||||
actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", "index": 0 } , "keys": ["ctrl+shift+c"] } ])"), OriginTag::User);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_ActionMap.size());
|
||||
|
||||
KeyChord ctrlC{ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 };
|
||||
@@ -271,32 +271,32 @@ namespace SettingsModelUnitTests
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings0Json, OriginTag::None);
|
||||
actionMap->LayerJson(bindings0Json, OriginTag::User);
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings1Json, OriginTag::None);
|
||||
actionMap->LayerJson(bindings1Json, OriginTag::User);
|
||||
VERIFY_IS_TRUE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings2Json, OriginTag::None);
|
||||
actionMap->LayerJson(bindings2Json, OriginTag::User);
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestArbitraryArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "command": "copy", "keys": ["ctrl+c"] },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": ["ctrl+shift+c"] },
|
||||
{ "command": { "action": "copy", "singleLine": true }, "keys": ["alt+shift+c"] },
|
||||
{ "command": "copy", "id": "Test.CopyNoArgs", "keys": ["ctrl+c"] },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "id": "Test.CopyMultiline", "keys": ["ctrl+shift+c"] },
|
||||
{ "command": { "action": "copy", "singleLine": true }, "id": "Test.CopySingleline", "keys": ["alt+shift+c"] },
|
||||
|
||||
{ "command": "newTab", "keys": ["ctrl+t"] },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": ["ctrl+shift+t"] },
|
||||
{ "command": { "action": "newTab", "index": 11 }, "keys": ["ctrl+shift+y"] },
|
||||
{ "command": "newTab", "id": "Test.NewTabNoArgs", "keys": ["ctrl+t"] },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "id": "Test.NewTab0", "keys": ["ctrl+shift+t"] },
|
||||
{ "command": { "action": "newTab", "index": 11 }, "id": "Test.NewTab11", "keys": ["ctrl+shift+y"] },
|
||||
|
||||
{ "command": { "action": "copy", "madeUpBool": true }, "keys": ["ctrl+b"] },
|
||||
{ "command": { "action": "copy" }, "keys": ["ctrl+shift+b"] },
|
||||
{ "command": { "action": "copy", "madeUpBool": true }, "id": "Test.CopyFakeArgs", "keys": ["ctrl+b"] },
|
||||
{ "command": { "action": "copy" }, "id": "Test.CopyNullArgs", "keys": ["ctrl+shift+b"] },
|
||||
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": ["ctrl+f"] },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "keys": ["ctrl+g"] }
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "id": "Test.EnlargeFont", "keys": ["ctrl+f"] },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "id": "Test.ReduceFont", "keys": ["ctrl+g"] }
|
||||
|
||||
])" };
|
||||
|
||||
@@ -428,10 +428,10 @@ namespace SettingsModelUnitTests
|
||||
void KeyBindingsTests::TestSplitPaneArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "keys": ["ctrl+g"], "command": { "action": "splitPane" } },
|
||||
{ "keys": ["ctrl+h"], "command": { "action": "splitPane", "split": "auto" } }
|
||||
{ "keys": ["ctrl+d"], "id": "Test.SplitPaneVertical", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+e"], "id": "Test.SplitPaneHorizontal", "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "keys": ["ctrl+g"], "id": "Test.SplitPane", "command": { "action": "splitPane" } },
|
||||
{ "keys": ["ctrl+h"], "id": "Test.SplitPaneAuto", "command": { "action": "splitPane", "split": "auto" } }
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -478,9 +478,9 @@ namespace SettingsModelUnitTests
|
||||
void KeyBindingsTests::TestSetTabColorArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "setTabColor", "color": null } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "setTabColor", "color": "#123456" } },
|
||||
{ "keys": ["ctrl+f"], "command": "setTabColor" },
|
||||
{ "keys": ["ctrl+c"], "id": "Test.SetTabColorNull", "command": { "action": "setTabColor", "color": null } },
|
||||
{ "keys": ["ctrl+d"], "id": "Test.SetTabColor", "command": { "action": "setTabColor", "color": "#123456" } },
|
||||
{ "keys": ["ctrl+f"], "id": "Test.SetTabColorNoArgs", "command": "setTabColor" },
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -521,7 +521,7 @@ namespace SettingsModelUnitTests
|
||||
void KeyBindingsTests::TestStringOverload()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "command": "copy", "keys": "ctrl+c" }
|
||||
{ "command": "copy", "id": "Test.Copy", "keys": "ctrl+c" }
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -543,12 +543,12 @@ namespace SettingsModelUnitTests
|
||||
void KeyBindingsTests::TestScrollArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["up"], "command": "scrollUp" },
|
||||
{ "keys": ["down"], "command": "scrollDown" },
|
||||
{ "keys": ["ctrl+up"], "command": { "action": "scrollUp" } },
|
||||
{ "keys": ["ctrl+down"], "command": { "action": "scrollDown" } },
|
||||
{ "keys": ["ctrl+shift+up"], "command": { "action": "scrollUp", "rowsToScroll": 10 } },
|
||||
{ "keys": ["ctrl+shift+down"], "command": { "action": "scrollDown", "rowsToScroll": 10 } }
|
||||
{ "keys": ["up"], "id": "Test.ScrollUp0", "command": "scrollUp" },
|
||||
{ "keys": ["down"], "id": "Test.ScrollDown0", "command": "scrollDown" },
|
||||
{ "keys": ["ctrl+up"], "id": "Test.ScrollUp1", "command": { "action": "scrollUp" } },
|
||||
{ "keys": ["ctrl+down"], "id": "Test.ScrollDown1", "command": { "action": "scrollDown" } },
|
||||
{ "keys": ["ctrl+shift+up"], "id": "Test.ScrollUp2", "command": { "action": "scrollUp", "rowsToScroll": 10 } },
|
||||
{ "keys": ["ctrl+shift+down"], "id": "Test.ScrollDown2", "command": { "action": "scrollDown", "rowsToScroll": 10 } }
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -620,8 +620,8 @@ namespace SettingsModelUnitTests
|
||||
void KeyBindingsTests::TestMoveTabArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["up"], "command": { "action": "moveTab", "direction": "forward" } },
|
||||
{ "keys": ["down"], "command": { "action": "moveTab", "direction": "backward" } }
|
||||
{ "keys": ["up"], "id": "Test.MoveTabUp", "command": { "action": "moveTab", "direction": "forward" } },
|
||||
{ "keys": ["down"], "id": "Test.MoveTabDown", "command": { "action": "moveTab", "direction": "backward" } }
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -665,9 +665,9 @@ namespace SettingsModelUnitTests
|
||||
void KeyBindingsTests::TestToggleCommandPaletteArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["up"], "command": "commandPalette" },
|
||||
{ "keys": ["ctrl+up"], "command": { "action": "commandPalette", "launchMode" : "action" } },
|
||||
{ "keys": ["ctrl+shift+up"], "command": { "action": "commandPalette", "launchMode" : "commandLine" } }
|
||||
{ "keys": ["up"], "id": "Test.CmdPal", "command": "commandPalette" },
|
||||
{ "keys": ["ctrl+up"], "id": "Test.CmdPalActionMode", "command": { "action": "commandPalette", "launchMode" : "action" } },
|
||||
{ "keys": ["ctrl+shift+up"], "id": "Test.CmdPalLineMode", "command": { "action": "commandPalette", "launchMode" : "commandLine" } }
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -712,10 +712,10 @@ namespace SettingsModelUnitTests
|
||||
|
||||
void KeyBindingsTests::TestGetKeyBindingForAction()
|
||||
{
|
||||
const std::string bindings0String{ R"([ { "command": "closeWindow", "keys": "ctrl+a" } ])" };
|
||||
const std::string bindings1String{ R"([ { "command": { "action": "copy", "singleLine": true }, "keys": "ctrl+b" } ])" };
|
||||
const std::string bindings2String{ R"([ { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+c" } ])" };
|
||||
const std::string bindings3String{ R"([ { "command": "commandPalette", "keys": "ctrl+shift+p" } ])" };
|
||||
const std::string bindings0String{ R"([ { "command": "closeWindow", "id": "Test.CloseWindow", "keys": "ctrl+a" } ])" };
|
||||
const std::string bindings1String{ R"([ { "command": { "action": "copy", "singleLine": true }, "id": "Test.Copy", "keys": "ctrl+b" } ])" };
|
||||
const std::string bindings2String{ R"([ { "command": { "action": "newTab", "index": 0 }, "id": "Test.NewTab", "keys": "ctrl+c" } ])" };
|
||||
const std::string bindings3String{ R"([ { "command": "commandPalette", "id": "Test.CmdPal", "keys": "ctrl+shift+p" } ])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
@@ -742,7 +742,7 @@ namespace SettingsModelUnitTests
|
||||
Log::Comment(L"simple command: no args");
|
||||
actionMap->LayerJson(bindings0Json, OriginTag::None);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CloseWindow) };
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(L"Test.CloseWindow") };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('A'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
@@ -750,10 +750,7 @@ namespace SettingsModelUnitTests
|
||||
actionMap->LayerJson(bindings1Json, OriginTag::None);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
|
||||
auto args{ winrt::make_self<implementation::CopyTextArgs>() };
|
||||
args->SingleLine(true);
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CopyText, *args) };
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(L"Test.Copy") };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('B'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
@@ -761,11 +758,7 @@ namespace SettingsModelUnitTests
|
||||
actionMap->LayerJson(bindings2Json, OriginTag::None);
|
||||
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
|
||||
|
||||
auto newTerminalArgs{ winrt::make_self<implementation::NewTerminalArgs>() };
|
||||
newTerminalArgs->ProfileIndex(0);
|
||||
auto args{ winrt::make_self<implementation::NewTabArgs>(*newTerminalArgs) };
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::NewTab, *args) };
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(L"Test.NewTab") };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
@@ -773,7 +766,7 @@ namespace SettingsModelUnitTests
|
||||
actionMap->LayerJson(bindings3Json, OriginTag::None);
|
||||
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(L"Test.CmdPal") };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift, static_cast<int32_t>('P'), 0 }, kbd);
|
||||
}
|
||||
}
|
||||
@@ -807,7 +800,7 @@ namespace SettingsModelUnitTests
|
||||
|
||||
void KeyBindingsTests::KeybindingsWithoutVkey()
|
||||
{
|
||||
const auto json = VerifyParseSucceeded(R"!([{"command": "quakeMode", "keys":"shift+sc(255)"}])!");
|
||||
const auto json = VerifyParseSucceeded(R"!([{"command": "quakeMode", "id": "Test.NoVKey", "keys":"shift+sc(255)"}])!");
|
||||
|
||||
const auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
actionMap->LayerJson(json, OriginTag::None);
|
||||
|
||||
@@ -47,6 +47,8 @@ namespace SettingsModelUnitTests
|
||||
TEST_METHOD(RoundtripGenerateActionID);
|
||||
TEST_METHOD(NoGeneratedIDsForIterableAndNestedCommands);
|
||||
TEST_METHOD(GeneratedActionIDsEqualForIdenticalCommands);
|
||||
TEST_METHOD(RoundtripLegacyToModernActions);
|
||||
TEST_METHOD(MultipleActionsAreCollapsed);
|
||||
|
||||
private:
|
||||
// Method Description:
|
||||
@@ -120,13 +122,15 @@ namespace SettingsModelUnitTests
|
||||
|
||||
"experimental.input.forceVT": false,
|
||||
|
||||
"actions": []
|
||||
"actions": [],
|
||||
"keybindings": []
|
||||
})" };
|
||||
|
||||
static constexpr std::string_view smallGlobalsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"actions": []
|
||||
"actions": [],
|
||||
"keybindings": []
|
||||
})" };
|
||||
|
||||
RoundtripTest<implementation::GlobalAppSettings>(globalsString);
|
||||
@@ -275,47 +279,50 @@ namespace SettingsModelUnitTests
|
||||
{
|
||||
// simple command
|
||||
static constexpr std::string_view actionsString1{ R"([
|
||||
{ "command": "paste" }
|
||||
{ "command": "paste", "id": "Test.Paste" }
|
||||
])" };
|
||||
|
||||
// complex command
|
||||
static constexpr std::string_view actionsString2A{ R"([
|
||||
{ "command": { "action": "setTabColor" } }
|
||||
{ "command": { "action": "setTabColor" }, "id": "Test.SetTabColor" }
|
||||
])" };
|
||||
static constexpr std::string_view actionsString2B{ R"([
|
||||
{ "command": { "action": "setTabColor", "color": "#112233" } }
|
||||
{ "command": { "action": "setTabColor", "color": "#112233" }, "id": "Test.SetTabColor112233" }
|
||||
])" };
|
||||
static constexpr std::string_view actionsString2C{ R"([
|
||||
{ "command": { "action": "copy" } },
|
||||
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } }
|
||||
{ "command": { "action": "copy" }, "id": "Test.Copy" },
|
||||
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" }, "id": "Test.CopyWithArgs" }
|
||||
])" };
|
||||
|
||||
// simple command with key chords
|
||||
static constexpr std::string_view actionsString3{ R"([
|
||||
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+a" },
|
||||
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+b" }
|
||||
])" };
|
||||
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" } ]})" };
|
||||
|
||||
// complex command with key chords
|
||||
static constexpr std::string_view actionsString4A{ R"([
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" }
|
||||
])" };
|
||||
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" } ]})" };
|
||||
|
||||
// command with name and icon and multiple key chords
|
||||
static constexpr std::string_view actionsString5{ R"([
|
||||
{ "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" },
|
||||
{ "command": "scrollToTop", "keys": "ctrl+f" }
|
||||
])" };
|
||||
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" } ]})" };
|
||||
|
||||
// complex command with new terminal args
|
||||
static constexpr std::string_view actionsString6{ R"([
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "id": "Test.NewTerminal" },
|
||||
])" };
|
||||
|
||||
// complex command with meaningful null arg
|
||||
static constexpr std::string_view actionsString7{ R"([
|
||||
{ "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" }
|
||||
{ "command": { "action": "renameWindow", "name": null }, "id": "Test.MeaningfulNull" }
|
||||
])" };
|
||||
|
||||
// nested command
|
||||
@@ -397,9 +404,9 @@ namespace SettingsModelUnitTests
|
||||
])"" };
|
||||
|
||||
// unbound command
|
||||
static constexpr std::string_view actionsString10{ R"([
|
||||
{ "command": "unbound", "keys": "ctrl+c" }
|
||||
])" };
|
||||
static constexpr std::string_view actionsString10{ R"({ "actions": [],
|
||||
"keybindings": [
|
||||
{ "id": null, "keys": "ctrl+c" } ]})" };
|
||||
|
||||
Log::Comment(L"simple command");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString1);
|
||||
@@ -409,14 +416,16 @@ namespace SettingsModelUnitTests
|
||||
RoundtripTest<implementation::ActionMap>(actionsString2B);
|
||||
RoundtripTest<implementation::ActionMap>(actionsString2C);
|
||||
|
||||
// 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>
|
||||
Log::Comment(L"simple command with key chords");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString3);
|
||||
RoundtripTest<implementation::GlobalAppSettings>(actionsString3);
|
||||
|
||||
Log::Comment(L"complex commands with key chords");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString4A);
|
||||
RoundtripTest<implementation::GlobalAppSettings>(actionsString4A);
|
||||
|
||||
Log::Comment(L"command with name and icon and multiple key chords");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString5);
|
||||
RoundtripTest<implementation::GlobalAppSettings>(actionsString5);
|
||||
|
||||
Log::Comment(L"complex command with new terminal args");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString6);
|
||||
@@ -434,7 +443,7 @@ namespace SettingsModelUnitTests
|
||||
RoundtripTest<implementation::ActionMap>(actionsString9D);
|
||||
|
||||
Log::Comment(L"unbound command");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString10);
|
||||
RoundtripTest<implementation::GlobalAppSettings>(actionsString10);
|
||||
}
|
||||
|
||||
void SerializationTests::CascadiaSettings()
|
||||
@@ -503,7 +512,10 @@ namespace SettingsModelUnitTests
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{ "command": { "action": "sendInput", "input": "VT Griese Mode" }, "id": "User.sendInput.E02B3DF9", "keys": "ctrl+k" }
|
||||
{ "command": { "action": "sendInput", "input": "VT Griese Mode" }, "id": "Test.SendInput" }
|
||||
],
|
||||
"keybindings": [
|
||||
{ "id": "Test.SendInput", "keys": "ctrl+k" }
|
||||
],
|
||||
"theme": "system",
|
||||
"themes": []
|
||||
@@ -995,7 +1007,6 @@ namespace SettingsModelUnitTests
|
||||
{
|
||||
"name": "foo",
|
||||
"command": "closePane",
|
||||
"keys": "ctrl+shift+w",
|
||||
"id": "thisIsMyClosePane"
|
||||
},
|
||||
{
|
||||
@@ -1065,4 +1076,123 @@ namespace SettingsModelUnitTests
|
||||
|
||||
VERIFY_ARE_EQUAL(sendInputCmd1.ID(), sendInputCmd1.ID());
|
||||
}
|
||||
|
||||
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::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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user