use std::{cell::RefCell, rc::Rc};
use rustyline::{
Helper, Result,
completion::{Completer, FilenameCompleter, Pair},
highlight::Highlighter,
hint::Hinter,
validate::Validator,
};
use crate::{Repl, commands::Edge};
pub struct ReplHelper {
filename_completer: FilenameCompleter,
repl: Rc<RefCell<Repl>>,
}
impl ReplHelper {
pub fn new(repl: Rc<RefCell<Repl>>) -> Self {
Self {
filename_completer: FilenameCompleter::new(),
repl,
}
}
fn complete_command(&self, line: &str, pos: usize) -> Result<(usize, Vec<Pair>)> {
let repl = self.repl.borrow();
let line = &line[..pos];
let mut args = line.split_whitespace().collect::<Vec<_>>();
if char::is_whitespace(line.chars().last().unwrap()) {
args.push("");
}
let Some(node) = repl.commands.traverse(&args[..args.len() - 1]) else {
return Ok((pos, Vec::new()));
};
let last_part = args.last().unwrap();
let last_part_start = line.rfind(last_part).unwrap_or(0);
let mut suggestions = Vec::new();
let keywords = node.next_keywords_by_prefix(last_part).map(|k| Pair {
display: k.to_owned(),
replacement: k.to_owned(),
});
suggestions.extend(keywords);
if node.edges.contains_key(&Edge::Filename) {
let (start, filenames) = self.filename_completer.complete_path_unsorted(line, pos)?;
debug_assert_eq!(start, last_part_start);
suggestions.extend(filenames);
}
if node.edges.contains_key(&Edge::Variable) {
}
suggestions.sort_by(|a, b| a.display.cmp(&b.display));
Ok((last_part_start, suggestions))
}
}
impl Completer for ReplHelper {
type Candidate = Pair;
fn complete(
&self,
line: &str,
pos: usize,
_ctx: &rustyline::Context,
) -> Result<(usize, Vec<Self::Candidate>)> {
if line.starts_with('.') {
self.complete_command(line, pos)
} else {
Ok((0, Vec::new()))
}
}
}
impl Hinter for ReplHelper {
type Hint = String;
}
impl Highlighter for ReplHelper {}
impl Validator for ReplHelper {}
impl Helper for ReplHelper {}