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

pull/11695/head
thomasschafer 3 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) 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 /// 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) /// or opening pair. `n` will skip n - 1 pairs (eg. n=2 will discard (only)
/// the first pair found and keep looking) /// the first pair found and keep looking)
@ -161,7 +167,7 @@ pub fn find_nth_pairs_pos(
text: RopeSlice, text: RopeSlice,
ch: char, ch: char,
range: Range, range: Range,
n: usize, find_type: FindType,
) -> Result<(usize, usize)> { ) -> Result<(usize, usize)> {
if text.len_chars() < 2 { if text.len_chars() < 2 {
return Err(Error::PairNotFound); return Err(Error::PairNotFound);
@ -172,6 +178,17 @@ pub fn find_nth_pairs_pos(
let (open, close) = get_pair(ch); let (open, close) = get_pair(ch);
let pos = range.cursor(text); 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 { let (open, close) = if open == close {
if Some(open) == text.get_char(pos) { if Some(open) == text.get_char(pos) {
@ -298,7 +315,7 @@ pub fn get_surround_pos(
for &range in selection { for &range in selection {
let (open_pos, close_pos) = { let (open_pos, close_pos) = {
let range_raw = match ch { 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)?, None => find_nth_closest_pairs_pos(syntax, text, range, skip)?,
}; };
let range = Range::new(range_raw.0, range_raw.1); let range = Range::new(range_raw.0, range_raw.1);
@ -392,8 +409,13 @@ mod test {
assert_eq!(2, expectations.len()); assert_eq!(2, expectations.len());
assert_eq!( assert_eq!(
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1) find_nth_pairs_pos(
.expect("find should succeed"), doc.slice(..),
'\'',
selection.primary(),
FindType::Surround(1)
)
.expect("find should succeed"),
(expectations[0], expectations[1]) (expectations[0], expectations[1])
) )
} }
@ -409,7 +431,46 @@ mod test {
assert_eq!(2, expectations.len()); assert_eq!(2, expectations.len());
assert_eq!( 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"), .expect("find should succeed"),
(expectations[0], expectations[1]) (expectations[0], expectations[1])
) )
@ -425,7 +486,12 @@ mod test {
); );
assert_eq!( 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) 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::graphemes::{next_grapheme_boundary, prev_grapheme_boundary};
use crate::line_ending::rope_is_line_ending; use crate::line_ending::rope_is_line_ending;
use crate::movement::Direction; use crate::movement::Direction;
use crate::surround::FindType;
use crate::syntax::LanguageConfiguration; use crate::syntax::LanguageConfiguration;
use crate::Range; use crate::Range;
use crate::{surround, Syntax}; use crate::{surround, Syntax};
@ -204,9 +205,15 @@ pub fn textobject_pair_surround(
range: Range, range: Range,
textobject: TextObject, textobject: TextObject,
ch: char, ch: char,
count: usize, find_type: FindType,
) -> Range { ) -> 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( pub fn textobject_pair_surround_closest(
@ -216,7 +223,18 @@ pub fn textobject_pair_surround_closest(
textobject: TextObject, textobject: TextObject,
count: usize, count: usize,
) -> Range { ) -> 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( fn textobject_pair_surround_impl(
@ -224,12 +242,15 @@ fn textobject_pair_surround_impl(
slice: RopeSlice, slice: RopeSlice,
range: Range, range: Range,
textobject: TextObject, textobject: TextObject,
ch: Option<char>, find_variant: FindVariant,
count: usize,
) -> Range { ) -> Range {
let pair_pos = match ch { let pair_pos = match find_variant {
Some(ch) => surround::find_nth_pairs_pos(slice, ch, range, count), FindVariant::Char((ch, find_type)) => {
None => surround::find_nth_closest_pairs_pos(syntax, slice, range, count), 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 pair_pos
.map(|(anchor, head)| match textobject { .map(|(anchor, head)| match textobject {
@ -576,8 +597,14 @@ mod test {
let slice = doc.slice(..); let slice = doc.slice(..);
for &case in scenario { for &case in scenario {
let (pos, objtype, expected_range, ch, count) = case; let (pos, objtype, expected_range, ch, count) = case;
let result = let result = textobject_pair_surround(
textobject_pair_surround(None, slice, Range::point(pos), objtype, ch, count); None,
slice,
Range::point(pos),
objtype,
ch,
FindType::Surround(count),
);
assert_eq!( assert_eq!(
result, result,
expected_range.into(), expected_range.into(),

@ -22,15 +22,16 @@ use helix_core::{
encoding, find_workspace, encoding, find_workspace,
graphemes::{self, next_grapheme_boundary, RevRopeGraphemes}, graphemes::{self, next_grapheme_boundary, RevRopeGraphemes},
history::UndoKind, history::UndoKind,
increment, indent, increment,
indent::IndentStyle, indent::{self, IndentStyle},
line_ending::{get_line_ending_of_str, line_end_char_index}, line_ending::{get_line_ending_of_str, line_end_char_index},
match_brackets, match_brackets,
movement::{self, move_vertically_visual, Direction}, movement::{self, move_vertically_visual, Direction},
object, pos_at_coords, object, pos_at_coords,
regex::{self, Regex}, regex::{self, Regex},
search::{self, CharMatcher}, search::{self, CharMatcher},
selection, shellwords, surround, selection, shellwords,
surround::{self, FindType},
syntax::{BlockCommentToken, LanguageServerFeature}, syntax::{BlockCommentToken, LanguageServerFeature},
text_annotations::{Overlay, TextAnnotations}, text_annotations::{Overlay, TextAnnotations},
textobject, textobject,
@ -725,8 +726,10 @@ impl FallbackCommand {
#[rustfmt::skip] #[rustfmt::skip]
static_fallback_commands!( static_fallback_commands!(
select_textobject_inside_surrounding_pair, "Select inside 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 acting as a 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) { 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) { 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( fn textobject_surrounding_pair(
cx: &mut Context, cx: &mut Context,
textobject: textobject::TextObject, textobject: textobject::TextObject,
pair_char: char, pair_char: char,
direction: Option<Direction>,
) { ) {
if pair_char.is_ascii_alphanumeric() { if pair_char.is_ascii_alphanumeric() {
return; return;
@ -5715,7 +5737,18 @@ fn textobject_surrounding_pair(
let text = doc.text().slice(..); let text = doc.text().slice(..);
let syntax = doc.syntax(); let syntax = doc.syntax();
let selection = doc.selection(view.id).clone().transform(|range| { 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); doc.set_selection(view.id, selection);
}; };

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

Loading…
Cancel
Save