瀏覽代碼

add smart identifier completion

Getty Ritter 2 年之前
父節點
當前提交
37e1901013
共有 4 個文件被更改,包括 79 次插入3 次删除
  1. 15 0
      src/interp.rs
  2. 1 0
      src/lib.rs
  3. 4 3
      src/main.rs
  4. 59 0
      src/repl.rs

+ 15 - 0
src/interp.rs

@@ -151,6 +151,21 @@ impl State {
         s
     }
 
+    pub fn autocomplete(&self, fragment: &str, at_beginning: bool) -> Vec<String> {
+        let mut possibilities = Vec::new();
+        for name in self.scope.keys() {
+            if name.starts_with(fragment) {
+                possibilities.push(name.clone());
+            }
+        }
+        if at_beginning {
+            if "puts".starts_with(fragment) {
+                possibilities.push("puts ".to_owned());
+            }
+        }
+        possibilities
+    }
+
     pub fn execute(&mut self, stmt: &Stmt) -> Result<(), Error> {
         Ok(match stmt {
             Stmt::Puts(expr) => {

+ 1 - 0
src/lib.rs

@@ -4,6 +4,7 @@ extern crate lalrpop_util;
 pub mod lexer;
 pub mod ast;
 pub mod interp;
+pub mod repl;
 
 #[cfg(test)]
 pub mod test {

+ 4 - 3
src/main.rs

@@ -14,8 +14,9 @@ fn run(src: &str) {
 }
 
 fn run_repl() -> Result<(), Box<dyn std::error::Error>> {
-    let mut rl = rustyline::Editor::<()>::new();
-    let mut state = State::new();
+    let mut rl = rustyline::Editor::<matzo::repl::Repl>::new();
+    let state = std::rc::Rc::new(std::cell::RefCell::new(State::new()));
+    rl.set_helper(Some(matzo::repl::Repl::new(state.clone())));
     let parser = StmtsParser::new();
     println!(
         "{}",
@@ -61,7 +62,7 @@ fn run_repl() -> Result<(), Box<dyn std::error::Error>> {
         };
 
         for stmt in stmts {
-            if let Err(err) = state.execute(&stmt) {
+            if let Err(err) = state.borrow_mut().execute(&stmt) {
                 eprintln!(
                     "{} {}",
                     ansi_term::Colour::Red.bold().paint("error:"),

+ 59 - 0
src/repl.rs

@@ -0,0 +1,59 @@
+use rustyline::{
+    Helper,
+    completion::Completer,
+    hint::Hinter,
+    highlight::Highlighter,
+    validate::Validator,
+};
+use std::cell::RefCell;
+use std::rc::Rc;
+
+pub struct Repl {
+    state: Rc<RefCell<crate::interp::State>>,
+}
+
+impl Repl {
+    pub fn new(state: Rc<RefCell<crate::interp::State>>) -> Repl {
+        Repl { state }
+    }
+}
+
+impl Completer for Repl {
+    type Candidate = String;
+
+    fn complete(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> rustyline::Result<(usize, Vec<String>)> {
+        if let Some(c) = line.chars().nth(pos-1) {
+            if c.is_alphabetic() {
+                // this means we're looking at maybe something
+                // alphabetic; let's see what the current typed thing
+                // is
+                let mut str_start = 0;
+                for (idx, ch) in line.chars().enumerate() {
+                    if ch.is_whitespace() {
+                        str_start = idx + 1;
+                    }
+                    if idx == pos {
+                        break;
+                    }
+                }
+                // we've now found the current fragment
+                let so_far = &line[str_start..pos];
+                return Ok((str_start, self.state.borrow().autocomplete(so_far, str_start == 0)))
+            }
+        }
+        Ok((pos, Vec::new()))
+    }
+}
+
+impl Hinter for Repl {
+    type Hint = String;
+}
+
+impl Highlighter for Repl {
+}
+
+impl Validator for Repl {
+}
+
+impl Helper for Repl {
+}