clap_complete/shells/
powershell.rs1use std::io::Write;
2
3use clap::builder::StyledStr;
4use clap::{Arg, Command};
5
6use crate::generator::{utils, Generator};
7use crate::INTERNAL_ERROR_MSG;
8
9#[derive(Copy, Clone, PartialEq, Eq, Debug)]
11pub struct PowerShell;
12
13impl Generator for PowerShell {
14 fn file_name(&self, name: &str) -> String {
15 format!("_{name}.ps1")
16 }
17
18 fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
19 let bin_name = cmd
20 .get_bin_name()
21 .expect("crate::generate should have set the bin_name");
22
23 let subcommands_cases = generate_inner(cmd, "");
24
25 let result = format!(
26 r#"
27using namespace System.Management.Automation
28using namespace System.Management.Automation.Language
29
30Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{
31 param($wordToComplete, $commandAst, $cursorPosition)
32
33 $commandElements = $commandAst.CommandElements
34 $command = @(
35 '{bin_name}'
36 for ($i = 1; $i -lt $commandElements.Count; $i++) {{
37 $element = $commandElements[$i]
38 if ($element -isnot [StringConstantExpressionAst] -or
39 $element.StringConstantType -ne [StringConstantType]::BareWord -or
40 $element.Value.StartsWith('-') -or
41 $element.Value -eq $wordToComplete) {{
42 break
43 }}
44 $element.Value
45 }}) -join ';'
46
47 $completions = @(switch ($command) {{{subcommands_cases}
48 }})
49
50 $completions.Where{{ $_.CompletionText -like "$wordToComplete*" }} |
51 Sort-Object -Property ListItemText
52}}
53"#
54 );
55
56 w!(buf, result.as_bytes());
57 }
58}
59
60fn escape_string(string: &str) -> String {
62 string.replace('\'', "''")
63}
64
65fn escape_help<T: ToString>(help: Option<&StyledStr>, data: T) -> String {
66 match help {
67 Some(help) => escape_string(&help.to_string().replace('\n', " ")),
68 _ => data.to_string(),
69 }
70}
71
72fn generate_inner(p: &Command, previous_command_name: &str) -> String {
73 debug!("generate_inner");
74
75 let command_names = if previous_command_name.is_empty() {
76 vec![p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()]
77 } else {
78 p.get_name_and_visible_aliases()
79 .into_iter()
80 .map(|name| format!("{};{}", previous_command_name, name))
81 .collect()
82 };
83
84 let mut completions = String::new();
85 let preamble = String::from("\n [CompletionResult]::new(");
86
87 for option in p.get_opts() {
88 generate_aliases(&mut completions, &preamble, option);
89 }
90
91 for flag in utils::flags(p) {
92 generate_aliases(&mut completions, &preamble, &flag);
93 }
94
95 for subcommand in p.get_subcommands() {
96 for name in subcommand.get_name_and_visible_aliases() {
97 let tooltip = escape_help(subcommand.get_about(), name);
98 completions.push_str(&preamble);
99 completions.push_str(&format!(
100 "'{name}', '{name}', [CompletionResultType]::ParameterValue, '{tooltip}')"
101 ));
102 }
103 }
104
105 let mut subcommands_cases = String::new();
106 for command_name in &command_names {
107 subcommands_cases.push_str(&format!(
108 r"
109 '{}' {{{}
110 break
111 }}",
112 command_name, completions
113 ));
114 }
115
116 for subcommand in p.get_subcommands() {
117 for command_name in &command_names {
118 let subcommand_subcommands_cases = generate_inner(subcommand, command_name);
119 subcommands_cases.push_str(&subcommand_subcommands_cases);
120 }
121 }
122
123 subcommands_cases
124}
125
126fn generate_aliases(completions: &mut String, preamble: &String, arg: &Arg) {
127 use std::fmt::Write as _;
128
129 if let Some(aliases) = arg.get_short_and_visible_aliases() {
130 let tooltip = escape_help(arg.get_help(), aliases[0]);
131 for alias in aliases {
132 let _ = write!(
133 completions,
134 "{preamble}'-{alias}', '{alias}{}', [CompletionResultType]::ParameterName, '{tooltip}')",
135 if alias.is_uppercase() { " " } else { "" },
137 );
138 }
139 }
140 if let Some(aliases) = arg.get_long_and_visible_aliases() {
141 let tooltip = escape_help(arg.get_help(), aliases[0]);
142 for alias in aliases {
143 let _ = write!(
144 completions,
145 "{preamble}'--{alias}', '{alias}', [CompletionResultType]::ParameterName, '{tooltip}')"
146 );
147 }
148 }
149}