1use crate::command::Command;
8
9#[derive(Debug, Clone, Copy)]
31pub enum Shell {
32 Bash,
34 Zsh,
36 Fish,
38}
39
40impl Command {
41 pub fn generate_completion(&self, shell: Shell) -> String {
75 match shell {
76 Shell::Bash => self.generate_bash_completion(),
77 Shell::Zsh => self.generate_zsh_completion(),
78 Shell::Fish => self.generate_fish_completion(),
79 }
80 }
81
82 fn generate_bash_completion(&self) -> String {
83 let name = self.name();
84 let upper = name.to_uppercase();
85 format!(
86 r#"# Bash completion for {name}
87_{name}_complete() {{
88 local cur prev words cword
89 _get_comp_words_by_ref -n : cur prev words cword
90
91 # Call our binary with special completion env var
92 local IFS=$'\n'
93 local response
94 response=$({upper}_COMPLETE=bash "${{words[0]}}" __complete "${{words[@]:1:$((cword-1))}}" "$cur" 2>/dev/null)
95
96 if [[ -n "$response" ]]; then
97 # Use printf to handle each line separately
98 local lines=()
99 local help_messages=()
100 while IFS= read -r line; do
101 if [[ "$line" == _activehelp_* ]]; then
102 # Extract help message
103 help_messages+=("${{line#_activehelp_ }}")
104 else
105 lines+=("$line")
106 fi
107 done <<< "$response"
108 COMPREPLY=( "${{lines[@]}}" )
109
110 # Display help messages if any
111 if [[ ${{#help_messages[@]}} -gt 0 ]]; then
112 printf '\n'
113 for msg in "${{help_messages[@]}}"; do
114 printf '%s\n' "$msg"
115 done
116 printf '\n'
117 fi
118 fi
119}}
120
121complete -F _{name}_complete {name}
122"#
123 )
124 }
125
126 fn generate_zsh_completion(&self) -> String {
127 let name = self.name();
128 let upper = name.to_uppercase();
129 format!(
130 r#"#compdef -P {name}
131# Zsh completion for {name}
132
133_{name}_complete() {{
134 local -a completions
135 local IFS=$'\n'
136
137 # Get the actual command from the command line
138 local cmd="${{words[1]}}"
139 if [[ "$cmd" != /* ]] && ! command -v "$cmd" &>/dev/null; then
140 # If not found in PATH, try relative path
141 if [[ -x "$cmd" ]]; then
142 cmd="./$cmd"
143 fi
144 fi
145
146 # Build completion arguments
147 local -a comp_line
148 comp_line=("__complete")
149
150 # Add all words except the command name
151 local i
152 for (( i = 2; i < CURRENT; i++ )); do
153 comp_line+=("${{words[$i]}}")
154 done
155
156 # Add the current word being completed
157 comp_line+=("${{words[CURRENT]}}")
158
159 # Call the command with completion environment variable
160 local response
161 response=$({upper}_COMPLETE=zsh "$cmd" "${{comp_line[@]}}" 2>/dev/null)
162
163 if [[ -n "$response" ]]; then
164 local -a values
165 local -a descriptions
166 local -a help_messages
167 local line
168
169 # Parse response lines
170 while IFS= read -r line; do
171 if [[ "$line" == _activehelp_::* ]]; then
172 # ActiveHelp message
173 help_messages+=("${{line#_activehelp_::}}")
174 elif [[ "$line" == *:* ]]; then
175 # Line has description
176 values+=("${{line%%:*}}")
177 descriptions+=("${{line#*:}}")
178 else
179 # No description
180 values+=("$line")
181 descriptions+=("")
182 fi
183 done <<< "$response"
184
185 # Display ActiveHelp messages if any
186 if [[ ${{#help_messages[@]}} -gt 0 ]]; then
187 local formatted_help=()
188 for msg in "${{help_messages[@]}}"; do
189 formatted_help+=("-- $msg --")
190 done
191 compadd -x "${{(j: :)formatted_help}}"
192 fi
193
194 # Add completions with descriptions
195 if [[ ${{#descriptions[@]}} -gt 0 ]] && [[ -n "${{descriptions[*]// }}" ]]; then
196 compadd -Q -d descriptions -a values
197 else
198 compadd -Q -a values
199 fi
200 fi
201}}
202
203compdef _{name}_complete {name}
204"#
205 )
206 }
207
208 fn generate_fish_completion(&self) -> String {
209 let name = self.name();
210 let upper = name.to_uppercase();
211 format!(
216 "# Fish completion for {name}
217function __{name}_complete
218 set -l cmd (commandline -opc)
219 set -l cursor (commandline -C)
220 set -l current (commandline -ct)
221
222 # Call our binary with special completion env var
223 set -l response (env {upper}_COMPLETE=fish $cmd[1] __complete $cmd[2..-1] $current 2>/dev/null)
224
225 # Process response and handle ActiveHelp
226 set -l help_messages
227 for line in $response
228 if string match -q '_activehelp_*' -- $line
229 # Extract help message
230 set -a help_messages (string replace '_activehelp_\t' '' -- $line)
231 else
232 echo $line
233 end
234 end
235
236 # Display help messages if any
237 if test (count $help_messages) -gt 0
238 for msg in $help_messages
239 echo \"ยป $msg\" >&2
240 end
241 end
242end
243
244complete -c {name} -f -a '(__{name}_complete)'
245"
246 )
247 }
248}