From dc609cafb506e9677d16632b6fede3c488d4c4bb Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Wed, 1 Sep 2021 10:46:35 +0900 Subject: Extract the shell command into a separate function --- helix-term/src/commands.rs | 90 +++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 40 deletions(-) (limited to 'helix-term') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index f479a7a0..797a5e99 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4322,9 +4322,46 @@ fn shell_keep_pipe(cx: &mut Context) { shell(cx, "keep-pipe:".into(), ShellBehavior::Filter); } -fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { +fn shell_impl( + shell: &[String], + cmd: &str, + input: Option<&[u8]>, +) -> anyhow::Result<(Tendril, bool)> { use std::io::Write; use std::process::{Command, Stdio}; + let mut process = match Command::new(&shell[0]) + .args(&shell[1..]) + .arg(cmd) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(process) => process, + Err(e) => { + log::error!("Failed to start shell: {}", e); + return Err(e.into()); + } + }; + if let Some(input) = input { + let mut stdin = process.stdin.take().unwrap(); + stdin.write_all(input)?; + } + let output = process.wait_with_output()?; + + if !output.status.success() { + if !output.stderr.is_empty() { + log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr)); + } + bail!("Command failed"); + } + + let tendril = Tendril::try_from_byte_slice(&output.stdout) + .map_err(|_| anyhow!("Process did not output valid UTF-8"))?; + Ok((tendril, output.status.success())) +} + +fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { if cx.editor.config.shell.is_empty() { cx.editor.set_error("No shell set".to_owned()); return; @@ -4338,67 +4375,40 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { Some('|'), |_input: &str| Vec::new(), move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { + let shell = &cx.editor.config.shell; if event != PromptEvent::Validate { return; } - let shell = &cx.editor.config.shell; let (view, doc) = current!(cx.editor); let selection = doc.selection(view.id); let mut changes = Vec::with_capacity(selection.len()); + let text = doc.text().slice(..); for range in selection.ranges() { - let mut process = match Command::new(&shell[0]) - .args(&shell[1..]) - .arg(input) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - { - Ok(process) => process, - Err(e) => { - log::error!("Failed to start shell: {}", e); - cx.editor.set_error("Failed to start shell".to_owned()); - return; - } - }; - if pipe { - let stdin = process.stdin.as_mut().unwrap(); - let fragment = range.fragment(doc.text().slice(..)); - stdin.write_all(fragment.as_bytes()).unwrap(); - } - let output = process.wait_with_output().unwrap(); - - if behavior != ShellBehavior::Filter { - if !output.status.success() { - if !output.stderr.is_empty() { - log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr)); - } - cx.editor.set_error("Command failed".to_owned()); - return; - } - let tendril = match Tendril::try_from_byte_slice(&output.stdout) { - Ok(tendril) => tendril, - Err(_) => { - cx.editor - .set_error("Process did not output valid UTF-8".to_owned()); + let fragment = range.fragment(text); + let (output, success) = + match shell_impl(shell, input, pipe.then(|| fragment.as_bytes())) { + Ok(result) => result, + Err(err) => { + cx.editor.set_error(err.to_string()); return; } }; + + if behavior != ShellBehavior::Filter { let (from, to) = match behavior { ShellBehavior::Replace => (range.from(), range.to()), ShellBehavior::Insert => (range.from(), range.from()), ShellBehavior::Append => (range.to(), range.to()), _ => (range.from(), range.from()), }; - changes.push((from, to, Some(tendril))); + changes.push((from, to, Some(output))); } else { // if the process exits successfully, keep the selection, otherwise delete it. - let keep = output.status.success(); changes.push(( range.from(), - if keep { range.from() } else { range.to() }, + if success { range.from() } else { range.to() }, None, )); } -- cgit v1.2.3-70-g09d2 From ce7ad2beb5b763e55cc40933d70ca7c7b325292e Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Wed, 1 Sep 2021 11:01:19 +0900 Subject: Reimplement keep-pipe, it needs to manipulate selections, not text --- book/src/keymap.md | 14 +++---- helix-term/src/commands.rs | 98 +++++++++++++++++++++++++++++++++------------- 2 files changed, 78 insertions(+), 34 deletions(-) (limited to 'helix-term') diff --git a/book/src/keymap.md b/book/src/keymap.md index d85fb936..2c6a9576 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -124,13 +124,13 @@ in reverse, or searching via smartcase. ### Shell -| Key | Description | Command | -| ------ | ----------- | ------- | -| `\|` | Pipe each selection through shell command, replacing with output | `shell_pipe` | -| `A-\|` | Pipe each selection into shell command, ignoring output | `shell_pipe_to` | -| `!` | Run shell command, inserting output before each selection | `shell_insert_output` | -| `A-!` | Run shell command, appending output after each selection | `shell_append_output` | -| `$` | Pipe each selection into shell command, removing if the command exits >0 | `shell_keep_pipe` | +| Key | Description | Command | +| ------ | ----------- | ------- | +| `\|` | Pipe each selection through shell command, replacing with output | `shell_pipe` | +| `A-\|` | Pipe each selection into shell command, ignoring output | `shell_pipe_to` | +| `!` | Run shell command, inserting output before each selection | `shell_insert_output` | +| `A-!` | 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` | ## Select / extend mode diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 797a5e99..5574afbf 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4299,7 +4299,6 @@ enum ShellBehavior { Ignore, Insert, Append, - Filter, } fn shell_pipe(cx: &mut Context) { @@ -4319,7 +4318,56 @@ fn shell_append_output(cx: &mut Context) { } fn shell_keep_pipe(cx: &mut Context) { - shell(cx, "keep-pipe:".into(), ShellBehavior::Filter); + let prompt = Prompt::new( + "keep-pipe:".into(), + Some('|'), + |_input: &str| Vec::new(), + move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { + let shell = &cx.editor.config.shell; + if event != PromptEvent::Validate { + return; + } + if input.is_empty() { + return; + } + let (view, doc) = current!(cx.editor); + let selection = doc.selection(view.id); + + let mut ranges = SmallVec::with_capacity(selection.len()); + let old_index = selection.primary_index(); + let mut index: Option = None; + let text = doc.text().slice(..); + + for (i, range) in selection.ranges().iter().enumerate() { + let fragment = range.fragment(text); + let (_output, success) = match shell_impl(shell, input, Some(fragment.as_bytes())) { + Ok(result) => result, + Err(err) => { + cx.editor.set_error(err.to_string()); + return; + } + }; + + // if the process exits successfully, keep the selection + if success { + ranges.push(*range); + if i >= old_index && index.is_none() { + index = Some(ranges.len() - 1); + } + } + } + + if ranges.is_empty() { + cx.editor.set_error("No selections remaining".to_string()); + return; + } + + let index = index.unwrap_or_else(|| ranges.len() - 1); + doc.set_selection(view.id, Selection::new(ranges, index)); + }, + ); + + cx.push_layer(Box::new(prompt)); } fn shell_impl( @@ -4329,6 +4377,10 @@ fn shell_impl( ) -> anyhow::Result<(Tendril, bool)> { use std::io::Write; use std::process::{Command, Stdio}; + if shell.is_empty() { + bail!("No shell set"); + } + let mut process = match Command::new(&shell[0]) .args(&shell[1..]) .arg(cmd) @@ -4349,11 +4401,8 @@ fn shell_impl( } let output = process.wait_with_output()?; - if !output.status.success() { - if !output.stderr.is_empty() { - log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr)); - } - bail!("Command failed"); + if !output.stderr.is_empty() { + log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr)); } let tendril = Tendril::try_from_byte_slice(&output.stdout) @@ -4362,12 +4411,8 @@ fn shell_impl( } fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { - if cx.editor.config.shell.is_empty() { - cx.editor.set_error("No shell set".to_owned()); - return; - } let pipe = match behavior { - ShellBehavior::Replace | ShellBehavior::Ignore | ShellBehavior::Filter => true, + ShellBehavior::Replace | ShellBehavior::Ignore => true, ShellBehavior::Insert | ShellBehavior::Append => false, }; let prompt = Prompt::new( @@ -4379,6 +4424,9 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { if event != PromptEvent::Validate { return; } + if input.is_empty() { + return; + } let (view, doc) = current!(cx.editor); let selection = doc.selection(view.id); @@ -4396,22 +4444,18 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { } }; - if behavior != ShellBehavior::Filter { - let (from, to) = match behavior { - ShellBehavior::Replace => (range.from(), range.to()), - ShellBehavior::Insert => (range.from(), range.from()), - ShellBehavior::Append => (range.to(), range.to()), - _ => (range.from(), range.from()), - }; - changes.push((from, to, Some(output))); - } else { - // if the process exits successfully, keep the selection, otherwise delete it. - changes.push(( - range.from(), - if success { range.from() } else { range.to() }, - None, - )); + if !success { + cx.editor.set_error("Command failed".to_string()); + return; } + + let (from, to) = match behavior { + ShellBehavior::Replace => (range.from(), range.to()), + ShellBehavior::Insert => (range.from(), range.from()), + ShellBehavior::Append => (range.to(), range.to()), + _ => (range.from(), range.from()), + }; + changes.push((from, to, Some(output))); } if behavior != ShellBehavior::Ignore { -- cgit v1.2.3-70-g09d2 From 825bceeab68276cdf120bda5d172b854867d8585 Mon Sep 17 00:00:00 2001 From: oberblastmeister Date: Wed, 1 Sep 2021 11:55:16 -0400 Subject: add_newline unimpaired mapping (#653) * added some keymaps * remove * remove wrong mappings * remove * wrong import * use enum * correct line ending * added to book * column--- book/src/keymap.md | 23 ++++++++++++----------- helix-term/src/commands.rs | 36 ++++++++++++++++++++++++++++++++++++ helix-term/src/keymap.rs | 2 ++ 3 files changed, 50 insertions(+), 11 deletions(-) (limited to 'helix-term') diff --git a/book/src/keymap.md b/book/src/keymap.md index 2c6a9576..861e46ac 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -110,17 +110,18 @@ in reverse, or searching via smartcase. | `N` | Add next search match to selection | `extend_search_next` | | `*` | Use current selection as the search pattern | `search_selection` | -### Diagnostics - -> NOTE: `[` and `]` will likely contain more pair mappings in the style of -> [vim-unimpaired](https://github.com/tpope/vim-unimpaired) - -| Key | Description | Command | -| ----- | ----------- | ------- | -| `[d` | Go to previous diagnostic | `goto_prev_diag` | -| `]d` | Go to next diagnostic | `goto_next_diag` | -| `[D` | Go to first diagnostic in document | `goto_first_diag` | -| `]D` | Go to last diagnostic in document | `goto_last_diag` | +### Unimpaired + +Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaired) + +| Key | Description | Command | +| ----- | ----------- | ------- | +| `[d` | Go to previous diagnostic | `goto_prev_diag` | +| `]d` | Go to next diagnostic | `goto_next_diag` | +| `[D` | Go to first diagnostic in document | `goto_first_diag` | +| `]D` | Go to last diagnostic in document | `goto_last_diag` | +| `[space` | Add newline above | `add_newline_above` | +| `]space` | Add newline below | `add_newline_below` | ### Shell diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 5574afbf..3bd63ab4 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -227,6 +227,8 @@ impl Command { select_mode, "Enter selection extend mode", exit_select_mode, "Exit selection mode", goto_definition, "Goto definition", + add_newline_above, "Add newline above", + add_newline_below, "Add newline below", goto_type_definition, "Goto type definition", goto_implementation, "Goto implementation", goto_file_start, "Goto file start/line", @@ -4473,3 +4475,37 @@ fn suspend(_cx: &mut Context) { #[cfg(not(windows))] signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap(); } + +fn add_newline_above(cx: &mut Context) { + add_newline_impl(cx, Open::Above); +} + +fn add_newline_below(cx: &mut Context) { + add_newline_impl(cx, Open::Below) +} + +fn add_newline_impl(cx: &mut Context, open: Open) { + let count = cx.count(); + let (view, doc) = current!(cx.editor); + let selection = doc.selection(view.id); + let text = doc.text(); + let slice = text.slice(..); + + let changes = selection.into_iter().map(|range| { + let (start, end) = range.line_range(slice); + let line = match open { + Open::Above => start, + Open::Below => end + 1, + }; + let pos = text.line_to_char(line); + ( + pos, + pos, + Some(doc.line_ending.as_str().repeat(count).into()), + ) + }); + + let transaction = Transaction::change(text, changes); + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); +} diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index f3e160b1..71ac01a9 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -412,10 +412,12 @@ impl Default for Keymaps { "[" => { "Left bracket" "d" => goto_prev_diag, "D" => goto_first_diag, + "space" => add_newline_above, }, "]" => { "Right bracket" "d" => goto_next_diag, "D" => goto_last_diag, + "space" => add_newline_below, }, "/" => search, -- cgit v1.2.3-70-g09d2 From 7e1123680f474bff5113db189f63ca7f948781a5 Mon Sep 17 00:00:00 2001 From: Wojciech Kępka Date: Thu, 2 Sep 2021 04:03:42 +0200 Subject: Expand `~` in `change-current-directory` command (#692) --- helix-term/src/commands.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'helix-term') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 3bd63ab4..116f39bd 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1838,7 +1838,11 @@ mod cmd { args: &[&str], _event: PromptEvent, ) -> anyhow::Result<()> { - let dir = args.first().context("target directory not provided")?; + let dir = helix_core::path::expand_tilde( + args.first() + .context("target directory not provided")? + .as_ref(), + ); if let Err(e) = std::env::set_current_dir(dir) { bail!("Couldn't change the current working directory: {}", e); -- cgit v1.2.3-70-g09d2