mod commands;
mod subcommands;
use crate::meta_command_catalog::{
HISTORY_BROWSE_TARGETS, HISTORY_CLEAR_TARGETS, IPC_SEND_POLICIES, MetaCommand, REPREX_MODES,
lookup_meta_command,
};
use reedline::{Completer, CompletionResult, Suggestion};
pub struct MetaCommandCompleter {
excluded_commands: Vec<&'static str>,
}
impl MetaCommandCompleter {
pub fn new() -> Self {
MetaCommandCompleter {
excluded_commands: vec![],
}
}
pub fn with_exclusions(excluded_commands: Vec<&'static str>) -> Self {
MetaCommandCompleter { excluded_commands }
}
pub(crate) fn shell_mode_exclusions() -> Vec<&'static str> {
vec![
"shell", "system", "restart", "reprex", "switch", "h", "help",
]
}
fn complete_commands(&self, line: &str, pos: usize) -> Vec<Suggestion> {
let trimmed = line.trim_start();
if !trimmed.starts_with(':') {
return vec![];
}
let after_colon = &trimmed[1..];
let has_trailing_space = after_colon.ends_with(' ') || after_colon.ends_with('\t');
let parts: Vec<&str> = after_colon.split_whitespace().collect();
let leading_whitespace = line.len() - trimmed.len();
if let Some(suggestions) = Self::complete_directory_argument(
line,
pos,
leading_whitespace,
&parts,
has_trailing_space,
) {
return suggestions;
}
match (parts.len(), has_trailing_space) {
(0, _) => self.complete_all_commands(leading_whitespace + 1, pos),
(1, false) => self.complete_command_name(parts[0], leading_whitespace + 1, pos),
(1, true) => self.complete_first_argument(line, pos, parts[0], ""),
(2, false) => self.complete_first_argument(line, pos, parts[0], parts[1]),
(2, true) => self.complete_second_argument(pos, parts[0], parts[1], ""),
(3, false) => self.complete_second_argument(pos, parts[0], parts[1], parts[2]),
_ => vec![],
}
}
fn complete_first_argument(
&self,
line: &str,
pos: usize,
cmd: &str,
partial: &str,
) -> Vec<Suggestion> {
match lookup_meta_command(cmd).map(|def| def.command) {
Some(MetaCommand::Switch) => self.complete_switch_versions(line, pos, partial),
Some(MetaCommand::History) => self.complete_history_subcommands(pos, partial),
Some(MetaCommand::Ipc) => self.complete_ipc_subcommands(pos, partial),
Some(MetaCommand::Reprex) => self.complete_targets(pos, partial, REPREX_MODES),
_ => vec![],
}
}
fn complete_second_argument(
&self,
pos: usize,
cmd: &str,
subcmd: &str,
partial: &str,
) -> Vec<Suggestion> {
match (lookup_meta_command(cmd).map(|def| def.command), subcmd) {
(Some(MetaCommand::History), "clear") => {
self.complete_targets(pos, partial, HISTORY_CLEAR_TARGETS)
}
(Some(MetaCommand::History), "browse") => {
self.complete_targets(pos, partial, HISTORY_BROWSE_TARGETS)
}
(Some(MetaCommand::Ipc), "send-policy") => {
self.complete_targets(pos, partial, IPC_SEND_POLICIES)
}
_ => vec![],
}
}
}
impl Default for MetaCommandCompleter {
fn default() -> Self {
Self::new()
}
}
impl Completer for MetaCommandCompleter {
fn complete(&mut self, line: &str, pos: usize) -> CompletionResult {
CompletionResult::fresh(self.complete_commands(line, pos))
}
}
#[cfg(test)]
mod tests;