Skip to content

Commit

Permalink
Use the system-clipboard only for explicit cut/copy/paste operation
Browse files Browse the repository at this point in the history
  • Loading branch information
Tastaturtaste committed Feb 27, 2024
1 parent 4fd1295 commit 6dd7c39
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 54 deletions.
49 changes: 28 additions & 21 deletions src/core_editor/clip_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,34 +50,23 @@ impl Clipboard for LocalClipboard {
}
}

/// Creates a local clipboard
pub fn get_local_clipboard() -> Box<dyn Clipboard> {
Box::new(LocalClipboard::new())
}

#[cfg(feature = "system_clipboard")]
pub use system_clipboard::SystemClipboard;

/// Creates a handle for the OS clipboard
#[cfg(feature = "system_clipboard")]
/// Helper to get a clipboard based on the `system_clipboard` feature flag:
///
/// Enabled -> [`SystemClipboard`], which talks to the system. If the system clipboard can't be
/// accessed, it will default to [`LocalClipboard`].
///
/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited to the [`crate::Reedline`] instance
pub fn get_default_clipboard() -> Box<dyn Clipboard> {
pub fn get_system_clipboard() -> Box<dyn Clipboard> {
SystemClipboard::new().map_or_else(
|_e| Box::new(LocalClipboard::new()) as Box<dyn Clipboard>,
|cb| Box::new(cb),
)
}

#[cfg(not(feature = "system_clipboard"))]
/// Helper to get a clipboard based on the `system_clipboard` feature flag:
///
/// Enabled -> `SystemClipboard`, which talks to the system. If the system clipboard can't be
/// accessed, it will default to [`LocalClipboard`].
///
/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited to the [`crate::Reedline`] instance
pub fn get_default_clipboard() -> Box<dyn Clipboard> {
Box::new(LocalClipboard::new())
}

#[cfg(feature = "system_clipboard")]
mod system_clipboard {
use super::*;
Expand Down Expand Up @@ -124,10 +113,28 @@ mod system_clipboard {

#[cfg(test)]
mod tests {
use super::{get_default_clipboard, ClipboardMode};
use super::{get_local_clipboard, get_system_clipboard, ClipboardMode};
#[test]
fn reads_back_local() {
let mut cb = get_local_clipboard();
// If the system clipboard is used we want to persist it for the user
let previous_state = cb.get().0;

// Actual test
cb.set("test", ClipboardMode::Normal);
assert_eq!(cb.len(), 4);
assert_eq!(cb.get().0, "test".to_owned());
cb.clear();
assert_eq!(cb.get().0, String::new());

// Restore!

cb.set(&previous_state, ClipboardMode::Normal);
}

#[test]
fn reads_back() {
let mut cb = get_default_clipboard();
fn reads_back_system() {
let mut cb = get_system_clipboard();
// If the system clipboard is used we want to persist it for the user
let previous_state = cb.get().0;

Expand Down
93 changes: 72 additions & 21 deletions src/core_editor/editor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{edit_stack::EditStack, Clipboard, ClipboardMode, LineBuffer};
use crate::enums::{EditType, UndoBehavior};
use crate::{core_editor::get_default_clipboard, EditCommand};
use crate::{core_editor::get_local_clipboard, core_editor::get_system_clipboard, EditCommand};
use std::ops::DerefMut;

/// Stateful editor executing changes to the underlying [`LineBuffer`]
///
Expand All @@ -9,6 +10,8 @@ use crate::{core_editor::get_default_clipboard, EditCommand};
pub struct Editor {
line_buffer: LineBuffer,
cut_buffer: Box<dyn Clipboard>,
#[cfg(feature = "system_clipboard")]
system_clipboard: Box<dyn Clipboard>,
edit_stack: EditStack<LineBuffer>,
last_undo_behavior: UndoBehavior,
selection_anchor: Option<usize>,
Expand All @@ -18,7 +21,9 @@ impl Default for Editor {
fn default() -> Self {
Editor {
line_buffer: LineBuffer::new(),
cut_buffer: get_default_clipboard(),
cut_buffer: get_local_clipboard(),
#[cfg(feature = "system_clipboard")]
system_clipboard: get_system_clipboard(),
edit_stack: EditStack::new(),
last_undo_behavior: UndoBehavior::CreateUndoPoint,
selection_anchor: None,
Expand Down Expand Up @@ -110,8 +115,24 @@ impl Editor {
self.move_left_until_char(*c, true, true, *select)
}
EditCommand::SelectAll => self.select_all(),
EditCommand::CutSelection => self.cut_selection(),
EditCommand::CopySelection => self.copy_selection(),
EditCommand::CutSelection {
system_clipboard: true,
} => self.cut_selection_to_system(),
EditCommand::CutSelection {
system_clipboard: false,
} => self.cut_selection_to_cut_buffer(),
EditCommand::CopySelection {
system_clipboard: true,
} => self.copy_selection_to_system(),
EditCommand::CopySelection {
system_clipboard: false,
} => self.copy_selection_to_cut_buffer(),
EditCommand::Paste {
system_clipboard: true,
} => self.paste_system_clipboard(),
EditCommand::Paste {
system_clipboard: false,
} => self.paste_cut_buffer(),
}
if !matches!(command.edit_type(), EditType::MoveCursor { select: true }) {
self.selection_anchor = None;
Expand Down Expand Up @@ -390,21 +411,7 @@ impl Editor {

fn insert_cut_buffer_before(&mut self) {
self.delete_selection();
match self.cut_buffer.get() {
(content, ClipboardMode::Normal) => {
self.line_buffer.insert_str(&content);
}
(mut content, ClipboardMode::Lines) => {
// TODO: Simplify that?
self.line_buffer.move_to_line_start();
self.line_buffer.move_line_up();
if !content.ends_with('\n') {
// TODO: Make sure platform requirements are met
content.push('\n');
}
self.line_buffer.insert_str(&content);
}
}
insert_clipboard_content_before(&mut self.line_buffer, self.cut_buffer.deref_mut())
}

fn insert_cut_buffer_after(&mut self) {
Expand Down Expand Up @@ -526,7 +533,16 @@ impl Editor {
self.line_buffer.move_to_end();
}

fn cut_selection(&mut self) {
fn cut_selection_to_system(&mut self) {
if let Some((start, end)) = self.get_selection() {
let cut_slice = &self.line_buffer.get_buffer()[start..end];
self.system_clipboard.set(cut_slice, ClipboardMode::Normal);
self.line_buffer.clear_range_safe(start, end);
self.selection_anchor = None;
}
}

fn cut_selection_to_cut_buffer(&mut self) {
if let Some((start, end)) = self.get_selection() {
let cut_slice = &self.line_buffer.get_buffer()[start..end];
self.cut_buffer.set(cut_slice, ClipboardMode::Normal);
Expand All @@ -535,7 +551,14 @@ impl Editor {
}
}

fn copy_selection(&mut self) {
fn copy_selection_to_system(&mut self) {
if let Some((start, end)) = self.get_selection() {
let cut_slice = &self.line_buffer.get_buffer()[start..end];
self.system_clipboard.set(cut_slice, ClipboardMode::Normal);
}
}

fn copy_selection_to_cut_buffer(&mut self) {
if let Some((start, end)) = self.get_selection() {
let cut_slice = &self.line_buffer.get_buffer()[start..end];
self.cut_buffer.set(cut_slice, ClipboardMode::Normal);
Expand Down Expand Up @@ -619,6 +642,34 @@ impl Editor {
self.delete_selection();
self.line_buffer.insert_newline();
}

fn paste_system_clipboard(&mut self) {
self.delete_selection();
insert_clipboard_content_before(&mut self.line_buffer, self.system_clipboard.deref_mut());
}

fn paste_cut_buffer(&mut self) {
self.delete_selection();
insert_clipboard_content_before(&mut self.line_buffer, self.cut_buffer.deref_mut());
}
}

fn insert_clipboard_content_before(line_buffer: &mut LineBuffer, clipboard: &mut dyn Clipboard) {
match clipboard.get() {
(content, ClipboardMode::Normal) => {
line_buffer.insert_str(&content);
}
(mut content, ClipboardMode::Lines) => {
// TODO: Simplify that?
line_buffer.move_to_line_start();
line_buffer.move_line_up();
if !content.ends_with('\n') {
// TODO: Make sure platform requirements are met
content.push('\n');
}
line_buffer.insert_str(&content);
}
}
}

#[cfg(test)]
Expand Down
4 changes: 3 additions & 1 deletion src/core_editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ mod edit_stack;
mod editor;
mod line_buffer;

pub(crate) use clip_buffer::{get_default_clipboard, Clipboard, ClipboardMode};
#[cfg(feature = "system_clipboard")]
pub(crate) use clip_buffer::get_system_clipboard;
pub(crate) use clip_buffer::{get_local_clipboard, Clipboard, ClipboardMode};
pub use editor::Editor;
pub use line_buffer::LineBuffer;
12 changes: 9 additions & 3 deletions src/edit_mode/keybindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,17 +217,23 @@ pub fn add_common_edit_bindings(kb: &mut Keybindings) {
kb.add_binding(
KM::CONTROL | KM::SHIFT,
KC::Char('x'),
edit_bind(EC::CutSelection),
edit_bind(EC::CutSelection {
system_clipboard: true,
}),
);
kb.add_binding(
KM::CONTROL | KM::SHIFT,
KC::Char('c'),
edit_bind(EC::CopySelection),
edit_bind(EC::CopySelection {
system_clipboard: true,
}),
);
kb.add_binding(
KM::CONTROL | KM::SHIFT,
KC::Char('v'),
edit_bind(EC::PasteCutBufferBefore),
edit_bind(EC::Paste {
system_clipboard: true,
}),
);
}

Expand Down
34 changes: 26 additions & 8 deletions src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,23 @@ pub enum EditCommand {
/// Select whole input buffer
SelectAll,

/// Cut selection
CutSelection,
/// Cut selection to local buffer or system clipboard
CutSelection {
/// Should the system clipboard be used as the destination of the content?
system_clipboard: bool,
},

/// Copy selection to local buffer or system clipboard
CopySelection {
/// Should the system clipboard be used as the destination of the content?
system_clipboard: bool,
},

/// Copy selection
CopySelection,
/// Paste content from local buffer or system clipboard at the current cursor position
Paste {
/// Is the system clipboard the source of the paste operation?
system_clipboard: bool,
},
}

impl Display for EditCommand {
Expand Down Expand Up @@ -346,8 +358,13 @@ impl Display for EditCommand {
EditCommand::CutLeftUntil(_) => write!(f, "CutLeftUntil Value: <char>"),
EditCommand::CutLeftBefore(_) => write!(f, "CutLeftBefore Value: <char>"),
EditCommand::SelectAll => write!(f, "SelectAll"),
EditCommand::CutSelection => write!(f, "CutSelection"),
EditCommand::CopySelection => write!(f, "CopySelection"),
EditCommand::CutSelection { .. } => {
write!(f, "CutSelectionToSystem system_clipboard: <bool>")
}
EditCommand::CopySelection { .. } => {
write!(f, "CopySelectionToSystem system_clipboard: <bool>")
}
EditCommand::Paste { .. } => write!(f, "PasteSystemClipboard system_clipboard: <bool>"),
}
}
}
Expand Down Expand Up @@ -418,11 +435,12 @@ impl EditCommand {
| EditCommand::CutRightBefore(_)
| EditCommand::CutLeftUntil(_)
| EditCommand::CutLeftBefore(_)
| EditCommand::CutSelection => EditType::EditText,
| EditCommand::CutSelection { .. }
| EditCommand::Paste { .. } => EditType::EditText,

EditCommand::Undo | EditCommand::Redo => EditType::UndoRedo,

EditCommand::CopySelection => EditType::NoOp,
EditCommand::CopySelection { .. } => EditType::NoOp,
}
}
}
Expand Down

0 comments on commit 6dd7c39

Please sign in to comment.