#[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<usize> {
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_exclusive_end_forward(line: &str, start_col: usize) -> Option<usize> {
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 prev != CharKind::Space && prev != cur {
return Some(col);
}
prev = cur;
}
None
}
pub fn find_word_inclusive_end_forward(line: &str, start_col: usize) -> Option<usize> {
let mut it = line.chars().enumerate().skip(start_col);
let (mut last_col, c) = it.next()?;
let mut prev = CharKind::new(c);
for (col, c) in it {
let cur = CharKind::new(c);
if prev != CharKind::Space && cur != prev {
return Some(col.saturating_sub(1));
}
prev = cur;
last_col = col;
}
if prev != CharKind::Space {
Some(last_col)
} else {
None
}
}
pub fn find_word_start_backward(line: &str, start_col: usize) -> Option<usize> {
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(|| 0)
}