aboutsummaryrefslogtreecommitdiff
path: root/helix-core/src
diff options
context:
space:
mode:
Diffstat (limited to 'helix-core/src')
-rw-r--r--helix-core/src/lib.rs1
-rw-r--r--helix-core/src/surround.rs58
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)
+}