File size: 2,772 Bytes
d90101d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 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![],
}
}
}
|