use std::ops::Range;
pub struct Completion {
range: Range<usize>,
candidates: Vec<String>,
chosen: usize,
taken: bool,
}
impl Completion {
pub fn new(text: &str, range: Range<usize>, candidates: Vec<String>) -> Option<Self> {
let typed = &text[range.clone()];
let candidates: Vec<String> = candidates
.into_iter()
.filter(|candidate| candidate.len() > typed.len() && candidate.starts_with(typed))
.collect();
(!candidates.is_empty()).then_some(Self {
range,
candidates,
chosen: 0,
taken: false,
})
}
pub fn chosen(&self) -> &str {
&self.candidates[self.chosen]
}
pub fn ghost(&self) -> &str {
&self.chosen()[self.range.len()..]
}
pub fn take(&mut self, forward: bool) -> (Range<usize>, &str) {
let count = self.candidates.len();
if self.taken || !forward {
self.chosen = if forward {
(self.chosen + 1) % count
} else {
(self.chosen + count - 1) % count
};
}
self.taken = true;
let replaced = self.range.clone();
self.range = replaced.start..replaced.start + self.chosen().len();
(replaced, &self.candidates[self.chosen])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_candidates_extending_the_typed_text_are_offered() {
let candidates = ["ideal", "id", "identity", "image"]
.map(String::from)
.to_vec();
let mut completion = Completion::new("x = id", 4..6, candidates).unwrap();
assert_eq!(completion.ghost(), "eal");
let (replaced, taken) = completion.take(true);
assert_eq!((replaced, taken), (4..6, "ideal"));
let (replaced, taken) = completion.take(true);
assert_eq!((replaced, taken), (4..9, "identity"));
let (_, taken) = completion.take(false);
assert_eq!(taken, "ideal");
assert!(Completion::new("id", 0..2, vec!["id".to_string()]).is_none());
}
}