diff options
| author | sigoden <sigoden@gmail.com> | 2023-11-02 15:33:22 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-02 15:33:22 +0800 |
| commit | 652b51509d461d2121e94304d6f00e714ebd0f9f (patch) | |
| tree | fdaeffaa52ca04553e06fcad92fe95f73c6bd2a7 /src/repl/completer.rs | |
| parent | 7c6841782d36faacc2aa3616dc3ed1b9403fa26e (diff) | |
| download | aichat-652b51509d461d2121e94304d6f00e714ebd0f9f.tar.gz | |
refactor: improve repl completer (#199)
Diffstat (limited to 'src/repl/completer.rs')
| -rw-r--r-- | src/repl/completer.rs | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/repl/completer.rs b/src/repl/completer.rs new file mode 100644 index 0000000..ea24be8 --- /dev/null +++ b/src/repl/completer.rs @@ -0,0 +1,94 @@ +use std::collections::HashMap; + +use super::{parse_command, REPL_COMMANDS}; + +use crate::config::GlobalConfig; + +use reedline::{Completer, Span, Suggestion}; + +impl Completer for ReplCompleter { + fn complete(&mut self, line: &str, pos: usize) -> Vec<Suggestion> { + let mut suggestions = vec![]; + if line.len() != pos { + return suggestions; + } + let line = &line[0..pos]; + if let Some((cmd, args)) = parse_command(line) { + let commands: Vec<_> = self + .commands + .iter() + .filter(|(cmd_name, _)| match args { + Some(args) => cmd_name.starts_with(&format!("{cmd} {args}")), + None => cmd_name.starts_with(cmd), + }) + .collect(); + + if args.is_some() || line.ends_with(' ') { + let args = args.unwrap_or_default(); + let start = line.chars().take_while(|c| *c == ' ').count() + cmd.len() + 1; + let span = Span::new(start, pos); + suggestions.extend( + self.config + .read() + .repl_complete(cmd, args) + .iter() + .map(|name| create_suggestion(name.clone(), None, span)), + ) + } + + if suggestions.is_empty() { + let start = line.chars().take_while(|c| *c == ' ').count(); + let span = Span::new(start, pos); + suggestions.extend(commands.iter().map(|(name, desc)| { + let has_group = self.groups.get(name).map(|v| *v > 1).unwrap_or_default(); + let name = if has_group { + name.to_string() + } else { + format!("{name} ") + }; + create_suggestion(name, Some(desc.to_string()), span) + })) + } + } + suggestions + } +} + +pub struct ReplCompleter { + config: GlobalConfig, + commands: Vec<(&'static str, &'static str)>, + groups: HashMap<&'static str, usize>, +} + +impl ReplCompleter { + pub fn new(config: &GlobalConfig) -> Self { + let mut groups = HashMap::new(); + + let mut commands = REPL_COMMANDS.to_vec(); + commands.sort_by(|(a, _), (b, _)| a.cmp(b)); + + for (name, _) in REPL_COMMANDS.iter() { + if let Some(count) = groups.get(name) { + groups.insert(*name, count + 1); + } else { + groups.insert(*name, 1); + } + } + + Self { + config: config.clone(), + commands, + groups, + } + } +} + +fn create_suggestion(value: String, description: Option<String>, span: Span) -> Suggestion { + Suggestion { + value, + description, + extra: None, + span, + append_whitespace: false, + } +} |
