@@ -23,6 +23,10 @@ use crate::{
23/// * Primary clipboard (`+`)
23/// * Primary clipboard (`+`)
24#[derive(Debug)]
24#[derive(Debug)]
25pub struct Registers {
25pub struct Registers {
26+ /// The mapping of register to values.
27+ /// Values are stored in reverse order when inserted with `Registers::write`.
28+ /// The order is reversed again in `Registers::read`. This allows us to
29+ /// efficiently prepend new values in `Registers::push`.
26 inner: HashMap<char, Vec<String>>,
30 inner: HashMap<char, Vec<String>>,
27 clipboard_provider: Box<dyn ClipboardProvider>,
31 clipboard_provider: Box<dyn ClipboardProvider>,
@@ -77,11 +81,11 @@ impl Registers {
79 .get(&name)
83 .get(&name)
80− .map(|values| RegisterValues::new(values.iter().map(Cow::from))),
84+ .map(|values| RegisterValues::new(values.iter().map(Cow::from).rev())),
84− pub fn write(&mut self, name: char, values: Vec<String>) -> Result<()> {
88+ pub fn write(&mut self, name: char, mut values: Vec<String>) -> Result<()> {
85 match name {
89 match name {
86 '_' => Ok(()),
90 '_' => Ok(()),
87 '#' | '.' | '%' => Err(anyhow::anyhow!("Register {name} does not support writing")),
91 '#' | '.' | '%' => Err(anyhow::anyhow!("Register {name} does not support writing")),
@@ -94,17 +98,19 @@ impl Registers {
94 _ => unreachable!(),
98 _ => unreachable!(),
97 self.inner.insert(name, values);
102 self.inner.insert(name, values);
101 self.inner.insert(name, values);
107 self.inner.insert(name, values);
107− pub fn push(&mut self, name: char, value: String) -> Result<()> {
113+ pub fn push(&mut self, name: char, mut value: String) -> Result<()> {
108 match name {
114 match name {
109 '_' => Ok(()),
115 '_' => Ok(()),
110 '#' | '.' | '%' => Err(anyhow::anyhow!("Register {name} does not support pushing")),
116 '#' | '.' | '%' => Err(anyhow::anyhow!("Register {name} does not support pushing")),
@@ -121,11 +127,13 @@ impl Registers {
121 anyhow::bail!("Failed to push to register {name}: clipboard does not match register contents");
127 anyhow::bail!("Failed to push to register {name}: clipboard does not match register contents");
124− saved_values.push(value);
130+ saved_values.push(value.clone());
131+ if !contents.is_empty() {
132+ value.push_str(NATIVE_LINE_ENDING.as_str());
134+ value.push_str(&contents);
125− self.clipboard_provider.set_contents(
135+ self.clipboard_provider
126− saved_values.join(NATIVE_LINE_ENDING.as_str()),
127− clipboard_type,
136+ .set_contents(value, clipboard_type)?;
@@ -150,7 +158,7 @@ impl Registers {
150 .filter(|(name, _)| !matches!(name, '*' | '+'))
158 .filter(|(name, _)| !matches!(name, '*' | '+'))
151 .map(|(name, values)| {
159 .map(|(name, values)| {
152 let preview = values
160 let preview = values
153− .first()
161+ .last()
154 .and_then(|s| s.lines().next())
162 .and_then(|s| s.lines().next())
155 .unwrap_or("<empty>");
163 .unwrap_or("<empty>");
@@ -222,7 +230,7 @@ fn read_from_clipboard<'a>(
222 let Some(values) = saved_values else { return RegisterValues::new(iter::once(contents.into())) };
230 let Some(values) = saved_values else { return RegisterValues::new(iter::once(contents.into())) };
224 if contents_are_saved(values, &contents) {
232 if contents_are_saved(values, &contents) {
225− RegisterValues::new(values.iter().map(Cow::from))
233+ RegisterValues::new(values.iter().map(Cow::from).rev())
227 RegisterValues::new(iter::once(contents.into()))
235 RegisterValues::new(iter::once(contents.into()))
@@ -243,7 +251,7 @@ fn read_from_clipboard<'a>(
244fn contents_are_saved(saved_values: &[String], mut contents: &str) -> bool {
252fn contents_are_saved(saved_values: &[String], mut contents: &str) -> bool {
245 let line_ending = NATIVE_LINE_ENDING.as_str();
253 let line_ending = NATIVE_LINE_ENDING.as_str();
246− let mut values = saved_values.iter();
254+ let mut values = saved_values.iter().rev();
248 match values.next() {
256 match values.next() {
249 Some(first) if contents.starts_with(first) => {
257 Some(first) if contents.starts_with(first) => {