-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to save input action from command line #16513
Conversation
@@ -857,6 +857,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation | |||
AddAction(*cmd); | |||
} | |||
|
|||
void ActionMap::AddSendInputAction(winrt::hstring name, winrt::hstring input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to add this in TerminalPage::_HandleSaveTask, the build errors from calling winrt::make(input) from that project defeated me.
- Save to disk - If command line is empty use selection - Show toast
7cc2dea
to
83e3b8c
Compare
4ac3c7a
to
5d99e7b
Compare
5d99e7b
to
8bec5ec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a drive by review for now:
- structurally, this looks like it hits in all the right places
- I like the confirmation toast. Great idea.
- Testing with the commandline mode of the command palette was a clever idea here, really liked that
- I'm excited about this and definitely need to make some additional time for this review soon
IsLightDismissEnabled="True"> | ||
<mux:TeachingTip.Content> | ||
<StackPanel Orientation="Vertical"> | ||
<TextBox x:Name="ActionSavedNameText" Text="" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These may need to be TextBlock
's, instead of TextBox
es. Block
s are just for displaying text, while the Box
is supposed to be for input. (super intuitive, I know 🙃)
winrt::hstring SaveTaskArgs::GenerateName() const | ||
{ | ||
return winrt::hstring{ | ||
fmt::format(L"Save Task commandline:{}, name: {}, keyChord {}", Commandline(), Name(), KeyChord()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we may want to omit the arg from the generated name, if the value of the arg is just empty. Like, in your gif, it always shows "keychord ," even when the keys weren't provided yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, let me know if that looks ok.
For the toast, I have it showing None when the keyChord is null, is it better to hide the TextBlocks from the toast's content?
Right now if the same name is used I think it will overwrite the existing action, does it make sense to have a --force arg, and fail if there is a existing action for the name and no force arg?
blank arges from generated name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More drive-by comments. We discussed possibly holding this from 1.20 (which is gonna be a smaller, more stablization-focused release), and then merge it in for 1.21.
Sorry that we've been slow here. Hopefully in a few weeks, we'll be able to better start moving code again 😄
} | ||
else | ||
{ | ||
args.Name(args.GenerateName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This right here might not be right. In the case that you don't pass a --name
explicitly, this ends up setting the SaveTask::Name
to something like "Save task commandline: git status". Then later, we'll build the SendInput
action, and set it's name to "Save task commandline: git status".
The json for this will end up like
{
"command":
{
"action": "sendInput",
"input": "git status"
},
"name": "Save Task commandline: git status"
},
I'm pretty sure we'd want to just leave that .Name
empty
{ | ||
std::wstring formattedString; | ||
|
||
if (!Name().empty() && !KeyChord().empty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to copy the pattern in SplitPaneArgs::GenerateName
, which uses a stringstream, and then just adds each successive parameter to the stream (if the param is provided)
|
||
if (!Name().empty() && !KeyChord().empty()) | ||
{ | ||
formattedString = fmt::format(L"Save Task commandline: {}, name: {}, keyChord {}", Commandline(), Name(), KeyChord()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Save Task" will almost definitely need to be in the resources.resw, and loaded with RS_
(like the other actions)
IsLightDismissEnabled="True"> | ||
<mux:TeachingTip.Content> | ||
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch"> | ||
<TextBlock x:Name="ActionSavedNameText" Text="" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Playing with this locally, there's still like, a faint background to these text blocks. I can't quite tell why - maybe there's a weird interaction with the acrylic of the Toast. Any chance you're seeing that? Maybe it's just me 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried a few different settings and wasn't able to notice anything. I am not able to see depths at all, so I may just be missing it.
Sounds good! No rush on my end. |
Notes for team discussion later this week:
But, this also lines up well with other work that's in progress (#13445 (comment)), so I'd like to get this in and keep iterating. |
Discussion feedback:
|
I think the key chord parsing got broken with the upstream merge. I think I can take a look tonight. |
It looks like I had broken that here. Should be fixed now.
|
that are not filled in
Is this along the line of what you were thinking for not displaying unfilled display params? d4eaec3 Wanted to try to address this one but wasn't sure what the behavior was. Is there a way that I can reproduce it? "should default to _the current window" |
for x-save. is this along the lines of what you were thinking? 50baa7b For the first, what does velocity mean in this context? |
Hey sorry about not replying earlier. I was... delayed... getting ready for Build. BUT THAT'S DONE NOW. NOW IT'S TIME FOR FUN. I'm just gonna quick leave this link here: I was prototyping some other fixes that would make the XAML binding less painful. Admittedly, that branched off probably two or three months ago, so I'm sure it's stale.
Exactly that! That's perfect.
"Velocity" is our hodgepodge'd way of controlling which features go into which branches. It's pretty much all in I'm gonna start looping back on this now, cause this is a pretty important part of the story I think we can tell with #17329. Thanks for all the patience! |
4e85136
to
3f15d6e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, my only concerns are over some comments? Yea that's not worth blocking over. (Things I mark with 📝 are meant as notes to myself while reviewing, which may be helpful for other reviewers)
The saving settings on the UI thread thing isn't great, but we should fix that for both the settings UI and this at once. That's not on you.
I'll come through after this and figure out how to yeet this action into the current terminal window using the Monarch. I'm not gonna ask you to try and wrap your head around the fuckiness that is our cross-window communication 😅
Thanks for all your patience here!
@@ -52,6 +52,7 @@ static constexpr std::string_view SwitchToTabKey{ "switchToTab" }; | |||
static constexpr std::string_view TabSearchKey{ "tabSearch" }; | |||
static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" }; | |||
static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" }; | |||
static constexpr std::string_view SaveTaskKey{ "saveTask" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay this is super annoying but I think we collectively decided to change the name of this feature to "snippets" to better align with VS tools. We only decided that in like the last month, so that's on me to actually come through and fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made an attempt at this in the commit below. Not totally sure I got the names right. Let me know if I should add this commit to the PR (totally understand if you want to update it yourself).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing this I realized that I have an issue where you can save a snippet without a command line and a validation warning about the incorrect configs pops up after. I think this commit would fix it (It may need to be localized)
{ | ||
cmd->Name(name); | ||
} | ||
cmd->RegisterKey(keys); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝: this feels like it'll change after ~#17215, but that shouldn't be too bad
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@e82eric FYI I believe that PR did merge now, so this one conflicts now. Let me know if you heed help with the conflict
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be updated now. I ran into 2 questions through.
- I copied the last 2 lines from
ActionMap::RegisterKeyBinding
instead of just callingActionMap::RegisterKeyBinding
to get the name to show up.
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>() };
cmd->ActionAndArgs(newAction);
if (!name.empty())
{
cmd->Name(name);
}
cmd->GenerateID();
AddAction(*cmd, keys);
}
- I keep wanting to move the above code to
TerminalPage::_HandleSaveSnippet
but I can't getwinrt::make<ActionAndArgs>()
to work. Should I keep trying to get that to work, or is it ok to have the one off method in ActionMap?
Orientation="Vertical"> | ||
<TextBlock x:Name="ActionSavedNameText" | ||
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(SavedActionName), Mode=OneWay}"> | ||
<Run Text="Name: " /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝: I think I'm fine with these not being localized, since they're literal params to the action args, and we've got precedent for that in the cmdpal already
void TerminalPage::_HandleSaveTask(const IInspectable& /*sender*/, | ||
const ActionEventArgs& args) | ||
{ | ||
if (Feature_SaveTask::IsEnabled()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO I'd just do an early-return if the feature isn't enabled, but I'm not sure if that messes with the compiler's ability to just optimize the entire rest of the function out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to early return. (Is there a way to tell if the compiler will struggle with this?)
void TerminalPage::_HandleSaveTask(const IInspectable& /*sender*/, | ||
const ActionEventArgs& args) | ||
{ | ||
if (Feature_SaveTask::IsEnabled()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh also:
if (Feature_SaveTask::IsEnabled()) | |
if constexpr (Feature_SaveTask::IsEnabled()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
keyChord = KeyChordSerialization::FromString(winrt::to_hstring(realArgs.KeyChord())); | ||
} | ||
_settings.GlobalSettings().ActionMap().AddSendInputAction(realArgs.Name(), realArgs.Commandline(), keyChord); | ||
_settings.WriteSettingsToDisk(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ WriteSettingsToDisk
writes the file on this thread. We definitely can't be doing this on the UI thread.
You might be able to just do a
auto saveSettings = [settings=_settings]() -> winrt::fire_and_forget {
co_await winrt::resume_background();
settings.WriteSettingsToDisk();
};
saveSettings();
to basically make a lambda that does the fire_and_forget thing, but then also just call it immediately. I haven't tested it, so I'm sure there's spooky coroutine reasons that's a bad idea, but 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also now that I'm looking at it, the Settings UI itself saves the settings on the UI thread! Egads! I don't think it's worth blocking you over this if we can't even do it right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave this a try just so I could get a better understanding of how the fire_and_forget stuff works.
It looked like it worked, the settings were saved to the file, and I was able to see it switch from the window thread to the background thread in the debugger when it did the write to disk.
In the debugger I started getting a AV. Adding a strong ref to settings seemed to prevent that.
auto saveSettings = [settings = _settings]() -> winrt::fire_and_forget {
const auto strongSettings = settings;
co_await winrt::resume_background();
strongSettings.WriteSettingsToDisk();
};
saveSettings();
// Build the NewTab action from the values we've parsed on the commandline. | ||
ActionAndArgs saveAction{}; | ||
saveAction.Action(ShortcutAction::SaveTask); | ||
// _getNewTerminalArgs MUST be called before parsing any other options, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a bit of copypasta here 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to be slightly more generic. // Build the action from the values we've parsed on the commandline.
Does this make sense or should I just remove it?
// _getNewTerminalArgs MUST be called before parsing any other options, | ||
// as it might clear those options while finding the commandline |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// _getNewTerminalArgs MUST be called before parsing any other options, | |
// as it might clear those options while finding the commandline | |
// First, parse out the commandline in the same way that | |
// _getNewTerminalArgs does it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to suggestion inside of commit 2bf8391
c9d3e5a
to
cd13fc3
Compare
{ | ||
const auto selections{ termControl.SelectedText(true) }; | ||
const auto selection = std::accumulate(selections.begin(), selections.end(), std::wstring()); | ||
realArgs.Commandline(selection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't modify the realArgs.Commandline()
but instead extract the hstring and then modify the local reference. Like this:
auto commandline = realArgs.Commandline();
if (commandline.empty() {
// ...
commandline = selection;
}
if (commandline.empty()) {
// ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
@@ -4184,6 +4184,66 @@ namespace winrt::TerminalApp::implementation | |||
} | |||
} | |||
|
|||
winrt::fire_and_forget TerminalPage::ActionSaved(winrt::hstring input, winrt::hstring name, winrt::hstring keyChord) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why put these two functions here instead of having them next to the related code inside AppActionHandlers.cpp
? I feel like it should either be all here or all there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
auto weakThis{ get_weak() }; | ||
co_await wil::resume_foreground(Dispatcher()); | ||
if (auto page{ weakThis.get() }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this code get called from a background thread?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, It was not running on a background thread, before and after the co_await it was on the Window thread. Updated to just use void and removed the co_await.
In the spec review, we agreed these didn't really need to be saved to the user's own settings file. This removes parsing and saving for the `experimental.saveSnippet` action, but we still have the action _internally_. This is powered by a new x-macro for "INTERNAL_" actions. Follow-up from #16513.
In the spec review, we agreed these didn't really need to be saved to the user's own settings file. This removes parsing and saving for the `experimental.saveSnippet` action, but we still have the action _internally_. This is powered by a new x-macro for "INTERNAL_" actions. Follow-up from #16513.
Hi wanted to make an attempt at 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...1cde67a
PR Checklist
sendInput
action #12857