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