Add ability to save input action from command line (#16513)

Hi wanted to make an attempt at
[12857](https://github.com/microsoft/terminal/issues/12857). This still
needs work but I think the initial version is ready to be reviewed.

## Summary of the Pull Request

Mostly copied from:
6f5b9fb...1cde67ac46

- Save to disk
- If command line is empty use selection
- Show toast
- No UI.  Trying out the different options now.

## PR Checklist
- [ ] Closes #12857
- [ ] Tests added/passed
- [ ] Documentation updated
- If checked, please file a pull request on [our docs
repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
- [ ] Schema updated (if necessary)

---------

Co-authored-by: Mike Griese <migrie@microsoft.com>
This commit is contained in:
e82eric
2024-07-02 07:27:20 -04:00
committed by GitHub
parent 8009b53819
commit d051f7047d
18 changed files with 357 additions and 33 deletions

View File

@@ -1264,6 +1264,113 @@ namespace winrt::TerminalApp::implementation
} }
} }
void TerminalPage::_HandleSaveSnippet(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if constexpr (!Feature_SaveSnippet::IsEnabled())
{
return;
}
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SaveSnippetArgs>())
{
auto commandLine = realArgs.Commandline();
if (commandLine.empty())
{
if (const auto termControl{ _GetActiveControl() })
{
if (termControl.HasSelection())
{
const auto selections{ termControl.SelectedText(true) };
const auto selection = std::accumulate(selections.begin(), selections.end(), std::wstring());
commandLine = selection;
}
}
}
if (commandLine.empty())
{
ActionSaveFailed(L"CommandLine is Required");
return;
}
try
{
KeyChord keyChord = nullptr;
if (!realArgs.KeyChord().empty())
{
keyChord = KeyChordSerialization::FromString(winrt::to_hstring(realArgs.KeyChord()));
}
_settings.GlobalSettings().ActionMap().AddSendInputAction(realArgs.Name(), commandLine, keyChord);
_settings.WriteSettingsToDisk();
ActionSaved(commandLine, realArgs.Name(), realArgs.KeyChord());
}
catch (const winrt::hresult_error& ex)
{
auto code = ex.code();
auto message = ex.message();
ActionSaveFailed(message);
args.Handled(true);
return;
}
args.Handled(true);
}
}
}
void TerminalPage::ActionSaved(winrt::hstring input, winrt::hstring name, winrt::hstring keyChord)
{
// If we haven't ever loaded the TeachingTip, then do so now and
// create the toast for it.
if (_actionSavedToast == nullptr)
{
if (auto tip{ FindName(L"ActionSavedToast").try_as<MUX::Controls::TeachingTip>() })
{
_actionSavedToast = std::make_shared<Toast>(tip);
// Make sure to use the weak ref when setting up this
// callback.
tip.Closed({ get_weak(), &TerminalPage::_FocusActiveControl });
}
}
_UpdateTeachingTipTheme(ActionSavedToast().try_as<winrt::Windows::UI::Xaml::FrameworkElement>());
SavedActionName(name);
SavedActionKeyChord(keyChord);
SavedActionCommandLine(input);
if (_actionSavedToast != nullptr)
{
_actionSavedToast->Open();
}
}
void TerminalPage::ActionSaveFailed(winrt::hstring message)
{
// If we haven't ever loaded the TeachingTip, then do so now and
// create the toast for it.
if (_actionSaveFailedToast == nullptr)
{
if (auto tip{ FindName(L"ActionSaveFailedToast").try_as<MUX::Controls::TeachingTip>() })
{
_actionSaveFailedToast = std::make_shared<Toast>(tip);
// Make sure to use the weak ref when setting up this
// callback.
tip.Closed({ get_weak(), &TerminalPage::_FocusActiveControl });
}
}
_UpdateTeachingTipTheme(ActionSaveFailedToast().try_as<winrt::Windows::UI::Xaml::FrameworkElement>());
ActionSaveFailedMessage().Text(message);
if (_actionSaveFailedToast != nullptr)
{
_actionSaveFailedToast->Open();
}
}
void TerminalPage::_HandleSelectCommand(const IInspectable& /*sender*/, void TerminalPage::_HandleSelectCommand(const IInspectable& /*sender*/,
const ActionEventArgs& args) const ActionEventArgs& args)
{ {

View File

@@ -209,6 +209,7 @@ void AppCommandlineArgs::_buildParser()
_buildMovePaneParser(); _buildMovePaneParser();
_buildSwapPaneParser(); _buildSwapPaneParser();
_buildFocusPaneParser(); _buildFocusPaneParser();
_buildSaveSnippetParser();
} }
// Method Description: // Method Description:
@@ -537,6 +538,72 @@ void AppCommandlineArgs::_buildFocusPaneParser()
setupSubcommand(_focusPaneShort); setupSubcommand(_focusPaneShort);
} }
void AppCommandlineArgs::_buildSaveSnippetParser()
{
_saveCommand = _app.add_subcommand("x-save-snippet", RS_A(L"SaveSnippetDesc"));
auto setupSubcommand = [this](auto* subcommand) {
subcommand->add_option("--name,-n", _saveInputName, RS_A(L"SaveSnippetArgDesc"));
subcommand->add_option("--keychord,-k", _keyChordOption, RS_A(L"KeyChordArgDesc"));
subcommand->add_option("command,", _commandline, RS_A(L"CmdCommandArgDesc"));
subcommand->positionals_at_end(true);
// When ParseCommand is called, if this subcommand was provided, this
// callback function will be triggered on the same thread. We can be sure
// that `this` will still be safe - this function just lets us know this
// command was parsed.
subcommand->callback([&, this]() {
// Build the action from the values we've parsed on the commandline.
ActionAndArgs saveSnippet{};
saveSnippet.Action(ShortcutAction::SaveSnippet);
// First, parse out the commandline in the same way that
// _getNewTerminalArgs does it
SaveSnippetArgs args{};
if (!_commandline.empty())
{
std::ostringstream cmdlineBuffer;
for (const auto& arg : _commandline)
{
if (cmdlineBuffer.tellp() != 0)
{
// If there's already something in here, prepend a space
cmdlineBuffer << ' ';
}
if (arg.find(" ") != std::string::npos)
{
cmdlineBuffer << '"' << arg << '"';
}
else
{
cmdlineBuffer << arg;
}
}
args.Commandline(winrt::to_hstring(cmdlineBuffer.str()));
}
if (!_keyChordOption.empty())
{
args.KeyChord(winrt::to_hstring(_keyChordOption));
}
if (!_saveInputName.empty())
{
winrt::hstring hString = winrt::to_hstring(_saveInputName);
args.Name(hString);
}
saveSnippet.Args(args);
_startupActions.push_back(saveSnippet);
});
};
setupSubcommand(_saveCommand);
}
// Method Description: // Method Description:
// - Add the `NewTerminalArgs` parameters to the given subcommand. This enables // - Add the `NewTerminalArgs` parameters to the given subcommand. This enables
// that subcommand to support all the properties in a NewTerminalArgs. // that subcommand to support all the properties in a NewTerminalArgs.
@@ -710,7 +777,8 @@ bool AppCommandlineArgs::_noCommandsProvided()
*_focusPaneCommand || *_focusPaneCommand ||
*_focusPaneShort || *_focusPaneShort ||
*_newPaneShort.subcommand || *_newPaneShort.subcommand ||
*_newPaneCommand.subcommand); *_newPaneCommand.subcommand ||
*_saveCommand);
} }
// Method Description: // Method Description:

View File

@@ -93,6 +93,7 @@ private:
CLI::App* _swapPaneCommand; CLI::App* _swapPaneCommand;
CLI::App* _focusPaneCommand; CLI::App* _focusPaneCommand;
CLI::App* _focusPaneShort; CLI::App* _focusPaneShort;
CLI::App* _saveCommand;
// Are you adding a new sub-command? Make sure to update _noCommandsProvided! // Are you adding a new sub-command? Make sure to update _noCommandsProvided!
@@ -123,6 +124,8 @@ private:
bool _focusPrevTab{ false }; bool _focusPrevTab{ false };
int _focusPaneTarget{ -1 }; int _focusPaneTarget{ -1 };
std::string _saveInputName;
std::string _keyChordOption;
// Are you adding more args here? Make sure to reset them in _resetStateToDefault // Are you adding more args here? Make sure to reset them in _resetStateToDefault
const Commandline* _currentCommandline{ nullptr }; const Commandline* _currentCommandline{ nullptr };
@@ -141,6 +144,7 @@ private:
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand); winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);
void _addNewTerminalArgs(NewTerminalSubcommand& subcommand); void _addNewTerminalArgs(NewTerminalSubcommand& subcommand);
void _buildParser(); void _buildParser();
void _buildSaveSnippetParser();
void _buildNewTabParser(); void _buildNewTabParser();
void _buildSplitPaneParser(); void _buildSplitPaneParser();
void _buildFocusTabParser(); void _buildFocusTabParser();

View File

@@ -288,6 +288,15 @@
<data name="CmdCommandArgDesc" xml:space="preserve"> <data name="CmdCommandArgDesc" xml:space="preserve">
<value>An optional command, with arguments, to be spawned in the new tab or pane</value> <value>An optional command, with arguments, to be spawned in the new tab or pane</value>
</data> </data>
<data name="SaveSnippetDesc" xml:space="preserve">
<value>Save command line as input action</value>
</data>
<data name="SaveSnippetArgDesc" xml:space="preserve">
<value>An optional argument</value>
</data>
<data name="KeyChordArgDesc" xml:space="preserve">
<value>An optional argument</value>
</data>
<data name="CmdFocusTabDesc" xml:space="preserve"> <data name="CmdFocusTabDesc" xml:space="preserve">
<value>Move focus to another tab</value> <value>Move focus to another tab</value>
</data> </data>
@@ -898,4 +907,10 @@
<data name="RestartConnectionToolTip" xml:space="preserve"> <data name="RestartConnectionToolTip" xml:space="preserve">
<value>Restart the active pane connection</value> <value>Restart the active pane connection</value>
</data> </data>
<data name="ActionSavedToast.Title" xml:space="preserve">
<value>Action saved</value>
</data>
<data name="ActionSaveFailedToast.Title" xml:space="preserve">
<value>Action save failed</value>
</data>
</root> </root>

View File

@@ -357,7 +357,9 @@
</ItemGroup> </ItemGroup>
<!-- ========================= Misc Files ======================== --> <!-- ========================= Misc Files ======================== -->
<ItemGroup> <ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw" /> <PRIResource Include="Resources\en-US\Resources.resw">
<SubType>Designer</SubType>
</PRIResource>
<PRIResource Include="Resources\en-US\ContextMenu.resw" /> <PRIResource Include="Resources\en-US\ContextMenu.resw" />
<OCResourceDirectory Include="Resources" /> <OCResourceDirectory Include="Resources" />
</ItemGroup> </ItemGroup>
@@ -466,10 +468,8 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<!-- ========================= Globals ======================== --> <!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" /> <Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. --> <!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" /> <Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<!-- <!--
By default, the PRI file will contain resource paths beginning with the By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf. project name. Since we enabled XBF embedding, this *also* includes App.xbf.
@@ -490,4 +490,4 @@
</ItemGroup> </ItemGroup>
</Target> </Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" /> <Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project> </Project>

View File

@@ -146,6 +146,8 @@ namespace winrt::TerminalApp::implementation
winrt::hstring KeyboardServiceDisabledText(); winrt::hstring KeyboardServiceDisabledText();
winrt::fire_and_forget IdentifyWindow(); winrt::fire_and_forget IdentifyWindow();
void ActionSaved(winrt::hstring input, winrt::hstring name, winrt::hstring keyChord);
void ActionSaveFailed(winrt::hstring message);
winrt::fire_and_forget RenameFailed(); winrt::fire_and_forget RenameFailed();
winrt::fire_and_forget ShowTerminalWorkingDirectory(); winrt::fire_and_forget ShowTerminalWorkingDirectory();
@@ -199,6 +201,10 @@ namespace winrt::TerminalApp::implementation
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, PropertyChanged.raise, nullptr); WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, PropertyChanged.raise, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, FrameBrush, PropertyChanged.raise, nullptr); WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, FrameBrush, PropertyChanged.raise, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SavedActionName, PropertyChanged.raise, L"");
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SavedActionKeyChord, PropertyChanged.raise, L"");
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SavedActionCommandLine, PropertyChanged.raise, L"");
private: private:
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
std::optional<HWND> _hostingHwnd; std::optional<HWND> _hostingHwnd;
@@ -258,6 +264,8 @@ namespace winrt::TerminalApp::implementation
bool _isEmbeddingInboundListener{ false }; bool _isEmbeddingInboundListener{ false };
std::shared_ptr<Toast> _windowIdToast{ nullptr }; std::shared_ptr<Toast> _windowIdToast{ nullptr };
std::shared_ptr<Toast> _actionSavedToast{ nullptr };
std::shared_ptr<Toast> _actionSaveFailedToast{ nullptr };
std::shared_ptr<Toast> _windowRenameFailedToast{ nullptr }; std::shared_ptr<Toast> _windowRenameFailedToast{ nullptr };
std::shared_ptr<Toast> _windowCwdToast{ nullptr }; std::shared_ptr<Toast> _windowCwdToast{ nullptr };

View File

@@ -72,6 +72,10 @@ namespace TerminalApp
void IdentifyWindow(); void IdentifyWindow();
void RenameFailed(); void RenameFailed();
String SavedActionName { get; };
String SavedActionKeyChord { get; };
String SavedActionCommandLine { get; };
// We cannot use the default XAML APIs because we want to make sure // We cannot use the default XAML APIs because we want to make sure
// that there's only one application-global dialog visible at a time, // that there's only one application-global dialog visible at a time,
// and because of GH#5224. // and because of GH#5224.

View File

@@ -8,6 +8,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp" xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mtu="using:Microsoft.Terminal.UI"
xmlns:mux="using:Microsoft.UI.Xaml.Controls" xmlns:mux="using:Microsoft.UI.Xaml.Controls"
Background="Transparent" Background="Transparent"
mc:Ignorable="d"> mc:Ignorable="d">
@@ -204,5 +205,43 @@
Title="{x:Bind WindowProperties.VirtualWorkingDirectory, Mode=OneWay}" Title="{x:Bind WindowProperties.VirtualWorkingDirectory, Mode=OneWay}"
x:Load="False" x:Load="False"
IsLightDismissEnabled="True" /> IsLightDismissEnabled="True" />
<mux:TeachingTip x:Name="ActionSavedToast"
x:Uid="ActionSavedToast"
Title="Action Saved"
HorizontalAlignment="Stretch"
x:Load="False"
IsLightDismissEnabled="True">
<mux:TeachingTip.Content>
<StackPanel HorizontalAlignment="Stretch"
Orientation="Vertical">
<TextBlock x:Name="ActionSavedNameText"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(SavedActionName), Mode=OneWay}">
<Run Text="Name: " />
<Run Text="{x:Bind SavedActionName, Mode=OneWay}" />
</TextBlock>
<TextBlock x:Name="ActionSavedKeyChordText"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(SavedActionKeyChord), Mode=OneWay}">
<Run Text="Key Chord: " />
<Run Text="{x:Bind SavedActionKeyChord, Mode=OneWay}" />
</TextBlock>
<TextBlock x:Name="ActionSavedCommandLineText"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(SavedActionCommandLine), Mode=OneWay}">
<Run Text="Input: " />
<Run Text="{x:Bind SavedActionCommandLine, Mode=OneWay}" />
</TextBlock>
</StackPanel>
</mux:TeachingTip.Content>
</mux:TeachingTip>
<mux:TeachingTip x:Name="ActionSaveFailedToast"
x:Uid="ActionSaveFailedToast"
Title="Action Save Failed"
x:Load="False"
IsLightDismissEnabled="True">
<mux:TeachingTip.Content>
<TextBlock x:Name="ActionSaveFailedMessage"
Text="" />
</mux:TeachingTip.Content>
</mux:TeachingTip>
</Grid> </Grid>
</Page> </Page>

View File

@@ -52,6 +52,7 @@ static constexpr std::string_view SwitchToTabKey{ "switchToTab" };
static constexpr std::string_view TabSearchKey{ "tabSearch" }; static constexpr std::string_view TabSearchKey{ "tabSearch" };
static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" }; static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" };
static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" }; static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
static constexpr std::string_view SaveSnippetKey{ "experimental.saveSnippet" };
static constexpr std::string_view SuggestionsKey{ "showSuggestions" }; static constexpr std::string_view SuggestionsKey{ "showSuggestions" };
static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" }; static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
static constexpr std::string_view SetFocusModeKey{ "setFocusMode" }; static constexpr std::string_view SetFocusModeKey{ "setFocusMode" };
@@ -389,6 +390,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::TabSearch, RS_(L"TabSearchCommandKey") }, { ShortcutAction::TabSearch, RS_(L"TabSearchCommandKey") },
{ ShortcutAction::ToggleAlwaysOnTop, RS_(L"ToggleAlwaysOnTopCommandKey") }, { ShortcutAction::ToggleAlwaysOnTop, RS_(L"ToggleAlwaysOnTopCommandKey") },
{ ShortcutAction::ToggleCommandPalette, MustGenerate }, { ShortcutAction::ToggleCommandPalette, MustGenerate },
{ ShortcutAction::SaveSnippet, MustGenerate },
{ ShortcutAction::Suggestions, MustGenerate }, { ShortcutAction::Suggestions, MustGenerate },
{ ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") }, { ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
{ ShortcutAction::SetFocusMode, MustGenerate }, { ShortcutAction::SetFocusMode, MustGenerate },

View File

@@ -33,6 +33,7 @@
#include "ScrollToMarkArgs.g.cpp" #include "ScrollToMarkArgs.g.cpp"
#include "AddMarkArgs.g.cpp" #include "AddMarkArgs.g.cpp"
#include "FindMatchArgs.g.cpp" #include "FindMatchArgs.g.cpp"
#include "SaveSnippetArgs.g.cpp"
#include "ToggleCommandPaletteArgs.g.cpp" #include "ToggleCommandPaletteArgs.g.cpp"
#include "SuggestionsArgs.g.cpp" #include "SuggestionsArgs.g.cpp"
#include "NewWindowArgs.g.cpp" #include "NewWindowArgs.g.cpp"
@@ -947,6 +948,29 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
} }
} }
winrt::hstring SaveSnippetArgs::GenerateName() const
{
if (Feature_SaveSnippet::IsEnabled())
{
std::wstringstream ss;
ss << RS_(L"SaveSnippetNamePrefix").c_str() << L" commandline: " << Commandline().c_str();
if (!Name().empty())
{
ss << L", name: " << Name().c_str();
}
if (!KeyChord().empty())
{
ss << L", keyChord " << KeyChord().c_str();
}
return winrt::hstring{ ss.str() };
}
return L"";
}
static winrt::hstring _FormatColorString(const Control::SelectionColor& selectionColor) static winrt::hstring _FormatColorString(const Control::SelectionColor& selectionColor)
{ {
if (!selectionColor) if (!selectionColor)

View File

@@ -34,6 +34,7 @@
#include "ScrollToMarkArgs.g.h" #include "ScrollToMarkArgs.g.h"
#include "AddMarkArgs.g.h" #include "AddMarkArgs.g.h"
#include "MoveTabArgs.g.h" #include "MoveTabArgs.g.h"
#include "SaveSnippetArgs.g.h"
#include "ToggleCommandPaletteArgs.g.h" #include "ToggleCommandPaletteArgs.g.h"
#include "SuggestionsArgs.g.h" #include "SuggestionsArgs.g.h"
#include "FindMatchArgs.g.h" #include "FindMatchArgs.g.h"
@@ -215,6 +216,12 @@ protected: \
#define TOGGLE_COMMAND_PALETTE_ARGS(X) \ #define TOGGLE_COMMAND_PALETTE_ARGS(X) \
X(CommandPaletteLaunchMode, LaunchMode, "launchMode", false, CommandPaletteLaunchMode::Action) X(CommandPaletteLaunchMode, LaunchMode, "launchMode", false, CommandPaletteLaunchMode::Action)
////////////////////////////////////////////////////////////////////////////////
#define SAVE_TASK_ARGS(X) \
X(winrt::hstring, Name, "name", false, L"") \
X(winrt::hstring, Commandline, "commandline", args->Commandline().empty(), L"") \
X(winrt::hstring, KeyChord, "keyChord", false, L"")
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#define SUGGESTIONS_ARGS(X) \ #define SUGGESTIONS_ARGS(X) \
X(SuggestionsSource, Source, "source", false, SuggestionsSource::Tasks) \ X(SuggestionsSource, Source, "source", false, SuggestionsSource::Tasks) \
@@ -819,6 +826,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(ToggleCommandPaletteArgs, TOGGLE_COMMAND_PALETTE_ARGS); ACTION_ARGS_STRUCT(ToggleCommandPaletteArgs, TOGGLE_COMMAND_PALETTE_ARGS);
ACTION_ARGS_STRUCT(SaveSnippetArgs, SAVE_TASK_ARGS);
ACTION_ARGS_STRUCT(SuggestionsArgs, SUGGESTIONS_ARGS); ACTION_ARGS_STRUCT(SuggestionsArgs, SUGGESTIONS_ARGS);
ACTION_ARGS_STRUCT(FindMatchArgs, FIND_MATCH_ARGS); ACTION_ARGS_STRUCT(FindMatchArgs, FIND_MATCH_ARGS);
@@ -941,6 +950,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(CloseTabArgs); BASIC_FACTORY(CloseTabArgs);
BASIC_FACTORY(MoveTabArgs); BASIC_FACTORY(MoveTabArgs);
BASIC_FACTORY(OpenSettingsArgs); BASIC_FACTORY(OpenSettingsArgs);
BASIC_FACTORY(SaveSnippetArgs);
BASIC_FACTORY(FindMatchArgs); BASIC_FACTORY(FindMatchArgs);
BASIC_FACTORY(NewWindowArgs); BASIC_FACTORY(NewWindowArgs);
BASIC_FACTORY(FocusPaneArgs); BASIC_FACTORY(FocusPaneArgs);

View File

@@ -357,6 +357,15 @@ namespace Microsoft.Terminal.Settings.Model
FindMatchDirection Direction { get; }; FindMatchDirection Direction { get; };
}; };
[default_interface] runtimeclass SaveSnippetArgs : IActionArgs
{
SaveSnippetArgs();
SaveSnippetArgs(String Name, String Commandline, String KeyChord);
String Name;
String Commandline;
String KeyChord;
};
[default_interface] runtimeclass NewWindowArgs : IActionArgs [default_interface] runtimeclass NewWindowArgs : IActionArgs
{ {
NewWindowArgs(INewContentArgs contentArgs); NewWindowArgs(INewContentArgs contentArgs);

View File

@@ -882,6 +882,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return results; return results;
} }
void ActionMap::AddSendInputAction(winrt::hstring name, winrt::hstring input, const Control::KeyChord keys)
{
auto newAction = winrt::make<ActionAndArgs>();
newAction.Action(ShortcutAction::SendInput);
auto sendInputArgs = winrt::make<SendInputArgs>(input);
newAction.Args(sendInputArgs);
auto cmd{ make_self<Command>() };
if (!name.empty())
{
cmd->Name(name);
}
cmd->ActionAndArgs(newAction);
cmd->GenerateID();
AddAction(*cmd, keys);
}
IVector<Model::Command> ActionMap::FilterToSendInput( IVector<Model::Command> ActionMap::FilterToSendInput(
winrt::hstring currentCommandline) winrt::hstring currentCommandline)
{ {

View File

@@ -78,6 +78,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys); bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys);
void DeleteKeyBinding(const Control::KeyChord& keys); void DeleteKeyBinding(const Control::KeyChord& keys);
void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action); void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action);
void AddSendInputAction(winrt::hstring name, winrt::hstring input, const Control::KeyChord keys);
Windows::Foundation::Collections::IVector<Model::Command> ExpandedCommands(); Windows::Foundation::Collections::IVector<Model::Command> ExpandedCommands();
void ExpandCommands(const Windows::Foundation::Collections::IVectorView<Model::Profile>& profiles, void ExpandCommands(const Windows::Foundation::Collections::IVectorView<Model::Profile>& profiles,

View File

@@ -31,5 +31,6 @@ namespace Microsoft.Terminal.Settings.Model
void DeleteKeyBinding(Microsoft.Terminal.Control.KeyChord keys); void DeleteKeyBinding(Microsoft.Terminal.Control.KeyChord keys);
void RegisterKeyBinding(Microsoft.Terminal.Control.KeyChord keys, ActionAndArgs action); void RegisterKeyBinding(Microsoft.Terminal.Control.KeyChord keys, ActionAndArgs action);
void AddSendInputAction(String name, String input, Microsoft.Terminal.Control.KeyChord keys);
} }
} }

View File

@@ -75,6 +75,7 @@
ON_ALL_ACTIONS(CloseTabsAfter) \ ON_ALL_ACTIONS(CloseTabsAfter) \
ON_ALL_ACTIONS(TabSearch) \ ON_ALL_ACTIONS(TabSearch) \
ON_ALL_ACTIONS(MoveTab) \ ON_ALL_ACTIONS(MoveTab) \
ON_ALL_ACTIONS(SaveSnippet) \
ON_ALL_ACTIONS(BreakIntoDebugger) \ ON_ALL_ACTIONS(BreakIntoDebugger) \
ON_ALL_ACTIONS(TogglePaneReadOnly) \ ON_ALL_ACTIONS(TogglePaneReadOnly) \
ON_ALL_ACTIONS(EnablePaneReadOnly) \ ON_ALL_ACTIONS(EnablePaneReadOnly) \
@@ -148,6 +149,7 @@
ON_ALL_ACTIONS_WITH_ARGS(SplitPane) \ ON_ALL_ACTIONS_WITH_ARGS(SplitPane) \
ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \ ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \
ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) \ ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) \
ON_ALL_ACTIONS_WITH_ARGS(SaveSnippet) \
ON_ALL_ACTIONS_WITH_ARGS(FocusPane) \ ON_ALL_ACTIONS_WITH_ARGS(FocusPane) \
ON_ALL_ACTIONS_WITH_ARGS(ExportBuffer) \ ON_ALL_ACTIONS_WITH_ARGS(ExportBuffer) \
ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer) \ ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer) \

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@@ -727,4 +727,7 @@
<value>Open about dialog</value> <value>Open about dialog</value>
<comment>This will open the "about" dialog, to display version info and other documentation</comment> <comment>This will open the "about" dialog, to display version info and other documentation</comment>
</data> </data>
</root> <data name="SaveSnippetNamePrefix" xml:space="preserve">
<value>Save Snippet</value>
</data>
</root>

View File

@@ -155,6 +155,17 @@
</alwaysEnabledBrandingTokens> </alwaysEnabledBrandingTokens>
</feature> </feature>
<feature>
<name>Feature_SaveSnippet</name>
<description>Save Snippet</description>
<id>9971</id>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Canary</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
<feature> <feature>
<name>Feature_QuickFix</name> <name>Feature_QuickFix</name>
<description>Enables the Quick Fix menu</description> <description>Enables the Quick Fix menu</description>