use super::clamp_to_cursor_position;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum VerticalDirection {
Up,
Down,
}
fn line_bounds_and_column(text: &str, index: usize) -> (usize, usize, usize) {
let index = clamp_to_cursor_position(text, index);
let line_start = text[..index]
.rfind('\n')
.map_or(0, |newline_index| newline_index + '\n'.len_utf8());
let line_end = text[index..]
.find('\n')
.map_or(text.len(), |newline_offset| index + newline_offset);
let column = text[line_start..index].chars().count();
(line_start, line_end, column)
}
pub(super) fn character_column(text: &str, index: usize) -> usize {
let (_line_start, _line_end, column) = line_bounds_and_column(text, index);
column
}
fn byte_index_for_column(text: &str, line_start: usize, line_end: usize, column: usize) -> usize {
let line_character_count = text[line_start..line_end].chars().count();
if line_character_count == 0 {
return line_start;
}
let column = column.min(line_character_count - 1);
text[line_start..line_end]
.char_indices()
.nth(column)
.map_or(line_end, |(offset, _character)| line_start + offset)
}
pub(super) fn vertical_move(text: &str, index: usize, direction: VerticalDirection) -> usize {
let column = character_column(text, index);
vertical_move_to_column(text, index, direction, column)
}
pub(super) fn vertical_move_to_column(
text: &str,
index: usize,
direction: VerticalDirection,
column: usize,
) -> usize {
let (line_start, line_end, _current_column) = line_bounds_and_column(text, index);
match direction {
VerticalDirection::Up => {
if line_start == 0 {
return clamp_to_cursor_position(text, index);
}
let previous_line_end = line_start - '\n'.len_utf8();
let previous_line_start = text[..previous_line_end]
.rfind('\n')
.map_or(0, |newline_index| newline_index + '\n'.len_utf8());
byte_index_for_column(text, previous_line_start, previous_line_end, column)
}
VerticalDirection::Down => {
if line_end == text.len() {
return clamp_to_cursor_position(text, index);
}
let next_line_start = line_end + '\n'.len_utf8();
let next_line_end = text[next_line_start..]
.find('\n')
.map_or(text.len(), |newline_offset| {
next_line_start + newline_offset
});
byte_index_for_column(text, next_line_start, next_line_end, column)
}
}
}