SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
d90101d verified
Raw
History Blame Contribute Delete
2.77 kB
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};
/// A display wrapper for `ForgeCommand` that renders the name and description
/// side-by-side for the interactive picker.
#[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> {
// Determine which sentinel the user typed (`:` or `/`), defaulting to `/`.
let sentinel = if line.starts_with(':') { ':' } else { '/' };
// Build the list of display names using the same sentinel the user typed.
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)
};
// Only include commands that match what the user has typed so far.
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![];
}
// Extract the initial query text (everything after the leading sentinel or
// `!`).
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![],
}
}
}