use std::collections::HashMap;
const MIN_WORD_LENGTH: usize = 3;
#[must_use]
pub fn is_word_char(character: char) -> bool {
character.is_alphanumeric() || character == '_'
}
#[must_use]
pub fn collect_words(text: &str) -> Vec<(String, usize)> {
let mut counts: HashMap<&str, usize> = HashMap::new();
for word in text
.split(|c: char| !is_word_char(c))
.filter(|word| word.chars().count() >= MIN_WORD_LENGTH)
{
*counts.entry(word).or_insert(0) += 1;
}
let mut words: Vec<(String, usize)> = counts
.into_iter()
.map(|(word, count)| (word.to_owned(), count))
.collect();
words.sort_unstable_by(|(left_word, left_count), (right_word, right_count)| {
right_count
.cmp(left_count)
.then_with(|| left_word.cmp(right_word))
});
words
}
#[must_use]
pub fn current_prefix(text: &str, cursor: usize) -> String {
let before: String = text.chars().take(cursor).collect();
let reversed: String = before
.chars()
.rev()
.take_while(|c| is_word_char(*c))
.collect();
reversed.chars().rev().collect()
}
#[must_use]
pub fn current_word(text: &str, cursor: usize) -> (usize, usize) {
let chars: Vec<char> = text.chars().collect();
let cursor = cursor.min(chars.len());
let leading = chars
.iter()
.take(cursor)
.rev()
.take_while(|character| is_word_char(**character))
.count();
let trailing = chars
.iter()
.skip(cursor)
.take_while(|character| is_word_char(**character))
.count();
(cursor - leading, cursor + trailing)
}
#[must_use]
pub fn matches<'a>(words: &'a [(String, usize)], prefix: &str) -> Vec<(&'a String, usize)> {
let found: Vec<(&'a String, usize)> = words
.iter()
.filter(|(word, _)| word.starts_with(prefix))
.map(|(word, count)| (word, *count))
.collect();
if found.iter().all(|(word, _)| word.as_str() == prefix) {
return Vec::new();
}
found
}
#[must_use]
pub fn char_to_byte(text: &str, char_index: usize) -> usize {
text.char_indices()
.nth(char_index)
.map_or(text.len(), |(byte, _)| byte)
}