Add '#' and '.' special registers

These come from Kakoune:

* '#' is the selection index register. It's read-only and produces the
  selection index numbers, 1-indexed.
* '.' is the selection contents register. It is also read-only and
  mirrors the contents of the current selections when read.

We switch the iterators returned from Selection's `fragments` and
`slices` methods to ExactSizeIterators because:

* The selection contents register can simply return the fragments
  iterator.
* ExactSizeIterator is already implemented for iterators over Vecs, so
  it's essentially free.
* The `len` method can be useful on its own.
pull/7793/head
Michael Davis 1 year ago committed by Blaž Hrastnik
parent 5eb1a25d8a
commit da2afe7353

@ -630,11 +630,19 @@ impl Selection {
self.transform(|range| Range::point(range.cursor(text))) self.transform(|range| Range::point(range.cursor(text)))
} }
pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a { pub fn fragments<'a>(
&'a self,
text: RopeSlice<'a>,
) -> impl DoubleEndedIterator<Item = Cow<'a, str>> + ExactSizeIterator<Item = Cow<str>> + 'a
{
self.ranges.iter().map(move |range| range.fragment(text)) self.ranges.iter().map(move |range| range.fragment(text))
} }
pub fn slices<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = RopeSlice> + 'a { pub fn slices<'a>(
&'a self,
text: RopeSlice<'a>,
) -> impl DoubleEndedIterator<Item = RopeSlice<'a>> + ExactSizeIterator<Item = RopeSlice<'a>> + 'a
{
self.ranges.iter().map(move |range| range.slice(text)) self.ranges.iter().map(move |range| range.slice(text))
} }

@ -11,15 +11,31 @@ use crate::Editor;
/// behaviors when read or written to: /// behaviors when read or written to:
/// ///
/// * Black hole (`_`): all values read and written are discarded /// * Black hole (`_`): all values read and written are discarded
/// * Selection indices (`#`): index number of each selection starting at 1
/// * Selection contents (`.`)
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Registers { pub struct Registers {
inner: HashMap<char, Vec<String>>, inner: HashMap<char, Vec<String>>,
} }
impl Registers { impl Registers {
pub fn read<'a>(&'a self, name: char, _editor: &'a Editor) -> Option<RegisterValues<'a>> { pub fn read<'a>(&'a self, name: char, editor: &'a Editor) -> Option<RegisterValues<'a>> {
match name { match name {
'_' => Some(RegisterValues::new(iter::empty())), '_' => Some(RegisterValues::new(iter::empty())),
'#' => {
let (view, doc) = current_ref!(editor);
let selections = doc.selection(view.id).len();
// ExactSizeIterator is implemented for Range<usize> but
// not RangeInclusive<usize>.
Some(RegisterValues::new(
(0..selections).map(|i| (i + 1).to_string().into()),
))
}
'.' => {
let (view, doc) = current_ref!(editor);
let text = doc.text().slice(..);
Some(RegisterValues::new(doc.selection(view.id).fragments(text)))
}
_ => self _ => self
.inner .inner
.get(&name) .get(&name)
@ -30,6 +46,7 @@ impl Registers {
pub fn write(&mut self, name: char, values: Vec<String>) -> Result<()> { pub fn write(&mut self, name: char, values: Vec<String>) -> Result<()> {
match name { match name {
'_' => Ok(()), '_' => Ok(()),
'#' | '.' => Err(anyhow::anyhow!("Register {name} does not support writing")),
_ => { _ => {
self.inner.insert(name, values); self.inner.insert(name, values);
Ok(()) Ok(())
@ -40,6 +57,7 @@ impl Registers {
pub fn push(&mut self, name: char, value: String) -> Result<()> { pub fn push(&mut self, name: char, value: String) -> Result<()> {
match name { match name {
'_' => Ok(()), '_' => Ok(()),
'#' | '.' => Err(anyhow::anyhow!("Register {name} does not support pushing")),
_ => { _ => {
self.inner.entry(name).or_insert_with(Vec::new).push(value); self.inner.entry(name).or_insert_with(Vec::new).push(value);
Ok(()) Ok(())
@ -66,7 +84,15 @@ impl Registers {
(*name, preview) (*name, preview)
}) })
.chain([('_', "<empty>")].iter().copied()) .chain(
[
('_', "<empty>"),
('#', "<selection indices>"),
('.', "<selection contents>"),
]
.iter()
.copied(),
)
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
@ -75,7 +101,7 @@ impl Registers {
pub fn remove(&mut self, name: char) -> bool { pub fn remove(&mut self, name: char) -> bool {
match name { match name {
'_' => false, '_' | '#' | '.' => false,
_ => self.inner.remove(&name).is_some(), _ => self.inner.remove(&name).is_some(),
} }
} }

Loading…
Cancel
Save