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>
This commit is contained in:
parent
d03982ee43
commit
7d51805e94
4 changed files with 341 additions and 45 deletions
|
@ -12,8 +12,8 @@ use helix_core::{
|
|||
};
|
||||
|
||||
use helix_view::{
|
||||
document::Mode, editor::Action, input::KeyEvent, keyboard::KeyCode, view::View, Document,
|
||||
DocumentId, Editor, ViewId,
|
||||
clipboard::ClipboardType, document::Mode, editor::Action, input::KeyEvent, keyboard::KeyCode,
|
||||
view::View, Document, DocumentId, Editor, ViewId,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context as _};
|
||||
|
@ -258,12 +258,17 @@ impl Command {
|
|||
yank, "Yank selection",
|
||||
yank_joined_to_clipboard, "Join and yank selections 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_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_before, "Paste before selection",
|
||||
paste_clipboard_after, "Paste clipboard after 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",
|
||||
unindent, "Unindent selection",
|
||||
format_selections, "Format selection",
|
||||
|
@ -1705,7 +1710,7 @@ mod cmd {
|
|||
_args: &[&str],
|
||||
_event: PromptEvent,
|
||||
) -> 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(
|
||||
|
@ -1718,7 +1723,28 @@ mod cmd {
|
|||
.first()
|
||||
.copied()
|
||||
.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(
|
||||
|
@ -1726,7 +1752,7 @@ mod cmd {
|
|||
_args: &[&str],
|
||||
_event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
paste_clipboard_impl(&mut cx.editor, Paste::After)
|
||||
paste_clipboard_impl(&mut cx.editor, Paste::After, ClipboardType::Clipboard)
|
||||
}
|
||||
|
||||
fn paste_clipboard_before(
|
||||
|
@ -1734,17 +1760,32 @@ mod cmd {
|
|||
_args: &[&str],
|
||||
_event: PromptEvent,
|
||||
) -> 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,
|
||||
_args: &[&str],
|
||||
_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<()> {
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
||||
match cx.editor.clipboard_provider.get_contents() {
|
||||
match cx.editor.clipboard_provider.get_contents(clipboard_type) {
|
||||
Ok(contents) => {
|
||||
let selection = doc.selection(view.id);
|
||||
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(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[&str],
|
||||
_event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
cx.editor
|
||||
.set_status(cx.editor.clipboard_provider.name().into());
|
||||
.set_status(cx.editor.clipboard_provider.name().to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1967,6 +2024,20 @@ mod cmd {
|
|||
fun: yank_joined_to_clipboard,
|
||||
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 {
|
||||
name: "clipboard-paste-after",
|
||||
alias: None,
|
||||
|
@ -1988,6 +2059,27 @@ mod cmd {
|
|||
fun: replace_selections_with_clipboard,
|
||||
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 {
|
||||
name: "show-clipboard-provider",
|
||||
alias: None,
|
||||
|
@ -3209,7 +3301,11 @@ fn yank(cx: &mut Context) {
|
|||
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 text = doc.text().slice(..);
|
||||
|
||||
|
@ -3228,7 +3324,7 @@ fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) -> anyhow
|
|||
|
||||
editor
|
||||
.clipboard_provider
|
||||
.set_contents(joined)
|
||||
.set_contents(joined, clipboard_type)
|
||||
.context("Couldn't set system clipboard content")?;
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 text = doc.text().slice(..);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -3310,12 +3429,16 @@ fn paste_impl(
|
|||
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);
|
||||
|
||||
match editor
|
||||
.clipboard_provider
|
||||
.get_contents()
|
||||
.get_contents(clipboard_type)
|
||||
.map(|contents| paste_impl(&[contents], doc, view, action))
|
||||
{
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
|
@ -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);
|
||||
|
||||
match editor.clipboard_provider.get_contents() {
|
||||
match editor.clipboard_provider.get_contents(clipboard_type) {
|
||||
Ok(contents) => {
|
||||
let selection = doc.selection(view.id);
|
||||
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) {
|
||||
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) {
|
||||
|
|
|
@ -785,6 +785,61 @@ impl EditorView {
|
|||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,15 @@
|
|||
use anyhow::Result;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub enum ClipboardType {
|
||||
Clipboard,
|
||||
Selection,
|
||||
}
|
||||
|
||||
pub trait ClipboardProvider: std::fmt::Debug {
|
||||
fn name(&self) -> Cow<str>;
|
||||
fn get_contents(&self) -> Result<String>;
|
||||
fn set_contents(&self, contents: String) -> Result<()>;
|
||||
fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String>;
|
||||
fn set_contents(&mut self, contents: String, clipboard_type: ClipboardType) -> Result<()>;
|
||||
}
|
||||
|
||||
macro_rules! command_provider {
|
||||
|
@ -20,6 +25,33 @@ macro_rules! command_provider {
|
|||
prg: $set_prg,
|
||||
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! {
|
||||
paste => "wl-paste", "--no-newline";
|
||||
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") {
|
||||
command_provider! {
|
||||
paste => "xclip", "-o", "-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"])
|
||||
{
|
||||
|
@ -49,6 +85,8 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
|
|||
command_provider! {
|
||||
paste => "xsel", "-o", "-b";
|
||||
copy => "xsel", "--nodetach", "-i", "-b";
|
||||
primary_paste => "xsel", "-o";
|
||||
primary_copy => "xsel", "-i";
|
||||
}
|
||||
} else if exists("lemonade") {
|
||||
command_provider! {
|
||||
|
@ -78,10 +116,10 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
|
|||
}
|
||||
} else {
|
||||
#[cfg(target_os = "windows")]
|
||||
return Box::new(provider::WindowsProvider);
|
||||
return Box::new(provider::WindowsProvider::new());
|
||||
|
||||
#[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 {
|
||||
use super::ClipboardProvider;
|
||||
use super::{ClipboardProvider, ClipboardType};
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[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 {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::Borrowed("none")
|
||||
}
|
||||
|
||||
fn get_contents(&self) -> Result<String> {
|
||||
Ok(String::new())
|
||||
fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[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")]
|
||||
impl ClipboardProvider for WindowsProvider {
|
||||
|
@ -134,13 +204,23 @@ mod provider {
|
|||
Cow::Borrowed("clipboard-win")
|
||||
}
|
||||
|
||||
fn get_contents(&self) -> Result<String> {
|
||||
let contents = clipboard_win::get_clipboard(clipboard_win::formats::Unicode)?;
|
||||
Ok(contents)
|
||||
fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
|
||||
match clipboard_type {
|
||||
ClipboardType::Clipboard => {
|
||||
let contents = clipboard_win::get_clipboard(clipboard_win::formats::Unicode)?;
|
||||
Ok(contents)
|
||||
}
|
||||
ClipboardType::Selection => Ok(String::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_contents(&self, contents: String) -> Result<()> {
|
||||
clipboard_win::set_clipboard(clipboard_win::formats::Unicode, contents)?;
|
||||
fn set_contents(&mut self, contents: String, clipboard_type: ClipboardType) -> Result<()> {
|
||||
match clipboard_type {
|
||||
ClipboardType::Clipboard => {
|
||||
clipboard_win::set_clipboard(clipboard_win::formats::Unicode, contents);
|
||||
}
|
||||
ClipboardType::Selection => {}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -192,6 +272,8 @@ mod provider {
|
|||
pub struct CommandProvider {
|
||||
pub get_cmd: CommandConfig,
|
||||
pub set_cmd: CommandConfig,
|
||||
pub get_primary_cmd: Option<CommandConfig>,
|
||||
pub set_primary_cmd: Option<CommandConfig>,
|
||||
}
|
||||
|
||||
impl ClipboardProvider for CommandProvider {
|
||||
|
@ -203,16 +285,34 @@ mod provider {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_contents(&self) -> Result<String> {
|
||||
let output = self
|
||||
.get_cmd
|
||||
.execute(None, true)?
|
||||
.context("output is missing")?;
|
||||
Ok(output)
|
||||
fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
|
||||
match clipboard_type {
|
||||
ClipboardType::Clipboard => Ok(self
|
||||
.get_cmd
|
||||
.execute(None, true)?
|
||||
.context("output is missing")?),
|
||||
ClipboardType::Selection => {
|
||||
if let Some(cmd) = &self.get_primary_cmd {
|
||||
return cmd.execute(None, true)?.context("output is missing");
|
||||
}
|
||||
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_contents(&self, value: String) -> Result<()> {
|
||||
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,
|
||||
/// Mouse support. Defaults to true.
|
||||
pub mouse: bool,
|
||||
/// Middle click paste support. Defaults to true
|
||||
pub middle_click_paste: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -37,6 +39,7 @@ impl Default for Config {
|
|||
scrolloff: 5,
|
||||
scroll_lines: 3,
|
||||
mouse: true,
|
||||
middle_click_paste: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue