From 1c27ed9f1f99da612130286866c065e8f6d649aa Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 9 Oct 2022 12:34:28 +0200 Subject: [PATCH 1/3] feat: Adds the ability to paste from a file Opted for a "paste" like function and reused the existing paste functionality. This allows for Paste::Before/After to be used. --- helix-term/src/commands.rs | 23 +++++++++++++ helix-term/src/commands/typed.rs | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 2db5bfcf3019..c1333d32366f 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3485,6 +3485,29 @@ fn paste_clipboard_impl( } } +fn paste_file_impl( + editor: &mut Editor, + action: Paste, + paths: &[PathBuf], + count: usize, +) -> anyhow::Result<()> { + use std::io::Read; + + // reverse the paths[] as its more intuative to expect the order to be respected + // paths[0], paths[1], paths[..N] when calling the function + for path in paths.iter().rev() { + let mut file = std::fs::File::open(&path).context(format!("unable to open {:?}", path))?; + let mut contents = String::new(); + file.read_to_string(&mut contents) + .context(format!("unable to read {:?}", path))?; + + let (view, doc) = current!(editor); + + paste_impl(&[contents], doc, view, action, count); + } + Ok(()) +} + fn paste_clipboard_after(cx: &mut Context) { let _ = paste_clipboard_impl( cx.editor, diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 96ff75c54a0f..a0a03e72f36b 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -846,6 +846,48 @@ fn paste_clipboard_before( paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1) } +fn paste_file_after( + cx: &mut compositor::Context, + args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + ensure!(!args.is_empty(), "wrong argument count"); + let paths: Vec = args + .iter() + .map(|arg| { + let (path, _) = args::parse_file(arg); + path + }) + .collect(); + + paste_file_impl(cx.editor, Paste::After, &paths, 1) +} + +fn paste_file_before( + cx: &mut compositor::Context, + args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + ensure!(!args.is_empty(), "wrong argument count"); + let paths: Vec = args + .iter() + .map(|arg| { + let (path, _) = args::parse_file(arg); + path + }) + .collect(); + + paste_file_impl(cx.editor, Paste::Before, &paths, 1) +} + fn paste_primary_clipboard_after( cx: &mut compositor::Context, _args: &[Cow], @@ -1862,6 +1904,20 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: replace_selections_with_clipboard, completer: None, }, + TypableCommand { + name: "file-paste-after", + aliases: &[], + doc: "Read a file from disk and paste afer selections.", + fun: paste_file_after, + completer: Some(completers::filename), + }, + TypableCommand { + name: "file-paste-before", + aliases: &[], + doc: "Read a file from disk and paste before selections.", + fun: paste_file_before, + completer: Some(completers::filename), + }, TypableCommand { name: "primary-clipboard-paste-after", aliases: &[], From 0249d650b2b6e12128f1215faf54488842fbfd7b Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 9 Oct 2022 12:47:41 +0200 Subject: [PATCH 2/3] doc: updated documentation --- book/src/generated/typable-cmd.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 4cbff3061a44..17aa56211ff2 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -36,6 +36,8 @@ | `:clipboard-paste-after` | Paste system clipboard after selections. | | `:clipboard-paste-before` | Paste system clipboard before selections. | | `:clipboard-paste-replace` | Replace selections with content of system clipboard. | +| `:file-paste-after` | Read a file from disk and paste afer selections. | +| `:file-paste-before` | Read a file from disk and paste before selections. | | `:primary-clipboard-paste-after` | Paste primary clipboard after selections. | | `:primary-clipboard-paste-before` | Paste primary clipboard before selections. | | `:primary-clipboard-paste-replace` | Replace selections with content of system primary clipboard. | From 079c3b1afa9073ce672b56a4c895c9902a05a1ef Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 10 Oct 2022 08:33:47 +0200 Subject: [PATCH 3/3] feat: integrated file-paste-after into file picker --- helix-term/src/commands.rs | 22 ++++++++++++++++++---- helix-term/src/ui/mod.rs | 29 ++++++++++++++++++++++------- helix-term/src/ui/picker.rs | 6 ++++++ helix-view/src/editor.rs | 5 +++++ 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c1333d32366f..15efc6454918 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3485,10 +3485,23 @@ fn paste_clipboard_impl( } } -fn paste_file_impl( +pub(crate) fn paste_file_after>( + cx: &mut compositor::Context, + paths: &[P], +) -> anyhow::Result<()> { + paste_file_impl(cx.editor, Paste::After, paths, 1) +} +pub(crate) fn paste_file_before>( + cx: &mut compositor::Context, + paths: &[P], +) -> anyhow::Result<()> { + paste_file_impl(cx.editor, Paste::Before, paths, 1) +} + +fn paste_file_impl>( editor: &mut Editor, action: Paste, - paths: &[PathBuf], + paths: &[P], count: usize, ) -> anyhow::Result<()> { use std::io::Read; @@ -3496,10 +3509,11 @@ fn paste_file_impl( // reverse the paths[] as its more intuative to expect the order to be respected // paths[0], paths[1], paths[..N] when calling the function for path in paths.iter().rev() { - let mut file = std::fs::File::open(&path).context(format!("unable to open {:?}", path))?; + let mut file = + std::fs::File::open(&path).context(format!("unable to open {:?}", path.as_ref()))?; let mut contents = String::new(); file.read_to_string(&mut contents) - .context(format!("unable to read {:?}", path))?; + .context(format!("unable to read {:?}", path.as_ref()))?; let (view, doc) = current!(editor); diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 8ab15bff0f2b..ca3ea95935c6 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -220,13 +220,28 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi files, root, move |cx, path: &PathBuf, action| { - if let Err(e) = cx.editor.open(path, action) { - let err = if let Some(err) = e.source() { - format!("{}", err) - } else { - format!("unable to open \"{}\"", path.display()) - }; - cx.editor.set_error(err); + if let Err(e) = match action { + helix_view::editor::Action::PasteBefore => { + crate::commands::paste_file_before(cx, &[path.clone()]) + } + helix_view::editor::Action::PasteAfter => { + crate::commands::paste_file_after(cx, &[path.clone()]) + } + _ => { + if let Err(e) = cx.editor.open(path, action) { + let err = if let Some(err) = e.source() { + format!("{}", err) + } else { + format!("unable to open \"{}\"", path.display()) + }; + + Err(anyhow::anyhow!(err)) + } else { + anyhow::Ok(()) + } + } + } { + cx.editor.set_error(e.to_string()); } }, |_editor, path| Some((path.clone(), None)), diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index a56455d7d569..04fdd1115557 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -551,6 +551,12 @@ impl Component for Picker { ctrl!('t') => { self.toggle_preview(); } + ctrl!('o') => { + if let Some(option) = self.selection() { + (self.callback_fn)(cx, option, Action::PasteAfter); + } + return close_fn; + } _ => { self.prompt_handle_event(event, cx); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index d09d0ac3eeba..892f47b97147 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -708,6 +708,8 @@ pub enum Action { Replace, HorizontalSplit, VerticalSplit, + PasteBefore, + PasteAfter, } /// Error thrown on failed document closed @@ -1018,6 +1020,9 @@ impl Editor { let doc = doc_mut!(self, &id); doc.ensure_view_init(view_id); } + Action::PasteBefore | Action::PasteAfter => { + // do nothing? + } } self._refresh();