mirror of https://github.com/helix-editor/helix
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.pull/7793/head
parent
57952c46a4
commit
5eb1a25d8a
@ -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 {}
|
Loading…
Reference in New Issue