Skip to content

Commit f96b8b7

Browse files
committed
Switch to a cleaner range-head moving abstraction.
Also fix a bunch of bugs related to it.
1 parent 2072349 commit f96b8b7

File tree

5 files changed

+111
-84
lines changed

5 files changed

+111
-84
lines changed

helix-core/src/movement.rs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,18 @@ pub fn move_horizontally(
4242
};
4343

4444
// Compute the new position.
45-
let mut new_pos = if dir == Direction::Backward {
45+
let new_pos = if dir == Direction::Backward {
4646
nth_prev_grapheme_boundary(slice, pos, count)
4747
} else {
4848
nth_next_grapheme_boundary(slice, pos, count)
4949
};
5050

51-
// Shift forward one grapheme if needed, for the
52-
// visual 1-width cursor.
53-
if behaviour == Extend && new_pos >= range.anchor {
54-
new_pos = next_grapheme_boundary(slice, new_pos);
55-
};
56-
5751
// Compute the final new range.
58-
range.put(slice, new_pos, behaviour == Extend)
52+
if behaviour == Extend {
53+
range.move_head(slice, new_pos, true)
54+
} else {
55+
Range::point(new_pos)
56+
}
5957
}
6058

6159
pub fn move_vertically(
@@ -78,14 +76,17 @@ pub fn move_vertically(
7876
let horiz = range.horiz.unwrap_or(col as u32);
7977

8078
// Compute the new position.
81-
let new_pos = {
79+
let (new_pos, new_row) = {
8280
let new_row = if dir == Direction::Backward {
8381
row.saturating_sub(count)
8482
} else {
8583
(row + count).min(slice.len_lines().saturating_sub(1))
8684
};
8785
let new_col = col.max(horiz as usize);
88-
pos_at_coords(slice, Position::new(new_row, new_col), true)
86+
(
87+
pos_at_coords(slice, Position::new(new_row, new_col), true),
88+
new_row,
89+
)
8990
};
9091

9192
// Compute the new range according to the type of movement.
@@ -97,15 +98,13 @@ pub fn move_vertically(
9798
},
9899

99100
Movement::Extend => {
100-
let new_head = if new_pos >= range.anchor {
101-
next_grapheme_boundary(slice, new_pos)
101+
if slice.line(new_row).len_chars() > 0 {
102+
let mut new_range = range.move_head(slice, new_pos, true);
103+
new_range.horiz = Some(horiz);
104+
new_range
102105
} else {
103-
new_pos
104-
};
105-
106-
let mut new_range = range.put(slice, new_head, true);
107-
new_range.horiz = Some(horiz);
108-
new_range
106+
range
107+
}
109108
}
110109
}
111110
}

helix-core/src/search.rs

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
use crate::RopeSlice;
22

3-
pub fn find_nth_next(
4-
text: RopeSlice,
5-
ch: char,
6-
mut pos: usize,
7-
n: usize,
8-
inclusive: bool,
9-
) -> Option<usize> {
3+
pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
104
if pos >= text.len_chars() || n == 0 {
115
return None;
126
}
@@ -25,20 +19,10 @@ pub fn find_nth_next(
2519
}
2620
}
2721

28-
if !inclusive {
29-
pos -= 1;
30-
}
31-
32-
Some(pos)
22+
Some(pos - 1)
3323
}
3424

35-
pub fn find_nth_prev(
36-
text: RopeSlice,
37-
ch: char,
38-
mut pos: usize,
39-
n: usize,
40-
inclusive: bool,
41-
) -> Option<usize> {
25+
pub fn find_nth_prev(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
4226
if pos == 0 || n == 0 {
4327
return None;
4428
}
@@ -57,9 +41,5 @@ pub fn find_nth_prev(
5741
}
5842
}
5943

60-
if !inclusive {
61-
pos += 1;
62-
}
63-
6444
Some(pos)
6545
}

helix-core/src/selection.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -248,26 +248,30 @@ impl Range {
248248
}
249249
}
250250

251-
/// Moves the `Range` to `char_idx`. If `extend == true`, then only the head
252-
/// is moved to `char_idx`, and the anchor is adjusted only as needed to
253-
/// preserve 1-width range semantics.
251+
/// Moves the head of the `Range` to `char_idx`, adjusting the anchor
252+
/// as needed to preserve 1-width range semantics.
253+
///
254+
/// `block_cursor` specifies whether it should treat `char_idx` as a block
255+
/// cursor position or as a range-end position.
254256
///
255257
/// This method assumes that the range and `char_idx` are already properly
256258
/// grapheme-aligned.
257259
#[must_use]
258260
#[inline]
259-
pub fn put(self, text: RopeSlice, char_idx: usize, extend: bool) -> Range {
260-
let anchor = if !extend {
261-
char_idx
262-
} else if self.head >= self.anchor && char_idx < self.anchor {
261+
pub fn move_head(self, text: RopeSlice, char_idx: usize, block_cursor: bool) -> Range {
262+
let anchor = if self.head >= self.anchor && char_idx < self.anchor {
263263
next_grapheme_boundary(text, self.anchor)
264264
} else if self.head < self.anchor && char_idx >= self.anchor {
265265
prev_grapheme_boundary(text, self.anchor)
266266
} else {
267267
self.anchor
268268
};
269269

270-
Range::new(anchor, char_idx)
270+
if block_cursor && anchor <= char_idx {
271+
Range::new(anchor, next_grapheme_boundary(text, char_idx))
272+
} else {
273+
Range::new(anchor, char_idx)
274+
}
271275
}
272276

273277
// groupAt

helix-core/src/surround.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,18 @@ pub fn find_nth_pairs_pos(
4949
if Some(open) == text.get_char(pos) {
5050
// Special case: cursor is directly on a matching char.
5151
match pos {
52-
0 => Some((pos, search::find_nth_next(text, close, pos + 1, n, true)?)),
53-
_ if (pos + 1) == text.len_chars() => Some((
54-
search::find_nth_prev(text, open, pos, n, true)?,
55-
text.len_chars(),
56-
)),
52+
0 => Some((pos, search::find_nth_next(text, close, pos + 1, n)? + 1)),
53+
_ if (pos + 1) == text.len_chars() => {
54+
Some((search::find_nth_prev(text, open, pos, n)?, text.len_chars()))
55+
}
5756
// We return no match because there's no way to know which
5857
// side of the char we should be searching on.
5958
_ => None,
6059
}
6160
} else {
6261
Some((
63-
search::find_nth_prev(text, open, pos, n, true)?,
64-
search::find_nth_next(text, close, pos, n, true)?,
62+
search::find_nth_prev(text, open, pos, n)?,
63+
search::find_nth_next(text, close, pos, n)? + 1,
6564
))
6665
}
6766
} else {

0 commit comments

Comments
 (0)