-
-
Notifications
You must be signed in to change notification settings - Fork 75
feat: support for external actions #669
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
base: main
Are you sure you want to change the base?
Conversation
bf58c52
to
4062c1a
Compare
4062c1a
to
fd6a4ef
Compare
This is awesome. It's pretty much what I was thinking of too which is great 😁 Here are the other things I had in mind for this summed up in part of a channel prototype: # open the selected entry/ies in vim without leaving tv (i.e. exiting vim returns to tv)
# this is calling `fork` behind the scenes
[actions.open-in-vim]
description = "open the selected entry with vim"
command = "vim '{}'"
become = false
# this makes tv become vim (i.e. the tv process turns into vim so leaving vim will
# return you to the shell)
# this is calling `execve` behind the scenes
[actions.become-vim]
description = "exit tv and open the selected entry in vim"
command = "vim '{}'"
become = true
# when you don't care about the command's output
[actions.rm]
description = "delete a file"
command = "rm '{}'"
silent = true # or 'verbose' or something like that
# when you actually care about the command's output
[actions.ls]
description = "list the entries in the currently selected directory"
command = "ls '{}'"
silent = false These extra features shouldn't be added directly in this PR, but we should try to shape the code in a way that makes it easier to add them later on. I also was thinking of adding a channel-like selection UI for the actions of a given channel, e.g.:
The UI would be very similar to what the remote control already looks like. And the code would be very similar too (the remote control is more or less implemented as a hard-coded channel to reuse as much existing logic as possible). What do you think? PS: was considering either using libc directly or https://crates.io/crates/nix (at least for unix) unless there's a simpler way to do the equivalent of |
@@ -12,3 +12,8 @@ env = { BAT_THEME = "ansi" } | |||
|
|||
[keybindings] | |||
shortcut = "f1" | |||
ctrl-f12 = "edit" |
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.
Maybe we should use a prefix here to avoid clashes with tv's builtin actions, e.g.:
[keybindings]
ctrl-f12 = "actions:edit"
ctrl-f11 = "actions:copy-entry-to-clipboard" # the action defined below
ctrl-f10 = "copy-entry-to-clipboard" # tv's builtin Action::CopyEntryToClipboard
[actions.edit]
[actions.copy-entry-to-clipboard]
This would also probably re-simplify deserialisation below.
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.
yup, makes sense, thanks
Action::ExternalAction(ref action_name) => { | ||
debug!("External action triggered: {}", action_name); | ||
|
||
// Handle external action execution | ||
if let Some(selected_entry) = | ||
self.television.get_selected_entry() | ||
{ | ||
debug!("Selected entry: {}", selected_entry.raw); | ||
|
||
if let Some(action_spec) = self | ||
.television | ||
.channel_prototype | ||
.actions | ||
.get(action_name) | ||
{ | ||
debug!( | ||
"Found action spec for: {}", | ||
action_name | ||
); | ||
// Store the external action info and exit - the command will be executed after terminal cleanup | ||
self.should_quit = true; | ||
self.render_tx.send(RenderingTask::Quit)?; | ||
return Ok(ActionOutcome::ExternalAction( | ||
action_spec.clone(), | ||
selected_entry.raw.clone(), | ||
)); | ||
} | ||
|
||
error!("Unknown action: {}", action_name); | ||
// List available actions for debugging | ||
let available_actions: Vec<&String> = self | ||
.television | ||
.channel_prototype | ||
.actions | ||
.keys() | ||
.collect(); | ||
debug!( | ||
"Available actions: {:?}", | ||
available_actions | ||
); | ||
self.action_tx.send(Action::Error(format!( | ||
"Unknown action: {}", | ||
action_name | ||
)))?; | ||
} else { | ||
error!("No entry selected for external action"); | ||
self.action_tx.send(Action::Error( | ||
"No entry selected for external action" | ||
.to_string(), | ||
))?; | ||
} | ||
} |
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 couple of things here:
- we possibly need to deal with multiple entries (the user might have selected 3 entries and wishes to execute
rm
on all three) - we won't always want to exit tv after performing an action (see PR main comment)
let formatted_command = | ||
action_spec.command.get_nth(0).format(&entry_value)?; |
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 kind of feels weird having to call get_nth(0)
here but I guess that's how we ended up implementing multiple sources per-channel 🤷🏻
not a big deal
.stdin(Stdio::inherit()) | ||
.stdout(Stdio::inherit()) | ||
.stderr(Stdio::inherit()) | ||
.spawn()?; |
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 is where we don't always want to:
- inherit std file descriptors
- spawn a new process
📺 PR Description
resolves #16; resolves #658, resolves #297
This PR adds support for definition of external actions, allowing the following
Checklist