From 5eb1a25d8a0aeb202358b5b5a3b44bb5f43eced6 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 10 Jul 2023 16:30:42 -0500 Subject: [PATCH] Refactor Registers to take Editor This sets up a new Registers type that will allow us to expand support for special registers. (See the child commits.) We start simple with the regular (`Vec`) registers and the simplest special register, the black hole. In the child commits we will expand these match arms with more special registers. The upcoming special registers will need a few things that aren't possible with the current Registers type in helix-core: * Access to the `Editor`. This is only necessary when reading from registers, so the `&Editor` parameter is only added to `Registers::read`. * Returning owned values. Registers in helix-core returns references to the values backed by the `Vec` but future special registers will need to return owned values. We refactor the return value of the read operations to give `Cow`s and iterators over those. * Returning a `Result` for write/push functions. This will be used by the clipboard special registers. --- helix-view/src/lib.rs | 1 + helix-view/src/register.rs | 136 +++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 helix-view/src/register.rs diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index c3f67345b..6a68e7d6f 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -15,6 +15,7 @@ pub mod base64; pub mod info; pub mod input; pub mod keyboard; +pub mod register; pub mod theme; pub mod tree; pub mod view; diff --git a/helix-view/src/register.rs b/helix-view/src/register.rs new file mode 100644 index 000000000..ca495ada4 --- /dev/null +++ b/helix-view/src/register.rs @@ -0,0 +1,136 @@ +use std::{borrow::Cow, collections::HashMap, iter}; + +use anyhow::Result; + +use crate::Editor; + +/// A key-value store for saving sets of values. +/// +/// Each register corresponds to a `char`. Most chars can be used to store any set of +/// values but a few chars are "special registers". Special registers have unique +/// behaviors when read or written to: +/// +/// * Black hole (`_`): all values read and written are discarded +#[derive(Debug, Default)] +pub struct Registers { + inner: HashMap>, +} + +impl Registers { + pub fn read<'a>(&'a self, name: char, _editor: &'a Editor) -> Option> { + match name { + '_' => Some(RegisterValues::new(iter::empty())), + _ => self + .inner + .get(&name) + .map(|values| RegisterValues::new(values.iter().map(Cow::from))), + } + } + + pub fn write(&mut self, name: char, values: Vec) -> Result<()> { + match name { + '_' => Ok(()), + _ => { + self.inner.insert(name, values); + Ok(()) + } + } + } + + pub fn push(&mut self, name: char, value: String) -> Result<()> { + match name { + '_' => Ok(()), + _ => { + self.inner.entry(name).or_insert_with(Vec::new).push(value); + Ok(()) + } + } + } + + pub fn first<'a>(&'a self, name: char, editor: &'a Editor) -> Option> { + self.read(name, editor).and_then(|mut values| values.next()) + } + + pub fn last<'a>(&'a self, name: char, editor: &'a Editor) -> Option> { + self.read(name, editor).and_then(|values| values.last()) + } + + pub fn iter_preview(&self) -> impl Iterator { + self.inner + .iter() + .map(|(name, values)| { + let preview = values + .first() + .and_then(|s| s.lines().next()) + .unwrap_or(""); + + (*name, preview) + }) + .chain([('_', "")].iter().copied()) + } + + pub fn clear(&mut self) { + self.inner.clear() + } + + pub fn remove(&mut self, name: char) -> bool { + match name { + '_' => false, + _ => self.inner.remove(&name).is_some(), + } + } +} + +// This is a wrapper of an iterator that is both double ended and exact size, +// and can return either owned or borrowed values. Regular registers can +// return borrowed values while some special registers need to return owned +// values. +pub struct RegisterValues<'a> { + iter: Box> + 'a>, +} + +impl<'a> RegisterValues<'a> { + fn new( + iter: impl DoubleEndedIterator> + + ExactSizeIterator> + + 'a, + ) -> Self { + Self { + iter: Box::new(iter), + } + } +} + +impl<'a> Iterator for RegisterValues<'a> { + type Item = Cow<'a, str>; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> DoubleEndedIterator for RegisterValues<'a> { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +impl<'a> ExactSizeIterator for RegisterValues<'a> { + fn len(&self) -> usize { + self.iter.len() + } +} + +// Each RegisterValues iterator is both double ended and exact size. We can't +// type RegisterValues as `Box` +// because only one non-auto trait is allowed in trait objects. So we need to +// create a new trait that covers both. `RegisterValues` wraps that type so that +// trait only needs to live in this module and not be imported for all register +// callsites. +trait DoubleEndedExactSizeIterator: DoubleEndedIterator + ExactSizeIterator {} + +impl DoubleEndedExactSizeIterator for I {}