diff options
author | Michael Davis | 2023-07-10 21:43:16 +0000 |
---|---|---|
committer | Blaž Hrastnik | 2023-07-31 06:05:38 +0000 |
commit | da2afe7353066743e30592f03a7a55f24df4dd5c (patch) | |
tree | ad8bd23402b1e9675e8eb4dd3a0fdf591883c8ba | |
parent | 5eb1a25d8a0aeb202358b5b5a3b44bb5f43eced6 (diff) |
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.
-rw-r--r-- | helix-core/src/selection.rs | 12 | ||||
-rw-r--r-- | helix-view/src/register.rs | 32 |
2 files changed, 39 insertions, 5 deletions
diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 9104c209..c44685ee 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -630,11 +630,19 @@ impl Selection { 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)) } - 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)) } diff --git a/helix-view/src/register.rs b/helix-view/src/register.rs index ca495ada..892d396f 100644 --- a/helix-view/src/register.rs +++ b/helix-view/src/register.rs @@ -11,15 +11,31 @@ use crate::Editor; /// behaviors when read or written to: /// /// * Black hole (`_`): all values read and written are discarded +/// * Selection indices (`#`): index number of each selection starting at 1 +/// * Selection contents (`.`) #[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>> { + pub fn read<'a>(&'a self, name: char, editor: &'a Editor) -> Option<RegisterValues<'a>> { match name { '_' => 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 .inner .get(&name) @@ -30,6 +46,7 @@ impl Registers { pub fn write(&mut self, name: char, values: Vec<String>) -> Result<()> { match name { '_' => Ok(()), + '#' | '.' => Err(anyhow::anyhow!("Register {name} does not support writing")), _ => { self.inner.insert(name, values); Ok(()) @@ -40,6 +57,7 @@ impl Registers { pub fn push(&mut self, name: char, value: String) -> Result<()> { match name { '_' => Ok(()), + '#' | '.' => Err(anyhow::anyhow!("Register {name} does not support pushing")), _ => { self.inner.entry(name).or_insert_with(Vec::new).push(value); Ok(()) @@ -66,7 +84,15 @@ impl Registers { (*name, preview) }) - .chain([('_', "<empty>")].iter().copied()) + .chain( + [ + ('_', "<empty>"), + ('#', "<selection indices>"), + ('.', "<selection contents>"), + ] + .iter() + .copied(), + ) } pub fn clear(&mut self) { @@ -75,7 +101,7 @@ impl Registers { pub fn remove(&mut self, name: char) -> bool { match name { - '_' => false, + '_' | '#' | '.' => false, _ => self.inner.remove(&name).is_some(), } } |