#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CharKind {
Space,
Punctuation,
Other,
}
impl CharKind {
fn new(c: char) -> Self {
if c.is_whitespace() {
Self::Space
} else if c.is_ascii_punctuation() {
Self::Punctuation
} else {
Self::Other
}
}
}
pub fn find_word_end_next(line: &str, start_col: usize) -> Option<usize> {
let mut it = line.chars().enumerate().skip(start_col);
let (mut cur_col, cur_char) = it.next()?;
let mut cur = CharKind::new(cur_char);
for (next_col, c) in it {
let next = CharKind::new(c);
if next_col.saturating_sub(start_col) > 1 && cur != CharKind::Space && next != cur {
return Some(next_col.saturating_sub(1));
}
cur = next;
cur_col = next_col;
}
if cur != CharKind::Space && cur_col.saturating_sub(start_col) >= 1 {
return Some(cur_col);
}
None
}
pub fn move_cursor_to_word_end(text: &[String], row: usize, col: usize) -> (usize, usize) {
if row >= text.len() {
return (row, col);
}
let line = &text[row];
if let Some(new_col) = find_word_end_next(line, col) {
return (row, new_col);
} else if row + 1 < text.len() {
if let Some(new_col) = find_word_end_next(&text[row + 1], 0) {
return (row + 1, new_col);
} else if !text[row + 1].is_empty() {
return (row + 1, text[row + 1].chars().count().saturating_sub(1));
}
}
(row, col)
}