Skip to main content

ase_shell/
repl.rs

1use crate::commands::complete_command;
2
3use anyhow::Context;
4use rustyline::completion::{Completer, FilenameCompleter, Pair, extract_word};
5use rustyline::config::{BellStyle, CompletionType, Configurer};
6use rustyline::highlight::Highlighter;
7use rustyline::hint::Hinter;
8use rustyline::history::FileHistory;
9use rustyline::validate::{ValidationContext, ValidationResult, Validator};
10use rustyline::{Config, Context as RlContext, Editor, Helper};
11
12pub type ReplEditor = Editor<AseHelper, FileHistory>;
13
14pub fn create_editor() -> anyhow::Result<ReplEditor> {
15  let config = Config::default();
16  let history = FileHistory::new();
17  let mut editor = Editor::<AseHelper, FileHistory>::with_history(config, history)
18    .context("create readline editor")?;
19  editor.set_helper(Some(AseHelper));
20  editor.set_completion_type(CompletionType::List);
21  editor.set_bell_style(BellStyle::Audible);
22  Ok(editor)
23}
24
25pub struct AseHelper;
26
27impl Default for AseHelper {
28  fn default() -> Self {
29    Self
30  }
31}
32
33impl Completer for AseHelper {
34  type Candidate = Pair;
35
36  fn complete(
37    &self,
38    line: &str,
39    pos: usize,
40    ctx: &RlContext<'_>,
41  ) -> rustyline::Result<(usize, Vec<Pair>)> {
42    let (start, word) = extract_word(line, pos, None, |c| c == ' ' || c == '\t');
43    let before = &line[..start];
44    let mut parts = before.split_whitespace();
45    let first = parts.next();
46
47    if first.is_none() {
48      let candidates = complete_command(word)
49        .into_iter()
50        .map(|s| Pair {
51          display: s.clone(),
52          replacement: s,
53        })
54        .collect();
55      return Ok((start, candidates));
56    }
57
58    let cmd_name = first.unwrap();
59    if cmd_name == "cd" {
60      let file_completer = FilenameCompleter::new();
61      return file_completer.complete(line, pos, ctx);
62    }
63
64    Ok((pos, Vec::new()))
65  }
66}
67
68pub struct EmptyHint;
69
70impl rustyline::hint::Hint for EmptyHint {
71  fn display(&self) -> &str {
72    ""
73  }
74  fn completion(&self) -> Option<&str> {
75    None
76  }
77}
78
79impl Hinter for AseHelper {
80  type Hint = EmptyHint;
81
82  fn hint(&self, _line: &str, _pos: usize, _ctx: &RlContext<'_>) -> Option<EmptyHint> {
83    None
84  }
85}
86
87impl Highlighter for AseHelper {}
88
89impl Validator for AseHelper {
90  fn validate(&self, _ctx: &mut ValidationContext<'_>) -> rustyline::Result<ValidationResult> {
91    Ok(ValidationResult::Valid(None))
92  }
93}
94
95impl Helper for AseHelper {}