119625d8cSopenharmony_ciuse std::io::Write;
219625d8cSopenharmony_ci
319625d8cSopenharmony_ciuse clap::builder::StyledStr;
419625d8cSopenharmony_ciuse clap::*;
519625d8cSopenharmony_ci
619625d8cSopenharmony_ciuse crate::generator::{utils, Generator};
719625d8cSopenharmony_ciuse crate::INTERNAL_ERROR_MSG;
819625d8cSopenharmony_ci
919625d8cSopenharmony_ci/// Generate elvish completion file
1019625d8cSopenharmony_ci#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1119625d8cSopenharmony_cipub struct Elvish;
1219625d8cSopenharmony_ci
1319625d8cSopenharmony_ciimpl Generator for Elvish {
1419625d8cSopenharmony_ci    fn file_name(&self, name: &str) -> String {
1519625d8cSopenharmony_ci        format!("{name}.elv")
1619625d8cSopenharmony_ci    }
1719625d8cSopenharmony_ci
1819625d8cSopenharmony_ci    fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
1919625d8cSopenharmony_ci        let bin_name = cmd
2019625d8cSopenharmony_ci            .get_bin_name()
2119625d8cSopenharmony_ci            .expect("crate::generate should have set the bin_name");
2219625d8cSopenharmony_ci
2319625d8cSopenharmony_ci        let subcommands_cases = generate_inner(cmd, "");
2419625d8cSopenharmony_ci
2519625d8cSopenharmony_ci        let result = format!(
2619625d8cSopenharmony_ci            r#"
2719625d8cSopenharmony_ciuse builtin;
2819625d8cSopenharmony_ciuse str;
2919625d8cSopenharmony_ci
3019625d8cSopenharmony_ciset edit:completion:arg-completer[{bin_name}] = {{|@words|
3119625d8cSopenharmony_ci    fn spaces {{|n|
3219625d8cSopenharmony_ci        builtin:repeat $n ' ' | str:join ''
3319625d8cSopenharmony_ci    }}
3419625d8cSopenharmony_ci    fn cand {{|text desc|
3519625d8cSopenharmony_ci        edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
3619625d8cSopenharmony_ci    }}
3719625d8cSopenharmony_ci    var command = '{bin_name}'
3819625d8cSopenharmony_ci    for word $words[1..-1] {{
3919625d8cSopenharmony_ci        if (str:has-prefix $word '-') {{
4019625d8cSopenharmony_ci            break
4119625d8cSopenharmony_ci        }}
4219625d8cSopenharmony_ci        set command = $command';'$word
4319625d8cSopenharmony_ci    }}
4419625d8cSopenharmony_ci    var completions = [{subcommands_cases}
4519625d8cSopenharmony_ci    ]
4619625d8cSopenharmony_ci    $completions[$command]
4719625d8cSopenharmony_ci}}
4819625d8cSopenharmony_ci"#,
4919625d8cSopenharmony_ci        );
5019625d8cSopenharmony_ci
5119625d8cSopenharmony_ci        w!(buf, result.as_bytes());
5219625d8cSopenharmony_ci    }
5319625d8cSopenharmony_ci}
5419625d8cSopenharmony_ci
5519625d8cSopenharmony_ci// Escape string inside single quotes
5619625d8cSopenharmony_cifn escape_string(string: &str) -> String {
5719625d8cSopenharmony_ci    string.replace('\'', "''")
5819625d8cSopenharmony_ci}
5919625d8cSopenharmony_ci
6019625d8cSopenharmony_cifn get_tooltip<T: ToString>(help: Option<&StyledStr>, data: T) -> String {
6119625d8cSopenharmony_ci    match help {
6219625d8cSopenharmony_ci        Some(help) => escape_string(&help.to_string()),
6319625d8cSopenharmony_ci        _ => data.to_string(),
6419625d8cSopenharmony_ci    }
6519625d8cSopenharmony_ci}
6619625d8cSopenharmony_ci
6719625d8cSopenharmony_cifn generate_inner(p: &Command, previous_command_name: &str) -> String {
6819625d8cSopenharmony_ci    debug!("generate_inner");
6919625d8cSopenharmony_ci
7019625d8cSopenharmony_ci    let command_name = if previous_command_name.is_empty() {
7119625d8cSopenharmony_ci        p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()
7219625d8cSopenharmony_ci    } else {
7319625d8cSopenharmony_ci        format!("{};{}", previous_command_name, &p.get_name())
7419625d8cSopenharmony_ci    };
7519625d8cSopenharmony_ci
7619625d8cSopenharmony_ci    let mut completions = String::new();
7719625d8cSopenharmony_ci    let preamble = String::from("\n            cand ");
7819625d8cSopenharmony_ci
7919625d8cSopenharmony_ci    for option in p.get_opts() {
8019625d8cSopenharmony_ci        if let Some(shorts) = option.get_short_and_visible_aliases() {
8119625d8cSopenharmony_ci            let tooltip = get_tooltip(option.get_help(), shorts[0]);
8219625d8cSopenharmony_ci            for short in shorts {
8319625d8cSopenharmony_ci                completions.push_str(&preamble);
8419625d8cSopenharmony_ci                completions.push_str(format!("-{short} '{tooltip}'").as_str());
8519625d8cSopenharmony_ci            }
8619625d8cSopenharmony_ci        }
8719625d8cSopenharmony_ci
8819625d8cSopenharmony_ci        if let Some(longs) = option.get_long_and_visible_aliases() {
8919625d8cSopenharmony_ci            let tooltip = get_tooltip(option.get_help(), longs[0]);
9019625d8cSopenharmony_ci            for long in longs {
9119625d8cSopenharmony_ci                completions.push_str(&preamble);
9219625d8cSopenharmony_ci                completions.push_str(format!("--{long} '{tooltip}'").as_str());
9319625d8cSopenharmony_ci            }
9419625d8cSopenharmony_ci        }
9519625d8cSopenharmony_ci    }
9619625d8cSopenharmony_ci
9719625d8cSopenharmony_ci    for flag in utils::flags(p) {
9819625d8cSopenharmony_ci        if let Some(shorts) = flag.get_short_and_visible_aliases() {
9919625d8cSopenharmony_ci            let tooltip = get_tooltip(flag.get_help(), shorts[0]);
10019625d8cSopenharmony_ci            for short in shorts {
10119625d8cSopenharmony_ci                completions.push_str(&preamble);
10219625d8cSopenharmony_ci                completions.push_str(format!("-{short} '{tooltip}'").as_str());
10319625d8cSopenharmony_ci            }
10419625d8cSopenharmony_ci        }
10519625d8cSopenharmony_ci
10619625d8cSopenharmony_ci        if let Some(longs) = flag.get_long_and_visible_aliases() {
10719625d8cSopenharmony_ci            let tooltip = get_tooltip(flag.get_help(), longs[0]);
10819625d8cSopenharmony_ci            for long in longs {
10919625d8cSopenharmony_ci                completions.push_str(&preamble);
11019625d8cSopenharmony_ci                completions.push_str(format!("--{long} '{tooltip}'").as_str());
11119625d8cSopenharmony_ci            }
11219625d8cSopenharmony_ci        }
11319625d8cSopenharmony_ci    }
11419625d8cSopenharmony_ci
11519625d8cSopenharmony_ci    for subcommand in p.get_subcommands() {
11619625d8cSopenharmony_ci        let data = &subcommand.get_name();
11719625d8cSopenharmony_ci        let tooltip = get_tooltip(subcommand.get_about(), data);
11819625d8cSopenharmony_ci
11919625d8cSopenharmony_ci        completions.push_str(&preamble);
12019625d8cSopenharmony_ci        completions.push_str(format!("{data} '{tooltip}'").as_str());
12119625d8cSopenharmony_ci    }
12219625d8cSopenharmony_ci
12319625d8cSopenharmony_ci    let mut subcommands_cases = format!(
12419625d8cSopenharmony_ci        r"
12519625d8cSopenharmony_ci        &'{}'= {{{}
12619625d8cSopenharmony_ci        }}",
12719625d8cSopenharmony_ci        &command_name, completions
12819625d8cSopenharmony_ci    );
12919625d8cSopenharmony_ci
13019625d8cSopenharmony_ci    for subcommand in p.get_subcommands() {
13119625d8cSopenharmony_ci        let subcommand_subcommands_cases = generate_inner(subcommand, &command_name);
13219625d8cSopenharmony_ci        subcommands_cases.push_str(&subcommand_subcommands_cases);
13319625d8cSopenharmony_ci    }
13419625d8cSopenharmony_ci
13519625d8cSopenharmony_ci    subcommands_cases
13619625d8cSopenharmony_ci}
137