on master help sign in
Pipe typable command (#1972)

Co-authored-by: DeviousStoat <devious@stoat.com>

commit2eca2901f31083af85ffd6f299c64ad80a8bfaf5 authorThomas <74479846+DeviousStoat@users.noreply.github.com> dateSun 17 Apr 2022, 05:03 (5 years ago) parentdc8fef5
3 files changed+67−45
book/src/generated/typable-cmd.md+1 helix-term/src/commands.rs+49−45 helix-term/src/commands/typed.rs+17
book/src/generated/typable-cmd.md+1
@@ -61,3 +61,4 @@
6161| `:tree-sitter-subtree`, `:ts-subtree` | Display tree sitter subtree under cursor, primarily for debugging queries. |
6262| `:config-reload` | Refreshes helix's config. |
6363| `:config-open` | Open the helix config.toml file. |
64+| `:pipe` | Pipe each selection to the shell command. |
helix-term/src/commands.rs+49−45
@@ -4150,19 +4150,19 @@ enum ShellBehavior {
41504150}
41514151
41524152fn shell_pipe(cx: &mut Context) {
4153− shell(cx, "pipe:".into(), ShellBehavior::Replace);
4153+ shell_prompt(cx, "pipe:".into(), ShellBehavior::Replace);
41544154}
41554155
41564156fn shell_pipe_to(cx: &mut Context) {
4157− shell(cx, "pipe-to:".into(), ShellBehavior::Ignore);
4157+ shell_prompt(cx, "pipe-to:".into(), ShellBehavior::Ignore);
41584158}
41594159
41604160fn shell_insert_output(cx: &mut Context) {
4161− shell(cx, "insert-output:".into(), ShellBehavior::Insert);
4161+ shell_prompt(cx, "insert-output:".into(), ShellBehavior::Insert);
41624162}
41634163
41644164fn shell_append_output(cx: &mut Context) {
4165− shell(cx, "append-output:".into(), ShellBehavior::Append);
4165+ shell_prompt(cx, "append-output:".into(), ShellBehavior::Append);
41664166}
41674167
41684168fn shell_keep_pipe(cx: &mut Context) {
@@ -4256,65 +4256,69 @@ fn shell_impl(
42564256 Ok((tendril, output.status.success()))
42574257}
42584258
4259−fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
4259+fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
42604260 let pipe = match behavior {
42614261 ShellBehavior::Replace | ShellBehavior::Ignore => true,
42624262 ShellBehavior::Insert | ShellBehavior::Append => false,
42634263 };
42644264
4265+ let config = cx.editor.config();
4266+ let shell = &config.shell;
4267+ let (view, doc) = current!(cx.editor);
4268+ let selection = doc.selection(view.id);
4269+
4270+ let mut changes = Vec::with_capacity(selection.len());
4271+ let text = doc.text().slice(..);
4272+
4273+ for range in selection.ranges() {
4274+ let fragment = range.fragment(text);
4275+ let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment.as_bytes())) {
4276+ Ok(result) => result,
4277+ Err(err) => {
4278+ cx.editor.set_error(err.to_string());
4279+ return;
4280+ }
4281+ };
4282+
4283+ if !success {
4284+ cx.editor.set_error("Command failed");
4285+ return;
4286+ }
4287+
4288+ let (from, to) = match behavior {
4289+ ShellBehavior::Replace => (range.from(), range.to()),
4290+ ShellBehavior::Insert => (range.from(), range.from()),
4291+ ShellBehavior::Append => (range.to(), range.to()),
4292+ _ => (range.from(), range.from()),
4293+ };
4294+ changes.push((from, to, Some(output)));
4295+ }
4296+
4297+ if behavior != &ShellBehavior::Ignore {
4298+ let transaction = Transaction::change(doc.text(), changes.into_iter());
4299+ doc.apply(&transaction, view.id);
4300+ }
4301+
4302+ // after replace cursor may be out of bounds, do this to
4303+ // make sure cursor is in view and update scroll as well
4304+ view.ensure_cursor_in_view(doc, config.scrolloff);
4305+}
4306+
4307+fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
42654308 ui::prompt(
42664309 cx,
42674310 prompt,
42684311 Some('|'),
42694312 ui::completers::none,
42704313 move |cx, input: &str, event: PromptEvent| {
4271− let config = cx.editor.config();
4272− let shell = &config.shell;
42734314 if event != PromptEvent::Validate {
42744315 return;
42754316 }
42764317 if input.is_empty() {
42774318 return;
42784319 }
4279− let (view, doc) = current!(cx.editor);
4280− let selection = doc.selection(view.id);
4281−
4282− let mut changes = Vec::with_capacity(selection.len());
4283− let text = doc.text().slice(..);
4284−
4285− for range in selection.ranges() {
4286− let fragment = range.fragment(text);
4287− let (output, success) =
4288− match shell_impl(shell, input, pipe.then(|| fragment.as_bytes())) {
4289− Ok(result) => result,
4290− Err(err) => {
4291− cx.editor.set_error(err.to_string());
4292− return;
4293− }
4294− };
4295−
4296− if !success {
4297− cx.editor.set_error("Command failed");
4298− return;
4299− }
4300−
4301− let (from, to) = match behavior {
4302− ShellBehavior::Replace => (range.from(), range.to()),
4303− ShellBehavior::Insert => (range.from(), range.from()),
4304− ShellBehavior::Append => (range.to(), range.to()),
4305− _ => (range.from(), range.from()),
4306− };
4307− changes.push((from, to, Some(output)));
4308− }
4309−
4310− if behavior != ShellBehavior::Ignore {
4311− let transaction = Transaction::change(doc.text(), changes.into_iter());
4312− doc.apply(&transaction, view.id);
4313− }
43144320
4315− // after replace cursor may be out of bounds, do this to
4316− // make sure cursor is in view and update scroll as well
4317− view.ensure_cursor_in_view(doc, config.scrolloff);
4321+ shell(cx, input, &behavior);
43184322 },
43194323 );
43204324}
helix-term/src/commands/typed.rs+17
@@ -1067,6 +1067,16 @@ fn refresh_config(
10671067 Ok(())
10681068}
10691069
1070+fn pipe(
1071+ cx: &mut compositor::Context,
1072+ args: &[Cow<str>],
1073+ _event: PromptEvent,
1074+) -> anyhow::Result<()> {
1075+ ensure!(!args.is_empty(), "Shell command required");
1076+ shell(cx, &args.join(" "), &ShellBehavior::Replace);
1077+ Ok(())
1078+}
1079+
10701080pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
10711081 TypableCommand {
10721082 name: "quit",
@@ -1495,6 +1505,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
14951505 fun: open_config,
14961506 completer: None,
14971507 },
1508+ TypableCommand {
1509+ name: "pipe",
1510+ aliases: &[],
1511+ doc: "Pipe each selection to the shell command.",
1512+ fun: pipe,
1513+ completer: None,
1514+ },
14981515 ];
14991516
15001517pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =