feat: execute helix command from pipe output

pull/11692/head
L-Trump 2 months ago
parent 237cbe4bca
commit 3c76d750a7

@ -81,6 +81,7 @@
| `:append-output` | Run shell command, appending output after each selection. | | `:append-output` | Run shell command, appending output after each selection. |
| `:pipe` | Pipe each selection to the shell command. | | `:pipe` | Pipe each selection to the shell command. |
| `:pipe-to` | Pipe each selection to the shell command, ignoring output. | | `:pipe-to` | Pipe each selection to the shell command, ignoring output. |
| `:pipe-execute` | Pipe each selection to the shell command, execute output as helix command. |
| `:run-shell-command`, `:sh` | Run a shell command | | `:run-shell-command`, `:sh` | Run a shell command |
| `:reset-diff-change`, `:diffget`, `:diffg` | Reset the diff change at the cursor position. | | `:reset-diff-change`, `:diffget`, `:diffg` | Reset the diff change at the cursor position. |
| `:clear-register` | Clear given register. If no argument is provided, clear all registers. | | `:clear-register` | Clear given register. If no argument is provided, clear all registers. |

@ -108,6 +108,7 @@ Normal mode is the default mode when you launch helix. You can return to it from
| `!` | Run shell command, inserting output before each selection | `shell_insert_output` | | `!` | Run shell command, inserting output before each selection | `shell_insert_output` |
| `Alt-!` | Run shell command, appending output after each selection | `shell_append_output` | | `Alt-!` | Run shell command, appending output after each selection | `shell_append_output` |
| `$` | Pipe each selection into shell command, keep selections where command returned 0 | `shell_keep_pipe` | | `$` | Pipe each selection into shell command, keep selections where command returned 0 | `shell_keep_pipe` |
| `Alt-$` | Pipe each selection into shell command, execute output as helix command | `shell_keep_pipe` |
### Selection manipulation ### Selection manipulation

@ -63,6 +63,7 @@ use crate::{
}; };
use crate::job::{self, Jobs}; use crate::job::{self, Jobs};
use std::str::FromStr;
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@ -555,6 +556,7 @@ impl MappableCommand {
dap_disable_exceptions, "Disable exception breakpoints", dap_disable_exceptions, "Disable exception breakpoints",
shell_pipe, "Pipe selections through shell command", shell_pipe, "Pipe selections through shell command",
shell_pipe_to, "Pipe selections into shell command ignoring output", shell_pipe_to, "Pipe selections into shell command ignoring output",
shell_pipe_execute, "Pipe selections into shell command, execute output as command",
shell_insert_output, "Insert shell command output before selections", shell_insert_output, "Insert shell command output before selections",
shell_append_output, "Append shell command output after selections", shell_append_output, "Append shell command output after selections",
shell_keep_pipe, "Filter selections with shell predicate", shell_keep_pipe, "Filter selections with shell predicate",
@ -5693,6 +5695,7 @@ enum ShellBehavior {
Ignore, Ignore,
Insert, Insert,
Append, Append,
Execute,
} }
fn shell_pipe(cx: &mut Context) { fn shell_pipe(cx: &mut Context) {
@ -5703,6 +5706,10 @@ fn shell_pipe_to(cx: &mut Context) {
shell_prompt(cx, "pipe-to:".into(), ShellBehavior::Ignore); shell_prompt(cx, "pipe-to:".into(), ShellBehavior::Ignore);
} }
fn shell_pipe_execute(cx: &mut Context) {
shell_prompt(cx, "pipe-execute:".into(), ShellBehavior::Execute);
}
fn shell_insert_output(cx: &mut Context) { fn shell_insert_output(cx: &mut Context) {
shell_prompt(cx, "insert-output:".into(), ShellBehavior::Insert); shell_prompt(cx, "insert-output:".into(), ShellBehavior::Insert);
} }
@ -5829,7 +5836,7 @@ async fn shell_impl_async(
fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) { fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
let pipe = match behavior { let pipe = match behavior {
ShellBehavior::Replace | ShellBehavior::Ignore => true, ShellBehavior::Replace | ShellBehavior::Ignore | ShellBehavior::Execute => true,
ShellBehavior::Insert | ShellBehavior::Append => false, ShellBehavior::Insert | ShellBehavior::Append => false,
}; };
@ -5876,6 +5883,7 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
ShellBehavior::Replace => (range.from(), range.to(), range.len()), ShellBehavior::Replace => (range.from(), range.to(), range.len()),
ShellBehavior::Insert => (range.from(), range.from(), 0), ShellBehavior::Insert => (range.from(), range.from(), 0),
ShellBehavior::Append => (range.to(), range.to(), 0), ShellBehavior::Append => (range.to(), range.to(), 0),
ShellBehavior::Execute => (range.from(), range.from(), 0),
_ => (range.from(), range.from(), 0), _ => (range.from(), range.from(), 0),
}; };
@ -5896,16 +5904,43 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
changes.push((from, to, Some(output))); changes.push((from, to, Some(output)));
} }
if behavior != &ShellBehavior::Ignore { let mut commands = Vec::with_capacity(changes.len());
match behavior {
ShellBehavior::Ignore => (),
ShellBehavior::Execute => {
for (_, _, command_text) in changes {
if let Some(command_text) = command_text {
if let Ok(command) = MappableCommand::from_str(&command_text[..]) {
commands.push(command)
}
}
}
}
_ => {
let transaction = Transaction::change(doc.text(), changes.into_iter()) let transaction = Transaction::change(doc.text(), changes.into_iter())
.with_selection(Selection::new(ranges, selection.primary_index())); .with_selection(Selection::new(ranges, selection.primary_index()));
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
} }
};
// after replace cursor may be out of bounds, do this to // after replace cursor may be out of bounds, do this to
// make sure cursor is in view and update scroll as well // make sure cursor is in view and update scroll as well
view.ensure_cursor_in_view(doc, config.scrolloff); view.ensure_cursor_in_view(doc, config.scrolloff);
if behavior == &ShellBehavior::Execute {
for command in commands {
let mut ctx = Context {
register: None,
count: None,
editor: cx.editor,
callback: Vec::new(),
on_next_key_callback: None,
jobs: cx.jobs,
};
command.execute(&mut ctx);
}
}
} }
fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {

@ -2263,6 +2263,14 @@ fn pipe_to(
pipe_impl(cx, args, event, &ShellBehavior::Ignore) pipe_impl(cx, args, event, &ShellBehavior::Ignore)
} }
fn pipe_execute(
cx: &mut compositor::Context,
args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
pipe_impl(cx, args, event, &ShellBehavior::Execute)
}
fn pipe(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> { fn pipe(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
pipe_impl(cx, args, event, &ShellBehavior::Replace) pipe_impl(cx, args, event, &ShellBehavior::Replace)
} }
@ -3092,6 +3100,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
fun: pipe_to, fun: pipe_to,
signature: CommandSignature::none(), signature: CommandSignature::none(),
}, },
TypableCommand {
name: "pipe-execute",
aliases: &[],
doc: "Pipe each selection to the shell command, execute output as command.",
fun: pipe_execute,
signature: CommandSignature::none(),
},
TypableCommand { TypableCommand {
name: "run-shell-command", name: "run-shell-command",
aliases: &["sh"], aliases: &["sh"],

@ -328,6 +328,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"!" => shell_insert_output, "!" => shell_insert_output,
"A-!" => shell_append_output, "A-!" => shell_append_output,
"$" => shell_keep_pipe, "$" => shell_keep_pipe,
"A-$" => shell_pipe_execute,
"C-z" => suspend, "C-z" => suspend,
"C-a" => increment, "C-a" => increment,

Loading…
Cancel
Save