diff --git a/Cargo.lock b/Cargo.lock index 48bc9d9..456e8f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,12 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "cc" version = "1.2.27" @@ -152,6 +158,28 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "clipboard" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "x11-clipboard", +] + +[[package]] +name = "clipboard-win" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b" +dependencies = [ + "winapi", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -234,6 +262,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.7.5" @@ -252,6 +289,35 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -648,12 +714,32 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "x11-clipboard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea" +dependencies = [ + "xcb", +] + +[[package]] +name = "xcb" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de" +dependencies = [ + "libc", + "log", +] + [[package]] name = "zte" version = "0.2.0" dependencies = [ "chumsky", "clap", + "clipboard", "crossterm", "slotmap", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index b1e2110..7d013ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ crossterm = "0.27" thiserror = "1.0" chumsky = { version = "0.10.1", features = ["pratt"] } unicode-display-width = "0.3.0" +clipboard = "0.5.0" [profile.dev] opt-level = 2 diff --git a/src/action.rs b/src/action.rs index 8bab26a..c4593e1 100644 --- a/src/action.rs +++ b/src/action.rs @@ -45,6 +45,9 @@ pub enum Action { Mouse(MouseAction, [isize; 2], bool), // (action, pos, is_ctrl) Undo, Redo, + Copy, + Cut, + Paste, } /// How far should movement go? @@ -429,7 +432,7 @@ impl RawEvent { } } - pub fn to_undo_redo(&self) -> Option { + pub fn to_edit(&self) -> Option { match &self.0 { TerminalEvent::Key(KeyEvent { code: KeyCode::Char('z'), @@ -443,6 +446,24 @@ impl RawEvent { kind: KeyEventKind::Press, .. }) => Some(Action::Redo), + TerminalEvent::Key(KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + .. + }) => Some(Action::Copy), + TerminalEvent::Key(KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + .. + }) => Some(Action::Cut), + TerminalEvent::Key(KeyEvent { + code: KeyCode::Char('v'), + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + .. + }) => Some(Action::Paste), _ => None, } } diff --git a/src/state.rs b/src/state.rs index 3fdb12c..ee78201 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,6 +3,7 @@ use crate::{ highlight::{Highlighter, Highlights}, theme, }; +use clipboard::{ClipboardContext, ClipboardProvider}; use slotmap::{HopSlotMap, new_key_type}; use std::{ collections::HashMap, @@ -734,6 +735,39 @@ impl Buffer { } } + pub fn copy(&mut self, cursor_id: CursorId) -> bool { + let Some(cursor) = self.cursors.get(cursor_id) else { + return false; + }; + if let Some(text) = cursor.selection().and_then(|s| self.text.chars().get(s)) + && ClipboardContext::new() + .and_then(|mut ctx| ctx.set_contents(text.iter().copied().collect())) + .is_ok() + { + true + } else { + false + } + } + + pub fn cut(&mut self, cursor_id: CursorId) -> bool { + if self.copy(cursor_id) { + self.backspace(cursor_id); + true + } else { + false + } + } + + pub fn paste(&mut self, cursor_id: CursorId) -> bool { + if let Ok(s) = ClipboardContext::new().and_then(|mut ctx| ctx.get_contents()) { + self.enter(cursor_id, s.chars()); + true + } else { + false + } + } + pub fn start_session(&mut self) -> CursorId { self.cursors.insert(Cursor::default()) } diff --git a/src/ui/input.rs b/src/ui/input.rs index c573ef8..f56974c 100644 --- a/src/ui/input.rs +++ b/src/ui/input.rs @@ -67,7 +67,7 @@ impl Input { .or_else(|| e.to_select_all()) .or_else(|| e.to_indent()) .or_else(|| e.to_mouse(self.last_area)) - .or_else(|| e.to_undo_redo()) + .or_else(|| e.to_edit()) }) { Some(Action::Char(c)) => { if c == '\x08' { @@ -164,6 +164,30 @@ impl Input { Ok(Resp::handled(Some(Event::Bell))) } } + Some(Action::Copy) => { + if buffer.copy(cursor_id) { + self.refocus(buffer, cursor_id); + Ok(Resp::handled(None)) + } else { + Ok(Resp::handled(Some(Event::Bell))) + } + } + Some(Action::Cut) => { + if buffer.cut(cursor_id) { + self.refocus(buffer, cursor_id); + Ok(Resp::handled(None)) + } else { + Ok(Resp::handled(Some(Event::Bell))) + } + } + Some(Action::Paste) => { + if buffer.paste(cursor_id) { + self.refocus(buffer, cursor_id); + Ok(Resp::handled(None)) + } else { + Ok(Resp::handled(Some(Event::Bell))) + } + } _ => Err(event), } }