diff options
Diffstat (limited to 'helix-core/src')
-rw-r--r-- | helix-core/src/lib.rs | 1 | ||||
-rw-r--r-- | helix-core/src/surround.rs | 58 |
2 files changed, 59 insertions, 0 deletions
diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index f697bc7f..7d51f88c 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -16,6 +16,7 @@ pub mod register; pub mod search; pub mod selection; mod state; +pub mod surround; pub mod syntax; mod transaction; diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs new file mode 100644 index 00000000..a629d2d3 --- /dev/null +++ b/helix-core/src/surround.rs @@ -0,0 +1,58 @@ +use crate::{search, Selection}; +use ropey::RopeSlice; + +pub const PAIRS: &[(char, char)] = &[('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')]; + +/// Given any char in [PAIRS], return the open and closing chars. If not found in +/// [PAIRS] return (ch, ch). +pub fn get_pair(ch: char) -> (char, char) { + PAIRS + .iter() + .find(|(open, close)| *open == ch || *close == ch) + .copied() + .unwrap_or((ch, ch)) +} + +/// Find the position of surround pairs of `ch` which can be either a closing +/// or opening pair. `n` will skip n - 1 pairs (eg. n=2 will discard (only) +/// the first pair found and keep looking) +pub fn find_nth_pairs_pos( + text: RopeSlice, + ch: char, + pos: usize, + n: usize, +) -> Option<(usize, usize)> { + let (open, close) = get_pair(ch); + let open_pos = search::find_nth_prev(text, open, pos, n, true)?; + let close_pos = search::find_nth_next(text, close, pos, n, true)?; + + Some((open_pos, close_pos)) +} + +/// Find position of surround characters around every cursor. Returns None +/// if any positions overlap. Note that the positions are in a flat Vec. +/// Use get_surround_pos().chunks(2) to get matching pairs of surround positions. +/// `ch` can be either closing or opening pair. +pub fn get_surround_pos( + text: RopeSlice, + selection: &Selection, + ch: char, + skip: usize, +) -> Option<Vec<usize>> { + let mut change_pos = Vec::new(); + + for range in selection { + let head = range.head; + + match find_nth_pairs_pos(text, ch, head, skip) { + Some((open_pos, close_pos)) => { + if change_pos.contains(&open_pos) || change_pos.contains(&close_pos) { + return None; + } + change_pos.extend_from_slice(&[open_pos, close_pos]); + } + None => return None, + } + } + Some(change_pos) +} |