diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 234902886..a8f6543f8 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2125,7 +2125,7 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir .unwrap_or(cx.editor.registers.last_search_register); let config = cx.editor.config(); let scrolloff = config.scrolloff; - if let Some(query) = cx.editor.registers.first(register, cx.editor) { + if let Some(query) = cx.editor.registers.latest(register, cx.editor) { let search_config = &config.search; let case_insensitive = if search_config.smart_case { !query.chars().any(char::is_uppercase) @@ -2205,7 +2205,7 @@ fn make_search_word_bounded(cx: &mut Context) { let register = cx .register .unwrap_or(cx.editor.registers.last_search_register); - let regex = match cx.editor.registers.first(register, cx.editor) { + let regex = match cx.editor.registers.latest(register, cx.editor) { Some(regex) => regex, None => return, }; diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 3518ddf77..ba0dd21d6 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -133,7 +133,7 @@ impl Prompt { editor: &'a Editor, ) -> Option> { self.history_register - .and_then(|reg| editor.registers.first(reg, editor)) + .and_then(|reg| editor.registers.latest(reg, editor)) } pub fn recalculate_completion(&mut self, editor: &Editor) { @@ -656,7 +656,7 @@ impl Component for Prompt { &context .editor .registers - .first(c, context.editor) + .latest(c, context.editor) .unwrap_or_default(), context.editor, ); diff --git a/helix-view/src/register.rs b/helix-view/src/register.rs index 050a4e4ce..edad8b1e2 100644 --- a/helix-view/src/register.rs +++ b/helix-view/src/register.rs @@ -216,14 +216,59 @@ impl Registers { } } - pub fn first<'a>(&'a self, name: char, editor: &'a Editor) -> Option> { - self.read(name, editor).and_then(|mut values| values.next()) + /// "Selects" the index at the given index for the given register. + /// + /// Selecting an item pulls it to the front of the register's history. + /// + /// If the register is a special register other than a clipboard register ('+' or '*') + /// or if the index is out of bounds for the given register, this command is a no-op. + pub fn select_history_entry(&mut self, name: char, index: usize) -> Result<()> { + match name { + '_' | '#' | '.' | '%' => { + Err(anyhow::anyhow!("Register {name} does not support writing")) + } + _ => { + let Some(register) = self.inner.get_mut(&name) else { + return Ok(()); + }; + register.select_history_entry(index); + self.sync_clipboard_register(name) + } + } } - pub fn last<'a>(&'a self, name: char, editor: &'a Editor) -> Option> { + fn sync_clipboard_register(&mut self, name: char) -> Result<()> { + let clipboard_type = match name { + '+' => ClipboardType::Clipboard, + '*' => ClipboardType::Selection, + _ => return Ok(()), + }; + + let mut contents = String::new(); + for val in self.inner[&name].values() { + if !contents.is_empty() { + contents.push_str(NATIVE_LINE_ENDING.as_str()); + } + contents.push_str(&val); + } + self.clipboard_provider + .set_contents(contents, clipboard_type) + } + + /// Returns the latest value in the given register. + /// + /// The latest value is the value most recently pushed to the register when + /// using `push`, or the last value returned by the iterator passed to [write]. + pub fn latest<'a>(&'a self, name: char, editor: &'a Editor) -> Option> { self.read(name, editor).and_then(|values| values.last()) } + /// Returns the oldest value in the given register. + /// This is the opposite of `latest`. + pub fn oldest<'a>(&'a self, name: char, editor: &'a Editor) -> Option> { + self.read(name, editor).and_then(|mut values| values.next()) + } + pub fn iter_preview(&self) -> impl Iterator { self.inner .iter()