Use requested direction for new textobject selection range

This changes the behavior of operations like `]f`/`[f` to set the
direction of the new range to the direction of the action.

The original behavior was to always use the head of the next function.
This is inconsistent with the behavior of goto_next_paragraph and makes
it impossible to create extend variants of the textobject motions.

This causes a behavior change when there are nested functions. The
behavior in the parent commit is that repeated uses of `]f` will
select every function in the file even if nested. With this commit,
functions are skipped.

It's notable that it's possible to emulate the original behavior by
using the `ensure_selections_forward` (A-:) command between invocations
of `]f`.
This commit is contained in:
Michael Davis 2022-07-30 09:20:07 -05:00 committed by Blaž Hrastnik
parent f3958aa1fd
commit 274f2ea459
3 changed files with 17 additions and 4 deletions

View file

@ -389,6 +389,8 @@ fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> boo
} }
} }
/// Finds the range of the next or previous textobject in the syntax sub-tree of `node`.
/// Returns the range in the forwards direction.
pub fn goto_treesitter_object( pub fn goto_treesitter_object(
slice: RopeSlice, slice: RopeSlice,
range: Range, range: Range,
@ -419,8 +421,8 @@ pub fn goto_treesitter_object(
.filter(|n| n.start_byte() > byte_pos) .filter(|n| n.start_byte() > byte_pos)
.min_by_key(|n| n.start_byte())?, .min_by_key(|n| n.start_byte())?,
Direction::Backward => nodes Direction::Backward => nodes
.filter(|n| n.start_byte() < byte_pos) .filter(|n| n.end_byte() < byte_pos)
.max_by_key(|n| n.start_byte())?, .max_by_key(|n| n.end_byte())?,
}; };
let len = slice.len_bytes(); let len = slice.len_bytes();
@ -434,7 +436,7 @@ pub fn goto_treesitter_object(
let end_char = slice.byte_to_char(end_byte); let end_char = slice.byte_to_char(end_byte);
// head of range should be at beginning // head of range should be at beginning
Some(Range::new(end_char, start_char)) Some(Range::new(start_char, end_char))
}; };
(0..count).fold(range, |range, _| get_range(range).unwrap_or(range)) (0..count).fold(range, |range, _| get_range(range).unwrap_or(range))
} }

View file

@ -122,7 +122,7 @@ impl Range {
} }
} }
// flips the direction of the selection /// Flips the direction of the selection
pub fn flip(&self) -> Self { pub fn flip(&self) -> Self {
Self { Self {
anchor: self.head, anchor: self.head,
@ -131,6 +131,16 @@ impl Range {
} }
} }
/// Returns the selection if it goes in the direction of `direction`,
/// flipping the selection otherwise.
pub fn with_direction(self, direction: Direction) -> Self {
if self.direction() == direction {
self
} else {
self.flip()
}
}
/// Check two ranges for overlap. /// Check two ranges for overlap.
#[must_use] #[must_use]
pub fn overlaps(&self, other: &Self) -> bool { pub fn overlaps(&self, other: &Self) -> bool {

View file

@ -4294,6 +4294,7 @@ fn goto_ts_object_impl(cx: &mut Context, object: &'static str, direction: Direct
lang_config, lang_config,
count, count,
) )
.with_direction(direction)
}); });
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);