Implement Range::put()
which manages range movements and extensions.
In particular, this wraps the annoying logic involved in keeping the cursor width to 1 grapheme.
This commit is contained in:
parent
85d5b399de
commit
753f7f381b
2 changed files with 47 additions and 63 deletions
|
@ -32,60 +32,31 @@ pub fn move_horizontally(
|
|||
count: usize,
|
||||
behaviour: Movement,
|
||||
) -> Range {
|
||||
match (behaviour, dir) {
|
||||
(Movement::Move, Direction::Backward) => {
|
||||
let count = if range.anchor < range.head {
|
||||
count + 1
|
||||
} else {
|
||||
count
|
||||
};
|
||||
let pos = nth_prev_grapheme_boundary(slice, range.head, count);
|
||||
Range::new(pos, pos)
|
||||
}
|
||||
(Movement::Move, Direction::Forward) => {
|
||||
let count = if range.anchor < range.head {
|
||||
count - 1
|
||||
} else {
|
||||
count
|
||||
};
|
||||
let pos = nth_next_grapheme_boundary(slice, range.head, count);
|
||||
Range::new(pos, pos)
|
||||
}
|
||||
(Movement::Extend, Direction::Backward) => {
|
||||
// Ensure a valid initial selection state.
|
||||
let range = range.min_width_1(slice);
|
||||
use Movement::Extend;
|
||||
|
||||
// Do the main movement.
|
||||
let mut head = nth_prev_grapheme_boundary(slice, range.head, count);
|
||||
let mut anchor = range.anchor;
|
||||
// Shift back one grapheme if needed, to account for
|
||||
// the cursor being visually 1-width.
|
||||
let pos = if range.head > range.anchor {
|
||||
prev_grapheme_boundary(slice, range.head)
|
||||
} else {
|
||||
range.head
|
||||
};
|
||||
|
||||
// If the head and anchor crossed over each other, we need to
|
||||
// fiddle around to make it behave like a 1-wide cursor.
|
||||
if head <= anchor && range.head > range.anchor {
|
||||
anchor = next_grapheme_boundary(slice, anchor);
|
||||
head = prev_grapheme_boundary(slice, head);
|
||||
}
|
||||
// Compute the new position.
|
||||
let mut new_pos = if dir == Direction::Backward {
|
||||
nth_prev_grapheme_boundary(slice, pos, count)
|
||||
} else {
|
||||
nth_next_grapheme_boundary(slice, pos, count)
|
||||
};
|
||||
|
||||
Range::new(anchor, head)
|
||||
}
|
||||
(Movement::Extend, Direction::Forward) => {
|
||||
// Ensure a valid initial selection state.
|
||||
let range = range.min_width_1(slice);
|
||||
// Shift forward one grapheme if needed, for the
|
||||
// visual 1-width cursor.
|
||||
if behaviour == Extend && new_pos >= range.anchor {
|
||||
new_pos = next_grapheme_boundary(slice, new_pos);
|
||||
};
|
||||
|
||||
// Do the main movement.
|
||||
let mut head = nth_next_grapheme_boundary(slice, range.head, count);
|
||||
let mut anchor = range.anchor;
|
||||
|
||||
// If the head and anchor crossed over each other, we need to
|
||||
// fiddle around to make it behave like a 1-wide cursor.
|
||||
if head >= anchor && range.head < range.anchor {
|
||||
anchor = prev_grapheme_boundary(slice, anchor);
|
||||
head = next_grapheme_boundary(slice, head);
|
||||
}
|
||||
|
||||
Range::new(anchor, head)
|
||||
}
|
||||
}
|
||||
// Compute the final new range.
|
||||
range.put(slice, behaviour == Extend, new_pos)
|
||||
}
|
||||
|
||||
pub fn move_vertically(
|
||||
|
@ -135,19 +106,9 @@ pub fn move_vertically(
|
|||
new_pos
|
||||
};
|
||||
|
||||
let new_anchor = if range.anchor <= range.head && range.anchor > new_head {
|
||||
next_grapheme_boundary(slice, range.anchor)
|
||||
} else if range.anchor > range.head && range.anchor < new_head {
|
||||
prev_grapheme_boundary(slice, range.anchor)
|
||||
} else {
|
||||
range.anchor
|
||||
};
|
||||
|
||||
Range {
|
||||
anchor: new_anchor,
|
||||
head: new_head,
|
||||
horiz: Some(horiz),
|
||||
}
|
||||
let mut new_range = range.put(slice, true, new_head);
|
||||
new_range.horiz = Some(horiz);
|
||||
new_range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use crate::{
|
||||
graphemes::{
|
||||
ensure_grapheme_boundary_next, ensure_grapheme_boundary_prev, next_grapheme_boundary,
|
||||
prev_grapheme_boundary,
|
||||
},
|
||||
Assoc, ChangeSet, RopeSlice,
|
||||
};
|
||||
|
@ -208,6 +209,28 @@ impl Range {
|
|||
}
|
||||
}
|
||||
|
||||
/// Moves the `Range` to `char_idx`. If `extend == true`, then only the head
|
||||
/// is moved to `char_idx`, and the anchor is adjusted only as needed to
|
||||
/// preserve 1-width range semantics.
|
||||
///
|
||||
/// This method assumes that the range and `char_idx` are already properly
|
||||
/// grapheme-aligned.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn put(self, text: RopeSlice, extend: bool, char_idx: usize) -> Range {
|
||||
let anchor = if !extend {
|
||||
char_idx
|
||||
} else if self.head >= self.anchor && char_idx < self.anchor {
|
||||
next_grapheme_boundary(text, self.anchor)
|
||||
} else if self.head < self.anchor && char_idx >= self.anchor {
|
||||
prev_grapheme_boundary(text, self.anchor)
|
||||
} else {
|
||||
self.anchor
|
||||
};
|
||||
|
||||
Range::new(anchor, char_idx)
|
||||
}
|
||||
|
||||
// groupAt
|
||||
|
||||
#[inline]
|
||||
|
|
Loading…
Add table
Reference in a new issue