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

View File

@ -22,7 +22,8 @@ ansi_term = "0.12.0"
atty = "0.2.0" atty = "0.2.0"
blake3 = { version = "1.5.0", features = ["rayon", "mmap"] } blake3 = { version = "1.5.0", features = ["rayon", "mmap"] }
camino = "1.0.4" 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"] } ctrlc = { version = "3.1.1", features = ["termination"] }
derivative = "2.0.0" derivative = "2.0.0"
dirs = "5.0.1" dirs = "5.0.1"

View File

@ -1,5 +1,5 @@
_just() { _just() {
local i cur prev words cword opts cmds local i cur prev words cword opts cmd
COMPREPLY=() COMPREPLY=()
# Modules use "::" as the separator, which is considered a wordbreak character in bash. # Modules use "::" as the separator, which is considered a wordbreak character in bash.
@ -19,19 +19,18 @@ _just() {
for i in ${words[@]} for i in ${words[@]}
do do
case "${i}" in case "${cmd},${i}" in
"$1") ",$1")
cmd="just" cmd="just"
;; ;;
*) *)
;; ;;
esac esac
done done
case "${cmd}" in case "${cmd}" in
just) "$1")
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>... " 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 if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
@ -53,7 +52,6 @@ _just() {
fi fi
fi fi
case "${prev}" in case "${prev}" in
--chooser) --chooser)
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
@ -82,7 +80,7 @@ _just() {
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
-f) -f)
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
@ -102,7 +100,7 @@ _just() {
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
-d) -d)
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
@ -110,19 +108,19 @@ _just() {
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
-c) -c)
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
--completions) --completions)
COMPREPLY=($(compgen -W "zsh bash fish powershell elvish" -- "${cur}")) COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}"))
return 0 return 0
;; ;;
--show) --show)
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
-s) -s)
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
@ -134,7 +132,7 @@ _just() {
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
-E) -E)
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
@ -145,8 +143,11 @@ _just() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
esac esac
} }
complete -F _just -o bashdefault -o default just 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]{ use builtin;
fn spaces [n]{ use str;
repeat $n ' ' | joins ''
set edit:completion:arg-completer[just] = {|@words|
fn spaces {|n|
builtin:repeat $n ' ' | str:join ''
} }
fn cand [text desc]{ fn cand {|text desc|
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
} }
command = 'just' var command = 'just'
for word $words[1:-1] { for word $words[1..-1] {
if (has-prefix $word '-') { if (str:has-prefix $word '-') {
break break
} }
command = $command';'$word set command = $command';'$word
} }
completions = [ var completions = [
&'just'= { &'just'= {
cand --chooser 'Override binary invoked by `--choose`' cand --chooser 'Override binary invoked by `--choose`'
cand --color 'Print colorful output' cand --color 'Print colorful output'
@ -65,10 +68,10 @@ edit:completion:arg-completer[just] = [@words]{
cand --list 'List available recipes and their arguments' cand --list 'List available recipes and their arguments'
cand --summary 'List names of available recipes' cand --summary 'List names of available recipes'
cand --variables 'List names of variables' cand --variables 'List names of variables'
cand -h 'Print help information' cand -h 'Print help'
cand --help 'Print help information' cand --help 'Print help'
cand -V 'Print version information' cand -V 'Print version'
cand --version 'Print version information' cand --version 'Print version'
} }
] ]
$completions[$command] $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)' complete -c just -a '(__fish_just_complete_recipes)'
# autogenerated completions # autogenerated completions
complete -c just -n "__fish_use_subcommand" -l chooser -d 'Override binary invoked by `--choose`' complete -c just -l chooser -d 'Override binary invoked by `--choose`' -r
complete -c just -n "__fish_use_subcommand" -l color -d 'Print colorful output' -r -f -a "auto always never" complete -c just -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 -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 -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 -l list-heading -d 'Print <TEXT> before list' -r
complete -c just -n "__fish_use_subcommand" -l list-prefix -d 'Print <TEXT> before each list item' complete -c just -l list-prefix -d 'Print <TEXT> before each list item' -r
complete -c just -n "__fish_use_subcommand" -s f -l justfile -d 'Use <JUSTFILE> as justfile' complete -c just -s f -l justfile -d 'Use <JUSTFILE> as justfile' -r -F
complete -c just -n "__fish_use_subcommand" -l set -d 'Override <VARIABLE> with <VALUE>' complete -c just -l set -d 'Override <VARIABLE> with <VALUE>' -r
complete -c just -n "__fish_use_subcommand" -l shell -d 'Invoke <SHELL> to run recipes' complete -c just -l shell -d 'Invoke <SHELL> to run recipes' -r
complete -c just -n "__fish_use_subcommand" -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument' complete -c just -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument' -r
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 -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set' -r -F
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 -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set' -r
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 -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}"
complete -c just -n "__fish_use_subcommand" -s s -l show -d 'Show information about <RECIPE>' complete -c just -s s -l show -d 'Show information about <RECIPE>' -r
complete -c just -n "__fish_use_subcommand" -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' complete -c just -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' -r
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 -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one' -r -F
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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -l unstable -d 'Enable unstable features'
complete -c just -n "__fish_use_subcommand" -s v -l verbose -d 'Use verbose output' complete -c just -s v -l verbose -d 'Use verbose output'
complete -c just -n "__fish_use_subcommand" -l changelog -d 'Print changelog' complete -c just -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -s h -l help -d 'Print help'
complete -c just -n "__fish_use_subcommand" -s V -l version -d 'Print version information' 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] $element = $commandElements[$i]
if ($element -isnot [StringConstantExpressionAst] -or if ($element -isnot [StringConstantExpressionAst] -or
$element.StringConstantType -ne [StringConstantType]::BareWord -or $element.StringConstantType -ne [StringConstantType]::BareWord -or
$element.Value.StartsWith('-')) { $element.Value.StartsWith('-') -or
$element.Value -eq $wordToComplete) {
break break
} }
$element.Value $element.Value
@ -38,7 +39,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>') [CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>') [CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
[CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`') [CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`')
[CompletionResult]::new('-E', 'E', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one') [CompletionResult]::new('-E', 'E ', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
[CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one') [CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
[CompletionResult]::new('--check', 'check', [CompletionResultType]::ParameterName, 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.') [CompletionResult]::new('--check', 'check', [CompletionResultType]::ParameterName, 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Automatically confirm all recipes.') [CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Automatically confirm all recipes.')
@ -70,10 +71,10 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes and their arguments') [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('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables') [CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information') [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information') [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information') [CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information') [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
break break
} }
}) })

View File

@ -15,35 +15,35 @@ _just() {
local context curcontext="$curcontext" state line local context curcontext="$curcontext" state line
local common=( local common=(
'--chooser=[Override binary invoked by `--choose`]' \ '--chooser=[Override binary invoked by \`--choose\`]: : ' \
'--color=[Print colorful output]: :(auto always never)' \ '--color=[Print colorful output]: :(auto always never)' \
'--command-color=[Echo recipe lines in <COMMAND-COLOR>]: :(black blue cyan green purple red yellow)' \ '--command-color=[Echo recipe lines in <COMMAND-COLOR>]: :(black blue cyan green purple red yellow)' \
'--dump-format=[Dump justfile as <FORMAT>]: :(just json)' \ '--dump-format=[Dump justfile as <FORMAT>]:FORMAT:(just json)' \
'--list-heading=[Print <TEXT> before list]' \ '--list-heading=[Print <TEXT> before list]:TEXT: ' \
'--list-prefix=[Print <TEXT> before each list item]' \ '--list-prefix=[Print <TEXT> before each list item]:TEXT: ' \
'-f+[Use <JUSTFILE> as justfile]' \ '-f+[Use <JUSTFILE> as justfile]: :_files' \
'--justfile=[Use <JUSTFILE> as justfile]' \ '--justfile=[Use <JUSTFILE> as justfile]: :_files' \
'*--set[Override <VARIABLE> with <VALUE>]: :_just_variables' \ '*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \
'--shell=[Invoke <SHELL> to run recipes]' \ '--shell=[Invoke <SHELL> to run recipes]: : ' \
'*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]' \ '*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]: : ' \
'-d+[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]' \ '-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]' \ '--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]' \ '*-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]' \ '*--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)' \ '*--completions=[Print shell completion script for <SHELL>]:SHELL:(bash elvish fish powershell zsh)' \
'-s+[Show information about <RECIPE>]: :_just_commands' \ '-s+[Show information about <RECIPE>]: :(_just_commands)' \
'--show=[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 --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]' \ '-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]' \ '--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.]' \ '--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.]' \ '--yes[Automatically confirm all recipes.]' \
'(-q --quiet)-n[Print what just would do without doing it]' \ '(-q --quiet)-n[Print what just would do without doing it]' \
'(-q --quiet)--dry-run[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]' \ '--highlight[Highlight echoed recipe lines in bold]' \
'--no-aliases[Don'\''t show aliases in list]' \ '--no-aliases[Don'\''t show aliases in list]' \
'--no-deps[Don'\''t run recipe dependencies]' \ '--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]' \ '--no-highlight[Don'\''t highlight echoed recipe lines in bold]' \
'(-n --dry-run)-q[Suppress all output]' \ '(-n --dry-run)-q[Suppress all output]' \
'(-n --dry-run)--quiet[Suppress all output]' \ '(-n --dry-run)--quiet[Suppress all output]' \
@ -55,10 +55,10 @@ _just() {
'*-v[Use verbose output]' \ '*-v[Use verbose output]' \
'*--verbose[Use verbose output]' \ '*--verbose[Use verbose output]' \
'--changelog[Print changelog]' \ '--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]' \ '--dump[Print justfile]' \
'-e[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`]' \ '--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.]' \ '--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]' \ '--fmt[Format and overwrite justfile]' \
'--init[Initialize new justfile in project root]' \ '--init[Initialize new justfile in project root]' \
@ -66,10 +66,10 @@ _just() {
'--list[List available recipes and their arguments]' \ '--list[List available recipes and their arguments]' \
'--summary[List names of available recipes]' \ '--summary[List names of available recipes]' \
'--variables[List names of variables]' \ '--variables[List names of variables]' \
'-h[Print help information]' \ '-h[Print help]' \
'--help[Print help information]' \ '--help[Print help]' \
'-V[Print version information]' \ '-V[Print version]' \
'--version[Print version information]' \ '--version[Print version]' \
) )
_arguments "${_arguments_options[@]}" $common \ _arguments "${_arguments_options[@]}" $common \
@ -114,6 +114,7 @@ _just() {
esac esac
return ret return ret
} }
(( $+functions[_just_commands] )) || (( $+functions[_just_commands] )) ||
@ -138,7 +139,8 @@ _just_commands() {
} }
(( $+functions[_just_variables] )) || if [ "$funcstack[1]" = "_just" ]; then
(( $+functions[_just_variables] )) ||
_just_variables() { _just_variables() {
[[ $PREFIX = -* ]] && return 1 [[ $PREFIX = -* ]] && return 1
integer ret=1 integer ret=1
@ -158,3 +160,6 @@ _just_variables() {
} }
_just "$@" _just "$@"
else
compdef _just just
fi

View File

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

View File

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

View File

@ -504,7 +504,10 @@ mod tests {
fn dir_not_unicode() { fn dir_not_unicode() {
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
assert_eq!( 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>", "unable to convert foo directory path to string: <20><><EFBFBD>",
); );
} }

View File

@ -42,10 +42,10 @@ pub(crate) use {
cmp, cmp,
collections::{BTreeMap, BTreeSet, HashMap}, collections::{BTreeMap, BTreeSet, HashMap},
env, env,
ffi::{OsStr, OsString}, ffi::OsString,
fmt::{self, Debug, Display, Formatter}, fmt::{self, Debug, Display, Formatter},
fs, fs,
io::{self, Cursor, Write}, io::{self, Write},
iter::{self, FromIterator}, iter::{self, FromIterator},
mem, mem,
ops::Deref, 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)), Some(tempdir) => tempdir_builder.tempdir_in(context.search.working_directory.join(tempdir)),
None => tempdir_builder.tempdir(), None => tempdir_builder.tempdir(),
} }
.map_err(|error| Error::TmpdirIo { .map_err(|error| Error::TempdirIo {
recipe: self.name(), recipe: self.name(),
io_error: error, io_error: error,
})?; })?;
@ -344,7 +344,7 @@ impl<'src, D> Recipe<'src, D> {
path.push(shebang.script_filename(self.name())); 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(), recipe: self.name(),
io_error: error, io_error: error,
})?; })?;
@ -372,14 +372,14 @@ impl<'src, D> Recipe<'src, D> {
} }
f.write_all(text.as_bytes()) f.write_all(text.as_bytes())
.map_err(|error| Error::TmpdirIo { .map_err(|error| Error::TempdirIo {
recipe: self.name(), recipe: self.name(),
io_error: error, io_error: error,
})?; })?;
} }
// make script executable // 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(), recipe: self.name(),
io_error: error, 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"; const INIT_JUSTFILE: &str = "default:\n echo 'Hello, world!'\n";
@ -15,7 +19,7 @@ pub(crate) enum Subcommand {
overrides: BTreeMap<String, String>, overrides: BTreeMap<String, String>,
}, },
Completions { Completions {
shell: String, shell: clap_complete::Shell,
}, },
Dump, Dump,
Edit, Edit,
@ -50,7 +54,7 @@ impl Subcommand {
Self::changelog(); Self::changelog();
return Ok(()); return Ok(());
} }
Completions { shell } => return Self::completions(shell), Completions { shell } => return Self::completions(*shell),
Init => return Self::init(config), Init => return Self::init(config),
Run { Run {
arguments, arguments,
@ -213,10 +217,7 @@ impl Subcommand {
return Err(Error::NoChoosableRecipes); return Err(Error::NoChoosableRecipes);
} }
let chooser = chooser let chooser = chooser.map_or_else(|| config::chooser_default(&search.justfile), From::from);
.map(OsString::from)
.or_else(|| env::var_os(config::CHOOSER_ENVIRONMENT_KEY))
.unwrap_or_else(|| config::chooser_default(&search.justfile));
let result = justfile let result = justfile
.settings .settings
@ -275,8 +276,8 @@ impl Subcommand {
justfile.run(config, search, overrides, &recipes) justfile.run(config, search, overrides, &recipes)
} }
fn completions(shell: &str) -> RunResult<'static, ()> { fn completions(shell: clap_complete::Shell) -> RunResult<'static, ()> {
use clap::Shell; use clap_complete::Shell;
fn replace(haystack: &mut String, needle: &str, replacement: &str) -> RunResult<'static, ()> { fn replace(haystack: &mut String, needle: &str, replacement: &str) -> RunResult<'static, ()> {
if let Some(index) = haystack.find(needle) { if let Some(index) = haystack.find(needle) {
@ -289,15 +290,28 @@ impl Subcommand {
} }
} }
let shell = shell let mut script = {
.parse::<Shell>() let mut tempfile = tempfile().map_err(|io_error| Error::TempfileIo { io_error })?;
.expect("Invalid value for clap::Shell");
let buffer = Vec::new(); clap_complete::generate(
let mut cursor = Cursor::new(buffer); shell,
Config::app().gen_completions_to(env!("CARGO_PKG_NAME"), shell, &mut cursor); &mut crate::config::Config::app(),
let buffer = cursor.into_inner(); env!("CARGO_PKG_NAME"),
let mut script = String::from_utf8(buffer).expect("Clap completion not UTF-8"); &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 { match shell {
Shell::Bash => { Shell::Bash => {
@ -319,7 +333,7 @@ impl Subcommand {
replace(&mut script, needle, replacement)?; replace(&mut script, needle, replacement)?;
} }
} }
Shell::Elvish => {} _ => {}
} }
println!("{}", script.trim()); println!("{}", script.trim());

View File

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

View File

@ -9,7 +9,7 @@ pub(crate) enum Verbosity {
} }
impl 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 { match flag_occurrences {
0 => Taciturn, 0 => Taciturn,
1 => Loquacious, 1 => Loquacious,

View File

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

View File

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

View File

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