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
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use super::{init_repo, print_iter};
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;
use crate::SEP;
use std::cmp::Reverse;
use structopt::StructOpt;

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::from_iter_safe(args) {
        Ok(root) => Ok(root),
        Err(e) => {
            log::warn!("展開別名時出錯 {}", e);
            // NOTE: -V 或 --help 也會走到這裡
            Err(Error::Completion)
        }
    }
}

async fn fuzz_arr<'a>(
    name: &str,
    iter: impl Iterator<Item = RepoEntry<'a>>,
) -> Result<Vec<RepoEntry<'a>>> {
    // TODO: 測試這個複雜的函式,包括前綴和次級結果
    let res = fuzz_with_multifuzz_ratio(name, iter, SEP, 0.6).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) -> Result {
    match comp {
        Completion::LS { name, args } => {
            let mut new_root = match Root::from_iter_safe(args) {
                Ok(Root {
                    subcmd: Some(Subs::Tags(_)),
                    ..
                }) => {
                    // TODO: 在補全腳本中處理,而不要在這邊
                    return Err(Error::Completion);
                }
                Ok(t) => t,
                Err(e) => {
                    log::warn!("補全時出錯 {}", e);
                    // NOTE: -V 或 --help 也會走到這裡
                    return Err(Error::Completion);
                }
            };
            log::info!("補完模式,參數為 {:?}", new_root);
            new_root.set_home_unless_from_alias()?;
            new_root.sanitize_flags();
            let mut repo = init_repo(new_root.root_args, false).await?;

            let iter = repo.iter_mut(false);
            let scripts = if let Some(name) = name {
                fuzz_arr(&name, iter).await?
            } else {
                let mut t: Vec<_> = iter.collect();
                sort(&mut t);
                t
            };

            print_iter(scripts.iter().map(|s| s.name.key()), " ");

            Ok(())
        }
        Completion::Alias { args } => {
            let root = parse_alias_root(&args)?;
            let home = path::compute_home_path_optional(root.root_args.hs_home.as_ref())?;
            let conf = Config::load(&home)?;
            if let Some(new_args) = root.expand_alias(&args, &conf) {
                print_iter(new_args, " ");
            } else {
                print_iter(args.iter(), " ");
            }
            Ok(())
        }
        Completion::Home { args } => {
            let root = parse_alias_root(&args)?;
            let home = root.root_args.hs_home.ok_or_else(|| Error::Completion)?;
            print!("{}", home);
            Ok(())
        }
    }
}