aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--helix-view/src/lib.rs1
-rw-r--r--helix-view/src/register.rs136
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 {}