mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-25 10:56:19 +04:00
Add fallback command to [ and ] to find in next pair
This commit is contained in:
parent
81b0dbc71a
commit
97910ca0e8
@ -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,8 +409,13 @@ fn test_find_nth_pairs_pos_quote_success() {
|
||||
|
||||
assert_eq!(2, expectations.len());
|
||||
assert_eq!(
|
||||
find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1)
|
||||
.expect("find should succeed"),
|
||||
find_nth_pairs_pos(
|
||||
doc.slice(..),
|
||||
'\'',
|
||||
selection.primary(),
|
||||
FindType::Surround(1)
|
||||
)
|
||||
.expect("find should succeed"),
|
||||
(expectations[0], expectations[1])
|
||||
)
|
||||
}
|
||||
@ -409,7 +431,46 @@ fn test_find_nth_pairs_pos_nested_quote_success() {
|
||||
|
||||
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 @@ fn test_find_nth_pairs_pos_inside_quote_ambiguous() {
|
||||
);
|
||||
|
||||
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::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 @@ fn test_textobject_surround() {
|
||||
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 @@
|
||||
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 @@ pub fn doc(&self) -> &str {
|
||||
|
||||
#[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…
Reference in New Issue
Block a user