Update zsh completion file (#606)

- Complete variable names after `--set`
- Complete recipe names
- Display recipe signature and body below command line
- Modify completions subcommand to produce enhanced zsh completion script
This commit is contained in:
Zhenhui Xie 2020-03-17 08:20:14 +08:00 committed by GitHub
parent 784232e98b
commit e79482f2dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 152 additions and 24 deletions

View File

@ -14,18 +14,18 @@ _just() {
fi fi
local context curcontext="$curcontext" state line local context curcontext="$curcontext" state line
_arguments "${_arguments_options[@]}" \ local common=(
'--color=[Print colorful output]: :(auto always never)' \ '--color=[Print colorful output]: :(auto always never)' \
'-f+[Use <JUSTFILE> as justfile.]' \ '-f+[Use <JUSTFILE> as justfile.]' \
'--justfile=[Use <JUSTFILE> as justfile.]' \ '--justfile=[Use <JUSTFILE> as justfile.]' \
'*--set=[Override <VARIABLE> with <VALUE>]' \ '*--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]' \
'--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]' \
'--completions=[Print shell completion script for <SHELL>]: :(zsh bash fish powershell elvish)' \ '--completions=[Print shell completion script for <SHELL>]: :(zsh bash fish powershell elvish)' \
'-s+[Show information about <RECIPE>]' \ '-s+[Show information about <RECIPE>]: :_just_commands' \
'--show=[Show information about <RECIPE>]' \ '--show=[Show information about <RECIPE>]: :_just_commands' \
'(-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-highlight[Don'\''t highlight echoed recipe lines in bold]' \ '--no-highlight[Don'\''t highlight echoed recipe lines in bold]' \
@ -47,17 +47,50 @@ _just() {
'--help[Print help information]' \ '--help[Print help information]' \
'-V[Print version information]' \ '-V[Print version information]' \
'--version[Print version information]' \ '--version[Print version information]' \
'::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the justfile:_files' \ )
&& ret=0
_arguments "${_arguments_options[@]}" $common \
'1: :_just_commands' \
'*: :->args' \
&& ret=0
case $state in
args)
curcontext="${curcontext%:*}-${words[2]}:"
local lastarg=${words[${#words}]}
if [[ ${lastarg} = */* ]]; then
# Arguments contain slash would be recognised as a file
_arguments -s -S $common '*:: :_files'
else
# Show usage message
_message "`just --show ${words[2]}`"
# Or complete with other commands
#_arguments -s -S $common '*:: :_just_commands'
fi
;;
esac
return ret
} }
(( $+functions[_just_commands] )) || (( $+functions[_just_commands] )) ||
_just_commands() { _just_commands() {
local commands; commands=( local commands; commands=(
${${${(M)"${(f)$(_call_program commands just --list)}":# *}/ ##/}/ ##/:Args: }
) )
_describe -t commands 'just commands' commands "$@" _describe -t commands 'just commands' commands "$@"
} }
(( $+functions[_just_variables] )) ||
_just_variables() {
local variables; variables=(
${(s: :)$(_call_program commands just --variables)}
)
_describe -t variables 'variables' variables
}
_just "$@" _just "$@"

View File

@ -417,7 +417,7 @@ impl Config {
} }
if let Completions { shell } = self.subcommand { if let Completions { shell } = self.subcommand {
return Self::completions(&shell); return Subcommand::completions(&shell);
} }
let search = let search =
@ -459,21 +459,6 @@ impl Config {
} }
} }
fn completions(shell: &str) -> Result<(), i32> {
let shell = shell
.parse::<clap::Shell>()
.expect("Invalid value for clap::Shell");
let buffer = Vec::new();
let mut cursor = Cursor::new(buffer);
Self::app().gen_completions_to(env!("CARGO_PKG_NAME"), shell, &mut cursor);
let buffer = cursor.into_inner();
let text = String::from_utf8(buffer).expect("Clap completion not UTF-8");
println!("{}", text.trim());
Ok(())
}
fn dump(justfile: Justfile) -> Result<(), i32> { fn dump(justfile: Justfile) -> Result<(), i32> {
println!("{}", justfile); println!("{}", justfile);
Ok(()) Ok(())

View File

@ -22,3 +22,113 @@ pub(crate) enum Subcommand {
Summary, Summary,
Variables, Variables,
} }
const ZSH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[
(
r#" _arguments "${_arguments_options[@]}" \"#,
r#" local common=("#,
),
(
r#"'*--set=[Override <VARIABLE> with <VALUE>]' \"#,
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' \"#,
),
(
"'::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the \
justfile:_files' \\
&& ret=0
\x20\x20\x20\x20
",
r#")
_arguments "${_arguments_options[@]}" $common \
'1: :_just_commands' \
'*: :->args' \
&& ret=0
case $state in
args)
curcontext="${curcontext%:*}-${words[2]}:"
local lastarg=${words[${#words}]}
if [[ ${lastarg} = */* ]]; then
# Arguments contain slash would be recognised as a file
_arguments -s -S $common '*:: :_files'
else
# Show usage message
_message "`just --show ${words[2]}`"
# Or complete with other commands
#_arguments -s -S $common '*:: :_just_commands'
fi
;;
esac
return ret
"#,
),
(
" local commands; commands=(
\x20\x20\x20\x20\x20\x20\x20\x20
)",
r#" local commands; commands=(
${${${(M)"${(f)$(_call_program commands just --list)}":# *}/ ##/}/ ##/:Args: }
)
"#,
),
(
r#"_just "$@""#,
r#"(( $+functions[_just_variables] )) ||
_just_variables() {
local variables; variables=(
${(s: :)$(_call_program commands just --variables)}
)
_describe -t variables 'variables' variables
}
_just "$@""#,
),
];
impl Subcommand {
pub(crate) fn completions(shell: &str) -> Result<(), i32> {
fn replace(haystack: &mut String, needle: &str, replacement: &str) -> Result<(), i32> {
if let Some(index) = haystack.find(needle) {
haystack.replace_range(index..index + needle.len(), replacement);
Ok(())
} else {
eprintln!("Failed to find text:");
eprintln!("{}", needle);
eprintln!("…in completion script:");
eprintln!("{}", haystack);
Err(EXIT_FAILURE)
}
}
let shell = shell
.parse::<clap::Shell>()
.expect("Invalid value for clap::Shell");
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");
if let clap::Shell::Zsh = shell {
for (needle, replacement) in ZSH_COMPLETION_REPLACEMENTS {
replace(&mut script, needle, replacement)?;
}
}
println!("{}", script.trim());
Ok(())
}
}