]> Repositorios git - scryer-prolog.git/commitdiff
wip: basic completion of predicates
authorEuan Lacy <[email protected]>
Tue, 10 May 2022 03:40:22 +0000 (04:40 +0100)
committerEuan Lacy <[email protected]>
Tue, 10 May 2022 17:10:18 +0000 (18:10 +0100)
src/lib.rs
src/machine/machine_state.rs
src/machine/system_calls.rs
src/read.rs
src/repl_helper.rs [new file with mode: 0644]

index 99c5d9f988c530c23627d68953ef338a7bf45a72..fc8d8ca539e5bbf8ffd909decf3a7aeb1e65e74a 100644 (file)
@@ -28,6 +28,7 @@ mod iterators;
 pub mod machine;
 mod raw_block;
 pub mod read;
+mod repl_helper;
 mod targets;
 pub mod types;
 
index a6b375b6068a8efe892dddf2a84eed6c2d6be179..f33f09014832f451fa33144330e4062f32a7c680 100644 (file)
@@ -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,
index 6272bcee9b9b549a906605026aa98909f6ca4304..2e016abd67ac8cb27d8385c2b22ac4a76044cbff 100644 (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 {
index c58e99f4f17fae9d0aea51a2a2cd04700e672ffe..399d9280019a7bc84ad23c87ef02ef76850f0579 100644 (file)
@@ -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<Helper>,
     pending_input: Cursor<String>,
     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<String>) {
+        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 (file)
index 0000000..96f63ef
--- /dev/null
@@ -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::Item> {
+//         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<String>,
+}
+
+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<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
+}
+
+impl Completer for Helper {
+    type Candidate = String;
+
+    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();
+            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;
+}