on master help sign in
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`.

commit274f2ea45979098a96b7a6d94ecc18351826eaea authorMichael Davis <mcarsondavis@gmail.com> dateSat 30 Jul 2022, 09:20 (4 years ago) parentf3958aa
3 files changed+17−4
helix-core/src/movement.rs+5−3 helix-core/src/selection.rs+11−1 helix-term/src/commands.rs+1
helix-core/src/movement.rs+5−3
@@ -389,6 +389,8 @@ fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> boo
389389 }
390390}
391391
392+/// Finds the range of the next or previous textobject in the syntax sub-tree of `node`.
393+/// Returns the range in the forwards direction.
392394pub fn goto_treesitter_object(
393395 slice: RopeSlice,
394396 range: Range,
@@ -419,8 +421,8 @@ pub fn goto_treesitter_object(
419421 .filter(|n| n.start_byte() > byte_pos)
420422 .min_by_key(|n| n.start_byte())?,
421423 Direction::Backward => nodes
422− .filter(|n| n.start_byte() < byte_pos)
423− .max_by_key(|n| n.start_byte())?,
424+ .filter(|n| n.end_byte() < byte_pos)
425+ .max_by_key(|n| n.end_byte())?,
424426 };
425427
426428 let len = slice.len_bytes();
@@ -434,7 +436,7 @@ pub fn goto_treesitter_object(
434436 let end_char = slice.byte_to_char(end_byte);
435437
436438 // head of range should be at beginning
437− Some(Range::new(end_char, start_char))
439+ Some(Range::new(start_char, end_char))
438440 };
439441 (0..count).fold(range, |range, _| get_range(range).unwrap_or(range))
440442}
helix-core/src/selection.rs+11−1
@@ -122,7 +122,7 @@ impl Range {
122122 }
123123 }
124124
125− // flips the direction of the selection
125+ /// Flips the direction of the selection
126126 pub fn flip(&self) -> Self {
127127 Self {
128128 anchor: self.head,
@@ -131,6 +131,16 @@ impl Range {
131131 }
132132 }
133133
134+ /// Returns the selection if it goes in the direction of `direction`,
135+ /// flipping the selection otherwise.
136+ pub fn with_direction(self, direction: Direction) -> Self {
137+ if self.direction() == direction {
138+ self
139+ } else {
140+ self.flip()
141+ }
142+ }
143+
134144 /// Check two ranges for overlap.
135145 #[must_use]
136146 pub fn overlaps(&self, other: &Self) -> bool {
helix-term/src/commands.rs+1
@@ -4294,6 +4294,7 @@ fn goto_ts_object_impl(cx: &mut Context, object: &'static str, direction: Direct
42944294 lang_config,
42954295 count,
42964296 )
4297+ .with_direction(direction)
42974298 });
42984299
42994300 doc.set_selection(view.id, selection);