aboutsummaryrefslogtreecommitdiff
path: root/helix-core
diff options
context:
space:
mode:
authorGokul Soumya2022-04-03 16:01:43 +0000
committerBlaž Hrastnik2022-04-29 06:51:14 +0000
commitde15d7017186cb735cdd31d12510f61a1707d5fb (patch)
treebd48c177f1ed50fdcaaa2cd57c8b993bd71f7bd7 /helix-core
parentc22873c33fa0163257b1421faa6f91268ad3a4d1 (diff)
Add `m` textobject to select closest surround pair
Diffstat (limited to 'helix-core')
-rw-r--r--helix-core/src/surround.rs39
-rw-r--r--helix-core/src/textobject.rs26
2 files changed, 64 insertions, 1 deletions
diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs
index c14456b7..4cba81bf 100644
--- a/helix-core/src/surround.rs
+++ b/helix-core/src/surround.rs
@@ -52,6 +52,45 @@ pub fn get_pair(ch: char) -> (char, char) {
.unwrap_or((ch, ch))
}
+pub fn find_nth_closest_pairs_pos(
+ text: RopeSlice,
+ range: Range,
+ n: usize,
+) -> Result<(usize, usize)> {
+ let is_open_pair = |ch| PAIRS.iter().any(|(open, _)| *open == ch);
+ let is_close_pair = |ch| PAIRS.iter().any(|(_, close)| *close == ch);
+
+ let mut stack = Vec::with_capacity(2);
+ let pos = range.cursor(text);
+
+ for ch in text.chars_at(pos) {
+ if is_open_pair(ch) {
+ // Track open pairs encountered so that we can step over
+ // the correspoding close pairs that will come up further
+ // down the loop. We want to find a lone close pair whose
+ // open pair is before the cursor position.
+ stack.push(ch);
+ continue;
+ } else if is_close_pair(ch) {
+ let (open, _) = get_pair(ch);
+ if stack.last() == Some(&open) {
+ stack.pop();
+ continue;
+ } else {
+ // In the ideal case the stack would be empty here and the
+ // current character would be the close pair that we are
+ // looking for. It could also be the case that the pairs
+ // are unbalanced and we encounter a close pair that doesn't
+ // close the last seen open pair. In either case use this
+ // char as the auto-detected closest pair.
+ return find_nth_pairs_pos(text, ch, range, n);
+ }
+ }
+ }
+
+ Err(Error::PairNotFound)
+}
+
/// 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)
diff --git a/helix-core/src/textobject.rs b/helix-core/src/textobject.rs
index ab418792..ee06bf47 100644
--- a/helix-core/src/textobject.rs
+++ b/helix-core/src/textobject.rs
@@ -205,7 +205,31 @@ pub fn textobject_surround(
ch: char,
count: usize,
) -> Range {
- surround::find_nth_pairs_pos(slice, ch, range, count)
+ textobject_surround_impl(slice, range, textobject, Some(ch), count)
+}
+
+pub fn textobject_surround_closest(
+ slice: RopeSlice,
+ range: Range,
+ textobject: TextObject,
+ count: usize,
+) -> Range {
+ textobject_surround_impl(slice, range, textobject, None, count)
+}
+
+fn textobject_surround_impl(
+ slice: RopeSlice,
+ range: Range,
+ textobject: TextObject,
+ ch: Option<char>,
+ count: usize,
+) -> Range {
+ let pair_pos = match ch {
+ Some(ch) => surround::find_nth_pairs_pos(slice, ch, range, count),
+ // Automatically find the closest surround pairs
+ None => surround::find_nth_closest_pairs_pos(slice, range, count),
+ };
+ pair_pos
.map(|(anchor, head)| match textobject {
TextObject::Inside => Range::new(next_grapheme_boundary(slice, anchor), head),
TextObject::Around => Range::new(anchor, next_grapheme_boundary(slice, head)),