use indexmap::IndexSet; use rustyline::completion::{Completer, Candidate}; use rustyline::hint::Hinter; use rustyline::validate::Validator; use rustyline::highlight::{MatchingBracketHighlighter, Highlighter}; use rustyline::{Helper as RlHelper, Result, Context}; use crate::atom_table::{Atom, STATIC_ATOMS_MAP}; // TODO: Maybe add validation to the helper pub struct Helper { highligher: MatchingBracketHighlighter, pub atoms: *const IndexSet, } impl Helper { pub fn new() -> Self { Self { highligher: MatchingBracketHighlighter::new(), atoms: std::ptr::null(), } } } impl RlHelper for Helper {} fn get_prefix(line: &str, pos: usize) -> Option { let mut start_of_atom = None; let mut first_letter = true; for (i, char) in line.chars().enumerate() { if first_letter { if char.is_alphabetic() && char.is_lowercase() { start_of_atom = Some(i); } first_letter = false; } if !char.is_alphanumeric() && char != '_' { first_letter = true; start_of_atom = None; } if i == pos { break } } if first_letter || pos == 0 { start_of_atom = Some(pos) } start_of_atom } pub struct StrPtr(*const str); impl Candidate for StrPtr { fn display(&self) -> &str { unsafe { self.0.as_ref().unwrap() } } fn replacement(&self) -> &str { unsafe { self.0.as_ref().unwrap() } } } impl Completer for Helper { type Candidate = StrPtr; fn complete(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Result<(usize, Vec)> { let start_of_prefix = get_prefix(line, pos); if let Some(idx) = start_of_prefix { let sub_str = line.get(idx..pos).unwrap(); Ok((idx, unsafe { let mut matching = (*self.atoms).iter() .chain(STATIC_ATOMS_MAP.values()) .filter(|a| a.as_str().starts_with(sub_str)) .map(|s| StrPtr(s.as_str())) .collect::>(); matching.sort_unstable_by(|a, b| Ord::cmp(&(*a.0).len(), &(*b.0).len())); matching })) } else { Ok((0, vec![])) } } } impl Highlighter for Helper { fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { self.highligher.highlight(line, pos) } fn highlight_char(&self, line: &str, pos: usize) -> bool { self.highligher.highlight_char(line, pos) } } impl Validator for Helper {} impl Hinter for Helper { type Hint = String; }