From: Euan Lacy Date: Tue, 10 May 2022 03:40:22 +0000 (+0100) Subject: wip: basic completion of predicates X-Git-Tag: v0.9.1~34^2~2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=aaa75c3ffc583d17247d2f147bd55d701e68ba1e;p=scryer-prolog.git wip: basic completion of predicates --- diff --git a/src/lib.rs b/src/lib.rs index 99c5d9f9..fc8d8ca5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ mod iterators; pub mod machine; mod raw_block; pub mod read; +mod repl_helper; mod targets; pub mod types; diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index a6b375b6..f33f0901 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -454,6 +454,21 @@ impl MachineState { self.b0 = self.b; } + pub fn read_term_from_user_input(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { + let atoms: Vec<_> = self.atom_tbl.table.iter().map(|a| a.as_str().to_string()).collect(); + + if let Stream::Readline(ptr) = stream { + unsafe { + let readline = ptr.as_ptr().as_mut().unwrap(); + readline.set_atoms_for_completion(atoms); + let ret = self.read_term(stream, indices); + return ret + } + } + + unreachable!("Stream must be a Stream::Readline(_)") + } + pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { fn push_var_eq_functors<'a>( heap: &mut Heap, diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 6272bcee..2e016abd 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4239,7 +4239,8 @@ impl Machine { self.user_input.reset(); set_prompt(true); - let result = self.machine_st.read_term(self.user_input, &mut self.indices); + // let result = self.machine_st.read_term(self.user_input, &mut self.indices); + let result = self.machine_st.read_term_from_user_input(self.user_input, &mut self.indices); set_prompt(false); match result { diff --git a/src/read.rs b/src/read.rs index c58e99f4..399d9280 100644 --- a/src/read.rs +++ b/src/read.rs @@ -9,12 +9,13 @@ use crate::machine::machine_indices::*; use crate::machine::machine_state::MachineState; use crate::machine::streams::*; use crate::parser::char_reader::*; +use crate::repl_helper::Helper; use crate::types::*; use fxhash::FxBuildHasher; use rustyline::error::ReadlineError; -use rustyline::{Cmd, Config, Editor, KeyEvent}; +use rustyline::{Config, Editor}; use std::collections::VecDeque; use std::io::{Cursor, Error, ErrorKind, Read}; @@ -77,7 +78,7 @@ fn get_prompt() -> &'static str { #[derive(Debug)] pub struct ReadlineStream { - rl: Editor<()>, + rl: Editor, pending_input: Cursor, add_history: bool, } @@ -86,7 +87,10 @@ impl ReadlineStream { #[inline] pub fn new(pending_input: &str, add_history: bool) -> Self { let config = Config::builder().check_cursor_position(true).build(); - let mut rl = Editor::<()>::with_config(config); + let helper = Helper::new(); + + let mut rl = Editor::with_config(config); + rl.set_helper(Some(helper)); if let Some(mut path) = dirs_next::home_dir() { path.push(HISTORY_FILE); @@ -95,7 +99,7 @@ impl ReadlineStream { } } - rl.bind_sequence(KeyEvent::from('\t'), Cmd::Insert(1, "\t".to_string())); + // rl.bind_sequence(KeyEvent::from('\t'), Cmd::Insert(1, "\t".to_string())); ReadlineStream { rl, @@ -104,6 +108,11 @@ impl ReadlineStream { } } + pub fn set_atoms_for_completion(&mut self, atoms: Vec) { + let helper = self.rl.helper_mut().unwrap(); + helper.atoms = atoms; + } + #[inline] pub fn reset(&mut self) { self.pending_input.get_mut().clear(); diff --git a/src/repl_helper.rs b/src/repl_helper.rs new file mode 100644 index 00000000..96f63efa --- /dev/null +++ b/src/repl_helper.rs @@ -0,0 +1,136 @@ +use rustyline::completion::Completer; +use rustyline::hint::Hinter; +use rustyline::validate::Validator; +use rustyline::highlight::{MatchingBracketHighlighter, Highlighter}; +use rustyline::{Helper as RlHelper, Result, Context}; + +// pub struct Atoms { +// atoms: *const *const str, +// len: usize, +// } + +// impl Atoms { +// pub fn new() -> Self { +// Self { +// atoms: std::ptr::null(), +// len: 0, +// } +// } +// } + +// pub struct AtomsIterator<'a> { +// atoms: &'a Atoms, +// idx: usize, +// } + +// impl<'a> Iterator for AtomsIterator<'a> { +// type Item = &'a str; + +// fn next(&mut self) -> Option { +// self.idx += 1; + +// if self.idx == self.atoms.len { +// None +// } else { +// unsafe { +// let next = self.atoms.atoms.offset(self.idx as isize); +// (*next).as_ref() +// } +// } +// } +// } + +// impl<'a> IntoIterator for &'a Atoms { +// type Item = &'a str; +// type IntoIter = AtomsIterator<'a>; + +// fn into_iter(self) -> Self::IntoIter { +// Self::IntoIter { +// atoms: self, +// idx: 0, +// } +// } +// } + +// TODO: Maybe add validation to the helper +pub struct Helper { + highligher: MatchingBracketHighlighter, + pub atoms: Vec, +} + +impl Helper { + pub fn new() -> Self { + Self { + highligher: MatchingBracketHighlighter::new(), + atoms: vec![], + } + } + + // pub fn set_atoms(&mut self, atoms_ptr: *const *const str, len: usize) { + // self.atoms.atoms = atoms_ptr; + // self.atoms.len = len; + // } +} + +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 +} + +impl Completer for Helper { + type Candidate = String; + + 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(); + let matching = self.atoms.iter().filter(|a| a.starts_with(sub_str)).map(|s| s.to_string()).collect(); + Ok((idx, 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; +}