Add fallback command to [ and ] to find in next pair

pull/11695/head
thomasschafer 2 months ago
parent 81b0dbc71a
commit 97910ca0e8
No known key found for this signature in database

@ -154,6 +154,12 @@ fn find_nth_closest_pairs_plain(
Err(Error::PairNotFound)
}
pub enum FindType {
Surround(usize),
Next(usize),
Prev(usize),
}
/// 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)
@ -161,7 +167,7 @@ pub fn find_nth_pairs_pos(
text: RopeSlice,
ch: char,
range: Range,
n: usize,
find_type: FindType,
) -> Result<(usize, usize)> {
if text.len_chars() < 2 {
return Err(Error::PairNotFound);
@ -172,6 +178,17 @@ pub fn find_nth_pairs_pos(
let (open, close) = get_pair(ch);
let pos = range.cursor(text);
let (pos, n) = match find_type {
FindType::Surround(n) => (pos, n),
FindType::Next(n) => match search::find_nth_next(text, open, pos, n) {
Some(next_pos) => (next_pos + 1, 1),
None => return Err(Error::PairNotFound),
},
FindType::Prev(n) => match search::find_nth_prev(text, close, pos, n) {
Some(next_pos) => (next_pos - 1, 1),
None => return Err(Error::PairNotFound),
},
};
let (open, close) = if open == close {
if Some(open) == text.get_char(pos) {
@ -298,7 +315,7 @@ pub fn get_surround_pos(
for &range in selection {
let (open_pos, close_pos) = {
let range_raw = match ch {
Some(ch) => find_nth_pairs_pos(text, ch, range, skip)?,
Some(ch) => find_nth_pairs_pos(text, ch, range, FindType::Surround(skip))?,
None => find_nth_closest_pairs_pos(syntax, text, range, skip)?,
};
let range = Range::new(range_raw.0, range_raw.1);
@ -392,7 +409,12 @@ mod test {
assert_eq!(2, expectations.len());
assert_eq!(
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1)
find_nth_pairs_pos(
doc.slice(..),
'\'',
selection.primary(),
FindType::Surround(1)
)
.expect("find should succeed"),
(expectations[0], expectations[1])
)
@ -409,7 +431,46 @@ mod test {
assert_eq!(2, expectations.len());
assert_eq!(
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 2)
find_nth_pairs_pos(
doc.slice(..),
'\'',
selection.primary(),
FindType::Surround(2)
)
.expect("find should succeed"),
(expectations[0], expectations[1])
)
}
#[test]
fn test_find_inside_third_next_quote() {
#[rustfmt::skip]
let (doc, selection, expectations) =
rope_with_selections_and_expectations(
"some 'nested 'quoted' text' on this 'line'\n'and this one'",
" ^ _ _ \n "
);
assert_eq!(2, expectations.len());
assert_eq!(
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), FindType::Next(3))
.expect("find should succeed"),
(expectations[0], expectations[1])
)
}
#[test]
fn test_find_inside_prev_quote() {
#[rustfmt::skip]
let (doc, selection, expectations) =
rope_with_selections_and_expectations(
"some 'nested 'quoted' text' on this 'line'\n'and this one'",
" _ _ ^ \n "
);
assert_eq!(2, expectations.len());
assert_eq!(
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), FindType::Prev(1))
.expect("find should succeed"),
(expectations[0], expectations[1])
)
@ -425,7 +486,12 @@ mod test {
);
assert_eq!(
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1),
find_nth_pairs_pos(
doc.slice(..),
'\'',
selection.primary(),
FindType::Surround(1)
),
Err(Error::CursorOnAmbiguousPair)
)
}

@ -7,6 +7,7 @@ use crate::chars::{categorize_char, char_is_whitespace, CharCategory};
use crate::graphemes::{next_grapheme_boundary, prev_grapheme_boundary};
use crate::line_ending::rope_is_line_ending;
use crate::movement::Direction;
use crate::surround::FindType;
use crate::syntax::LanguageConfiguration;
use crate::Range;
use crate::{surround, Syntax};
@ -204,9 +205,15 @@ pub fn textobject_pair_surround(
range: Range,
textobject: TextObject,
ch: char,
count: usize,
find_type: FindType,
) -> Range {
textobject_pair_surround_impl(syntax, slice, range, textobject, Some(ch), count)
textobject_pair_surround_impl(
syntax,
slice,
range,
textobject,
FindVariant::Char((ch, find_type)),
)
}
pub fn textobject_pair_surround_closest(
@ -216,7 +223,18 @@ pub fn textobject_pair_surround_closest(
textobject: TextObject,
count: usize,
) -> Range {
textobject_pair_surround_impl(syntax, slice, range, textobject, None, count)
textobject_pair_surround_impl(
syntax,
slice,
range,
textobject,
FindVariant::Closest(count),
)
}
enum FindVariant {
Char((char, FindType)),
Closest(usize),
}
fn textobject_pair_surround_impl(
@ -224,12 +242,15 @@ fn textobject_pair_surround_impl(
slice: RopeSlice,
range: Range,
textobject: TextObject,
ch: Option<char>,
count: usize,
find_variant: FindVariant,
) -> Range {
let pair_pos = match ch {
Some(ch) => surround::find_nth_pairs_pos(slice, ch, range, count),
None => surround::find_nth_closest_pairs_pos(syntax, slice, range, count),
let pair_pos = match find_variant {
FindVariant::Char((ch, find_type)) => {
surround::find_nth_pairs_pos(slice, ch, range, find_type)
}
FindVariant::Closest(count) => {
surround::find_nth_closest_pairs_pos(syntax, slice, range, count)
}
};
pair_pos
.map(|(anchor, head)| match textobject {
@ -576,8 +597,14 @@ mod test {
let slice = doc.slice(..);
for &case in scenario {
let (pos, objtype, expected_range, ch, count) = case;
let result =
textobject_pair_surround(None, slice, Range::point(pos), objtype, ch, count);
let result = textobject_pair_surround(
None,
slice,
Range::point(pos),
objtype,
ch,
FindType::Surround(count),
);
assert_eq!(
result,
expected_range.into(),

@ -22,15 +22,16 @@ use helix_core::{
encoding, find_workspace,
graphemes::{self, next_grapheme_boundary, RevRopeGraphemes},
history::UndoKind,
increment, indent,
indent::IndentStyle,
increment,
indent::{self, IndentStyle},
line_ending::{get_line_ending_of_str, line_end_char_index},
match_brackets,
movement::{self, move_vertically_visual, Direction},
object, pos_at_coords,
regex::{self, Regex},
search::{self, CharMatcher},
selection, shellwords, surround,
selection, shellwords,
surround::{self, FindType},
syntax::{BlockCommentToken, LanguageServerFeature},
text_annotations::{Overlay, TextAnnotations},
textobject,
@ -725,8 +726,10 @@ impl FallbackCommand {
#[rustfmt::skip]
static_fallback_commands!(
select_textobject_inside_surrounding_pair, "Select inside any character acting as a pair (tree-sitter)",
select_textobject_around_surrounding_pair, "Select around any character acting as a pair (tree-sitter)",
select_textobject_inside_surrounding_pair, "Select inside any character pair (tree-sitter)",
select_textobject_around_surrounding_pair, "Select around any character pair (tree-sitter)",
select_textobject_inside_prev_pair, "Select inside previous character pair (tree-sitter)",
select_textobject_inside_next_pair, "Select inside next character pair (tree-sitter)",
);
}
@ -5693,17 +5696,36 @@ fn textobject_change(cx: &mut Context) {
}
fn select_textobject_inside_surrounding_pair(cx: &mut Context, ch: char) {
textobject_surrounding_pair(cx, textobject::TextObject::Inside, ch);
textobject_surrounding_pair(cx, textobject::TextObject::Inside, ch, None);
}
fn select_textobject_around_surrounding_pair(cx: &mut Context, ch: char) {
textobject_surrounding_pair(cx, textobject::TextObject::Around, ch);
textobject_surrounding_pair(cx, textobject::TextObject::Around, ch, None);
}
fn select_textobject_inside_prev_pair(cx: &mut Context, ch: char) {
textobject_surrounding_pair(
cx,
textobject::TextObject::Inside,
ch,
Some(Direction::Backward),
);
}
fn select_textobject_inside_next_pair(cx: &mut Context, ch: char) {
textobject_surrounding_pair(
cx,
textobject::TextObject::Inside,
ch,
Some(Direction::Forward),
);
}
fn textobject_surrounding_pair(
cx: &mut Context,
textobject: textobject::TextObject,
pair_char: char,
direction: Option<Direction>,
) {
if pair_char.is_ascii_alphanumeric() {
return;
@ -5715,7 +5737,18 @@ fn textobject_surrounding_pair(
let text = doc.text().slice(..);
let syntax = doc.syntax();
let selection = doc.selection(view.id).clone().transform(|range| {
textobject::textobject_pair_surround(syntax, text, range, textobject, pair_char, count)
let find_type = match direction {
None => FindType::Surround,
Some(Direction::Forward) => FindType::Next,
Some(Direction::Backward) => FindType::Prev,
}(count);
let mut range = textobject::textobject_pair_surround(
syntax, text, range, textobject, pair_char, find_type,
);
if let Some(direction) = direction {
range = range.with_direction(direction);
}
range
});
doc.set_selection(view.id, selection);
};

@ -131,7 +131,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"g" => select_textobject_around_change,
},
},
"[" => { "Left bracket"
"[" => { "Left bracket" fallback=select_textobject_inside_prev_pair
"d" => goto_prev_diag,
"D" => goto_first_diag,
"g" => goto_prev_change,
@ -145,7 +145,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"p" => goto_prev_paragraph,
"space" => add_newline_above,
},
"]" => { "Right bracket"
"]" => { "Right bracket" fallback=select_textobject_inside_next_pair
"d" => goto_next_diag,
"D" => goto_last_diag,
"g" => goto_next_change,

Loading…
Cancel
Save