Files
scryer-prolog/src/repl_helper.rs
2022-05-11 21:37:03 +01:00

111 lines
2.7 KiB
Rust

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<Atom>,
}
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<usize> {
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<Self::Candidate>)> {
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::<Vec<_>>();
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;
}