use super::Editor;
impl Editor {
pub(crate) fn register(&self, name: Option<char>) -> &(String, bool) {
static EMPTY: (String, bool) = (String::new(), false);
self.registers.get(&name.unwrap_or('"')).unwrap_or(&EMPTY)
}
pub(crate) fn set_register(&mut self, name: Option<char>, text: String, linewise: bool) {
if name == Some('+') {
self.osc52 = Some(text.clone());
}
self.registers.insert(name.unwrap_or('"'), (text, linewise));
}
pub(crate) fn paste_n(&mut self, count: usize, before: bool) {
let (text, linewise) = self.register(None).clone();
if text.is_empty() {
return;
}
self.paste_text(text.repeat(count), linewise, before);
}
pub(crate) fn paste(&mut self, name: Option<char>, before: bool) {
if self.buf().readonly {
self.message = "readonly buffer".into();
return;
}
if name == Some('+') {
self.clipboard_paste(before);
return;
}
let (text, linewise) = self.register(name).clone();
if text.is_empty() {
return;
}
self.paste_text(text, linewise, before);
}
pub(crate) fn clipboard_paste(&mut self, before: bool) {
if self.buf().readonly {
self.message = "readonly buffer".into();
return;
}
if self.clip_paste_pending.is_some() {
return; }
self.clip_paste_pending = Some(before);
let tx = self.clip_tx.clone();
std::thread::spawn(move || {
let _ = tx.send(read_system_clipboard());
});
}
pub fn drain_clipboard(&mut self) {
if self.docs.is_empty() {
return;
}
while let Ok(result) = self.clip_rx.try_recv() {
let Some(before) = self.clip_paste_pending.take() else {
continue;
};
match result {
Some(text) if !text.is_empty() => {
let linewise = text.len() > 1 && text.ends_with('\n');
self.paste_text(text, linewise, before);
}
_ => {
self.message =
"clipboard: empty or no provider (wl-paste/xclip/xsel/pbpaste)".into()
}
}
}
}
fn paste_points(
&self,
cursor: usize,
text_len: usize,
linewise: bool,
before: bool,
) -> (usize, usize) {
if linewise {
let line = self.buf().line_of(cursor);
let at = if before {
self.buf().line_start(line)
} else {
self.buf().line_start(line + 1)
};
(
at.min(self.buf().len_bytes()),
at.min(self.buf().len_bytes()),
)
} else {
let at = if before {
cursor
} else {
(cursor + 1).min(self.buf().len_bytes())
};
let land = at + text_len.saturating_sub(1);
(at, land)
}
}
fn paste_text(&mut self, text: String, linewise: bool, before: bool) {
self.tx_begin();
let cursors = self.all_cursors();
if cursors.len() == 1 {
let (at, land) = self.paste_points(self.head(), text.len(), linewise, before);
self.buf_mut().insert(at, &text);
self.set_head(land);
self.clamp_cursor();
self.tx_commit();
return;
}
let primary = self.head();
let mut jobs: Vec<(usize, usize, bool)> = cursors
.into_iter()
.map(|c| {
let (at, land) = self.paste_points(c, text.len(), linewise, before);
(at, land, c == primary)
})
.collect();
jobs.sort_by_key(|j| j.0);
jobs.dedup_by_key(|j| j.0); let mut shift = 0usize;
for j in &mut jobs {
j.1 += shift;
shift += text.len();
}
for (at, _, _) in jobs.iter().rev() {
self.buf_mut().insert(*at, &text);
}
self.sels
.set_extras(jobs.iter().filter(|j| !j.2).map(|j| j.1));
self.set_head(jobs.iter().find(|j| j.2).map(|j| j.1).unwrap_or(primary));
self.normalize_cursors();
self.clamp_cursor();
self.tx_commit();
}
}
fn read_system_clipboard() -> Option<String> {
let providers: [(&str, &[&str]); 4] = [
("wl-paste", &[]),
("xclip", &["-selection", "clipboard", "-o"]),
("xsel", &["--clipboard", "--output"]),
("pbpaste", &[]),
];
for (cmd, args) in providers {
let Ok(out) = std::process::Command::new(cmd).args(args).output() else {
continue; };
if out.status.success() {
return String::from_utf8(out.stdout).ok();
}
}
None
}