Skip to content

Commit 97bba1a

Browse files
committed
feat: Group options by tag (in zsh)
1 parent 94f3378 commit 97bba1a

4 files changed

Lines changed: 125 additions & 41 deletions

File tree

clap_complete/src/engine/complete.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,12 @@ fn complete_arg_value(
402402
if comp.get_tag().is_some() {
403403
comp
404404
} else {
405-
comp.tag(Some(arg.to_string().into()))
405+
comp.tag(Some(
406+
arg.get_help_heading()
407+
.unwrap_or(DEFAULT_COMPLETION_TAG)
408+
.to_owned()
409+
.into(),
410+
))
406411
}
407412
})
408413
.collect();

clap_complete/src/env/shells.rs

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::ffi::OsString;
22
use std::str::FromStr;
33

44
use super::EnvCompleter;
5+
use clap::builder::StyledStr;
56

67
/// Bash completion adapter
78
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -376,25 +377,55 @@ function _clap_dynamic_completer_NAME() {
376377
)}")
377378
378379
if [[ -n $completions ]]; then
379-
local -a dirs=()
380-
local -a other=()
380+
local -A tag_map tag_map_dirs
381381
local completion
382+
local tag
383+
local value
384+
local to_add
385+
382386
for completion in $completions; do
383-
local value="${completion%%:*}"
387+
IFS=: read -r tag value <<< "$completion"
388+
384389
if [[ "$value" == */ ]]; then
385390
local dir_no_slash="${value%/}"
386-
if [[ "$completion" == *:* ]]; then
387-
local desc="${completion#*:}"
388-
dirs+=("$dir_no_slash:$desc")
391+
392+
if [[ "$value" == *:* ]]; then
393+
local desc="${value#*:}"
394+
to_add="$dir_no_slash:$desc"
389395
else
390-
dirs+=("$dir_no_slash")
396+
to_add="$dir_no_slash"
391397
fi
398+
399+
tag_map_dirs[$tag]+=${tag_map_dirs[$tag]:+$'\n'}"$to_add"
392400
else
393-
other+=("$completion")
401+
tag_map[$tag]+=${tag_map[$tag]:+$'\n'}"$value"
402+
fi
403+
done
404+
405+
local -A tags
406+
for k in ${(k)tag_map} ${(k)tag_map_dirs}; do
407+
tags[$k]=1
408+
done
409+
410+
for tag in ${(k)tags}; do
411+
local values=("${(@f)tag_map[$tag]}") # split on newline
412+
413+
if [[ -n "$tag" ]]; then
414+
(( $#values )) &&
415+
_describe -t "$tag" "$tag" values
416+
417+
values=("${(@f)tag_map_dirs[$tag]}")
418+
(( $#values )) &&
419+
_describe -t "$tag" "$tag" values -S '/' -r '/'
420+
else
421+
(( $#values )) &&
422+
_describe options values
423+
424+
values=("${(@f)tag_map_dirs[$tag]}")
425+
(( $#values )) &&
426+
_describe options values -S '/' -r '/'
394427
fi
395428
done
396-
[[ -n $dirs ]] && _describe 'values' dirs -S '/' -r '/'
397-
[[ -n $other ]] && _describe 'values' other
398429
fi
399430
}
400431
@@ -431,6 +462,10 @@ compdef _clap_dynamic_completer_NAME BIN"#
431462
if i != 0 {
432463
write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
433464
}
465+
466+
let tag = format!("{}", candidate.get_tag().unwrap_or(&StyledStr::from("")));
467+
write!(buf, "{}:", Self::escape_value(&tag),)?;
468+
434469
write!(
435470
buf,
436471
"{}",

clap_complete/tests/snapshots/home/dynamic-env/exhaustive/zsh/zsh/_exhaustive

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,55 @@ function _clap_dynamic_completer_exhaustive() {
1111
)}")
1212

1313
if [[ -n $completions ]]; then
14-
local -a dirs=()
15-
local -a other=()
14+
local -A tag_map tag_map_dirs
1615
local completion
16+
local tag
17+
local value
18+
local to_add
19+
1720
for completion in $completions; do
18-
local value="${completion%%:*}"
21+
IFS=: read -r tag value <<< "$completion"
22+
1923
if [[ "$value" == */ ]]; then
2024
local dir_no_slash="${value%/}"
21-
if [[ "$completion" == *:* ]]; then
22-
local desc="${completion#*:}"
23-
dirs+=("$dir_no_slash:$desc")
25+
26+
if [[ "$value" == *:* ]]; then
27+
local desc="${value#*:}"
28+
to_add="$dir_no_slash:$desc"
2429
else
25-
dirs+=("$dir_no_slash")
30+
to_add="$dir_no_slash"
2631
fi
32+
33+
tag_map_dirs[$tag]+=${tag_map_dirs[$tag]:+$'\n'}"$to_add"
2734
else
28-
other+=("$completion")
35+
tag_map[$tag]+=${tag_map[$tag]:+$'\n'}"$value"
36+
fi
37+
done
38+
39+
local -A tags
40+
for k in ${(k)tag_map} ${(k)tag_map_dirs}; do
41+
tags[$k]=1
42+
done
43+
44+
for tag in ${(k)tags}; do
45+
local values=("${(@f)tag_map[$tag]}") # split on newline
46+
47+
if [[ -n "$tag" ]]; then
48+
(( $#values )) &&
49+
_describe -t "$tag" "$tag" values
50+
51+
values=("${(@f)tag_map_dirs[$tag]}")
52+
(( $#values )) &&
53+
_describe -t "$tag" "$tag" values -S '/' -r '/'
54+
else
55+
(( $#values )) &&
56+
_describe options values
57+
58+
values=("${(@f)tag_map_dirs[$tag]}")
59+
(( $#values )) &&
60+
_describe options values -S '/' -r '/'
2961
fi
3062
done
31-
[[ -n $dirs ]] && _describe 'values' dirs -S '/' -r '/'
32-
[[ -n $other ]] && _describe 'values' other
3363
fi
3464
}
3565

clap_complete/tests/testsuite/zsh.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ fn complete_dynamic_env_toplevel() {
253253
% exhaustive
254254
--generate -- generate
255255
--help -- Print help
256-
help -- Print this message or the help of the given subcommand(s)
256+
help -- Print this message or the help of the given subcommand(s)
257257
--empty-choice alias global last quote
258258
action empty hint pacman value
259259
"#]];
@@ -275,15 +275,21 @@ fn complete_dynamic_env_quoted_help() {
275275
let input = "exhaustive quote \t\t";
276276
let expected = snapbox::str![[r#"
277277
% exhaustive quote
278-
--help -- Print help (see more with '--help')
279-
cmd-backslash --backslash -- Avoid '/n'
280-
cmd-backticks --backticks -- For more information see `echo test`
281-
cmd-brackets --brackets -- List packages [filter]
282-
cmd-double-quotes --double-quotes -- Can be "always", "auto", or "never"
283-
cmd-expansions --expansions -- Execute the shell command with $SHELL
284-
cmd-single-quotes --single-quotes -- Can be 'always', 'auto', or 'never'
285-
escape-help -- /tab/t"'
286-
help -- Print this message or the help of the given subcommand(s)
278+
--backslash -- Avoid '/n'
279+
--backticks -- For more information see `echo test`
280+
--brackets -- List packages [filter]
281+
--double-quotes -- Can be "always", "auto", or "never"
282+
--expansions -- Execute the shell command with $SHELL
283+
--help -- Print help (see more with '--help')
284+
--single-quotes -- Can be 'always', 'auto', or 'never'
285+
cmd-backslash -- Avoid '/n'
286+
cmd-backticks -- For more information see `echo test`
287+
cmd-brackets -- List packages [filter]
288+
cmd-double-quotes -- Can be "always", "auto", or "never"
289+
cmd-expansions -- Execute the shell command with $SHELL
290+
cmd-single-quotes -- Can be 'always', 'auto', or 'never'
291+
escape-help -- /tab "'
292+
help -- Print this message or the help of the given subcommand(s)
287293
--choice
288294
"#]];
289295
let actual = runtime.complete(input, &term).unwrap();
@@ -392,15 +398,21 @@ fn complete_dynamic_empty_space() {
392398
let input = "exhaustive quote -\x1b[D\x1b[D\t\t";
393399
let expected = snapbox::str![[r#"
394400
% exhaustive quote -
395-
--help -- Print help (see more with '--help')
396-
cmd-backslash --backslash -- Avoid '/n'
397-
cmd-backticks --backticks -- For more information see `echo test`
398-
cmd-brackets --brackets -- List packages [filter]
399-
cmd-double-quotes --double-quotes -- Can be "always", "auto", or "never"
400-
cmd-expansions --expansions -- Execute the shell command with $SHELL
401-
cmd-single-quotes --single-quotes -- Can be 'always', 'auto', or 'never'
402-
escape-help -- /tab/t"'
403-
help -- Print this message or the help of the given subcommand(s)
401+
--backslash -- Avoid '/n'
402+
--backticks -- For more information see `echo test`
403+
--brackets -- List packages [filter]
404+
--double-quotes -- Can be "always", "auto", or "never"
405+
--expansions -- Execute the shell command with $SHELL
406+
--help -- Print help (see more with '--help')
407+
--single-quotes -- Can be 'always', 'auto', or 'never'
408+
cmd-backslash -- Avoid '/n'
409+
cmd-backticks -- For more information see `echo test`
410+
cmd-brackets -- List packages [filter]
411+
cmd-double-quotes -- Can be "always", "auto", or "never"
412+
cmd-expansions -- Execute the shell command with $SHELL
413+
cmd-single-quotes -- Can be 'always', 'auto', or 'never'
414+
escape-help -- /tab "'
415+
help -- Print this message or the help of the given subcommand(s)
404416
--choice
405417
"#]];
406418
let actual = runtime.complete(input, &term).unwrap();
@@ -450,11 +462,13 @@ fn complete_dynamic_tagged_options() {
450462
let input = [
451463
"zstyle ':completion:*:descriptions' format '%d'",
452464
"exhaustive -\t\t",
453-
].join("\n");
465+
]
466+
.join("\n");
454467

455468
let expected = snapbox::str![[r#"
456469
% zstyle ':completion:*:descriptions' format '%d'
457470
% exhaustive -
471+
Options
458472
--generate -- generate
459473
-h -- Print help
460474
--empty-choice

0 commit comments

Comments
 (0)