use super::{init_repo, print_iter, to_display_args};
use crate::args::{AliasRoot, Completion, Root, Subs};
use crate::config::Config;
use crate::error::{Error, Result};
use crate::fuzzy::{fuzz_with_multifuzz_ratio, is_prefix, FuzzResult};
use crate::path;
use crate::script_repo::{RepoEntry, ScriptRepo, Visibility};
use crate::SEP;
use clap::Parser;
use std::cmp::Reverse;
fn sort(v: &mut Vec<RepoEntry<'_>>) {
v.sort_by_key(|s| Reverse(s.last_time()));
}
fn parse_alias_root(args: &[String]) -> Result<AliasRoot> {
match AliasRoot::try_parse_from(args) {
Ok(root) => Ok(root),
Err(e) => {
log::warn!("展開別名時出錯 {}", e);
Err(Error::Completion)
}
}
}
async fn fuzz_arr<'a>(
name: &str,
iter: impl Iterator<Item = RepoEntry<'a>>,
) -> Result<Vec<RepoEntry<'a>>> {
let res = fuzz_with_multifuzz_ratio(name, iter, SEP, Some(60)).await?;
Ok(match res {
None => vec![],
Some(FuzzResult::High(t) | FuzzResult::Low(t)) => vec![t],
Some(FuzzResult::Multi {
ans,
others,
mut still_others,
}) => {
let prefix = ans.name.key();
let mut first_others = vec![];
let mut prefixed_others = vec![];
for candidate in others.into_iter() {
if is_prefix(&*prefix, &*candidate.name.key(), SEP) {
prefixed_others.push(candidate);
} else {
first_others.push(candidate);
}
}
first_others.push(ans);
sort(&mut first_others);
sort(&mut prefixed_others);
sort(&mut still_others);
first_others.append(&mut prefixed_others);
first_others.append(&mut still_others);
first_others
}
})
}
pub async fn handle_completion(comp: Completion, repo: &mut Option<ScriptRepo>) -> Result {
match comp {
Completion::LS {
name,
args,
limit,
bang,
} => {
let mut new_root = match Root::try_parse_from(args) {
Ok(Root {
subcmd: Some(Subs::Tags(_) | Subs::Types(_) | Subs::Alias { before: None, .. }),
..
}) => {
return Err(Error::Completion);
}
Ok(t) => t,
Err(e) => {
log::warn!("補全時出錯 {}", e);
return Err(Error::Completion);
}
};
log::info!("補完模式,參數為 {:?}", new_root);
new_root.set_home_unless_from_alias(false)?;
new_root.sanitize_flags(bang);
*repo = Some(init_repo(new_root.root_args, false).await?);
let iter = repo.as_mut().unwrap().iter_mut(Visibility::Normal);
let scripts = if let Some(name) = name {
fuzz_arr(&name, iter).await?
} else {
let mut t: Vec<_> = iter.collect();
sort(&mut t);
t
};
let iter = scripts.iter().map(|s| s.name.key());
if let Some(limit) = limit {
print_iter(iter.take(limit.get()), " ");
} else {
print_iter(iter, " ");
}
}
Completion::NoSubcommand { args } => {
if let Ok(root) = parse_alias_root(&args) {
if root.subcmd.is_some() {
log::debug!("子命令 = {:?}", root.subcmd);
return Err(Error::Completion);
}
} }
Completion::Alias { args } => {
let root = parse_alias_root(&args)?;
if root.root_args.no_alias {
log::info!("無別名模式");
return Err(Error::Completion);
}
let home = path::compute_home_path_optional(root.root_args.hs_home.as_ref(), false)?;
let conf = Config::load(&home)?;
if let Some((false, new_args)) = root.expand_alias(&args, &conf) {
print_iter(new_args, " ");
} else {
log::info!("並非別名");
return Err(Error::Completion);
};
}
Completion::Home { args } => {
let root = parse_alias_root(&args)?;
let home = root.root_args.hs_home.ok_or_else(|| Error::Completion)?;
print!("{}", home);
}
Completion::ParseRun { args } => {
let mut root = Root::try_parse_from(args).map_err(|e| {
log::warn!("補全時出錯 {}", e);
Error::Completion
})?;
root.sanitize()?;
match root.subcmd {
Some(Subs::Run {
script_query, args, ..
}) => {
let iter = std::iter::once(script_query.to_string())
.chain(args.into_iter().map(|s| to_display_args(s)));
print_iter(iter, " ");
}
res @ _ => {
log::warn!("非執行指令 {:?}", res);
return Err(Error::Completion);
}
}
}
}
Ok(())
}