From 03ad9e0bfa37222762683aeb84be2d324d166930 Mon Sep 17 00:00:00 2001 From: Omnikar Date: Sun, 29 Aug 2021 20:15:49 -0400 Subject: Fix code indentation (#671) --- helix-term/src/ui/editor.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'helix-term/src') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 72b8adc1..de0d065e 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -148,21 +148,21 @@ impl EditorView { syntax .highlight_iter(text.slice(..), Some(range), None, |language| { loader - .language_config_for_scope(&format!("source.{}", language)) - .and_then(|language_config| { - let config = language_config.highlight_config(scopes)?; - let config_ref = config.as_ref(); - // SAFETY: the referenced `HighlightConfiguration` behind - // the `Arc` is guaranteed to remain valid throughout the - // duration of the highlight. - let config_ref = unsafe { - std::mem::transmute::< - _, - &'static syntax::HighlightConfiguration, - >(config_ref) - }; - Some(config_ref) - }) + .language_config_for_scope(&format!("source.{}", language)) + .and_then(|language_config| { + let config = language_config.highlight_config(scopes)?; + let config_ref = config.as_ref(); + // SAFETY: the referenced `HighlightConfiguration` behind + // the `Arc` is guaranteed to remain valid throughout the + // duration of the highlight. + let config_ref = unsafe { + std::mem::transmute::< + _, + &'static syntax::HighlightConfiguration, + >(config_ref) + }; + Some(config_ref) + }) }) .map(|event| event.unwrap()) .collect() // TODO: we collect here to avoid holding the lock, fix later -- cgit v1.2.3-70-g09d2 From b590504143412f185f02ee21a54ad4e6d966b257 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Mon, 30 Aug 2021 20:28:40 +0200 Subject: fix: use head instead of anchor for relative line --- helix-term/src/ui/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'helix-term/src') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index de0d065e..2c34ae96 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -427,7 +427,7 @@ impl EditorView { let current_line = doc .text() - .char_to_line(doc.selection(view.id).primary().anchor); + .char_to_line(doc.selection(view.id).primary().head); // it's used inside an iterator so the collect isn't needless: // https://github.com/rust-lang/rust-clippy/issues/6164 -- cgit v1.2.3-70-g09d2 From 9c5752cbac2450388ff4c94dfa82b389b3746a9c Mon Sep 17 00:00:00 2001 From: gbaranski Date: Mon, 30 Aug 2021 20:49:17 +0200 Subject: fix: use .cursor() instead of .head --- helix-term/src/ui/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'helix-term/src') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 2c34ae96..4b9c56e7 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -427,7 +427,7 @@ impl EditorView { let current_line = doc .text() - .char_to_line(doc.selection(view.id).primary().head); + .char_to_line(doc.selection(view.id).primary().cursor(text)); // it's used inside an iterator so the collect isn't needless: // https://github.com/rust-lang/rust-clippy/issues/6164 -- cgit v1.2.3-70-g09d2 From e772808a5b0417e4d074eb9683d79376f83dae2d Mon Sep 17 00:00:00 2001 From: Omnikar Date: Tue, 31 Aug 2021 05:13:16 -0400 Subject: Shell commands (#547) * Implement shell interaction commands * Use slice instead of iterator for shell invocation * Default to `sh` instead of `$SHELL` for shell commands * Enforce trailing comma in `commands` macro * Use `|` register for shell commands * Move shell config to `editor` and use in command * Update shell command prompts * Remove clone of shell config * Change shell function names to match prompts * Log stderr contents upon external command error * Remove `unwrap` calls on potential common errors `shell` will no longer panic if: * The user-configured shell cannot be found * The shell command does not output UTF-8 * Remove redundant `pipe` parameter * Rename `ShellBehavior::None` to `Ignore` * Display error when shell command is used and `shell = []` * Document shell commands in `keymap.md`--- book/src/keymap.md | 10 ++++ helix-term/src/commands.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++- helix-term/src/keymap.rs | 5 ++ helix-view/src/editor.rs | 7 +++ 4 files changed, 156 insertions(+), 2 deletions(-) (limited to 'helix-term/src') diff --git a/book/src/keymap.md b/book/src/keymap.md index 61378863..d85fb936 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -122,6 +122,16 @@ in reverse, or searching via smartcase. | `[D` | Go to first diagnostic in document | `goto_first_diag` | | `]D` | Go to last diagnostic in document | `goto_last_diag` | +### 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` | + ## Select / extend mode I'm still pondering whether to keep this mode or not. It changes movement diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index d21bbe42..6437bf52 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -131,7 +131,7 @@ pub struct Command { } macro_rules! commands { - ( $($name:ident, $doc:literal),* ) => { + ( $($name:ident, $doc:literal,)* ) => { $( #[allow(non_upper_case_globals)] pub const $name: Self = Self { @@ -302,7 +302,12 @@ impl Command { surround_delete, "Surround delete", select_textobject_around, "Select around object", select_textobject_inner, "Select inside object", - suspend, "Suspend" + shell_pipe, "Pipe selections through shell command", + shell_pipe_to, "Pipe selections into shell command, ignoring command output", + shell_insert_output, "Insert output of shell command before each selection", + shell_append_output, "Append output of shell command after each selection", + shell_keep_pipe, "Filter selections with shell predicate", + suspend, "Suspend", ); } @@ -4292,6 +4297,133 @@ fn surround_delete(cx: &mut Context) { }) } +#[derive(Eq, PartialEq)] +enum ShellBehavior { + Replace, + Ignore, + Insert, + Append, + Filter, +} + +fn shell_pipe(cx: &mut Context) { + shell(cx, "pipe:", ShellBehavior::Replace); +} + +fn shell_pipe_to(cx: &mut Context) { + shell(cx, "pipe-to:", ShellBehavior::Ignore); +} + +fn shell_insert_output(cx: &mut Context) { + shell(cx, "insert-output:", ShellBehavior::Insert); +} + +fn shell_append_output(cx: &mut Context) { + shell(cx, "append-output:", ShellBehavior::Append); +} + +fn shell_keep_pipe(cx: &mut Context) { + shell(cx, "keep-pipe:", ShellBehavior::Filter); +} + +fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) { + use std::io::Write; + use std::process::{Command, Stdio}; + 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::Insert | ShellBehavior::Append => false, + }; + let prompt = Prompt::new( + prompt.to_owned(), + Some('|'), + |_input: &str| Vec::new(), + move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { + let shell = &cx.editor.config.shell; + if event == PromptEvent::Validate { + let (view, doc) = current!(cx.editor); + let selection = doc.selection(view.id); + let mut error: Option<&str> = None; + let transaction = + Transaction::change_by_selection(doc.text(), selection, |range| { + let mut process; + match Command::new(&shell[0]) + .args(&shell[1..]) + .arg(input) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(p) => process = p, + Err(e) => { + log::error!("Failed to start shell: {}", e); + error = Some("Failed to start shell"); + return (0, 0, None); + } + } + 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() { + let stderr = output.stderr; + if !stderr.is_empty() { + log::error!( + "Shell error: {}", + String::from_utf8_lossy(&stderr) + ); + } + error = Some("Command failed"); + return (0, 0, None); + } + let stdout = output.stdout; + let tendril; + match Tendril::try_from_byte_slice(&stdout) { + Ok(t) => tendril = t, + Err(_) => { + error = Some("Process did not output valid UTF-8"); + return (0, 0, None); + } + } + 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()), + }; + (from, to, Some(tendril)) + } else { + // if the process exits successfully, keep the selection, otherwise delete it. + let keep = output.status.success(); + ( + range.from(), + if keep { range.from() } else { range.to() }, + None, + ) + } + }); + + if let Some(error) = error { + cx.editor.set_error(error.to_owned()); + } else if behavior != ShellBehavior::Ignore { + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); + } + } + }, + ); + + cx.push_layer(Box::new(prompt)); +} + fn suspend(_cx: &mut Context) { #[cfg(not(windows))] signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap(); diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 492dc292..f3e160b1 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -508,6 +508,11 @@ impl Default for Keymaps { }, "\"" => select_register, + "|" => shell_pipe, + "A-|" => shell_pipe_to, + "!" => shell_insert_output, + "A-!" => shell_append_output, + "$" => shell_keep_pipe, "C-z" => suspend, }); let mut select = normal.clone(); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 18cb9106..e5ff93ad 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -33,6 +33,8 @@ pub struct Config { pub scroll_lines: isize, /// Mouse support. Defaults to true. pub mouse: bool, + /// Shell to use for shell commands. Defaults to ["cmd", "/C"] on Windows and ["sh", "-c"] otherwise. + pub shell: Vec, /// Line number mode. pub line_number: LineNumber, /// Middle click paste support. Defaults to true @@ -55,6 +57,11 @@ impl Default for Config { scrolloff: 5, scroll_lines: 3, mouse: true, + shell: if cfg!(windows) { + vec!["cmd".to_owned(), "/C".to_owned()] + } else { + vec!["sh".to_owned(), "-c".to_owned()] + }, line_number: LineNumber::Absolute, middle_click_paste: true, } -- cgit v1.2.3-70-g09d2 From 4a76ea8f8834a880d63246c8d43587693ac7cc81 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Tue, 31 Aug 2021 18:17:22 +0900 Subject: shell: Move changes outside so we can properly handle errors --- helix-term/src/commands.rs | 123 ++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 62 deletions(-) (limited to 'helix-term/src') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 6437bf52..42495b7e 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4346,74 +4346,73 @@ fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) { if event == PromptEvent::Validate { let (view, doc) = current!(cx.editor); let selection = doc.selection(view.id); - let mut error: Option<&str> = None; - let transaction = - Transaction::change_by_selection(doc.text(), selection, |range| { - let mut process; - match Command::new(&shell[0]) - .args(&shell[1..]) - .arg(input) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - { - Ok(p) => process = p, - Err(e) => { - log::error!("Failed to start shell: {}", e); - error = Some("Failed to start shell"); - return (0, 0, None); - } - } - if pipe { - let stdin = process.stdin.as_mut().unwrap(); - let fragment = range.fragment(doc.text().slice(..)); - stdin.write_all(fragment.as_bytes()).unwrap(); + + let mut changes = Vec::with_capacity(selection.len()); + + 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(p) => process = p, + 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() { - let stderr = output.stderr; - if !stderr.is_empty() { - log::error!( - "Shell error: {}", - String::from_utf8_lossy(&stderr) - ); - } - error = Some("Command failed"); - return (0, 0, None); + let output = process.wait_with_output().unwrap(); + if behavior != ShellBehavior::Filter { + if !output.status.success() { + let stderr = output.stderr; + if !stderr.is_empty() { + log::error!("Shell error: {}", String::from_utf8_lossy(&stderr)); } - let stdout = output.stdout; - let tendril; - match Tendril::try_from_byte_slice(&stdout) { - Ok(t) => tendril = t, - Err(_) => { - error = Some("Process did not output valid UTF-8"); - return (0, 0, None); - } + cx.editor.set_error("Command failed".to_owned()); + return; + } + let stdout = output.stdout; + let tendril; + match Tendril::try_from_byte_slice(&stdout) { + Ok(t) => tendril = t, + Err(_) => { + cx.editor + .set_error("Process did not output valid UTF-8".to_owned()); + 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()), - }; - (from, to, Some(tendril)) - } else { - // if the process exits successfully, keep the selection, otherwise delete it. - let keep = output.status.success(); - ( - range.from(), - if keep { range.from() } else { range.to() }, - None, - ) } - }); + 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))); + } 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() }, + None, + )); + } + } + + let transaction = Transaction::change(doc.text(), changes.into_iter()); - if let Some(error) = error { - cx.editor.set_error(error.to_owned()); - } else if behavior != ShellBehavior::Ignore { + if behavior != ShellBehavior::Ignore { doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); } -- cgit v1.2.3-70-g09d2 From 9b96bb5ac802189f567b101f7c8da4b370a2fdd1 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Tue, 31 Aug 2021 18:24:24 +0900 Subject: Refactor a bit further --- helix-term/src/commands.rs | 127 ++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 65 deletions(-) (limited to 'helix-term/src') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 42495b7e..e75d46dc 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4342,80 +4342,77 @@ fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) { Some('|'), |_input: &str| Vec::new(), move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { + if event != PromptEvent::Validate { + return; + } let shell = &cx.editor.config.shell; - if event == PromptEvent::Validate { - let (view, doc) = current!(cx.editor); - let selection = doc.selection(view.id); + let (view, doc) = current!(cx.editor); + let selection = doc.selection(view.id); - let mut changes = Vec::with_capacity(selection.len()); - - 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(p) => process = p, - 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 mut changes = Vec::with_capacity(selection.len()); + + 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(); - let output = process.wait_with_output().unwrap(); - if behavior != ShellBehavior::Filter { - if !output.status.success() { - let stderr = output.stderr; - if !stderr.is_empty() { - log::error!("Shell error: {}", String::from_utf8_lossy(&stderr)); - } - cx.editor.set_error("Command failed".to_owned()); - return; + if behavior != ShellBehavior::Filter { + if !output.status.success() { + if !output.stderr.is_empty() { + log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr)); } - let stdout = output.stdout; - let tendril; - match Tendril::try_from_byte_slice(&stdout) { - Ok(t) => tendril = t, - Err(_) => { - cx.editor - .set_error("Process did not output valid UTF-8".to_owned()); - 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(tendril))); - } 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() }, - None, - )); + 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()); + 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(tendril))); + } 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() }, + None, + )); } + } - let transaction = Transaction::change(doc.text(), changes.into_iter()); + let transaction = Transaction::change(doc.text(), changes.into_iter()); - if behavior != ShellBehavior::Ignore { - doc.apply(&transaction, view.id); - doc.append_changes_to_history(view.id); - } + if behavior != ShellBehavior::Ignore { + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); } }, ); -- cgit v1.2.3-70-g09d2 From a3bd80a6fa4f07b0fa581eae416376763ba94345 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Tue, 31 Aug 2021 18:29:24 +0900 Subject: ui: prompt: Avoid allocating a prompt name if it's a static string --- helix-term/src/commands.rs | 39 +++++++++++++++++---------------------- helix-term/src/ui/mod.rs | 2 +- helix-term/src/ui/picker.rs | 2 +- helix-term/src/ui/prompt.rs | 4 ++-- 4 files changed, 21 insertions(+), 26 deletions(-) (limited to 'helix-term/src') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e75d46dc..f479a7a0 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1079,7 +1079,7 @@ fn select_all(cx: &mut Context) { } fn select_regex(cx: &mut Context) { - let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, _, regex| { + let prompt = ui::regex_prompt(cx, "select:".into(), move |view, doc, _, regex| { let text = doc.text().slice(..); if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), ®ex) { @@ -1091,7 +1091,7 @@ fn select_regex(cx: &mut Context) { } fn split_selection(cx: &mut Context) { - let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, _, regex| { + let prompt = ui::regex_prompt(cx, "split:".into(), move |view, doc, _, regex| { let text = doc.text().slice(..); let selection = selection::split_on_matches(text, doc.selection(view.id), ®ex); doc.set_selection(view.id, selection); @@ -1157,15 +1157,11 @@ fn search(cx: &mut Context) { // feed chunks into the regex yet let contents = doc.text().slice(..).to_string(); - let prompt = ui::regex_prompt( - cx, - "search:".to_string(), - move |view, doc, registers, regex| { - search_impl(doc, view, &contents, ®ex, false); - // TODO: only store on enter (accept), not update - registers.write('/', vec![regex.as_str().to_string()]); - }, - ); + let prompt = ui::regex_prompt(cx, "search:".into(), move |view, doc, registers, regex| { + search_impl(doc, view, &contents, ®ex, false); + // TODO: only store on enter (accept), not update + registers.write('/', vec![regex.as_str().to_string()]); + }); cx.push_layer(Box::new(prompt)); } @@ -2210,7 +2206,7 @@ mod cmd { fn command_mode(cx: &mut Context) { let mut prompt = Prompt::new( - ":".to_owned(), + ":".into(), Some(':'), |input: &str| { // we use .this over split_whitespace() because we care about empty segments @@ -3819,7 +3815,7 @@ fn join_selections(cx: &mut Context) { fn keep_selections(cx: &mut Context) { // keep selections matching regex - let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, _, regex| { + let prompt = ui::regex_prompt(cx, "keep:".into(), move |view, doc, _, regex| { let text = doc.text().slice(..); if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), ®ex) { @@ -4307,26 +4303,26 @@ enum ShellBehavior { } fn shell_pipe(cx: &mut Context) { - shell(cx, "pipe:", ShellBehavior::Replace); + shell(cx, "pipe:".into(), ShellBehavior::Replace); } fn shell_pipe_to(cx: &mut Context) { - shell(cx, "pipe-to:", ShellBehavior::Ignore); + shell(cx, "pipe-to:".into(), ShellBehavior::Ignore); } fn shell_insert_output(cx: &mut Context) { - shell(cx, "insert-output:", ShellBehavior::Insert); + shell(cx, "insert-output:".into(), ShellBehavior::Insert); } fn shell_append_output(cx: &mut Context) { - shell(cx, "append-output:", ShellBehavior::Append); + shell(cx, "append-output:".into(), ShellBehavior::Append); } fn shell_keep_pipe(cx: &mut Context) { - shell(cx, "keep-pipe:", ShellBehavior::Filter); + shell(cx, "keep-pipe:".into(), ShellBehavior::Filter); } -fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) { +fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { use std::io::Write; use std::process::{Command, Stdio}; if cx.editor.config.shell.is_empty() { @@ -4338,7 +4334,7 @@ fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) { ShellBehavior::Insert | ShellBehavior::Append => false, }; let prompt = Prompt::new( - prompt.to_owned(), + prompt, Some('|'), |_input: &str| Vec::new(), move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { @@ -4408,9 +4404,8 @@ fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) { } } - let transaction = Transaction::change(doc.text(), changes.into_iter()); - if behavior != ShellBehavior::Ignore { + let transaction = Transaction::change(doc.text(), changes.into_iter()); doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index f3f8670e..0a1e24b5 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -27,7 +27,7 @@ use std::path::PathBuf; pub fn regex_prompt( cx: &mut crate::commands::Context, - prompt: String, + prompt: std::borrow::Cow<'static, str>, fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static, ) -> Prompt { let (view, doc) = current!(cx.editor); diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index ef2c434c..06e424ea 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -202,7 +202,7 @@ impl Picker { callback_fn: impl Fn(&mut Editor, &T, Action) + 'static, ) -> Self { let prompt = Prompt::new( - "".to_string(), + "".into(), None, |_pattern: &str| Vec::new(), |_editor: &mut Context, _pattern: &str, _event: PromptEvent| { diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 7197adea..1d512ad2 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -15,7 +15,7 @@ use helix_view::{ pub type Completion = (RangeFrom, Cow<'static, str>); pub struct Prompt { - prompt: String, + prompt: Cow<'static, str>, pub line: String, cursor: usize, completion: Vec, @@ -55,7 +55,7 @@ pub enum Movement { impl Prompt { pub fn new( - prompt: String, + prompt: Cow<'static, str>, history_register: Option, mut completion_fn: impl FnMut(&str) -> Vec + 'static, callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static, -- cgit v1.2.3-70-g09d2