pub fn spaces(size: u8) -> &'static str { const SPACES: &str = " "; &SPACES[..size as usize] } #[derive(Debug, Clone)] pub struct Pos { pub row: usize, pub col: usize, pub offset: usize, } impl Pos { pub fn new(row: usize, col: usize, offset: usize) -> Self { Self { row, col, offset } } } #[derive(PartialEq, Eq, Clone, Copy)] enum CharKind { Space, Punct, Other, } impl CharKind { fn new(c: char) -> Self { if c.is_whitespace() { Self::Space } else if c.is_ascii_punctuation() { Self::Punct } else { Self::Other } } } pub fn find_word_start_forward(line: &str, start_col: usize) -> Option { let mut it = line.chars().enumerate().skip(start_col); let mut prev = CharKind::new(it.next()?.1); for (col, c) in it { let cur = CharKind::new(c); if cur != CharKind::Space && prev != cur { return Some(col); } prev = cur; } None } pub fn find_word_start_backward(line: &str, start_col: usize) -> Option { let idx = line.char_indices().nth(start_col).map(|(i, _)| i).unwrap_or(line.len()); let mut it = line[..idx].chars().rev().enumerate(); let mut cur = CharKind::new(it.next()?.1); for (i, c) in it { let next = CharKind::new(c); if cur != CharKind::Space && next != cur { return Some(start_col - i); } cur = next; } (cur != CharKind::Space).then_some(0) }