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
use crate::complete_gen::Complete;
use std::ffi::OsStr;

fn dump_bash_completer(name: &str) {
    println!(
        r#"_bpaf_dynamic_completion()
{{
    line="$1 --bpaf-complete-rev=8 ${{COMP_WORDS[@]:1}}"
    if [[ ${{COMP_WORDS[-1]}} == "" ]]; then
        line="${{line}} \"\""
    fi
    source <( eval ${{line}})
}}
complete -o nosort -F _bpaf_dynamic_completion {name}"#,
        name = name,
    );
}

fn dump_zsh_completer(name: &str) {
    println!(
        r#"#compdef {name}
local line
line="${{words[1]}} --bpaf-complete-rev=7 ${{words[@]:1}}"
if [[ ${{words[-1]}} == "" ]]; then
    line="${{line}} \"\""
fi
source <(eval ${{line}})
"#,
        name = name
    );
}

fn dump_fish_completer(_name: &str) {
    println!(
        r#"set -l current (commandline --tokenize --current-process)
set -l tmpline $current[1] --bpaf-complete-rev=9 $current[2..]
if test (commandline --current-process) != (string trim (commandline --current-process))
    set tmpline $tmpline ""
end
source ( $tmpline | psub )"#
    );
}

// I would love to support elvish better but debugger is not a thing
// and on any error in code it simply replies "no candidates" with no
// obvious way even to print "you are here"...
// https://github.com/elves/elvish/issues/803
fn dump_elvish_completer(name: &str) {
    println!(
        "\
set edit:completion:arg-completer[{name}] = {{ |@args| var args = $args[1..];
     var @lines = ( {name} --bpaf-complete-rev={rev} $@args );
     use str;
     for line $lines {{
         var @arg = (str:split \"\\t\" $line)
         try {{
             edit:complex-candidate $arg[0] &display=( printf \"%-19s %s\" $arg[0] $arg[1] )
         }} catch {{
             edit:complex-candidate $line
         }}
     }}
}}",
        name = name,
        rev = 1,
    );
}

#[derive(Debug)]
pub(crate) struct ArgScanner<'a> {
    pub(crate) revision: Option<usize>,
    pub(crate) name: Option<&'a str>,
}

impl ArgScanner<'_> {
    pub(crate) fn check_next(&mut self, arg: &OsStr) -> bool {
        let arg = match arg.to_str() {
            Some(arg) => arg,
            None => return false,
        };
        // this only works when there's a name
        if let Some(name) = &self.name {
            let mut matched = true;
            match arg {
                "--bpaf-complete-style-zsh" => dump_zsh_completer(name),
                "--bpaf-complete-style-bash" => dump_bash_completer(name),
                "--bpaf-complete-style-fish" => dump_fish_completer(name),
                "--bpaf-complete-style-elvish" => dump_elvish_completer(name),
                _ => {
                    matched = false;
                }
            }
            if matched {
                std::process::exit(0)
            }
        }
        if let Some(ver) = arg.strip_prefix("--bpaf-complete-rev=") {
            if let Ok(ver) = ver.parse::<usize>() {
                self.revision = Some(ver);
            }
            return true;
        }
        false
    }
    pub(crate) fn done(&self) -> Option<Complete> {
        Some(Complete::new(self.revision?))
    }
}