Support primary clipboard (#548)

* clipboard-none: add in-memory fallback buffer

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* view: add Wayland primary clipboard

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* Format

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: copy to primary selection after mouse move stops

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: don't update primary selection if it is a single character

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: discard result of setting primary selection

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: add commands for interaction with primary clipboard

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* editor: implement primary selection copy/paste using commands

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* clipboard: support xsel for primary selection

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* clipboard: support xclip for primary selection

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: multiple cursor support for middle click paste

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* rename primary selection to primary clipboard in scope of PR

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: make middle click paste optional

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* Format

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* Update helix-term/src/ui/editor.rs

* fix formatting

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* config: correct defaults if terminal prop is not set

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* refactor: merge clipboard and primary selection implementations

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* Tidy up code

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* view: remove names for different clipboard/selection providers

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* Update helix-view/src/clipboard.rs

Co-authored-by: Gokul Soumya <gokulps15@gmail.com>

* helix-view: tidy macros

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: refactor paste-replace commands

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* helix-term: use new config for middle-click-paste

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* clipboard: remove memory fallback for command and windows providers

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* clipboard-win: fix build

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* clipboard: return empty string when primary clipboard is missing

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

* clipboard: fix errors in Windows build

Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com>

Co-authored-by: Gokul Soumya <gokulps15@gmail.com>
pull/579/head
Dmitry Sharshakov 3 years ago committed by GitHub
parent d03982ee43
commit 7d51805e94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,8 +12,8 @@ use helix_core::{
}; };
use helix_view::{ use helix_view::{
document::Mode, editor::Action, input::KeyEvent, keyboard::KeyCode, view::View, Document, clipboard::ClipboardType, document::Mode, editor::Action, input::KeyEvent, keyboard::KeyCode,
DocumentId, Editor, ViewId, view::View, Document, DocumentId, Editor, ViewId,
}; };
use anyhow::{anyhow, bail, Context as _}; use anyhow::{anyhow, bail, Context as _};
@ -258,12 +258,17 @@ impl Command {
yank, "Yank selection", yank, "Yank selection",
yank_joined_to_clipboard, "Join and yank selections to clipboard", yank_joined_to_clipboard, "Join and yank selections to clipboard",
yank_main_selection_to_clipboard, "Yank main selection to clipboard", yank_main_selection_to_clipboard, "Yank main selection to clipboard",
yank_joined_to_primary_clipboard, "Join and yank selections to primary clipboard",
yank_main_selection_to_primary_clipboard, "Yank main selection to primary clipboard",
replace_with_yanked, "Replace with yanked text", replace_with_yanked, "Replace with yanked text",
replace_selections_with_clipboard, "Replace selections by clipboard content", replace_selections_with_clipboard, "Replace selections by clipboard content",
replace_selections_with_primary_clipboard, "Replace selections by primary clipboard content",
paste_after, "Paste after selection", paste_after, "Paste after selection",
paste_before, "Paste before selection", paste_before, "Paste before selection",
paste_clipboard_after, "Paste clipboard after selections", paste_clipboard_after, "Paste clipboard after selections",
paste_clipboard_before, "Paste clipboard before selections", paste_clipboard_before, "Paste clipboard before selections",
paste_primary_clipboard_after, "Paste primary clipboard after selections",
paste_primary_clipboard_before, "Paste primary clipboard before selections",
indent, "Indent selection", indent, "Indent selection",
unindent, "Unindent selection", unindent, "Unindent selection",
format_selections, "Format selection", format_selections, "Format selection",
@ -1705,7 +1710,7 @@ mod cmd {
_args: &[&str], _args: &[&str],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
yank_main_selection_to_clipboard_impl(&mut cx.editor) yank_main_selection_to_clipboard_impl(&mut cx.editor, ClipboardType::Clipboard)
} }
fn yank_joined_to_clipboard( fn yank_joined_to_clipboard(
@ -1718,7 +1723,28 @@ mod cmd {
.first() .first()
.copied() .copied()
.unwrap_or_else(|| doc.line_ending.as_str()); .unwrap_or_else(|| doc.line_ending.as_str());
yank_joined_to_clipboard_impl(&mut cx.editor, separator) yank_joined_to_clipboard_impl(&mut cx.editor, separator, ClipboardType::Clipboard)
}
fn yank_main_selection_to_primary_clipboard(
cx: &mut compositor::Context,
_args: &[&str],
_event: PromptEvent,
) -> anyhow::Result<()> {
yank_main_selection_to_clipboard_impl(&mut cx.editor, ClipboardType::Selection)
}
fn yank_joined_to_primary_clipboard(
cx: &mut compositor::Context,
args: &[&str],
_event: PromptEvent,
) -> anyhow::Result<()> {
let (_, doc) = current!(cx.editor);
let separator = args
.first()
.copied()
.unwrap_or_else(|| doc.line_ending.as_str());
yank_joined_to_clipboard_impl(&mut cx.editor, separator, ClipboardType::Selection)
} }
fn paste_clipboard_after( fn paste_clipboard_after(
@ -1726,7 +1752,7 @@ mod cmd {
_args: &[&str], _args: &[&str],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
paste_clipboard_impl(&mut cx.editor, Paste::After) paste_clipboard_impl(&mut cx.editor, Paste::After, ClipboardType::Clipboard)
} }
fn paste_clipboard_before( fn paste_clipboard_before(
@ -1734,17 +1760,32 @@ mod cmd {
_args: &[&str], _args: &[&str],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
paste_clipboard_impl(&mut cx.editor, Paste::After) paste_clipboard_impl(&mut cx.editor, Paste::After, ClipboardType::Clipboard)
} }
fn replace_selections_with_clipboard( fn paste_primary_clipboard_after(
cx: &mut compositor::Context, cx: &mut compositor::Context,
_args: &[&str], _args: &[&str],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> {
paste_clipboard_impl(&mut cx.editor, Paste::After, ClipboardType::Selection)
}
fn paste_primary_clipboard_before(
cx: &mut compositor::Context,
_args: &[&str],
_event: PromptEvent,
) -> anyhow::Result<()> {
paste_clipboard_impl(&mut cx.editor, Paste::After, ClipboardType::Selection)
}
fn replace_selections_with_clipboard_impl(
cx: &mut compositor::Context,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
match cx.editor.clipboard_provider.get_contents() { match cx.editor.clipboard_provider.get_contents(clipboard_type) {
Ok(contents) => { Ok(contents) => {
let selection = doc.selection(view.id); let selection = doc.selection(view.id);
let transaction = let transaction =
@ -1760,13 +1801,29 @@ mod cmd {
} }
} }
fn replace_selections_with_clipboard(
cx: &mut compositor::Context,
_args: &[&str],
_event: PromptEvent,
) -> anyhow::Result<()> {
replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
}
fn replace_selections_with_primary_clipboard(
cx: &mut compositor::Context,
_args: &[&str],
_event: PromptEvent,
) -> anyhow::Result<()> {
replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
}
fn show_clipboard_provider( fn show_clipboard_provider(
cx: &mut compositor::Context, cx: &mut compositor::Context,
_args: &[&str], _args: &[&str],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
cx.editor cx.editor
.set_status(cx.editor.clipboard_provider.name().into()); .set_status(cx.editor.clipboard_provider.name().to_string());
Ok(()) Ok(())
} }
@ -1967,6 +2024,20 @@ mod cmd {
fun: yank_joined_to_clipboard, fun: yank_joined_to_clipboard,
completer: None, completer: None,
}, },
TypableCommand {
name: "primary-clipboard-yank",
alias: None,
doc: "Yank main selection into system primary clipboard.",
fun: yank_main_selection_to_primary_clipboard,
completer: None,
},
TypableCommand {
name: "primary-clipboard-yank-join",
alias: None,
doc: "Yank joined selections into system primary 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_primary_clipboard,
completer: None,
},
TypableCommand { TypableCommand {
name: "clipboard-paste-after", name: "clipboard-paste-after",
alias: None, alias: None,
@ -1988,6 +2059,27 @@ mod cmd {
fun: replace_selections_with_clipboard, fun: replace_selections_with_clipboard,
completer: None, completer: None,
}, },
TypableCommand {
name: "primary-clipboard-paste-after",
alias: None,
doc: "Paste primary clipboard after selections.",
fun: paste_primary_clipboard_after,
completer: None,
},
TypableCommand {
name: "primary-clipboard-paste-before",
alias: None,
doc: "Paste primary clipboard before selections.",
fun: paste_primary_clipboard_before,
completer: None,
},
TypableCommand {
name: "primary-clipboard-paste-replace",
alias: None,
doc: "Replace selections with content of system primary clipboard.",
fun: replace_selections_with_primary_clipboard,
completer: None,
},
TypableCommand { TypableCommand {
name: "show-clipboard-provider", name: "show-clipboard-provider",
alias: None, alias: None,
@ -3209,7 +3301,11 @@ fn yank(cx: &mut Context) {
exit_select_mode(cx); exit_select_mode(cx);
} }
fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) -> anyhow::Result<()> { fn yank_joined_to_clipboard_impl(
editor: &mut Editor,
separator: &str,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
let text = doc.text().slice(..); let text = doc.text().slice(..);
@ -3228,7 +3324,7 @@ fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) -> anyhow
editor editor
.clipboard_provider .clipboard_provider
.set_contents(joined) .set_contents(joined, clipboard_type)
.context("Couldn't set system clipboard content")?; .context("Couldn't set system clipboard content")?;
editor.set_status(msg); editor.set_status(msg);
@ -3238,17 +3334,27 @@ fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) -> anyhow
fn yank_joined_to_clipboard(cx: &mut Context) { fn yank_joined_to_clipboard(cx: &mut Context) {
let line_ending = current!(cx.editor).1.line_ending; let line_ending = current!(cx.editor).1.line_ending;
let _ = yank_joined_to_clipboard_impl(&mut cx.editor, line_ending.as_str()); let _ = yank_joined_to_clipboard_impl(
&mut cx.editor,
line_ending.as_str(),
ClipboardType::Clipboard,
);
exit_select_mode(cx); exit_select_mode(cx);
} }
fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) -> anyhow::Result<()> { fn yank_main_selection_to_clipboard_impl(
editor: &mut Editor,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
let text = doc.text().slice(..); let text = doc.text().slice(..);
let value = doc.selection(view.id).primary().fragment(text); let value = doc.selection(view.id).primary().fragment(text);
if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) { if let Err(e) = editor
.clipboard_provider
.set_contents(value.into_owned(), clipboard_type)
{
bail!("Couldn't set system clipboard content: {:?}", e); bail!("Couldn't set system clipboard content: {:?}", e);
} }
@ -3257,7 +3363,20 @@ fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) -> anyhow::Result<
} }
fn yank_main_selection_to_clipboard(cx: &mut Context) { fn yank_main_selection_to_clipboard(cx: &mut Context) {
let _ = yank_main_selection_to_clipboard_impl(&mut cx.editor); let _ = yank_main_selection_to_clipboard_impl(&mut cx.editor, ClipboardType::Clipboard);
}
fn yank_joined_to_primary_clipboard(cx: &mut Context) {
let line_ending = current!(cx.editor).1.line_ending;
let _ = yank_joined_to_clipboard_impl(
&mut cx.editor,
line_ending.as_str(),
ClipboardType::Selection,
);
}
fn yank_main_selection_to_primary_clipboard(cx: &mut Context) {
let _ = yank_main_selection_to_clipboard_impl(&mut cx.editor, ClipboardType::Selection);
exit_select_mode(cx); exit_select_mode(cx);
} }
@ -3310,12 +3429,16 @@ fn paste_impl(
Some(transaction) Some(transaction)
} }
fn paste_clipboard_impl(editor: &mut Editor, action: Paste) -> anyhow::Result<()> { fn paste_clipboard_impl(
editor: &mut Editor,
action: Paste,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
match editor match editor
.clipboard_provider .clipboard_provider
.get_contents() .get_contents(clipboard_type)
.map(|contents| paste_impl(&[contents], doc, view, action)) .map(|contents| paste_impl(&[contents], doc, view, action))
{ {
Ok(Some(transaction)) => { Ok(Some(transaction)) => {
@ -3329,11 +3452,19 @@ fn paste_clipboard_impl(editor: &mut Editor, action: Paste) -> anyhow::Result<()
} }
fn paste_clipboard_after(cx: &mut Context) { fn paste_clipboard_after(cx: &mut Context) {
let _ = paste_clipboard_impl(&mut cx.editor, Paste::After); let _ = paste_clipboard_impl(&mut cx.editor, Paste::After, ClipboardType::Clipboard);
} }
fn paste_clipboard_before(cx: &mut Context) { fn paste_clipboard_before(cx: &mut Context) {
let _ = paste_clipboard_impl(&mut cx.editor, Paste::Before); let _ = paste_clipboard_impl(&mut cx.editor, Paste::Before, ClipboardType::Clipboard);
}
fn paste_primary_clipboard_after(cx: &mut Context) {
let _ = paste_clipboard_impl(&mut cx.editor, Paste::After, ClipboardType::Selection);
}
fn paste_primary_clipboard_before(cx: &mut Context) {
let _ = paste_clipboard_impl(&mut cx.editor, Paste::Before, ClipboardType::Selection);
} }
fn replace_with_yanked(cx: &mut Context) { fn replace_with_yanked(cx: &mut Context) {
@ -3358,10 +3489,13 @@ fn replace_with_yanked(cx: &mut Context) {
} }
} }
fn replace_selections_with_clipboard_impl(editor: &mut Editor) -> anyhow::Result<()> { fn replace_selections_with_clipboard_impl(
editor: &mut Editor,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
match editor.clipboard_provider.get_contents() { match editor.clipboard_provider.get_contents(clipboard_type) {
Ok(contents) => { Ok(contents) => {
let selection = doc.selection(view.id); let selection = doc.selection(view.id);
let transaction = Transaction::change_by_selection(doc.text(), selection, |range| { let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
@ -3377,7 +3511,11 @@ fn replace_selections_with_clipboard_impl(editor: &mut Editor) -> anyhow::Result
} }
fn replace_selections_with_clipboard(cx: &mut Context) { fn replace_selections_with_clipboard(cx: &mut Context) {
let _ = replace_selections_with_clipboard_impl(&mut cx.editor); let _ = replace_selections_with_clipboard_impl(&mut cx.editor, ClipboardType::Clipboard);
}
fn replace_selections_with_primary_clipboard(cx: &mut Context) {
let _ = replace_selections_with_clipboard_impl(&mut cx.editor, ClipboardType::Selection);
} }
fn paste_after(cx: &mut Context) { fn paste_after(cx: &mut Context) {

@ -785,6 +785,61 @@ impl EditorView {
EventResult::Consumed(None) EventResult::Consumed(None)
} }
MouseEvent {
kind: MouseEventKind::Up(MouseButton::Left),
..
} => {
if !cxt.editor.config.middle_click_paste {
return EventResult::Ignored;
}
let (view, doc) = current!(cxt.editor);
let range = doc.selection(view.id).primary();
if range.to() - range.from() <= 1 {
return EventResult::Ignored;
}
commands::Command::yank_main_selection_to_primary_clipboard.execute(cxt);
EventResult::Consumed(None)
}
MouseEvent {
kind: MouseEventKind::Up(MouseButton::Middle),
row,
column,
modifiers,
..
} => {
let editor = &mut cxt.editor;
if !editor.config.middle_click_paste {
return EventResult::Ignored;
}
if modifiers == crossterm::event::KeyModifiers::ALT {
commands::Command::replace_selections_with_primary_clipboard.execute(cxt);
return EventResult::Consumed(None);
}
let result = editor.tree.views().find_map(|(view, _focus)| {
view.pos_at_screen_coords(&editor.documents[view.doc], row, column)
.map(|pos| (pos, view.id))
});
if let Some((pos, view_id)) = result {
let doc = &mut editor.documents[editor.tree.get(view_id).doc];
doc.set_selection(view_id, Selection::point(pos));
editor.tree.focus = view_id;
commands::Command::paste_primary_clipboard_before.execute(cxt);
return EventResult::Consumed(None);
}
EventResult::Ignored
}
_ => EventResult::Ignored, _ => EventResult::Ignored,
} }
} }

@ -3,10 +3,15 @@
use anyhow::Result; use anyhow::Result;
use std::borrow::Cow; use std::borrow::Cow;
pub enum ClipboardType {
Clipboard,
Selection,
}
pub trait ClipboardProvider: std::fmt::Debug { pub trait ClipboardProvider: std::fmt::Debug {
fn name(&self) -> Cow<str>; fn name(&self) -> Cow<str>;
fn get_contents(&self) -> Result<String>; fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String>;
fn set_contents(&self, contents: String) -> Result<()>; fn set_contents(&mut self, contents: String, clipboard_type: ClipboardType) -> Result<()>;
} }
macro_rules! command_provider { macro_rules! command_provider {
@ -20,6 +25,33 @@ macro_rules! command_provider {
prg: $set_prg, prg: $set_prg,
args: &[ $( $set_arg ),* ], args: &[ $( $set_arg ),* ],
}, },
get_primary_cmd: None,
set_primary_cmd: None,
})
}};
(paste => $get_prg:literal $( , $get_arg:literal )* ;
copy => $set_prg:literal $( , $set_arg:literal )* ;
primary_paste => $pr_get_prg:literal $( , $pr_get_arg:literal )* ;
primary_copy => $pr_set_prg:literal $( , $pr_set_arg:literal )* ;
) => {{
Box::new(provider::CommandProvider {
get_cmd: provider::CommandConfig {
prg: $get_prg,
args: &[ $( $get_arg ),* ],
},
set_cmd: provider::CommandConfig {
prg: $set_prg,
args: &[ $( $set_arg ),* ],
},
get_primary_cmd: Some(provider::CommandConfig {
prg: $pr_get_prg,
args: &[ $( $pr_get_arg ),* ],
}),
set_primary_cmd: Some(provider::CommandConfig {
prg: $pr_set_prg,
args: &[ $( $pr_set_arg ),* ],
}),
}) })
}}; }};
} }
@ -37,11 +69,15 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
command_provider! { command_provider! {
paste => "wl-paste", "--no-newline"; paste => "wl-paste", "--no-newline";
copy => "wl-copy", "--type", "text/plain"; copy => "wl-copy", "--type", "text/plain";
primary_paste => "wl-paste", "-p", "--no-newline";
primary_copy => "wl-copy", "-p", "--type", "text/plain";
} }
} else if env_var_is_set("DISPLAY") && exists("xclip") { } else if env_var_is_set("DISPLAY") && exists("xclip") {
command_provider! { command_provider! {
paste => "xclip", "-o", "-selection", "clipboard"; paste => "xclip", "-o", "-selection", "clipboard";
copy => "xclip", "-i", "-selection", "clipboard"; copy => "xclip", "-i", "-selection", "clipboard";
primary_paste => "xclip", "-o";
primary_copy => "xclip", "-i";
} }
} else if env_var_is_set("DISPLAY") && exists("xsel") && is_exit_success("xsel", &["-o", "-b"]) } else if env_var_is_set("DISPLAY") && exists("xsel") && is_exit_success("xsel", &["-o", "-b"])
{ {
@ -49,6 +85,8 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
command_provider! { command_provider! {
paste => "xsel", "-o", "-b"; paste => "xsel", "-o", "-b";
copy => "xsel", "--nodetach", "-i", "-b"; copy => "xsel", "--nodetach", "-i", "-b";
primary_paste => "xsel", "-o";
primary_copy => "xsel", "-i";
} }
} else if exists("lemonade") { } else if exists("lemonade") {
command_provider! { command_provider! {
@ -78,10 +116,10 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
} }
} else { } else {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
return Box::new(provider::WindowsProvider); return Box::new(provider::WindowsProvider::new());
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
return Box::new(provider::NopProvider); return Box::new(provider::NopProvider::new());
} }
} }
@ -103,30 +141,62 @@ fn is_exit_success(program: &str, args: &[&str]) -> bool {
} }
mod provider { mod provider {
use super::ClipboardProvider; use super::{ClipboardProvider, ClipboardType};
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
use std::borrow::Cow; use std::borrow::Cow;
#[derive(Debug)] #[derive(Debug)]
pub struct NopProvider; pub struct NopProvider {
buf: String,
primary_buf: String,
}
impl NopProvider {
pub fn new() -> Self {
Self {
buf: String::new(),
primary_buf: String::new(),
}
}
}
impl ClipboardProvider for NopProvider { impl ClipboardProvider for NopProvider {
fn name(&self) -> Cow<str> { fn name(&self) -> Cow<str> {
Cow::Borrowed("none") Cow::Borrowed("none")
} }
fn get_contents(&self) -> Result<String> { fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
Ok(String::new()) let value = match clipboard_type {
ClipboardType::Clipboard => self.buf.clone(),
ClipboardType::Selection => self.primary_buf.clone(),
};
Ok(value)
} }
fn set_contents(&self, _: String) -> Result<()> { fn set_contents(&mut self, content: String, clipboard_type: ClipboardType) -> Result<()> {
match clipboard_type {
ClipboardType::Clipboard => self.buf = content,
ClipboardType::Selection => self.primary_buf = content,
}
Ok(()) Ok(())
} }
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
#[derive(Debug)] #[derive(Debug)]
pub struct WindowsProvider; pub struct WindowsProvider {
selection_buf: String,
}
#[cfg(target_os = "windows")]
impl WindowsProvider {
pub fn new() -> Self {
Self {
selection_buf: String::new(),
}
}
}
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
impl ClipboardProvider for WindowsProvider { impl ClipboardProvider for WindowsProvider {
@ -134,13 +204,23 @@ mod provider {
Cow::Borrowed("clipboard-win") Cow::Borrowed("clipboard-win")
} }
fn get_contents(&self) -> Result<String> { fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
match clipboard_type {
ClipboardType::Clipboard => {
let contents = clipboard_win::get_clipboard(clipboard_win::formats::Unicode)?; let contents = clipboard_win::get_clipboard(clipboard_win::formats::Unicode)?;
Ok(contents) Ok(contents)
} }
ClipboardType::Selection => Ok(String::new()),
}
}
fn set_contents(&self, contents: String) -> Result<()> { fn set_contents(&mut self, contents: String, clipboard_type: ClipboardType) -> Result<()> {
clipboard_win::set_clipboard(clipboard_win::formats::Unicode, contents)?; match clipboard_type {
ClipboardType::Clipboard => {
clipboard_win::set_clipboard(clipboard_win::formats::Unicode, contents);
}
ClipboardType::Selection => {}
};
Ok(()) Ok(())
} }
} }
@ -192,6 +272,8 @@ mod provider {
pub struct CommandProvider { pub struct CommandProvider {
pub get_cmd: CommandConfig, pub get_cmd: CommandConfig,
pub set_cmd: CommandConfig, pub set_cmd: CommandConfig,
pub get_primary_cmd: Option<CommandConfig>,
pub set_primary_cmd: Option<CommandConfig>,
} }
impl ClipboardProvider for CommandProvider { impl ClipboardProvider for CommandProvider {
@ -203,16 +285,34 @@ mod provider {
} }
} }
fn get_contents(&self) -> Result<String> { fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
let output = self match clipboard_type {
ClipboardType::Clipboard => Ok(self
.get_cmd .get_cmd
.execute(None, true)? .execute(None, true)?
.context("output is missing")?; .context("output is missing")?),
Ok(output) ClipboardType::Selection => {
if let Some(cmd) = &self.get_primary_cmd {
return cmd.execute(None, true)?.context("output is missing");
} }
fn set_contents(&self, value: String) -> Result<()> { Ok(String::new())
self.set_cmd.execute(Some(&value), false).map(|_| ()) }
}
}
fn set_contents(&mut self, value: String, clipboard_type: ClipboardType) -> Result<()> {
let cmd = match clipboard_type {
ClipboardType::Clipboard => &self.set_cmd,
ClipboardType::Selection => {
if let Some(cmd) = &self.set_primary_cmd {
cmd
} else {
return Ok(());
}
}
};
cmd.execute(Some(&value), false).map(|_| ())
} }
} }
} }

@ -29,6 +29,8 @@ pub struct Config {
pub scroll_lines: isize, pub scroll_lines: isize,
/// Mouse support. Defaults to true. /// Mouse support. Defaults to true.
pub mouse: bool, pub mouse: bool,
/// Middle click paste support. Defaults to true
pub middle_click_paste: bool,
} }
impl Default for Config { impl Default for Config {
@ -37,6 +39,7 @@ impl Default for Config {
scrolloff: 5, scrolloff: 5,
scroll_lines: 3, scroll_lines: 3,
mouse: true, mouse: true,
middle_click_paste: true,
} }
} }
} }

Loading…
Cancel
Save