on master help sign in
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<String>`) 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<String>` but future special registers will need to return owned values. We refactor the return value of the read operations to give `Cow<str>`s and iterators over those. * Returning a `Result` for write/push functions. This will be used by the clipboard special registers.

commit5eb1a25d8a0aeb202358b5b5a3b44bb5f43eced6 authorMichael Davis <mcarsondavis@gmail.com> dateMon 10 Jul 2023, 16:30 (3 years ago) parent57952c4
2 files changed+137
helix-view/src/lib.rs+1 helix-view/src/register.rs+136
helix-view/src/lib.rs+1
@@ -15,6 +15,7 @@ pub mod base64;
1515pub mod info;
1616pub mod input;
1717pub mod keyboard;
18+pub mod register;
1819pub mod theme;
1920pub mod tree;
2021pub mod view;
helix-view/src/register.rsnew file+136
@@ -0,0 +1,136 @@
1+use std::{borrow::Cow, collections::HashMap, iter};
2+
3+use anyhow::Result;
4+
5+use crate::Editor;
6+
7+/// A key-value store for saving sets of values.
8+///
9+/// Each register corresponds to a `char`. Most chars can be used to store any set of
10+/// values but a few chars are "special registers". Special registers have unique
11+/// behaviors when read or written to:
12+///
13+/// * Black hole (`_`): all values read and written are discarded
14+#[derive(Debug, Default)]
15+pub struct Registers {
16+ inner: HashMap<char, Vec<String>>,
17+}
18+
19+impl Registers {
20+ pub fn read<'a>(&'a self, name: char, _editor: &'a Editor) -> Option<RegisterValues<'a>> {
21+ match name {
22+ '_' => Some(RegisterValues::new(iter::empty())),
23+ _ => self
24+ .inner
25+ .get(&name)
26+ .map(|values| RegisterValues::new(values.iter().map(Cow::from))),
27+ }
28+ }
29+
30+ pub fn write(&mut self, name: char, values: Vec<String>) -> Result<()> {
31+ match name {
32+ '_' => Ok(()),
33+ _ => {
34+ self.inner.insert(name, values);
35+ Ok(())
36+ }
37+ }
38+ }
39+
40+ pub fn push(&mut self, name: char, value: String) -> Result<()> {
41+ match name {
42+ '_' => Ok(()),
43+ _ => {
44+ self.inner.entry(name).or_insert_with(Vec::new).push(value);
45+ Ok(())
46+ }
47+ }
48+ }
49+
50+ pub fn first<'a>(&'a self, name: char, editor: &'a Editor) -> Option<Cow<'a, str>> {
51+ self.read(name, editor).and_then(|mut values| values.next())
52+ }
53+
54+ pub fn last<'a>(&'a self, name: char, editor: &'a Editor) -> Option<Cow<'a, str>> {
55+ self.read(name, editor).and_then(|values| values.last())
56+ }
57+
58+ pub fn iter_preview(&self) -> impl Iterator<Item = (char, &str)> {
59+ self.inner
60+ .iter()
61+ .map(|(name, values)| {
62+ let preview = values
63+ .first()
64+ .and_then(|s| s.lines().next())
65+ .unwrap_or("<empty>");
66+
67+ (*name, preview)
68+ })
69+ .chain([('_', "<empty>")].iter().copied())
70+ }
71+
72+ pub fn clear(&mut self) {
73+ self.inner.clear()
74+ }
75+
76+ pub fn remove(&mut self, name: char) -> bool {
77+ match name {
78+ '_' => false,
79+ _ => self.inner.remove(&name).is_some(),
80+ }
81+ }
82+}
83+
84+// This is a wrapper of an iterator that is both double ended and exact size,
85+// and can return either owned or borrowed values. Regular registers can
86+// return borrowed values while some special registers need to return owned
87+// values.
88+pub struct RegisterValues<'a> {
89+ iter: Box<dyn DoubleEndedExactSizeIterator<Item = Cow<'a, str>> + 'a>,
90+}
91+
92+impl<'a> RegisterValues<'a> {
93+ fn new(
94+ iter: impl DoubleEndedIterator<Item = Cow<'a, str>>
95+ + ExactSizeIterator<Item = Cow<'a, str>>
96+ + 'a,
97+ ) -> Self {
98+ Self {
99+ iter: Box::new(iter),
100+ }
101+ }
102+}
103+
104+impl<'a> Iterator for RegisterValues<'a> {
105+ type Item = Cow<'a, str>;
106+
107+ fn next(&mut self) -> Option<Self::Item> {
108+ self.iter.next()
109+ }
110+
111+ fn size_hint(&self) -> (usize, Option<usize>) {
112+ self.iter.size_hint()
113+ }
114+}
115+
116+impl<'a> DoubleEndedIterator for RegisterValues<'a> {
117+ fn next_back(&mut self) -> Option<Self::Item> {
118+ self.iter.next_back()
119+ }
120+}
121+
122+impl<'a> ExactSizeIterator for RegisterValues<'a> {
123+ fn len(&self) -> usize {
124+ self.iter.len()
125+ }
126+}
127+
128+// Each RegisterValues iterator is both double ended and exact size. We can't
129+// type RegisterValues as `Box<dyn DoubleEndedIterator + ExactSizeIterator>`
130+// because only one non-auto trait is allowed in trait objects. So we need to
131+// create a new trait that covers both. `RegisterValues` wraps that type so that
132+// trait only needs to live in this module and not be imported for all register
133+// callsites.
134+trait DoubleEndedExactSizeIterator: DoubleEndedIterator + ExactSizeIterator {}
135+
136+impl<I: DoubleEndedIterator + ExactSizeIterator> DoubleEndedExactSizeIterator for I {}