Global clipboard with local fallback

This commit is contained in:
Joshua Barretto 2025-10-28 21:42:57 +00:00
parent 35c5f0816b
commit 71a0a89f5f
5 changed files with 65 additions and 20 deletions

View file

@ -733,14 +733,12 @@ impl Buffer {
}
}
pub fn copy(&mut self, cursor_id: CursorId) -> bool {
pub fn copy(&mut self, clipboard: &mut Clipboard, 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()
&& clipboard.set(text.iter().copied().collect()).is_ok()
{
true
} else {
@ -748,8 +746,8 @@ impl Buffer {
}
}
pub fn cut(&mut self, cursor_id: CursorId) -> bool {
if self.copy(cursor_id) {
pub fn cut(&mut self, clipboard: &mut Clipboard, cursor_id: CursorId) -> bool {
if self.copy(clipboard, cursor_id) {
self.backspace(cursor_id);
true
} else {
@ -757,8 +755,8 @@ impl Buffer {
}
}
pub fn paste(&mut self, cursor_id: CursorId) -> bool {
if let Ok(s) = ClipboardContext::new().and_then(|mut ctx| ctx.get_contents()) {
pub fn paste(&mut self, clipboard: &mut Clipboard, cursor_id: CursorId) -> bool {
if let Ok(s) = clipboard.get() {
self.enter(cursor_id, s.chars());
true
} else {
@ -895,11 +893,33 @@ fn classify(c: char) -> Option<u8> {
}
}
pub struct Clipboard {
// If a global clipboard cannot be established, use a local clipboard instead
ctx: Result<ClipboardContext, String>,
}
impl Clipboard {
fn get(&mut self) -> Result<String, ()> {
match &mut self.ctx {
Ok(ctx) => ctx.get_contents().map_err(|_| ()),
Err(contents) => Ok(contents.clone()),
}
}
fn set(&mut self, text: String) -> Result<(), ()> {
match &mut self.ctx {
Ok(ctx) => ctx.set_contents(text).map_err(|_| ()),
Err(contents) => Ok(*contents = text),
}
}
}
pub struct State {
pub buffers: HopSlotMap<BufferId, Buffer>,
pub tick: u64,
pub theme: theme::Theme,
pub most_recent_counter: usize,
pub clipboard: Clipboard,
}
impl TryFrom<Args> for State {
@ -910,6 +930,9 @@ impl TryFrom<Args> for State {
tick: 0,
theme: theme::Theme::default(),
most_recent_counter: 0,
clipboard: Clipboard {
ctx: ClipboardContext::new().map_err(|_| String::new()),
},
};
if args.paths.is_empty() {

View file

@ -153,7 +153,8 @@ impl Element for Doc {
let Some(buffer) = state.buffers.get_mut(self.buffer) else {
return Err(event);
};
self.input.handle(buffer, cursor_id, event)
self.input
.handle(&mut state.clipboard, buffer, cursor_id, event)
}
}
}
@ -326,7 +327,12 @@ impl Finder {
}
_ => self
.input
.handle(&mut self.buffer, self.cursor_id, event)
.handle(
&mut state.clipboard,
&mut self.buffer,
self.cursor_id,
event,
)
.map(Resp::into_can_end),
};

View file

@ -1,6 +1,6 @@
use super::*;
use crate::{
state::{Buffer, CursorId},
state::{Buffer, Clipboard, CursorId},
terminal::CursorStyle,
};
@ -56,6 +56,7 @@ impl Input {
pub fn handle(
&mut self,
clipboard: &mut Clipboard,
buffer: &mut Buffer,
cursor_id: CursorId,
event: Event,
@ -191,7 +192,7 @@ impl Input {
}
}
Some(Action::Copy) => {
if buffer.copy(cursor_id) {
if buffer.copy(clipboard, cursor_id) {
self.refocus(buffer, cursor_id);
Ok(Resp::handled(None))
} else {
@ -199,7 +200,7 @@ impl Input {
}
}
Some(Action::Cut) => {
if buffer.cut(cursor_id) {
if buffer.cut(clipboard, cursor_id) {
self.refocus(buffer, cursor_id);
Ok(Resp::handled(None))
} else {
@ -207,7 +208,7 @@ impl Input {
}
}
Some(Action::Paste) => {
if buffer.paste(cursor_id) {
if buffer.paste(clipboard, cursor_id) {
self.refocus(buffer, cursor_id);
Ok(Resp::handled(None))
} else {

View file

@ -88,7 +88,12 @@ impl Element<()> for Prompt {
},
_ => self
.input
.handle(&mut self.buffer, self.cursor_id, event)
.handle(
&mut state.clipboard,
&mut self.buffer,
self.cursor_id,
event,
)
.map(Resp::into_can_end),
}
}
@ -231,7 +236,12 @@ impl Element<()> for Switcher {
Err(event) => {
let res = self
.input
.handle(&mut self.buffer, self.cursor_id, event)
.handle(
&mut state.clipboard,
&mut self.buffer,
self.cursor_id,
event,
)
.map(Resp::into_can_end);
// Score entries
let filter = self.buffer.text.to_string();
@ -416,12 +426,12 @@ impl Element<()> for Opener {
Err(event) => {
let res = match self
.input
.handle(&mut self.buffer, self.cursor_id, event)
.handle(&mut state.clipboard, &mut self.buffer, self.cursor_id, event)
.map(Resp::into_can_end)
{
Ok(x) => Ok(x),
Err(event) => if let Some((buffer, cursor_id, input)) = &mut self.preview {
input.handle(buffer, *cursor_id, event).map(Resp::into_can_end)
input.handle(&mut state.clipboard, buffer, *cursor_id, event).map(Resp::into_can_end)
} else {
Err(event)
},

View file

@ -130,14 +130,19 @@ impl Element<()> for Searcher {
Err(event) => {
let res = match self
.input
.handle(&mut self.buffer, self.cursor_id, event)
.handle(
&mut state.clipboard,
&mut self.buffer,
self.cursor_id,
event,
)
.map(Resp::into_can_end)
{
Ok(x) => Ok(x),
Err(event) => {
if let Some((buffer, cursor_id, input, _)) = &mut self.preview {
input
.handle(buffer, *cursor_id, event)
.handle(&mut state.clipboard, buffer, *cursor_id, event)
.map(Resp::into_can_end)
} else {
Err(event)