Merge pull request #1467 from euanlacy/master

Completion of predicates in repl
This commit is contained in:
Mark Thom
2022-05-11 22:32:09 -06:00
committed by GitHub
6 changed files with 145 additions and 6 deletions

View File

@@ -170,7 +170,7 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
#((#static_strs) => { Atom { index: #indices_iter } };)*
}
static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! {
pub static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! {
#(#static_strs => { Atom { index: #indices } },)*
};
}

View File

@@ -28,6 +28,7 @@ mod iterators;
pub mod machine;
mod raw_block;
pub mod read;
mod repl_helper;
mod targets;
pub mod types;

View File

@@ -454,6 +454,23 @@ impl MachineState {
self.b0 = self.b;
}
// Safety: the atom_tbl lives for the lifetime of the machine, as does the helper, so the ptr
// will always be valid.
pub fn read_term_from_user_input(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult {
let atoms_ptr = (&self.atom_tbl.table) as *const indexmap::IndexSet<Atom>;
if let Stream::Readline(ptr) = stream {
unsafe {
let readline = ptr.as_ptr().as_mut().unwrap();
readline.set_atoms_for_completion(atoms_ptr);
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,

View File

@@ -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 {

View File

@@ -9,12 +9,14 @@ 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 indexmap::IndexSet;
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 +79,7 @@ fn get_prompt() -> &'static str {
#[derive(Debug)]
pub struct ReadlineStream {
rl: Editor<()>,
rl: Editor<Helper>,
pending_input: Cursor<String>,
add_history: bool,
}
@@ -86,7 +88,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 +100,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 +109,11 @@ impl ReadlineStream {
}
}
pub fn set_atoms_for_completion(&mut self, atoms: *const IndexSet<Atom>) {
let helper = self.rl.helper_mut().unwrap();
helper.atoms = atoms;
}
#[inline]
pub fn reset(&mut self) {
self.pending_input.get_mut().clear();

110
src/repl_helper.rs Normal file
View File

@@ -0,0 +1,110 @@
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;
}