Added basic clipboard support

This commit is contained in:
Joshua Barretto 2025-09-23 13:04:48 +01:00
parent c5e61823f8
commit cec0ae50e5
5 changed files with 168 additions and 2 deletions

86
Cargo.lock generated
View file

@ -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",

View file

@ -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

View file

@ -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<Action> {
pub fn to_edit(&self) -> Option<Action> {
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,
}
}

View file

@ -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())
}

View file

@ -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),
}
}