Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clap_complete/examples/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ fn command() -> clap::Command {
.short('F')
.value_parser(["json", "yaml", "toml"]),
)
.arg(
clap::Arg::new("verbose")
.long("verbose")
.short('v')
.global(true),
)
.args_conflicts_with_subcommands(true)
}

Expand Down
35 changes: 28 additions & 7 deletions clap_complete/src/env/shells.rs
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test output changed in this commit. Are the commits not atomic or are they not covering the right behavior?

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::ffi::OsString;
use std::str::FromStr;

use super::EnvCompleter;
use clap::builder::StyledStr;

/// Bash completion adapter
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -376,25 +377,39 @@ function _clap_dynamic_completer_NAME() {
)}")

if [[ -n $completions ]]; then
local -A tag_map
local -a dirs=()
local -a other=()
local completion
local tag
local value

for completion in $completions; do
local value="${completion%%:*}"
IFS=: read -r tag value <<< "$completion"
if [[ "$value" == */ ]]; then
local dir_no_slash="${value%/}"
if [[ "$completion" == *:* ]]; then
local desc="${completion#*:}"
if [[ "$value" == *:* ]]; then
local desc="${value#*:}"
dirs+=("$dir_no_slash:$desc")
else
dirs+=("$dir_no_slash")
fi
else
other+=("$completion")
if (( ${+tag_map["$tag"]} )); then # key exists?
tag_map["$tag"]+=$'\n'"$value"
else
tag_map["$tag"]="$value"
fi
fi
Comment on lines 388 to +402
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we aren't getting the / handling if a tag is present?

done
[[ -n $dirs ]] && _describe -t dirs 'values' dirs -S '/' -r '/'
for tag in ${(k)tag_map}; do
values=("${(@f)tag_map[$tag]}") # split on newline
if [[ -n $tag ]]; then
_describe -t "$tag" "$tag options" values
else
_describe "options" values
fi
done
[[ -n $dirs ]] && _describe 'values' dirs -S '/' -r '/'
[[ -n $other ]] && _describe 'values' other
fi
}

Expand Down Expand Up @@ -431,6 +446,12 @@ compdef _clap_dynamic_completer_NAME BIN"#
if i != 0 {
write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
}
write!(
buf,
"{}:",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is : is in a tag?

candidate.get_tag().unwrap_or(&StyledStr::from("")),
)?;

write!(
buf,
"{}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#compdef dynamic
function _clap_dynamic_completer_dynamic() {
local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
local _CLAP_IFS=$'\n'

local completions=("${(@f)$( \
_CLAP_IFS="$_CLAP_IFS" \
_CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
COMPLETE="zsh" \
dynamic -- "${words[@]}" 2>/dev/null \
)}")

if [[ -n $completions ]]; then
local -A tag_map
local -a dirs=()
local completion
local tag
local value

for completion in $completions; do
IFS=: read -r tag value <<< "$completion"
if [[ "$value" == */ ]]; then
local dir_no_slash="${value%/}"
if [[ "$value" == *:* ]]; then
local desc="${value#*:}"
dirs+=("$dir_no_slash:$desc")
else
dirs+=("$dir_no_slash")
fi
else
if (( ${+tag_map["$tag"]} )); then # key exists?
tag_map["$tag"]+=$'\n'"$value"
else
tag_map["$tag"]="$value"
fi
fi
done
[[ -n $dirs ]] && _describe -t dirs 'values' dirs -S '/' -r '/'
for tag in ${(k)tag_map}; do
values=("${(@f)tag_map[$tag]}") # split on newline
if [[ -n $tag ]]; then
_describe -t "$tag" "$tag options" values
else
_describe "options" values
fi
done
fi
}

compdef _clap_dynamic_completer_dynamic dynamic
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,39 @@ function _clap_dynamic_completer_exhaustive() {
)}")

if [[ -n $completions ]]; then
local -A tag_map
local -a dirs=()
local -a other=()
local completion
local tag
local value

for completion in $completions; do
local value="${completion%%:*}"
IFS=: read -r tag value <<< "$completion"
if [[ "$value" == */ ]]; then
local dir_no_slash="${value%/}"
if [[ "$completion" == *:* ]]; then
local desc="${completion#*:}"
if [[ "$value" == *:* ]]; then
local desc="${value#*:}"
dirs+=("$dir_no_slash:$desc")
else
dirs+=("$dir_no_slash")
fi
else
other+=("$completion")
if (( ${+tag_map["$tag"]} )); then # key exists?
tag_map["$tag"]+=$'\n'"$value"
else
tag_map["$tag"]="$value"
fi
fi
done
[[ -n $dirs ]] && _describe -t dirs 'values' dirs -S '/' -r '/'
for tag in ${(k)tag_map}; do
values=("${(@f)tag_map[$tag]}") # split on newline
if [[ -n $tag ]]; then
_describe -t "$tag" "$tag options" values
else
_describe "options" values
fi
done
[[ -n $dirs ]] && _describe 'values' dirs -S '/' -r '/'
[[ -n $other ]] && _describe 'values' other
fi
}

Expand Down
90 changes: 67 additions & 23 deletions clap_complete/tests/testsuite/zsh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ fn complete() {
let expected = snapbox::str![[r#"
% exhaustive
help -- Print this message or the help of the given subcommand(s)
hint
pacman action global alias value quote empty last --
hint
pacman action global alias value quote empty last --
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
Expand All @@ -235,6 +235,7 @@ pacman action global alias value quote empty last --
#[cfg(feature = "unstable-shell-tests")]
fn register_dynamic_env() {
common::register_example::<RuntimeBuilder>("dynamic-env", "exhaustive");
common::register_example::<RuntimeBuilder>("dynamic-env", "dynamic");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests are testing using exhaustive which already has a global(true) on it.

Is this unused? Do we then need to add the global arg?

}

#[test]
Expand All @@ -253,9 +254,9 @@ fn complete_dynamic_env_toplevel() {
% exhaustive
--generate -- generate
--help -- Print help
help -- Print this message or the help of the given subcommand(s)
--empty-choice alias global last quote
action empty hint pacman value
help -- Print this message or the help of the given subcommand(s)
--empty-choice alias global last quote
action empty hint pacman value
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
Expand All @@ -275,15 +276,21 @@ fn complete_dynamic_env_quoted_help() {
let input = "exhaustive quote \t\t";
let expected = snapbox::str![[r#"
% exhaustive quote
--help -- Print help (see more with '--help')
cmd-backslash --backslash -- Avoid '/n'
cmd-backticks --backticks -- For more information see `echo test`
cmd-brackets --brackets -- List packages [filter]
cmd-double-quotes --double-quotes -- Can be "always", "auto", or "never"
cmd-expansions --expansions -- Execute the shell command with $SHELL
cmd-single-quotes --single-quotes -- Can be 'always', 'auto', or 'never'
escape-help -- /tab/t"'
help -- Print this message or the help of the given subcommand(s)
--backslash -- Avoid '/n'
--backticks -- For more information see `echo test`
--brackets -- List packages [filter]
--double-quotes -- Can be "always", "auto", or "never"
--expansions -- Execute the shell command with $SHELL
--help -- Print help (see more with '--help')
--single-quotes -- Can be 'always', 'auto', or 'never'
cmd-backslash -- Avoid '/n'
cmd-backticks -- For more information see `echo test`
cmd-brackets -- List packages [filter]
cmd-double-quotes -- Can be "always", "auto", or "never"
cmd-expansions -- Execute the shell command with $SHELL
cmd-single-quotes -- Can be 'always', 'auto', or 'never'
escape-help -- /tab "'
help -- Print this message or the help of the given subcommand(s)
--choice
"#]];
let actual = runtime.complete(input, &term).unwrap();
Expand Down Expand Up @@ -392,15 +399,21 @@ fn complete_dynamic_empty_space() {
let input = "exhaustive quote -\x1b[D\x1b[D\t\t";
let expected = snapbox::str![[r#"
% exhaustive quote -
--help -- Print help (see more with '--help')
cmd-backslash --backslash -- Avoid '/n'
cmd-backticks --backticks -- For more information see `echo test`
cmd-brackets --brackets -- List packages [filter]
cmd-double-quotes --double-quotes -- Can be "always", "auto", or "never"
cmd-expansions --expansions -- Execute the shell command with $SHELL
cmd-single-quotes --single-quotes -- Can be 'always', 'auto', or 'never'
escape-help -- /tab/t"'
help -- Print this message or the help of the given subcommand(s)
--backslash -- Avoid '/n'
--backticks -- For more information see `echo test`
--brackets -- List packages [filter]
--double-quotes -- Can be "always", "auto", or "never"
--expansions -- Execute the shell command with $SHELL
--help -- Print help (see more with '--help')
--single-quotes -- Can be 'always', 'auto', or 'never'
cmd-backslash -- Avoid '/n'
cmd-backticks -- For more information see `echo test`
cmd-brackets -- List packages [filter]
cmd-double-quotes -- Can be "always", "auto", or "never"
cmd-expansions -- Execute the shell command with $SHELL
cmd-single-quotes -- Can be 'always', 'auto', or 'never'
escape-help -- /tab "'
help -- Print this message or the help of the given subcommand(s)
--choice
"#]];
let actual = runtime.complete(input, &term).unwrap();
Expand Down Expand Up @@ -435,3 +448,34 @@ tests/examples.rs tests/snapshots tests/testsuite
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}

#[test]
#[cfg(all(unix, feature = "unstable-dynamic"))]
#[cfg(feature = "unstable-shell-tests")]
fn complete_dynamic_tagged_options() {
if !common::has_command(CMD) {
return;
}

let term = completest::Term::new();
let mut runtime = common::load_runtime::<RuntimeBuilder>("dynamic-env", "dynamic");

let input = [
"zstyle ':completion:*' group-name ''",
"zstyle ':completion:*:descriptions' format '%d'",
"dynamic -\t\t",
].join("\n");

let expected = snapbox::str![[r#"
% zstyle ':completion:*' group-name ''
% zstyle ':completion:*:descriptions' format '%d'
% dynamic -
completing "Options" options
-F -- --format
-h -- Print help
-i -- --input
-v -- --verbose
"#]];
let actual = runtime.complete(&input, &term).unwrap();
assert_data_eq!(actual, expected);
}
Loading