Update clap to version 4 (#1924)

This commit is contained in:
Poliorcetics 2024-05-15 05:29:40 +02:00 committed by GitHub
parent b85540007e
commit caace0a115
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 498 additions and 376 deletions

62
Cargo.lock generated
View File

@ -172,13 +172,49 @@ dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim",
"term_size",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim 0.11.1",
"terminal_size",
]
[[package]]
name = "clap_complete"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e"
dependencies = [
"clap 4.5.4",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.1"
@ -474,7 +510,8 @@ dependencies = [
"atty",
"blake3",
"camino",
"clap",
"clap 4.5.4",
"clap_complete",
"cradle",
"ctrlc",
"derivative",
@ -847,13 +884,19 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"clap 2.34.0",
"lazy_static",
"structopt-derive",
]
@ -943,13 +986,13 @@ dependencies = [
]
[[package]]
name = "term_size"
version = "0.3.2"
name = "terminal_size"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
dependencies = [
"libc",
"winapi",
"rustix",
"windows-sys 0.48.0",
]
[[package]]
@ -958,7 +1001,6 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"term_size",
"unicode-width",
]

View File

@ -22,7 +22,8 @@ ansi_term = "0.12.0"
atty = "0.2.0"
blake3 = { version = "1.5.0", features = ["rayon", "mmap"] }
camino = "1.0.4"
clap = { version = "2.33.0", features = ["wrap_help"] }
clap = { version = "4.0.0", features = ["env", "wrap_help"] }
clap_complete = "4.0.0"
ctrlc = { version = "3.1.1", features = ["termination"] }
derivative = "2.0.0"
dirs = "5.0.1"

View File

@ -1,5 +1,5 @@
_just() {
local i cur prev words cword opts cmds
local i cur prev words cword opts cmd
COMPREPLY=()
# Modules use "::" as the separator, which is considered a wordbreak character in bash.
@ -19,19 +19,18 @@ _just() {
for i in ${words[@]}
do
case "${i}" in
"$1")
case "${cmd},${i}" in
",$1")
cmd="just"
;;
*)
;;
esac
done
case "${cmd}" in
just)
opts=" -n -q -u -v -e -l -h -V -f -d -c -s -E --check --yes --dry-run --highlight --no-aliases --no-deps --no-dotenv --no-highlight --quiet --shell-command --clear-shell-args --unsorted --unstable --verbose --changelog --choose --dump --edit --evaluate --fmt --init --list --summary --variables --help --version --chooser --color --command-color --dump-format --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --command --completions --show --dotenv-filename --dotenv-path <ARGUMENTS>... "
"$1")
opts="-n -f -q -u -v -d -c -e -l -s -E -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --show --summary --variables --dotenv-filename --dotenv-path --help --version [ARGUMENTS]..."
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -53,7 +52,6 @@ _just() {
fi
fi
case "${prev}" in
--chooser)
COMPREPLY=($(compgen -f "${cur}"))
return 0
@ -115,7 +113,7 @@ _just() {
return 0
;;
--completions)
COMPREPLY=($(compgen -W "zsh bash fish powershell elvish" -- "${cur}"))
COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}"))
return 0
;;
--show)
@ -145,8 +143,11 @@ _just() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
esac
}
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -F _just -o nosort -o bashdefault -o default just
else
complete -F _just -o bashdefault -o default just
fi

View File

@ -1,18 +1,21 @@
edit:completion:arg-completer[just] = [@words]{
fn spaces [n]{
repeat $n ' ' | joins ''
use builtin;
use str;
set edit:completion:arg-completer[just] = {|@words|
fn spaces {|n|
builtin:repeat $n ' ' | str:join ''
}
fn cand [text desc]{
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
fn cand {|text desc|
edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
}
command = 'just'
for word $words[1:-1] {
if (has-prefix $word '-') {
var command = 'just'
for word $words[1..-1] {
if (str:has-prefix $word '-') {
break
}
command = $command';'$word
set command = $command';'$word
}
completions = [
var completions = [
&'just'= {
cand --chooser 'Override binary invoked by `--choose`'
cand --color 'Print colorful output'
@ -65,10 +68,10 @@ edit:completion:arg-completer[just] = [@words]{
cand --list 'List available recipes and their arguments'
cand --summary 'List names of available recipes'
cand --variables 'List names of variables'
cand -h 'Print help information'
cand --help 'Print help information'
cand -V 'Print version information'
cand --version 'Print version information'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
}
]
$completions[$command]

View File

@ -35,45 +35,45 @@ complete -c just -n "__fish_is_first_arg" --no-files
complete -c just -a '(__fish_just_complete_recipes)'
# autogenerated completions
complete -c just -n "__fish_use_subcommand" -l chooser -d 'Override binary invoked by `--choose`'
complete -c just -n "__fish_use_subcommand" -l color -d 'Print colorful output' -r -f -a "auto always never"
complete -c just -n "__fish_use_subcommand" -l command-color -d 'Echo recipe lines in <COMMAND-COLOR>' -r -f -a "black blue cyan green purple red yellow"
complete -c just -n "__fish_use_subcommand" -l dump-format -d 'Dump justfile as <FORMAT>' -r -f -a "just json"
complete -c just -n "__fish_use_subcommand" -l list-heading -d 'Print <TEXT> before list'
complete -c just -n "__fish_use_subcommand" -l list-prefix -d 'Print <TEXT> before each list item'
complete -c just -n "__fish_use_subcommand" -s f -l justfile -d 'Use <JUSTFILE> as justfile'
complete -c just -n "__fish_use_subcommand" -l set -d 'Override <VARIABLE> with <VALUE>'
complete -c just -n "__fish_use_subcommand" -l shell -d 'Invoke <SHELL> to run recipes'
complete -c just -n "__fish_use_subcommand" -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument'
complete -c just -n "__fish_use_subcommand" -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
complete -c just -n "__fish_use_subcommand" -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
complete -c just -n "__fish_use_subcommand" -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "zsh bash fish powershell elvish"
complete -c just -n "__fish_use_subcommand" -s s -l show -d 'Show information about <RECIPE>'
complete -c just -n "__fish_use_subcommand" -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
complete -c just -n "__fish_use_subcommand" -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one'
complete -c just -n "__fish_use_subcommand" -l check -d 'Run `--fmt` in \'check\' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
complete -c just -n "__fish_use_subcommand" -l yes -d 'Automatically confirm all recipes.'
complete -c just -n "__fish_use_subcommand" -s n -l dry-run -d 'Print what just would do without doing it'
complete -c just -n "__fish_use_subcommand" -l highlight -d 'Highlight echoed recipe lines in bold'
complete -c just -n "__fish_use_subcommand" -l no-aliases -d 'Don\'t show aliases in list'
complete -c just -n "__fish_use_subcommand" -l no-deps -d 'Don\'t run recipe dependencies'
complete -c just -n "__fish_use_subcommand" -l no-dotenv -d 'Don\'t load `.env` file'
complete -c just -n "__fish_use_subcommand" -l no-highlight -d 'Don\'t highlight echoed recipe lines in bold'
complete -c just -n "__fish_use_subcommand" -s q -l quiet -d 'Suppress all output'
complete -c just -n "__fish_use_subcommand" -l shell-command -d 'Invoke <COMMAND> with the shell used to run recipe lines and backticks'
complete -c just -n "__fish_use_subcommand" -l clear-shell-args -d 'Clear shell arguments'
complete -c just -n "__fish_use_subcommand" -s u -l unsorted -d 'Return list and summary entries in source order'
complete -c just -n "__fish_use_subcommand" -l unstable -d 'Enable unstable features'
complete -c just -n "__fish_use_subcommand" -s v -l verbose -d 'Use verbose output'
complete -c just -n "__fish_use_subcommand" -l changelog -d 'Print changelog'
complete -c just -n "__fish_use_subcommand" -l choose -d 'Select one or more recipes to run using a binary chooser. If `--chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`'
complete -c just -n "__fish_use_subcommand" -l dump -d 'Print justfile'
complete -c just -n "__fish_use_subcommand" -s e -l edit -d 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
complete -c just -n "__fish_use_subcommand" -l evaluate -d 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable\'s value.'
complete -c just -n "__fish_use_subcommand" -l fmt -d 'Format and overwrite justfile'
complete -c just -n "__fish_use_subcommand" -l init -d 'Initialize new justfile in project root'
complete -c just -n "__fish_use_subcommand" -s l -l list -d 'List available recipes and their arguments'
complete -c just -n "__fish_use_subcommand" -l summary -d 'List names of available recipes'
complete -c just -n "__fish_use_subcommand" -l variables -d 'List names of variables'
complete -c just -n "__fish_use_subcommand" -s h -l help -d 'Print help information'
complete -c just -n "__fish_use_subcommand" -s V -l version -d 'Print version information'
complete -c just -l chooser -d 'Override binary invoked by `--choose`' -r
complete -c just -l color -d 'Print colorful output' -r -f -a "{auto '',always '',never ''}"
complete -c just -l command-color -d 'Echo recipe lines in <COMMAND-COLOR>' -r -f -a "{black '',blue '',cyan '',green '',purple '',red '',yellow ''}"
complete -c just -l dump-format -d 'Dump justfile as <FORMAT>' -r -f -a "{just '',json ''}"
complete -c just -l list-heading -d 'Print <TEXT> before list' -r
complete -c just -l list-prefix -d 'Print <TEXT> before each list item' -r
complete -c just -s f -l justfile -d 'Use <JUSTFILE> as justfile' -r -F
complete -c just -l set -d 'Override <VARIABLE> with <VALUE>' -r
complete -c just -l shell -d 'Invoke <SHELL> to run recipes' -r
complete -c just -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument' -r
complete -c just -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set' -r -F
complete -c just -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set' -r
complete -c just -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}"
complete -c just -s s -l show -d 'Show information about <RECIPE>' -r
complete -c just -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' -r
complete -c just -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one' -r -F
complete -c just -l check -d 'Run `--fmt` in \'check\' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
complete -c just -l yes -d 'Automatically confirm all recipes.'
complete -c just -s n -l dry-run -d 'Print what just would do without doing it'
complete -c just -l highlight -d 'Highlight echoed recipe lines in bold'
complete -c just -l no-aliases -d 'Don\'t show aliases in list'
complete -c just -l no-deps -d 'Don\'t run recipe dependencies'
complete -c just -l no-dotenv -d 'Don\'t load `.env` file'
complete -c just -l no-highlight -d 'Don\'t highlight echoed recipe lines in bold'
complete -c just -s q -l quiet -d 'Suppress all output'
complete -c just -l shell-command -d 'Invoke <COMMAND> with the shell used to run recipe lines and backticks'
complete -c just -l clear-shell-args -d 'Clear shell arguments'
complete -c just -s u -l unsorted -d 'Return list and summary entries in source order'
complete -c just -l unstable -d 'Enable unstable features'
complete -c just -s v -l verbose -d 'Use verbose output'
complete -c just -l changelog -d 'Print changelog'
complete -c just -l choose -d 'Select one or more recipes to run using a binary chooser. If `--chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`'
complete -c just -l dump -d 'Print justfile'
complete -c just -s e -l edit -d 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
complete -c just -l evaluate -d 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable\'s value.'
complete -c just -l fmt -d 'Format and overwrite justfile'
complete -c just -l init -d 'Initialize new justfile in project root'
complete -c just -s l -l list -d 'List available recipes and their arguments'
complete -c just -l summary -d 'List names of available recipes'
complete -c just -l variables -d 'List names of variables'
complete -c just -s h -l help -d 'Print help'
complete -c just -s V -l version -d 'Print version'

View File

@ -11,7 +11,8 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
$element = $commandElements[$i]
if ($element -isnot [StringConstantExpressionAst] -or
$element.StringConstantType -ne [StringConstantType]::BareWord -or
$element.Value.StartsWith('-')) {
$element.Value.StartsWith('-') -or
$element.Value -eq $wordToComplete) {
break
}
$element.Value
@ -70,10 +71,10 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes and their arguments')
[CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
break
}
})

View File

@ -15,35 +15,35 @@ _just() {
local context curcontext="$curcontext" state line
local common=(
'--chooser=[Override binary invoked by `--choose`]' \
'--chooser=[Override binary invoked by \`--choose\`]: : ' \
'--color=[Print colorful output]: :(auto always never)' \
'--command-color=[Echo recipe lines in <COMMAND-COLOR>]: :(black blue cyan green purple red yellow)' \
'--dump-format=[Dump justfile as <FORMAT>]: :(just json)' \
'--list-heading=[Print <TEXT> before list]' \
'--list-prefix=[Print <TEXT> before each list item]' \
'-f+[Use <JUSTFILE> as justfile]' \
'--justfile=[Use <JUSTFILE> as justfile]' \
'*--set[Override <VARIABLE> with <VALUE>]: :_just_variables' \
'--shell=[Invoke <SHELL> to run recipes]' \
'*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]' \
'-d+[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]' \
'--working-directory=[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]' \
'-c+[Run an arbitrary command with the working directory, `.env`, overrides, and exports set]' \
'--command=[Run an arbitrary command with the working directory, `.env`, overrides, and exports set]' \
'--completions=[Print shell completion script for <SHELL>]: :(zsh bash fish powershell elvish)' \
'-s+[Show information about <RECIPE>]: :_just_commands' \
'--show=[Show information about <RECIPE>]: :_just_commands' \
'(-E --dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of `.env`]' \
'-E+[Load <DOTENV-PATH> as environment file instead of searching for one]' \
'--dotenv-path=[Load <DOTENV-PATH> as environment file instead of searching for one]' \
'--check[Run `--fmt` in '\''check'\'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.]' \
'--dump-format=[Dump justfile as <FORMAT>]:FORMAT:(just json)' \
'--list-heading=[Print <TEXT> before list]:TEXT: ' \
'--list-prefix=[Print <TEXT> before each list item]:TEXT: ' \
'-f+[Use <JUSTFILE> as justfile]: :_files' \
'--justfile=[Use <JUSTFILE> as justfile]: :_files' \
'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \
'--shell=[Invoke <SHELL> to run recipes]: : ' \
'*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]: : ' \
'-d+[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]: :_files' \
'--working-directory=[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]: :_files' \
'*-c+[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
'*--command=[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
'*--completions=[Print shell completion script for <SHELL>]:SHELL:(bash elvish fish powershell zsh)' \
'-s+[Show information about <RECIPE>]: :(_just_commands)' \
'--show=[Show information about <RECIPE>]: :(_just_commands)' \
'(-E --dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of \`.env\`]: : ' \
'-E+[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
'--dotenv-path=[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
'--check[Run \`--fmt\` in '\''check'\'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.]' \
'--yes[Automatically confirm all recipes.]' \
'(-q --quiet)-n[Print what just would do without doing it]' \
'(-q --quiet)--dry-run[Print what just would do without doing it]' \
'--highlight[Highlight echoed recipe lines in bold]' \
'--no-aliases[Don'\''t show aliases in list]' \
'--no-deps[Don'\''t run recipe dependencies]' \
'--no-dotenv[Don'\''t load `.env` file]' \
'--no-dotenv[Don'\''t load \`.env\` file]' \
'--no-highlight[Don'\''t highlight echoed recipe lines in bold]' \
'(-n --dry-run)-q[Suppress all output]' \
'(-n --dry-run)--quiet[Suppress all output]' \
@ -55,10 +55,10 @@ _just() {
'*-v[Use verbose output]' \
'*--verbose[Use verbose output]' \
'--changelog[Print changelog]' \
'--choose[Select one or more recipes to run using a binary chooser. If `--chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`]' \
'--choose[Select one or more recipes to run using a binary chooser. If \`--chooser\` is not passed the chooser defaults to the value of \$JUST_CHOOSER, falling back to \`fzf\`]' \
'--dump[Print justfile]' \
'-e[Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`]' \
'--edit[Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`]' \
'-e[Edit justfile with editor given by \$VISUAL or \$EDITOR, falling back to \`vim\`]' \
'--edit[Edit justfile with editor given by \$VISUAL or \$EDITOR, falling back to \`vim\`]' \
'--evaluate[Evaluate and print all variables. If a variable name is given as an argument, only print that variable'\''s value.]' \
'--fmt[Format and overwrite justfile]' \
'--init[Initialize new justfile in project root]' \
@ -66,10 +66,10 @@ _just() {
'--list[List available recipes and their arguments]' \
'--summary[List names of available recipes]' \
'--variables[List names of variables]' \
'-h[Print help information]' \
'--help[Print help information]' \
'-V[Print version information]' \
'--version[Print version information]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
)
_arguments "${_arguments_options[@]}" $common \
@ -114,6 +114,7 @@ _just() {
esac
return ret
}
(( $+functions[_just_commands] )) ||
@ -138,6 +139,7 @@ _just_commands() {
}
if [ "$funcstack[1]" = "_just" ]; then
(( $+functions[_just_variables] )) ||
_just_variables() {
[[ $PREFIX = -* ]] && return 1
@ -158,3 +160,6 @@ _just_variables() {
}
_just "$@"
else
compdef _just just
fi

View File

@ -43,21 +43,19 @@ pub(crate) const ZSH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[
r" local common=(",
),
(
r"'*--set=[Override <VARIABLE> with <VALUE>]' \",
r"'*--set[Override <VARIABLE> with <VALUE>]: :_just_variables' \",
r"'*--set=[Override <VARIABLE> with <VALUE>]:VARIABLE: :VARIABLE: ' \",
r"'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \",
),
(
r"'-s+[Show information about <RECIPE>]' \
'--show=[Show information about <RECIPE>]' \",
r"'-s+[Show information about <RECIPE>]: :_just_commands' \
'--show=[Show information about <RECIPE>]: :_just_commands' \",
r"'()-s+[Show information about <RECIPE>]:RECIPE: ' \
'()--show=[Show information about <RECIPE>]:RECIPE: ' \",
r"'-s+[Show information about <RECIPE>]: :(_just_commands)' \
'--show=[Show information about <RECIPE>]: :(_just_commands)' \",
),
(
"'::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the \
justfile:_files' \\
&& ret=0
\x20\x20\x20\x20
",
"'*::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the \
justfile:' \\
&& ret=0",
r#")
_arguments "${_arguments_options[@]}" $common \
@ -105,9 +103,7 @@ pub(crate) const ZSH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[
"#,
),
(
" local commands; commands=(
\x20\x20\x20\x20\x20\x20\x20\x20
)",
" local commands; commands=()",
r#" [[ $PREFIX = -* ]] && return 1
integer ret=1
local variables; variables=(
@ -208,8 +204,8 @@ pub(crate) const BASH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[
),
(r" just)", r#" "$1")"#),
(
r"local i cur prev opts cmds",
r"local i cur prev words cword opts cmds",
r"local i cur prev opts cmd",
r"local i cur prev words cword opts cmd",
),
(
r#" cur="${COMP_WORDS[COMP_CWORD]}"

View File

@ -1,11 +1,12 @@
use {
super::*,
clap::{App, AppSettings, Arg, ArgGroup, ArgMatches, ArgSettings},
clap::{
builder::{styling::AnsiColor, FalseyValueParser, PossibleValuesParser, Styles},
value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command,
},
};
pub(crate) const CHOOSER_ENVIRONMENT_KEY: &str = "JUST_CHOOSER";
pub(crate) const CHOOSE_HELP: &str = "Select one or more recipes to run using a binary chooser. \
const CHOOSE_HELP: &str = "Select one or more recipes to run using a binary chooser. \
If `--chooser` is not passed the chooser defaults to the \
value of $JUST_CHOOSER, falling back to `fzf`";
@ -76,16 +77,7 @@ mod cmd {
];
pub(crate) const ARGLESS: &[&str] = &[
CHANGELOG,
COMPLETIONS,
DUMP,
EDIT,
FORMAT,
INIT,
LIST,
SHOW,
SUMMARY,
VARIABLES,
CHANGELOG, DUMP, EDIT, FORMAT, INIT, LIST, SUMMARY, VARIABLES,
];
}
@ -147,270 +139,308 @@ mod arg {
}
impl Config {
pub(crate) fn app() -> App<'static, 'static> {
let app = App::new(env!("CARGO_PKG_NAME"))
.help_message("Print help information")
.version_message("Print version information")
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::TrailingVarArg)
pub(crate) fn app() -> Command {
let app = Command::new(env!("CARGO_PKG_NAME"))
.bin_name(env!("CARGO_PKG_NAME"))
.trailing_var_arg(true)
.styles(
Styles::styled()
.header(AnsiColor::Yellow.on_default())
.usage(AnsiColor::Yellow.on_default())
.literal(AnsiColor::Green.on_default())
.placeholder(AnsiColor::Green.on_default())
)
.arg(
Arg::with_name(arg::CHECK)
Arg::new(arg::CHECK)
.long("check")
.action(ArgAction::SetTrue)
.requires(cmd::FORMAT)
.help("Run `--fmt` in 'check' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required."),
)
.arg(
Arg::with_name(arg::CHOOSER)
Arg::new(arg::CHOOSER)
.long("chooser")
.takes_value(true)
.env("JUST_CHOOSER")
.action(ArgAction::Set)
.help("Override binary invoked by `--choose`"),
)
.arg(
Arg::with_name(arg::COLOR)
Arg::new(arg::COLOR)
.long("color")
.takes_value(true)
.possible_values(arg::COLOR_VALUES)
.action(ArgAction::Set)
.value_parser(PossibleValuesParser::new(arg::COLOR_VALUES))
.default_value(arg::COLOR_AUTO)
.help("Print colorful output"),
)
.arg(
Arg::with_name(arg::COMMAND_COLOR)
Arg::new(arg::COMMAND_COLOR)
.long("command-color")
.takes_value(true)
.possible_values(arg::COMMAND_COLOR_VALUES)
.action(ArgAction::Set)
.value_parser(PossibleValuesParser::new(arg::COMMAND_COLOR_VALUES))
.help("Echo recipe lines in <COMMAND-COLOR>"),
)
.arg(Arg::with_name(arg::YES).long("yes").help("Automatically confirm all recipes."))
.arg(Arg::new(arg::YES).long("yes").action(ArgAction::SetTrue).help("Automatically confirm all recipes."))
.arg(
Arg::with_name(arg::DRY_RUN)
.short("n")
Arg::new(arg::DRY_RUN)
.short('n')
.long("dry-run")
.action(ArgAction::SetTrue)
.help("Print what just would do without doing it")
.conflicts_with(arg::QUIET),
)
.arg(
Arg::with_name(arg::DUMP_FORMAT)
Arg::new(arg::DUMP_FORMAT)
.long("dump-format")
.takes_value(true)
.possible_values(arg::DUMP_FORMAT_VALUES)
.action(ArgAction::Set)
.value_parser(PossibleValuesParser::new(arg::DUMP_FORMAT_VALUES))
.default_value(arg::DUMP_FORMAT_JUST)
.value_name("FORMAT")
.help("Dump justfile as <FORMAT>"),
)
.arg(
Arg::with_name(arg::HIGHLIGHT)
Arg::new(arg::HIGHLIGHT)
.long("highlight")
.action(ArgAction::SetTrue)
.help("Highlight echoed recipe lines in bold")
.overrides_with(arg::NO_HIGHLIGHT),
)
.arg(
Arg::with_name(arg::LIST_HEADING)
Arg::new(arg::LIST_HEADING)
.long("list-heading")
.help("Print <TEXT> before list")
.value_name("TEXT")
.takes_value(true),
.action(ArgAction::Set),
)
.arg(
Arg::with_name(arg::LIST_PREFIX)
Arg::new(arg::LIST_PREFIX)
.long("list-prefix")
.help("Print <TEXT> before each list item")
.value_name("TEXT")
.takes_value(true),
.action(ArgAction::Set),
)
.arg(
Arg::with_name(arg::NO_ALIASES)
Arg::new(arg::NO_ALIASES)
.long("no-aliases")
.help("Don't show aliases in list")
.action(ArgAction::SetTrue)
.help("Don't show aliases in list"),
)
.arg (
Arg::with_name(arg::NO_DEPS)
Arg::new(arg::NO_DEPS)
.long("no-deps")
.alias("no-dependencies")
.action(ArgAction::SetTrue)
.help("Don't run recipe dependencies")
)
.arg(
Arg::with_name(arg::NO_DOTENV)
Arg::new(arg::NO_DOTENV)
.long("no-dotenv")
.action(ArgAction::SetTrue)
.help("Don't load `.env` file"),
)
.arg(
Arg::with_name(arg::NO_HIGHLIGHT)
Arg::new(arg::NO_HIGHLIGHT)
.long("no-highlight")
.action(ArgAction::SetTrue)
.help("Don't highlight echoed recipe lines in bold")
.overrides_with(arg::HIGHLIGHT),
)
.arg(
Arg::with_name(arg::JUSTFILE)
.short("f")
Arg::new(arg::JUSTFILE)
.short('f')
.long("justfile")
.takes_value(true)
.action(ArgAction::Set)
.value_parser(value_parser!(PathBuf))
.help("Use <JUSTFILE> as justfile"),
)
.arg(
Arg::with_name(arg::QUIET)
.short("q")
Arg::new(arg::QUIET)
.short('q')
.long("quiet")
.action(ArgAction::SetTrue)
.help("Suppress all output")
.conflicts_with(arg::DRY_RUN),
)
.arg(
Arg::with_name(arg::SET)
Arg::new(arg::SET)
.long("set")
.takes_value(true)
.action(ArgAction::Append)
.number_of_values(2)
.value_names(&["VARIABLE", "VALUE"])
.multiple(true)
.value_names(["VARIABLE", "VALUE"])
.help("Override <VARIABLE> with <VALUE>"),
)
.arg(
Arg::with_name(arg::SHELL)
Arg::new(arg::SHELL)
.long("shell")
.takes_value(true)
.action(ArgAction::Set)
.help("Invoke <SHELL> to run recipes"),
)
.arg(
Arg::with_name(arg::SHELL_ARG)
Arg::new(arg::SHELL_ARG)
.long("shell-arg")
.takes_value(true)
.multiple(true)
.number_of_values(1)
.action(ArgAction::Append)
.allow_hyphen_values(true)
.overrides_with(arg::CLEAR_SHELL_ARGS)
.help("Invoke shell with <SHELL-ARG> as an argument"),
)
.arg(
Arg::with_name(arg::SHELL_COMMAND)
Arg::new(arg::SHELL_COMMAND)
.long("shell-command")
.requires(cmd::COMMAND)
.action(ArgAction::SetTrue)
.help("Invoke <COMMAND> with the shell used to run recipe lines and backticks"),
)
.arg(
Arg::with_name(arg::CLEAR_SHELL_ARGS)
Arg::new(arg::CLEAR_SHELL_ARGS)
.long("clear-shell-args")
.action(ArgAction::SetTrue)
.overrides_with(arg::SHELL_ARG)
.help("Clear shell arguments"),
)
.arg(
Arg::with_name(arg::UNSORTED)
Arg::new(arg::UNSORTED)
.long("unsorted")
.short("u")
.short('u')
.action(ArgAction::SetTrue)
.help("Return list and summary entries in source order"),
)
.arg(
Arg::with_name(arg::UNSTABLE)
Arg::new(arg::UNSTABLE)
.long("unstable")
.env("JUST_UNSTABLE")
.action(ArgAction::SetTrue)
.value_parser(FalseyValueParser::new())
.help("Enable unstable features"),
)
.arg(
Arg::with_name(arg::VERBOSE)
.short("v")
Arg::new(arg::VERBOSE)
.short('v')
.long("verbose")
.multiple(true)
.action(ArgAction::Count)
.help("Use verbose output"),
)
.arg(
Arg::with_name(arg::WORKING_DIRECTORY)
.short("d")
Arg::new(arg::WORKING_DIRECTORY)
.short('d')
.long("working-directory")
.takes_value(true)
.action(ArgAction::Set)
.value_parser(value_parser!(PathBuf))
.help("Use <WORKING-DIRECTORY> as working directory. --justfile must also be set")
.requires(arg::JUSTFILE),
)
.arg(
Arg::with_name(cmd::CHANGELOG)
Arg::new(cmd::CHANGELOG)
.long("changelog")
.action(ArgAction::SetTrue)
.help("Print changelog"),
)
.arg(Arg::with_name(cmd::CHOOSE).long("choose").help(CHOOSE_HELP))
.arg(Arg::new(cmd::CHOOSE).long("choose").action(ArgAction::SetTrue).help(CHOOSE_HELP))
.arg(
Arg::with_name(cmd::COMMAND)
Arg::new(cmd::COMMAND)
.long("command")
.short("c")
.min_values(1)
.short('c')
.num_args(1..)
.allow_hyphen_values(true)
.action(ArgAction::Append)
.value_parser(value_parser!(std::ffi::OsString))
.help(
"Run an arbitrary command with the working directory, `.env`, overrides, and exports \
set",
),
)
.arg(
Arg::with_name(cmd::COMPLETIONS)
Arg::new(cmd::COMPLETIONS)
.long("completions")
.takes_value(true)
.action(ArgAction::Append)
.num_args(1..)
.value_name("SHELL")
.possible_values(&clap::Shell::variants())
.set(ArgSettings::CaseInsensitive)
.value_parser(value_parser!(clap_complete::Shell))
.ignore_case(true)
.help("Print shell completion script for <SHELL>"),
)
.arg(
Arg::with_name(cmd::DUMP)
Arg::new(cmd::DUMP)
.long("dump")
.action(ArgAction::SetTrue)
.help("Print justfile"),
)
.arg(
Arg::with_name(cmd::EDIT)
.short("e")
Arg::new(cmd::EDIT)
.short('e')
.long("edit")
.action(ArgAction::SetTrue)
.help("Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`"),
)
.arg(Arg::with_name(cmd::EVALUATE).long("evaluate").help(
"Evaluate and print all variables. If a variable name is given as an argument, only print \
that variable's value.",
))
.arg(
Arg::with_name(cmd::FORMAT)
Arg::new(cmd::EVALUATE)
.long("evaluate")
.action(ArgAction::SetTrue)
.help(
"Evaluate and print all variables. If a variable name is given as an argument, only \
print that variable's value.",
),
)
.arg(
Arg::new(cmd::FORMAT)
.long("fmt")
.alias("format")
.action(ArgAction::SetTrue)
.help("Format and overwrite justfile"),
)
.arg(
Arg::with_name(cmd::INIT)
Arg::new(cmd::INIT)
.long("init")
.alias("initialize")
.action(ArgAction::SetTrue)
.help("Initialize new justfile in project root"),
)
.arg(
Arg::with_name(cmd::LIST)
.short("l")
Arg::new(cmd::LIST)
.short('l')
.long("list")
.action(ArgAction::SetTrue)
.help("List available recipes and their arguments"),
)
.arg(
Arg::with_name(cmd::SHOW)
.short("s")
Arg::new(cmd::SHOW)
.short('s')
.long("show")
.takes_value(true)
.action(ArgAction::Set)
.value_name("RECIPE")
.conflicts_with(arg::ARGUMENTS)
.help("Show information about <RECIPE>"),
)
.arg(
Arg::with_name(cmd::SUMMARY)
Arg::new(cmd::SUMMARY)
.long("summary")
.action(ArgAction::SetTrue)
.help("List names of available recipes"),
)
.arg(
Arg::with_name(cmd::VARIABLES)
Arg::new(cmd::VARIABLES)
.long("variables")
.action(ArgAction::SetTrue)
.help("List names of variables"),
)
.arg(
Arg::with_name(arg::DOTENV_FILENAME)
Arg::new(arg::DOTENV_FILENAME)
.long("dotenv-filename")
.takes_value(true)
.action(ArgAction::Set)
.help("Search for environment file named <DOTENV-FILENAME> instead of `.env`")
.conflicts_with(arg::DOTENV_PATH),
)
.arg(
Arg::with_name(arg::DOTENV_PATH)
.short("E")
Arg::new(arg::DOTENV_PATH)
.short('E')
.long("dotenv-path")
.action(ArgAction::Set)
.value_parser(value_parser!(PathBuf))
.help("Load <DOTENV-PATH> as environment file instead of searching for one")
.takes_value(true),
)
.group(ArgGroup::with_name("SUBCOMMAND").args(cmd::ALL))
.group(ArgGroup::new("SUBCOMMAND").args(cmd::ALL))
.arg(
Arg::with_name(arg::ARGUMENTS)
.multiple(true)
Arg::new(arg::ARGUMENTS)
.num_args(1..)
.action(ArgAction::Append)
.help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"),
);
@ -434,12 +464,12 @@ impl Config {
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
let value = matches
.value_of(arg::COLOR)
.get_one::<String>(arg::COLOR)
.ok_or_else(|| ConfigError::Internal {
message: "`--color` had no value".to_string(),
})?;
match value {
match value.as_str() {
arg::COLOR_AUTO => Ok(Color::auto()),
arg::COLOR_ALWAYS => Ok(Color::always()),
arg::COLOR_NEVER => Ok(Color::never()),
@ -450,8 +480,8 @@ impl Config {
}
fn command_color_from_matches(matches: &ArgMatches) -> ConfigResult<Option<ansi_term::Color>> {
if let Some(value) = matches.value_of(arg::COMMAND_COLOR) {
match value {
if let Some(value) = matches.get_one::<String>(arg::COMMAND_COLOR) {
match value.as_str() {
arg::COMMAND_COLOR_BLACK => Ok(Some(ansi_term::Color::Black)),
arg::COMMAND_COLOR_BLUE => Ok(Some(ansi_term::Color::Blue)),
arg::COMMAND_COLOR_CYAN => Ok(Some(ansi_term::Color::Cyan)),
@ -469,13 +499,14 @@ impl Config {
}
fn dump_format_from_matches(matches: &ArgMatches) -> ConfigResult<DumpFormat> {
let value = matches
.value_of(arg::DUMP_FORMAT)
let value =
matches
.get_one::<String>(arg::DUMP_FORMAT)
.ok_or_else(|| ConfigError::Internal {
message: "`--dump-format` had no value".to_string(),
})?;
match value {
match value.as_str() {
arg::DUMP_FORMAT_JSON => Ok(DumpFormat::Json),
arg::DUMP_FORMAT_JUST => Ok(DumpFormat::Just),
_ => Err(ConfigError::Internal {
@ -487,36 +518,37 @@ impl Config {
pub(crate) fn from_matches(matches: &ArgMatches) -> ConfigResult<Self> {
let invocation_directory = env::current_dir().context(config_error::CurrentDirContext)?;
let verbosity = if matches.is_present(arg::QUIET) {
let verbosity = if matches.get_flag(arg::QUIET) {
Verbosity::Quiet
} else {
Verbosity::from_flag_occurrences(matches.occurrences_of(arg::VERBOSE))
Verbosity::from_flag_occurrences(matches.get_count(arg::VERBOSE))
};
let color = Self::color_from_matches(matches)?;
let command_color = Self::command_color_from_matches(matches)?;
let set_count = matches.occurrences_of(arg::SET);
let mut overrides = BTreeMap::new();
if set_count > 0 {
let mut values = matches.values_of(arg::SET).unwrap();
for _ in 0..set_count {
overrides.insert(
values.next().unwrap().to_owned(),
values.next().unwrap().to_owned(),
);
if let Some(mut values) = matches.get_many::<String>(arg::SET) {
while let (Some(k), Some(v)) = (values.next(), values.next()) {
overrides.insert(k.into(), v.into());
}
}
let positional = Positional::from_values(matches.values_of(arg::ARGUMENTS));
let positional = Positional::from_values(
matches
.get_many::<String>(arg::ARGUMENTS)
.map(|s| s.map(String::as_str)),
);
for (name, value) in positional.overrides {
overrides.insert(name.clone(), value.clone());
}
let search_config = {
let justfile = matches.value_of(arg::JUSTFILE).map(PathBuf::from);
let working_directory = matches.value_of(arg::WORKING_DIRECTORY).map(PathBuf::from);
let justfile = matches.get_one::<PathBuf>(arg::JUSTFILE).map(Into::into);
let working_directory = matches
.get_one::<PathBuf>(arg::WORKING_DIRECTORY)
.map(Into::into);
if let Some(search_directory) = positional.search_directory.map(PathBuf::from) {
if justfile.is_some() || working_directory.is_some() {
@ -543,7 +575,7 @@ impl Config {
};
for subcommand in cmd::ARGLESS {
if matches.is_present(subcommand) {
if matches.get_flag(subcommand) {
match (!overrides.is_empty(), !positional.arguments.is_empty()) {
(false, false) => {}
(true, false) => {
@ -569,41 +601,37 @@ impl Config {
}
}
let subcommand = if matches.is_present(cmd::CHANGELOG) {
let subcommand = if matches.get_flag(cmd::CHANGELOG) {
Subcommand::Changelog
} else if matches.is_present(cmd::CHOOSE) {
} else if matches.get_flag(cmd::CHOOSE) {
Subcommand::Choose {
chooser: matches.value_of(arg::CHOOSER).map(str::to_owned),
chooser: matches.get_one::<String>(arg::CHOOSER).map(Into::into),
overrides,
}
} else if let Some(values) = matches.values_of_os(cmd::COMMAND) {
let mut arguments = values.map(OsStr::to_owned).collect::<Vec<OsString>>();
} else if let Some(values) = matches.get_many::<OsString>(cmd::COMMAND) {
let mut arguments = values.map(Into::into).collect::<Vec<OsString>>();
Subcommand::Command {
binary: arguments.remove(0),
arguments,
overrides,
}
} else if let Some(shell) = matches.value_of(cmd::COMPLETIONS) {
Subcommand::Completions {
shell: shell.to_owned(),
}
} else if matches.is_present(cmd::EDIT) {
} else if let Some(&shell) = matches.get_one::<clap_complete::Shell>(cmd::COMPLETIONS) {
Subcommand::Completions { shell }
} else if matches.get_flag(cmd::EDIT) {
Subcommand::Edit
} else if matches.is_present(cmd::SUMMARY) {
} else if matches.get_flag(cmd::SUMMARY) {
Subcommand::Summary
} else if matches.is_present(cmd::DUMP) {
} else if matches.get_flag(cmd::DUMP) {
Subcommand::Dump
} else if matches.is_present(cmd::FORMAT) {
} else if matches.get_flag(cmd::FORMAT) {
Subcommand::Format
} else if matches.is_present(cmd::INIT) {
} else if matches.get_flag(cmd::INIT) {
Subcommand::Init
} else if matches.is_present(cmd::LIST) {
} else if matches.get_flag(cmd::LIST) {
Subcommand::List
} else if let Some(name) = matches.value_of(cmd::SHOW) {
Subcommand::Show {
name: name.to_owned(),
}
} else if matches.is_present(cmd::EVALUATE) {
} else if let Some(name) = matches.get_one::<String>(cmd::SHOW).map(Into::into) {
Subcommand::Show { name }
} else if matches.get_flag(cmd::EVALUATE) {
if positional.arguments.len() > 1 {
return Err(ConfigError::SubcommandArguments {
subcommand: cmd::EVALUATE,
@ -619,7 +647,7 @@ impl Config {
variable: positional.arguments.into_iter().next(),
overrides,
}
} else if matches.is_present(cmd::VARIABLES) {
} else if matches.get_flag(cmd::VARIABLES) {
Subcommand::Variables
} else {
Subcommand::Run {
@ -628,55 +656,46 @@ impl Config {
}
};
let shell_args = if matches.occurrences_of(arg::SHELL_ARG) > 0
|| matches.occurrences_of(arg::CLEAR_SHELL_ARGS) > 0
{
Some(
matches
.values_of(arg::SHELL_ARG)
.map_or(Vec::new(), |shell_args| {
shell_args.map(str::to_owned).collect()
}),
)
let shell_args = if matches.get_flag(arg::CLEAR_SHELL_ARGS) {
Some(Vec::new())
} else {
None
matches
.get_many::<String>(arg::SHELL_ARG)
.map(|s| s.map(Into::into).collect())
};
let unstable = matches.is_present(arg::UNSTABLE)
|| env::var_os("JUST_UNSTABLE")
.map(|val| !(val == "false" || val == "0" || val.is_empty()))
.unwrap_or_default();
let unstable = matches.get_flag(arg::UNSTABLE);
Ok(Self {
check: matches.is_present(arg::CHECK),
check: matches.get_flag(arg::CHECK),
color,
command_color,
dotenv_filename: matches.value_of(arg::DOTENV_FILENAME).map(str::to_owned),
dotenv_path: matches.value_of(arg::DOTENV_PATH).map(PathBuf::from),
dry_run: matches.is_present(arg::DRY_RUN),
dotenv_filename: matches
.get_one::<String>(arg::DOTENV_FILENAME)
.map(Into::into),
dotenv_path: matches.get_one::<PathBuf>(arg::DOTENV_PATH).map(Into::into),
dry_run: matches.get_flag(arg::DRY_RUN),
dump_format: Self::dump_format_from_matches(matches)?,
highlight: !matches.is_present(arg::NO_HIGHLIGHT),
highlight: !matches.get_flag(arg::NO_HIGHLIGHT),
invocation_directory,
list_heading: matches
.value_of(arg::LIST_HEADING)
.unwrap_or("Available recipes:\n")
.to_owned(),
.get_one::<String>(arg::LIST_HEADING)
.map_or_else(|| "Available recipes:\n".into(), Into::into),
list_prefix: matches
.value_of(arg::LIST_PREFIX)
.unwrap_or(" ")
.to_owned(),
load_dotenv: !matches.is_present(arg::NO_DOTENV),
no_aliases: matches.is_present(arg::NO_ALIASES),
no_dependencies: matches.is_present(arg::NO_DEPS),
.get_one::<String>(arg::LIST_PREFIX)
.map_or_else(|| " ".into(), Into::into),
load_dotenv: !matches.get_flag(arg::NO_DOTENV),
no_aliases: matches.get_flag(arg::NO_ALIASES),
no_dependencies: matches.get_flag(arg::NO_DEPS),
search_config,
shell: matches.value_of(arg::SHELL).map(str::to_owned),
shell: matches.get_one::<String>(arg::SHELL).map(Into::into),
shell_args,
shell_command: matches.is_present(arg::SHELL_COMMAND),
shell_command: matches.get_flag(arg::SHELL_COMMAND),
subcommand,
unsorted: matches.is_present(arg::UNSORTED),
unsorted: matches.get_flag(arg::UNSORTED),
unstable,
verbosity,
yes: matches.is_present(arg::YES),
yes: matches.get_flag(arg::YES),
})
}
@ -701,9 +720,11 @@ impl Config {
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use {
super::*,
clap::error::{ContextKind, ContextValue},
pretty_assertions::assert_eq,
};
macro_rules! test {
{
@ -748,10 +769,11 @@ mod tests {
}
}
#[track_caller]
fn test(arguments: &[&str], want: Config) {
let app = Config::app();
let matches = app
.get_matches_from_safe(arguments)
.try_get_matches_from(arguments)
.expect("argument parsing failed");
let have = Config::from_matches(&matches).expect("config parsing failed");
assert_eq!(have, want);
@ -771,7 +793,7 @@ mod tests {
let app = Config::app();
app.get_matches_from_safe(arguments).expect_err("Expected clap error");
app.try_get_matches_from(arguments).expect_err("Expected clap error");
}
};
{
@ -789,7 +811,7 @@ mod tests {
let app = Config::app();
let matches = app.get_matches_from_safe(arguments).expect("Matching fails");
let matches = app.try_get_matches_from(arguments).expect("Matching fails");
match Config::from_matches(&matches).expect_err("config parsing succeeded") {
$error => { $($check)? }
@ -799,6 +821,30 @@ mod tests {
}
}
macro_rules! error_matches {
(
name: $name:ident,
args: [$($arg:expr),*],
error: $error:pat,
$(check: $check:block,)?
) => {
#[test]
fn $name() {
let arguments = &[
"just",
$($arg,)*
];
let app = Config::app();
match app.try_get_matches_from(arguments) {
Err($error) => { $($check)? }
other => panic!("Unexpected result from get matches: {other:?}")
}
}
};
}
macro_rules! map {
{} => {
BTreeMap::new()
@ -1124,13 +1170,13 @@ mod tests {
test! {
name: subcommand_completions,
args: ["--completions", "bash"],
subcommand: Subcommand::Completions{shell: "bash".to_owned()},
subcommand: Subcommand::Completions{ shell: clap_complete::Shell::Bash },
}
test! {
name: subcommand_completions_uppercase,
args: ["--completions", "BASH"],
subcommand: Subcommand::Completions{shell: "BASH".to_owned()},
subcommand: Subcommand::Completions{ shell: clap_complete::Shell::Bash },
}
error! {
@ -1400,13 +1446,17 @@ mod tests {
error: ConfigError::SearchDirConflict,
}
error! {
error_matches! {
name: completions_arguments,
args: ["--completions", "zsh", "foo"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
error: error,
check: {
assert_eq!(subcommand, cmd::COMPLETIONS);
assert_eq!(arguments, &["foo"]);
assert_eq!(error.kind(), clap::error::ErrorKind::InvalidValue);
assert_eq!(error.context().collect::<Vec<_>>(), vec![
(ContextKind::InvalidArg, &ContextValue::String("--completions <SHELL>...".into())),
(ContextKind::InvalidValue, &ContextValue::String("foo".into())),
(ContextKind::ValidValue, &ContextValue::Strings(["bash".into(), "elvish".into(), "fish".into(), "powershell".into(), "zsh".into()].into())),
]);
},
}
@ -1490,13 +1540,17 @@ mod tests {
},
}
error! {
error_matches! {
name: show_arguments,
args: ["--show", "foo", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
error: error,
check: {
assert_eq!(subcommand, cmd::SHOW);
assert_eq!(arguments, &["bar"]);
assert_eq!(error.kind(), clap::error::ErrorKind::ArgumentConflict);
assert_eq!(error.context().collect::<Vec<_>>(), vec![
(ContextKind::InvalidArg, &ContextValue::String("--show <RECIPE>".into())),
(ContextKind::PriorArg, &ContextValue::String("[ARGUMENTS]...".into())),
(ContextKind::Usage, &ContextValue::StyledStr("\u{1b}[33mUsage:\u{1b}[0m \u{1b}[32mjust\u{1b}[0m \u{1b}[32m--show\u{1b}[0m\u{1b}[32m \u{1b}[0m\u{1b}[32m<RECIPE>\u{1b}[0m \u{1b}[32m[ARGUMENTS]...\u{1b}[0m".into())),
]);
},
}

View File

@ -142,10 +142,13 @@ pub(crate) enum Error<'src> {
line_number: Option<usize>,
signal: i32,
},
TmpdirIo {
TempdirIo {
recipe: &'src str,
io_error: io::Error,
},
TempfileIo {
io_error: io::Error,
},
Unknown {
recipe: &'src str,
line_number: Option<usize>,
@ -403,10 +406,13 @@ impl<'src> ColorDisplay for Error<'src> {
write!(f, "Recipe `{recipe}` was terminated by signal {signal}")?;
}
}
TmpdirIo { recipe, io_error } => {
TempdirIo { recipe, io_error } => {
write!(f, "Recipe `{recipe}` could not be run because of an IO error while trying to create a temporary \
directory or write a file to that directory: {io_error}")?;
}
TempfileIo { io_error } => {
write!(f, "Tempfile I/O error: {io_error}")?;
}
Unknown { recipe, line_number} => {
if let Some(n) = line_number {
write!(f, "Recipe `{recipe}` failed on line {n} for an unknown reason")?;

View File

@ -504,7 +504,10 @@ mod tests {
fn dir_not_unicode() {
use std::os::unix::ffi::OsStrExt;
assert_eq!(
dir("foo", || Some(OsStr::from_bytes(b"\xe0\x80\x80").into())).unwrap_err(),
dir("foo", || Some(
std::ffi::OsStr::from_bytes(b"\xe0\x80\x80").into()
))
.unwrap_err(),
"unable to convert foo directory path to string: <20><><EFBFBD>",
);
}

View File

@ -42,10 +42,10 @@ pub(crate) use {
cmp,
collections::{BTreeMap, BTreeSet, HashMap},
env,
ffi::{OsStr, OsString},
ffi::OsString,
fmt::{self, Debug, Display, Formatter},
fs,
io::{self, Cursor, Write},
io::{self, Write},
iter::{self, FromIterator},
mem,
ops::Deref,

View File

@ -336,7 +336,7 @@ impl<'src, D> Recipe<'src, D> {
Some(tempdir) => tempdir_builder.tempdir_in(context.search.working_directory.join(tempdir)),
None => tempdir_builder.tempdir(),
}
.map_err(|error| Error::TmpdirIo {
.map_err(|error| Error::TempdirIo {
recipe: self.name(),
io_error: error,
})?;
@ -344,7 +344,7 @@ impl<'src, D> Recipe<'src, D> {
path.push(shebang.script_filename(self.name()));
{
let mut f = fs::File::create(&path).map_err(|error| Error::TmpdirIo {
let mut f = fs::File::create(&path).map_err(|error| Error::TempdirIo {
recipe: self.name(),
io_error: error,
})?;
@ -372,14 +372,14 @@ impl<'src, D> Recipe<'src, D> {
}
f.write_all(text.as_bytes())
.map_err(|error| Error::TmpdirIo {
.map_err(|error| Error::TempdirIo {
recipe: self.name(),
io_error: error,
})?;
}
// make script executable
Platform::set_execute_permission(&path).map_err(|error| Error::TmpdirIo {
Platform::set_execute_permission(&path).map_err(|error| Error::TempdirIo {
recipe: self.name(),
io_error: error,
})?;

View File

@ -1,4 +1,8 @@
use super::*;
use {
super::*,
std::io::{Read, Seek},
tempfile::tempfile,
};
const INIT_JUSTFILE: &str = "default:\n echo 'Hello, world!'\n";
@ -15,7 +19,7 @@ pub(crate) enum Subcommand {
overrides: BTreeMap<String, String>,
},
Completions {
shell: String,
shell: clap_complete::Shell,
},
Dump,
Edit,
@ -50,7 +54,7 @@ impl Subcommand {
Self::changelog();
return Ok(());
}
Completions { shell } => return Self::completions(shell),
Completions { shell } => return Self::completions(*shell),
Init => return Self::init(config),
Run {
arguments,
@ -213,10 +217,7 @@ impl Subcommand {
return Err(Error::NoChoosableRecipes);
}
let chooser = chooser
.map(OsString::from)
.or_else(|| env::var_os(config::CHOOSER_ENVIRONMENT_KEY))
.unwrap_or_else(|| config::chooser_default(&search.justfile));
let chooser = chooser.map_or_else(|| config::chooser_default(&search.justfile), From::from);
let result = justfile
.settings
@ -275,8 +276,8 @@ impl Subcommand {
justfile.run(config, search, overrides, &recipes)
}
fn completions(shell: &str) -> RunResult<'static, ()> {
use clap::Shell;
fn completions(shell: clap_complete::Shell) -> RunResult<'static, ()> {
use clap_complete::Shell;
fn replace(haystack: &mut String, needle: &str, replacement: &str) -> RunResult<'static, ()> {
if let Some(index) = haystack.find(needle) {
@ -289,15 +290,28 @@ impl Subcommand {
}
}
let shell = shell
.parse::<Shell>()
.expect("Invalid value for clap::Shell");
let mut script = {
let mut tempfile = tempfile().map_err(|io_error| Error::TempfileIo { io_error })?;
let buffer = Vec::new();
let mut cursor = Cursor::new(buffer);
Config::app().gen_completions_to(env!("CARGO_PKG_NAME"), shell, &mut cursor);
let buffer = cursor.into_inner();
let mut script = String::from_utf8(buffer).expect("Clap completion not UTF-8");
clap_complete::generate(
shell,
&mut crate::config::Config::app(),
env!("CARGO_PKG_NAME"),
&mut tempfile,
);
tempfile
.rewind()
.map_err(|io_error| Error::TempfileIo { io_error })?;
let mut buffer = String::new();
tempfile
.read_to_string(&mut buffer)
.map_err(|io_error| Error::TempfileIo { io_error })?;
buffer
};
match shell {
Shell::Bash => {
@ -319,7 +333,7 @@ impl Subcommand {
replace(&mut script, needle, replacement)?;
}
}
Shell::Elvish => {}
_ => {}
}
println!("{}", script.trim());

View File

@ -10,7 +10,7 @@ pub(crate) fn config(args: &[&str]) -> Config {
let app = Config::app();
let matches = app.get_matches_from_safe(args).unwrap();
let matches = app.try_get_matches_from(args).unwrap();
Config::from_matches(&matches).unwrap()
}

View File

@ -9,7 +9,7 @@ pub(crate) enum Verbosity {
}
impl Verbosity {
pub(crate) fn from_flag_occurrences(flag_occurrences: u64) -> Self {
pub(crate) fn from_flag_occurrences(flag_occurrences: u8) -> Self {
match flag_occurrences {
0 => Taciturn,
1 => Loquacious,

View File

@ -39,17 +39,12 @@ test! {
echo XYZ
",
args: ("--command"),
stderr: &format!("
error: The argument '--command <COMMAND>' requires a value but none was supplied
stderr: "
error: a value is required for '--command <COMMAND>...' but none was supplied
USAGE:
just{EXE_SUFFIX} --color <COLOR> --dump-format <FORMAT> --shell <SHELL> \
<--changelog|--choose|--command <COMMAND>|--completions <SHELL>|--dump|--edit|\
--evaluate|--fmt|--init|--list|--show <RECIPE>|--summary|--variables>
For more information try --help
"),
status: EXIT_FAILURE,
For more information, try '--help'.
",
status: 2,
}
test! {

View File

@ -15,10 +15,10 @@ test! {
name: check_without_fmt,
justfile: "",
args: ("--check"),
stderr_regex: "error: The following required arguments were not provided:
stderr_regex: "error: the following required arguments were not provided:
--fmt
(.|\\n)+",
status: EXIT_FAILURE,
status: 2,
}
test! {

View File

@ -189,6 +189,7 @@ impl Test {
}
impl Test {
#[track_caller]
pub(crate) fn run(self) -> Output {
if let Some(justfile) = &self.justfile {
let justfile = unindent(justfile);