diff --git a/clap_complete/src/shells/zsh.rs b/clap_complete/src/shells/zsh.rs index 580de77a2ec..0ed2b51e4d4 100644 --- a/clap_complete/src/shells/zsh.rs +++ b/clap_complete/src/shells/zsh.rs @@ -621,13 +621,20 @@ fn write_positionals_of(p: &Command) -> String { debug!("write_positionals_of;"); let mut ret = vec![]; + let mut cardinality_value; for arg in p.get_positionals() { debug!("write_positionals_of:iter: arg={}", arg.get_id()); let num_args = arg.get_num_args().expect("built"); let cardinality = if num_args.max_values() > 1 { - "*:" + match arg.get_value_terminator() { + Some(terminator) => { + cardinality_value = format!("*{}:", escape_value(terminator)); + cardinality_value.as_str() + } + None => "*:", + } } else if !arg.is_required_set() { ":" } else { diff --git a/clap_complete/tests/bash.rs b/clap_complete/tests/bash.rs index a3e454deaf4..d01651f26cd 100644 --- a/clap_complete/tests/bash.rs +++ b/clap_complete/tests/bash.rs @@ -84,6 +84,18 @@ fn value_hint() { ); } +#[test] +fn value_terminator() { + let name = "my-app"; + let cmd = common::value_terminator_command(name); + common::assert_matches_path( + "tests/snapshots/value_terminator.bash", + clap_complete::shells::Bash, + cmd, + name, + ); +} + #[cfg(feature = "unstable-dynamic")] #[test] fn register_minimal() { diff --git a/clap_complete/tests/common.rs b/clap_complete/tests/common.rs index ab52fe93a71..b6064031e68 100644 --- a/clap_complete/tests/common.rs +++ b/clap_complete/tests/common.rs @@ -252,6 +252,16 @@ pub fn value_hint_command(name: &'static str) -> clap::Command { ) } +pub fn value_terminator_command(name: &'static str) -> clap::Command { + clap::Command::new(name) + .arg( + clap::Arg::new("arguments") + .help("multi-valued argument with a value terminator") + .num_args(1..) + .value_terminator("--"), + ) +} + pub fn assert_matches_path( expected_path: impl AsRef, gen: impl clap_complete::Generator, diff --git a/clap_complete/tests/elvish.rs b/clap_complete/tests/elvish.rs index 76beb98366e..26075a67ac1 100644 --- a/clap_complete/tests/elvish.rs +++ b/clap_complete/tests/elvish.rs @@ -83,3 +83,15 @@ fn value_hint() { name, ); } + +#[test] +fn value_terminator() { + let name = "my-app"; + let cmd = common::value_terminator_command(name); + common::assert_matches_path( + "tests/snapshots/value_terminator.elvish", + clap_complete::shells::Elvish, + cmd, + name, + ); +} diff --git a/clap_complete/tests/fish.rs b/clap_complete/tests/fish.rs index 92c1c4a4efc..9393ed699aa 100644 --- a/clap_complete/tests/fish.rs +++ b/clap_complete/tests/fish.rs @@ -83,3 +83,15 @@ fn value_hint() { name, ); } + +#[test] +fn value_terminator() { + let name = "my-app"; + let cmd = common::value_terminator_command(name); + common::assert_matches_path( + "tests/snapshots/value_terminator.fish", + clap_complete::shells::Fish, + cmd, + name, + ); +} diff --git a/clap_complete/tests/powershell.rs b/clap_complete/tests/powershell.rs index fa6657d3723..1f18618b7d2 100644 --- a/clap_complete/tests/powershell.rs +++ b/clap_complete/tests/powershell.rs @@ -83,3 +83,15 @@ fn value_hint() { name, ); } + +#[test] +fn value_terminator() { + let name = "my-app"; + let cmd = common::value_terminator_command(name); + common::assert_matches_path( + "tests/snapshots/value_terminator.ps1", + clap_complete::shells::PowerShell, + cmd, + name, + ); +} diff --git a/clap_complete/tests/snapshots/value_terminator.bash b/clap_complete/tests/snapshots/value_terminator.bash new file mode 100644 index 00000000000..3b4bc730211 --- /dev/null +++ b/clap_complete/tests/snapshots/value_terminator.bash @@ -0,0 +1,38 @@ +_my-app() { + local i cur prev opts cmds + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + cmd="" + opts="" + + for i in ${COMP_WORDS[@]} + do + case "${cmd},${i}" in + ",$1") + cmd="my__app" + ;; + *) + ;; + esac + done + + case "${cmd}" in + my__app) + opts="-h --help [arguments]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + esac +} + +complete -F _my-app -o bashdefault -o default my-app diff --git a/clap_complete/tests/snapshots/value_terminator.elvish b/clap_complete/tests/snapshots/value_terminator.elvish new file mode 100644 index 00000000000..89b26e0005a --- /dev/null +++ b/clap_complete/tests/snapshots/value_terminator.elvish @@ -0,0 +1,26 @@ + +use builtin; +use str; + +set edit:completion:arg-completer[my-app] = {|@words| + fn spaces {|n| + builtin:repeat $n ' ' | str:join '' + } + fn cand {|text desc| + edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc + } + var command = 'my-app' + for word $words[1..-1] { + if (str:has-prefix $word '-') { + break + } + set command = $command';'$word + } + var completions = [ + &'my-app'= { + cand -h 'Print help' + cand --help 'Print help' + } + ] + $completions[$command] +} diff --git a/clap_complete/tests/snapshots/value_terminator.fish b/clap_complete/tests/snapshots/value_terminator.fish new file mode 100644 index 00000000000..a33c671acf5 --- /dev/null +++ b/clap_complete/tests/snapshots/value_terminator.fish @@ -0,0 +1 @@ +complete -c my-app -s h -l help -d 'Print help' diff --git a/clap_complete/tests/snapshots/value_terminator.ps1 b/clap_complete/tests/snapshots/value_terminator.ps1 new file mode 100644 index 00000000000..30262afe031 --- /dev/null +++ b/clap_complete/tests/snapshots/value_terminator.ps1 @@ -0,0 +1,32 @@ + +using namespace System.Management.Automation +using namespace System.Management.Automation.Language + +Register-ArgumentCompleter -Native -CommandName 'my-app' -ScriptBlock { + param($wordToComplete, $commandAst, $cursorPosition) + + $commandElements = $commandAst.CommandElements + $command = @( + 'my-app' + for ($i = 1; $i -lt $commandElements.Count; $i++) { + $element = $commandElements[$i] + if ($element -isnot [StringConstantExpressionAst] -or + $element.StringConstantType -ne [StringConstantType]::BareWord -or + $element.Value.StartsWith('-') -or + $element.Value -eq $wordToComplete) { + break + } + $element.Value + }) -join ';' + + $completions = @(switch ($command) { + 'my-app' { + [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') + [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') + break + } + }) + + $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | + Sort-Object -Property ListItemText +} diff --git a/clap_complete/tests/snapshots/value_terminator.zsh b/clap_complete/tests/snapshots/value_terminator.zsh new file mode 100644 index 00000000000..f0d5ef908b4 --- /dev/null +++ b/clap_complete/tests/snapshots/value_terminator.zsh @@ -0,0 +1,30 @@ +#compdef my-app + +autoload -U is-at-least + +_my-app() { + typeset -A opt_args + typeset -a _arguments_options + local ret=1 + + if is-at-least 5.2; then + _arguments_options=(-s -S -C) + else + _arguments_options=(-s -C) + fi + + local context curcontext="$curcontext" state line + _arguments "${_arguments_options[@]}" \ +'-h[Print help]' \ +'--help[Print help]' \ +'*--::arguments -- multi-valued argument with a value terminator:' \ +&& ret=0 +} + +(( $+functions[_my-app_commands] )) || +_my-app_commands() { + local commands; commands=() + _describe -t commands 'my-app commands' commands "$@" +} + +_my-app "$@" diff --git a/clap_complete/tests/zsh.rs b/clap_complete/tests/zsh.rs index df6a27781fe..57040bde465 100644 --- a/clap_complete/tests/zsh.rs +++ b/clap_complete/tests/zsh.rs @@ -83,3 +83,15 @@ fn value_hint() { name, ); } + +#[test] +fn value_terminator() { + let name = "my-app"; + let cmd = common::value_terminator_command(name); + common::assert_matches_path( + "tests/snapshots/value_terminator.zsh", + clap_complete::shells::Zsh, + cmd, + name, + ); +}