Skip to content

Commit 66f0f38

Browse files
committed
Add fallback command to [ and ] to find in next pair
1 parent 3e67431 commit 66f0f38

File tree

4 files changed

+150
-24
lines changed

4 files changed

+150
-24
lines changed

helix-core/src/surround.rs

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,20 @@ fn find_nth_closest_pairs_plain(
154154
Err(Error::PairNotFound)
155155
}
156156

157+
pub enum FindType {
158+
Surround(usize),
159+
Next(usize),
160+
Prev(usize),
161+
}
162+
157163
/// Find the position of surround pairs of `ch` which can be either a closing
158164
/// or opening pair. `n` will skip n - 1 pairs (eg. n=2 will discard (only)
159165
/// the first pair found and keep looking)
160166
pub fn find_nth_pairs_pos(
161167
text: RopeSlice,
162168
ch: char,
163169
range: Range,
164-
n: usize,
170+
find_type: FindType,
165171
) -> Result<(usize, usize)> {
166172
if text.len_chars() < 2 {
167173
return Err(Error::PairNotFound);
@@ -172,6 +178,17 @@ pub fn find_nth_pairs_pos(
172178

173179
let (open, close) = get_pair(ch);
174180
let pos = range.cursor(text);
181+
let (pos, n) = match find_type {
182+
FindType::Surround(n) => (pos, n),
183+
FindType::Next(n) => match search::find_nth_next(text, open, pos, n) {
184+
Some(next_pos) => (next_pos + 1, 1),
185+
None => return Err(Error::PairNotFound),
186+
},
187+
FindType::Prev(n) => match search::find_nth_prev(text, close, pos, n) {
188+
Some(next_pos) => (next_pos - 1, 1),
189+
None => return Err(Error::PairNotFound),
190+
},
191+
};
175192

176193
let (open, close) = if open == close {
177194
if Some(open) == text.get_char(pos) {
@@ -298,7 +315,7 @@ pub fn get_surround_pos(
298315
for &range in selection {
299316
let (open_pos, close_pos) = {
300317
let range_raw = match ch {
301-
Some(ch) => find_nth_pairs_pos(text, ch, range, skip)?,
318+
Some(ch) => find_nth_pairs_pos(text, ch, range, FindType::Surround(skip))?,
302319
None => find_nth_closest_pairs_pos(syntax, text, range, skip)?,
303320
};
304321
let range = Range::new(range_raw.0, range_raw.1);
@@ -392,8 +409,13 @@ mod test {
392409

393410
assert_eq!(2, expectations.len());
394411
assert_eq!(
395-
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1)
396-
.expect("find should succeed"),
412+
find_nth_pairs_pos(
413+
doc.slice(..),
414+
'\'',
415+
selection.primary(),
416+
FindType::Surround(1)
417+
)
418+
.expect("find should succeed"),
397419
(expectations[0], expectations[1])
398420
)
399421
}
@@ -409,7 +431,46 @@ mod test {
409431

410432
assert_eq!(2, expectations.len());
411433
assert_eq!(
412-
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 2)
434+
find_nth_pairs_pos(
435+
doc.slice(..),
436+
'\'',
437+
selection.primary(),
438+
FindType::Surround(2)
439+
)
440+
.expect("find should succeed"),
441+
(expectations[0], expectations[1])
442+
)
443+
}
444+
445+
#[test]
446+
fn test_find_inside_third_next_quote() {
447+
#[rustfmt::skip]
448+
let (doc, selection, expectations) =
449+
rope_with_selections_and_expectations(
450+
"some 'nested 'quoted' text' on this 'line'\n'and this one'",
451+
" ^ _ _ \n "
452+
);
453+
454+
assert_eq!(2, expectations.len());
455+
assert_eq!(
456+
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), FindType::Next(3))
457+
.expect("find should succeed"),
458+
(expectations[0], expectations[1])
459+
)
460+
}
461+
462+
#[test]
463+
fn test_find_inside_prev_quote() {
464+
#[rustfmt::skip]
465+
let (doc, selection, expectations) =
466+
rope_with_selections_and_expectations(
467+
"some 'nested 'quoted' text' on this 'line'\n'and this one'",
468+
" _ _ ^ \n "
469+
);
470+
471+
assert_eq!(2, expectations.len());
472+
assert_eq!(
473+
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), FindType::Prev(1))
413474
.expect("find should succeed"),
414475
(expectations[0], expectations[1])
415476
)
@@ -425,7 +486,12 @@ mod test {
425486
);
426487

427488
assert_eq!(
428-
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1),
489+
find_nth_pairs_pos(
490+
doc.slice(..),
491+
'\'',
492+
selection.primary(),
493+
FindType::Surround(1)
494+
),
429495
Err(Error::CursorOnAmbiguousPair)
430496
)
431497
}

helix-core/src/textobject.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::chars::{categorize_char, char_is_whitespace, CharCategory};
77
use crate::graphemes::{next_grapheme_boundary, prev_grapheme_boundary};
88
use crate::line_ending::rope_is_line_ending;
99
use crate::movement::Direction;
10+
use crate::surround::FindType;
1011
use crate::syntax::LanguageConfiguration;
1112
use crate::Range;
1213
use crate::{surround, Syntax};
@@ -204,9 +205,15 @@ pub fn textobject_pair_surround(
204205
range: Range,
205206
textobject: TextObject,
206207
ch: char,
207-
count: usize,
208+
find_type: FindType,
208209
) -> Range {
209-
textobject_pair_surround_impl(syntax, slice, range, textobject, Some(ch), count)
210+
textobject_pair_surround_impl(
211+
syntax,
212+
slice,
213+
range,
214+
textobject,
215+
FindVariant::Char((ch, find_type)),
216+
)
210217
}
211218

212219
pub fn textobject_pair_surround_closest(
@@ -216,20 +223,34 @@ pub fn textobject_pair_surround_closest(
216223
textobject: TextObject,
217224
count: usize,
218225
) -> Range {
219-
textobject_pair_surround_impl(syntax, slice, range, textobject, None, count)
226+
textobject_pair_surround_impl(
227+
syntax,
228+
slice,
229+
range,
230+
textobject,
231+
FindVariant::Closest(count),
232+
)
233+
}
234+
235+
enum FindVariant {
236+
Char((char, FindType)),
237+
Closest(usize),
220238
}
221239

222240
fn textobject_pair_surround_impl(
223241
syntax: Option<&Syntax>,
224242
slice: RopeSlice,
225243
range: Range,
226244
textobject: TextObject,
227-
ch: Option<char>,
228-
count: usize,
245+
find_variant: FindVariant,
229246
) -> Range {
230-
let pair_pos = match ch {
231-
Some(ch) => surround::find_nth_pairs_pos(slice, ch, range, count),
232-
None => surround::find_nth_closest_pairs_pos(syntax, slice, range, count),
247+
let pair_pos = match find_variant {
248+
FindVariant::Char((ch, find_type)) => {
249+
surround::find_nth_pairs_pos(slice, ch, range, find_type)
250+
}
251+
FindVariant::Closest(count) => {
252+
surround::find_nth_closest_pairs_pos(syntax, slice, range, count)
253+
}
233254
};
234255
pair_pos
235256
.map(|(anchor, head)| match textobject {
@@ -576,8 +597,14 @@ mod test {
576597
let slice = doc.slice(..);
577598
for &case in scenario {
578599
let (pos, objtype, expected_range, ch, count) = case;
579-
let result =
580-
textobject_pair_surround(None, slice, Range::point(pos), objtype, ch, count);
600+
let result = textobject_pair_surround(
601+
None,
602+
slice,
603+
Range::point(pos),
604+
objtype,
605+
ch,
606+
FindType::Surround(count),
607+
);
581608
assert_eq!(
582609
result,
583610
expected_range.into(),

helix-term/src/commands.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ use helix_core::{
3030
object, pos_at_coords,
3131
regex::{self, Regex},
3232
search::{self, CharMatcher},
33-
selection, shellwords, surround,
33+
selection, shellwords,
34+
surround::{self, FindType},
3435
syntax::{BlockCommentToken, LanguageServerFeature},
3536
text_annotations::{Overlay, TextAnnotations},
3637
textobject,
@@ -726,8 +727,10 @@ impl FallbackCommand {
726727

727728
#[rustfmt::skip]
728729
static_fallback_commands!(
729-
select_textobject_inside_surrounding_pair, "Select inside any character acting as a pair (tree-sitter)",
730-
select_textobject_around_surrounding_pair, "Select around any character acting as a pair (tree-sitter)",
730+
select_textobject_inside_surrounding_pair, "Select inside any character pair (tree-sitter)",
731+
select_textobject_around_surrounding_pair, "Select around any character pair (tree-sitter)",
732+
select_textobject_inside_prev_pair, "Select inside previous character pair (tree-sitter)",
733+
select_textobject_inside_next_pair, "Select inside next character pair (tree-sitter)",
731734
);
732735
}
733736

@@ -5803,17 +5806,36 @@ fn textobject_change(cx: &mut Context) {
58035806
}
58045807

58055808
fn select_textobject_inside_surrounding_pair(cx: &mut Context, ch: char) {
5806-
textobject_surrounding_pair(cx, textobject::TextObject::Inside, ch);
5809+
textobject_surrounding_pair(cx, textobject::TextObject::Inside, ch, None);
58075810
}
58085811

58095812
fn select_textobject_around_surrounding_pair(cx: &mut Context, ch: char) {
5810-
textobject_surrounding_pair(cx, textobject::TextObject::Around, ch);
5813+
textobject_surrounding_pair(cx, textobject::TextObject::Around, ch, None);
5814+
}
5815+
5816+
fn select_textobject_inside_prev_pair(cx: &mut Context, ch: char) {
5817+
textobject_surrounding_pair(
5818+
cx,
5819+
textobject::TextObject::Inside,
5820+
ch,
5821+
Some(Direction::Backward),
5822+
);
5823+
}
5824+
5825+
fn select_textobject_inside_next_pair(cx: &mut Context, ch: char) {
5826+
textobject_surrounding_pair(
5827+
cx,
5828+
textobject::TextObject::Inside,
5829+
ch,
5830+
Some(Direction::Forward),
5831+
);
58115832
}
58125833

58135834
fn textobject_surrounding_pair(
58145835
cx: &mut Context,
58155836
textobject: textobject::TextObject,
58165837
pair_char: char,
5838+
direction: Option<Direction>,
58175839
) {
58185840
if pair_char.is_ascii_alphanumeric() {
58195841
return;
@@ -5825,7 +5847,18 @@ fn textobject_surrounding_pair(
58255847
let text = doc.text().slice(..);
58265848
let syntax = doc.syntax();
58275849
let selection = doc.selection(view.id).clone().transform(|range| {
5828-
textobject::textobject_pair_surround(syntax, text, range, textobject, pair_char, count)
5850+
let find_type = match direction {
5851+
None => FindType::Surround,
5852+
Some(Direction::Forward) => FindType::Next,
5853+
Some(Direction::Backward) => FindType::Prev,
5854+
}(count);
5855+
let mut range = textobject::textobject_pair_surround(
5856+
syntax, text, range, textobject, pair_char, find_type,
5857+
);
5858+
if let Some(direction) = direction {
5859+
range = range.with_direction(direction);
5860+
}
5861+
range
58295862
});
58305863
doc.set_selection(view.id, selection);
58315864
};

helix-term/src/keymap/default.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
131131
"g" => select_textobject_around_change,
132132
},
133133
},
134-
"[" => { "Left bracket"
134+
"[" => { "Left bracket" fallback=select_textobject_inside_prev_pair
135135
"d" => goto_prev_diag,
136136
"D" => goto_first_diag,
137137
"g" => goto_prev_change,
@@ -145,7 +145,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
145145
"p" => goto_prev_paragraph,
146146
"space" => add_newline_above,
147147
},
148-
"]" => { "Right bracket"
148+
"]" => { "Right bracket" fallback=select_textobject_inside_next_pair
149149
"d" => goto_next_diag,
150150
"D" => goto_last_diag,
151151
"g" => goto_next_change,

0 commit comments

Comments
 (0)