diff options
-rw-r--r-- | helix-view/src/lib.rs | 1 | ||||
-rw-r--r-- | helix-view/src/register.rs | 136 |
2 files changed, 137 insertions, 0 deletions
diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index c3f67345..6a68e7d6 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 00000000..ca495ada --- /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<char, Vec<String>>, +} + +impl Registers { + pub fn read<'a>(&'a self, name: char, _editor: &'a Editor) -> Option<RegisterValues<'a>> { + 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<String>) -> 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<Cow<'a, str>> { + self.read(name, editor).and_then(|mut values| values.next()) + } + + pub fn last<'a>(&'a self, name: char, editor: &'a Editor) -> Option<Cow<'a, str>> { + self.read(name, editor).and_then(|values| values.last()) + } + + pub fn iter_preview(&self) -> impl Iterator<Item = (char, &str)> { + self.inner + .iter() + .map(|(name, values)| { + let preview = values + .first() + .and_then(|s| s.lines().next()) + .unwrap_or("<empty>"); + + (*name, preview) + }) + .chain([('_', "<empty>")].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<dyn DoubleEndedExactSizeIterator<Item = Cow<'a, str>> + 'a>, +} + +impl<'a> RegisterValues<'a> { + fn new( + iter: impl DoubleEndedIterator<Item = Cow<'a, str>> + + ExactSizeIterator<Item = Cow<'a, str>> + + 'a, + ) -> Self { + Self { + iter: Box::new(iter), + } + } +} + +impl<'a> Iterator for RegisterValues<'a> { + type Item = Cow<'a, str>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +impl<'a> DoubleEndedIterator for RegisterValues<'a> { + fn next_back(&mut self) -> Option<Self::Item> { + 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<dyn DoubleEndedIterator + ExactSizeIterator>` +// 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<I: DoubleEndedIterator + ExactSizeIterator> DoubleEndedExactSizeIterator for I {} |