| use std::sync::Arc; |
|
|
| use forge_select::ForgeWidget; |
|
|
| use crate::completer::input_completer::InputSuggestion; |
| use crate::completer::search_term::Span; |
| use crate::model::{ForgeCommand, ForgeCommandManager}; |
|
|
| |
| |
| #[derive(Clone)] |
| struct CommandRow(ForgeCommand); |
|
|
| impl std::fmt::Display for CommandRow { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| write!(f, "{:<30} {}", self.0.name, self.0.description) |
| } |
| } |
|
|
| #[derive(Clone)] |
| pub struct CommandCompleter(Arc<ForgeCommandManager>); |
|
|
| impl CommandCompleter { |
| pub fn new(command_manager: Arc<ForgeCommandManager>) -> Self { |
| Self(command_manager) |
| } |
| } |
|
|
| impl CommandCompleter { |
| pub fn complete(&mut self, line: &str, _: usize) -> Vec<InputSuggestion> { |
| |
| let sentinel = if line.starts_with(':') { ':' } else { '/' }; |
|
|
| |
| let commands: Vec<CommandRow> = self |
| .0 |
| .list() |
| .into_iter() |
| .filter_map(|cmd| { |
| let display_name = if cmd.name.starts_with('!') { |
| cmd.name.clone() |
| } else { |
| format!("{}{}", sentinel, cmd.name) |
| }; |
|
|
| |
| if display_name.starts_with(line) { |
| Some(CommandRow(ForgeCommand { |
| name: display_name, |
| description: cmd.description, |
| value: cmd.value, |
| })) |
| } else { |
| None |
| } |
| }) |
| .collect(); |
|
|
| if commands.is_empty() { |
| return vec![]; |
| } |
|
|
| |
| |
| let initial_query = line |
| .strip_prefix('/') |
| .or_else(|| line.strip_prefix(':')) |
| .or_else(|| line.strip_prefix('!')) |
| .unwrap_or(line); |
|
|
| let mut builder = ForgeWidget::select("Command", commands); |
| if !initial_query.is_empty() { |
| builder = builder.with_initial_text(initial_query); |
| } |
|
|
| match builder.prompt() { |
| Ok(Some(row)) => { |
| vec![InputSuggestion { |
| value: row.0.name, |
| span: Span::new(0, line.len()), |
| append_whitespace: true, |
| }] |
| } |
| _ => vec![], |
| } |
| } |
| } |
|
|