From f50261c944d8cfbb5775dc9350f64a6eefde0295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20CORTIER?= Date: Sat, 19 Jun 2021 02:27:43 -0400 Subject: [PATCH] Add mappable commands for system clipboard System clipboard integration exists now in two favors: typable and mappable. Default mappings are: - SPC p: paste clipboard after - SPC P: paste clipboard before - SPC y: join and yank selection to clipboard - SPC Y: yank main selection to clipboard - SPC R: replace selections by clipboard contents --- helix-term/src/commands.rs | 170 ++++++++++++++++++++++++------------- 1 file changed, 112 insertions(+), 58 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 5cfee75d0..8c2909799 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -222,9 +222,14 @@ impl Command { undo, redo, yank, + yank_joined_to_clipboard, + yank_main_selection_to_clipboard, replace_with_yanked, + replace_selections_with_clipboard, paste_after, paste_before, + paste_clipboard_after, + paste_clipboard_before, indent, unindent, format_selections, @@ -1280,74 +1285,24 @@ mod cmd { editor.set_theme_from_name(theme); } - fn yank_main_selection_to_clipboard(editor: &mut Editor, args: &[&str], event: PromptEvent) { - let (view, doc) = current!(editor); - - // TODO: currently the main selection is the first one. This needs to be revisited later. - let range = doc - .selection(view.id) - .ranges() - .first() - .expect("at least one selection"); - - let value = range.fragment(doc.text().slice(..)); - - if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) { - log::error!("Couldn't set system clipboard content: {:?}", e); - } - - editor.set_status("yanked main selection to system clipboard".to_owned()); + fn yank_main_selection_to_clipboard(editor: &mut Editor, _: &[&str], _: PromptEvent) { + yank_main_selection_to_clipboard_impl(editor); } - fn yank_joined_to_clipboard(editor: &mut Editor, args: &[&str], event: PromptEvent) { - let (view, doc) = current!(editor); - - let values: Vec = doc - .selection(view.id) - .fragments(doc.text().slice(..)) - .map(Cow::into_owned) - .collect(); - - let msg = format!( - "joined and yanked {} selection(s) to system clipboard", - values.len(), - ); - - let joined = values.join("\n"); - - if let Err(e) = editor.clipboard_provider.set_contents(joined) { - log::error!("Couldn't set system clipboard content: {:?}", e); - } - - editor.set_status(msg); - } - - fn paste_clipboard_impl(editor: &mut Editor, action: Paste) { - let (view, doc) = current!(editor); - - match editor - .clipboard_provider - .get_contents() - .map(|contents| paste_impl(&[contents], doc, view, action)) - { - Ok(Some(transaction)) => { - doc.apply(&transaction, view.id); - doc.append_changes_to_history(view.id); - } - Ok(None) => {} - Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), - } + fn yank_joined_to_clipboard(editor: &mut Editor, args: &[&str], _: PromptEvent) { + let separator = args.first().copied().unwrap_or("\n"); + yank_joined_to_clipboard_impl(editor, separator); } fn paste_clipboard_after(editor: &mut Editor, _: &[&str], _: PromptEvent) { paste_clipboard_impl(editor, Paste::After); } - fn paste_clipboard_before(editor: &mut Editor, args: &[&str], event: PromptEvent) { + fn paste_clipboard_before(editor: &mut Editor, _: &[&str], _: PromptEvent) { paste_clipboard_impl(editor, Paste::After); } - fn replace_selections_with_clipboard(editor: &mut Editor, args: &[&str], event: PromptEvent) { + fn replace_selections_with_clipboard(editor: &mut Editor, _: &[&str], _: PromptEvent) { let (view, doc) = current!(editor); match editor.clipboard_provider.get_contents() { @@ -1500,7 +1455,7 @@ mod cmd { TypableCommand { name: "clipboard-yank-join", alias: None, - doc: "Yank joined selections into system clipboard.", + doc: "Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline.", // FIXME: current UI can't display long doc. fun: yank_joined_to_clipboard, completer: None, }, @@ -2525,6 +2480,52 @@ fn yank(cx: &mut Context) { cx.editor.set_status(msg) } +fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) { + let (view, doc) = current!(editor); + + let values: Vec = doc + .selection(view.id) + .fragments(doc.text().slice(..)) + .map(Cow::into_owned) + .collect(); + + let msg = format!( + "joined and yanked {} selection(s) to system clipboard", + values.len(), + ); + + let joined = values.join(separator); + + if let Err(e) = editor.clipboard_provider.set_contents(joined) { + log::error!("Couldn't set system clipboard content: {:?}", e); + } + + editor.set_status(msg); +} + +fn yank_joined_to_clipboard(cx: &mut Context) { + yank_joined_to_clipboard_impl(&mut cx.editor, "\n"); +} + +fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) { + let (view, doc) = current!(editor); + + let value = doc + .selection(view.id) + .primary() + .fragment(doc.text().slice(..)); + + if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) { + log::error!("Couldn't set system clipboard content: {:?}", e); + } + + editor.set_status("yanked main selection to system clipboard".to_owned()); +} + +fn yank_main_selection_to_clipboard(cx: &mut Context) { + yank_main_selection_to_clipboard_impl(&mut cx.editor); +} + #[derive(Copy, Clone)] enum Paste { Before, @@ -2568,6 +2569,31 @@ fn paste_impl( Some(transaction) } +fn paste_clipboard_impl(editor: &mut Editor, action: Paste) { + let (view, doc) = current!(editor); + + match editor + .clipboard_provider + .get_contents() + .map(|contents| paste_impl(&[contents], doc, view, action)) + { + Ok(Some(transaction)) => { + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); + } + Ok(None) => {} + Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), + } +} + +fn paste_clipboard_after(cx: &mut Context) { + paste_clipboard_impl(&mut cx.editor, Paste::After); +} + +fn paste_clipboard_before(cx: &mut Context) { + paste_clipboard_impl(&mut cx.editor, Paste::Before); +} + fn replace_with_yanked(cx: &mut Context) { let reg_name = cx.selected_register.name(); let (view, doc) = current!(cx.editor); @@ -2588,6 +2614,29 @@ fn replace_with_yanked(cx: &mut Context) { } } +fn replace_selections_with_clipboard_impl(editor: &mut Editor) { + let (view, doc) = current!(editor); + + match editor.clipboard_provider.get_contents() { + Ok(contents) => { + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + let max_to = doc.text().len_chars().saturating_sub(1); + let to = std::cmp::min(max_to, range.to() + 1); + (range.from(), to, Some(contents.as_str().into())) + }); + + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); + } + Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), + } +} + +fn replace_selections_with_clipboard(cx: &mut Context) { + replace_selections_with_clipboard_impl(&mut cx.editor); +} + // alt-p => paste every yanked selection after selected text // alt-P => paste every yanked selection before selected text // R => replace selected text with yanked text @@ -3108,6 +3157,11 @@ fn space_mode(cx: &mut Context) { 'b' => buffer_picker(cx), 's' => symbol_picker(cx), 'w' => window_mode(cx), + 'y' => yank_joined_to_clipboard(cx), + 'Y' => yank_main_selection_to_clipboard(cx), + 'p' => paste_clipboard_after(cx), + 'P' => paste_clipboard_before(cx), + 'R' => replace_selections_with_clipboard(cx), // ' ' => toggle_alternate_buffer(cx), // TODO: temporary since space mode took its old key ' ' => keep_primary_selection(cx),