From f65bdfd344f49ed1d2948d13328c98457dfa90f8 Mon Sep 17 00:00:00 2001 From: jk-gan Date: Wed, 17 Aug 2022 15:46:10 +0800 Subject: [PATCH 1/4] feat: completion WIP --- Cargo.toml | 3 + build.rs | 10 ++ completions/_wukong | 257 +++++++++++++++++++++++++++ completions/wukong.bash | 374 ++++++++++++++++++++++++++++++++++++++++ src/app.rs | 20 ++- src/clap_app.rs | 5 + src/main.rs | 2 +- 7 files changed, 669 insertions(+), 2 deletions(-) create mode 100644 build.rs create mode 100644 completions/_wukong create mode 100644 completions/wukong.bash diff --git a/Cargo.toml b/Cargo.toml index 5ef21fe83..6bb72bc23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,6 @@ openidconnect = "2.3.2" url = "2.2.2" webbrowser = "0.7.1" env_config = { package = "config", version = "0.13.2"} +clap_complete = "3.2.4" + +[build-dependencies] diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..09236b841 --- /dev/null +++ b/build.rs @@ -0,0 +1,10 @@ +// use crate::commands::CommandGroup; +// use clap::{Clap, CommandFactory, Parser}; + +// include!("src/clap_app.rs"); + +fn main() { + // let mut app = ClapApp::command(); + + // todo!("generate the completion scripts!"); +} diff --git a/completions/_wukong b/completions/_wukong new file mode 100644 index 000000000..75feb3b6a --- /dev/null +++ b/completions/_wukong @@ -0,0 +1,257 @@ +#compdef wukong + +autoload -U is-at-least + +_wukong() { + typeset -A opt_args + typeset -a _arguments_options + local ret=1 + + if is-at-least 5.2; then + _arguments_options=(-s -S -C) + else + _arguments_options=(-s -C) + fi + + local context curcontext="$curcontext" state line + _arguments "${_arguments_options[@]}" \ +'--generate=[If provided, outputs the completion file for given shell]:GENERATOR:(bash elvish fish powershell zsh)' \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +'-V[Print version information]' \ +'--version[Print version information]' \ +":: :_wukong_commands" \ +"*::: :->wukong" \ +&& ret=0 + case $state in + (wukong) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:wukong-command-$line[1]:" + case $line[1] in + (init) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +&& ret=0 +;; +(pipeline) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +":: :_wukong__pipeline_commands" \ +"*::: :->pipeline" \ +&& ret=0 + + case $state in + (pipeline) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:wukong-pipeline-command-$line[1]:" + case $line[1] in + (list) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +&& ret=0 +;; +(describe) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +':name -- The pipeline name:' \ +&& ret=0 +;; +(ci-status) +_arguments "${_arguments_options[@]}" \ +'--repo-url=[Repository url]:REPO_URL: ' \ +'--branch=[Branch name]:BRANCH: ' \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'*::subcommand -- The subcommand whose help message to display:' \ +&& ret=0 +;; + esac + ;; +esac +;; +(config) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +":: :_wukong__config_commands" \ +"*::: :->config" \ +&& ret=0 + + case $state in + (config) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:wukong-config-command-$line[1]:" + case $line[1] in + (list) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +&& ret=0 +;; +(set) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +':config-name -- The config name:(application collect-telemetry enable-log log-dir)' \ +':config-value -- The config value:' \ +&& ret=0 +;; +(get) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +':config-name -- The config name:(application collect-telemetry enable-log log-dir)' \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'*::subcommand -- The subcommand whose help message to display:' \ +&& ret=0 +;; + esac + ;; +esac +;; +(login) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'*::subcommand -- The subcommand whose help message to display:' \ +&& ret=0 +;; + esac + ;; +esac +} + +(( $+functions[_wukong_commands] )) || +_wukong_commands() { + local commands; commands=( +'init:Initialize Wukong'\''s configurations' \ +'pipeline:This contains the commands to view & interact with an application’s pipeline' \ +'config:This contains the commands to view & interact with Wukong'\''s configurations' \ +'login:Login to start using wukong command' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'wukong commands' commands "$@" +} +(( $+functions[_wukong__pipeline__ci-status_commands] )) || +_wukong__pipeline__ci-status_commands() { + local commands; commands=() + _describe -t commands 'wukong pipeline ci-status commands' commands "$@" +} +(( $+functions[_wukong__config_commands] )) || +_wukong__config_commands() { + local commands; commands=( +'list:List the configurations' \ +'set:Set the value of a configuration' \ +'get:Print the value of a configuration' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'wukong config commands' commands "$@" +} +(( $+functions[_wukong__pipeline__describe_commands] )) || +_wukong__pipeline__describe_commands() { + local commands; commands=() + _describe -t commands 'wukong pipeline describe commands' commands "$@" +} +(( $+functions[_wukong__config__get_commands] )) || +_wukong__config__get_commands() { + local commands; commands=() + _describe -t commands 'wukong config get commands' commands "$@" +} +(( $+functions[_wukong__config__help_commands] )) || +_wukong__config__help_commands() { + local commands; commands=() + _describe -t commands 'wukong config help commands' commands "$@" +} +(( $+functions[_wukong__help_commands] )) || +_wukong__help_commands() { + local commands; commands=() + _describe -t commands 'wukong help commands' commands "$@" +} +(( $+functions[_wukong__pipeline__help_commands] )) || +_wukong__pipeline__help_commands() { + local commands; commands=() + _describe -t commands 'wukong pipeline help commands' commands "$@" +} +(( $+functions[_wukong__init_commands] )) || +_wukong__init_commands() { + local commands; commands=() + _describe -t commands 'wukong init commands' commands "$@" +} +(( $+functions[_wukong__config__list_commands] )) || +_wukong__config__list_commands() { + local commands; commands=() + _describe -t commands 'wukong config list commands' commands "$@" +} +(( $+functions[_wukong__pipeline__list_commands] )) || +_wukong__pipeline__list_commands() { + local commands; commands=() + _describe -t commands 'wukong pipeline list commands' commands "$@" +} +(( $+functions[_wukong__login_commands] )) || +_wukong__login_commands() { + local commands; commands=() + _describe -t commands 'wukong login commands' commands "$@" +} +(( $+functions[_wukong__pipeline_commands] )) || +_wukong__pipeline_commands() { + local commands; commands=( +'list:List the current pipelines of the application' \ +'describe:Show the details of a pipeline' \ +'ci-status:Show the build status and (possible) errors on branch ci pipeline' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'wukong pipeline commands' commands "$@" +} +(( $+functions[_wukong__config__set_commands] )) || +_wukong__config__set_commands() { + local commands; commands=() + _describe -t commands 'wukong config set commands' commands "$@" +} + +_wukong "$@" diff --git a/completions/wukong.bash b/completions/wukong.bash new file mode 100644 index 000000000..173662df0 --- /dev/null +++ b/completions/wukong.bash @@ -0,0 +1,374 @@ +_wukong() { + local i cur prev opts cmds + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + cmd="" + opts="" + + for i in ${COMP_WORDS[@]} + do + case "${i}" in + "$1") + cmd="wukong" + ;; + ci-status) + cmd+="__ci__status" + ;; + config) + cmd+="__config" + ;; + describe) + cmd+="__describe" + ;; + get) + cmd+="__get" + ;; + help) + cmd+="__help" + ;; + init) + cmd+="__init" + ;; + list) + cmd+="__list" + ;; + login) + cmd+="__login" + ;; + pipeline) + cmd+="__pipeline" + ;; + set) + cmd+="__set" + ;; + *) + ;; + esac + done + + case "${cmd}" in + wukong) + opts="-h -V -a --help --version --generate --application init pipeline config login help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --generate) + COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}")) + return 0 + ;; + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__config) + opts="-h -a --help --application list set get help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__config__get) + opts="-h -a --help --application application collect-telemetry enable-log log-dir" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__config__help) + opts="-a --application ..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__config__list) + opts="-h -a --help --application" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__config__set) + opts="-h -a --help --application application collect-telemetry enable-log log-dir " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__help) + opts="-a --application ..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__init) + opts="-h -a --help --application" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__login) + opts="-h -a --help --application" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__pipeline) + opts="-h -a --help --application list describe ci-status help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__pipeline__ci__status) + opts="-h -a --repo-url --branch --help --application" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --repo-url) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --branch) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__pipeline__describe) + opts="-h -a --help --application " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__pipeline__help) + opts="-a --application ..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__pipeline__list) + opts="-h -a --help --application" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + esac +} + +complete -F _wukong -o bashdefault -o default wukong diff --git a/src/app.rs b/src/app.rs index 8ba76ba51..e529da0b0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,7 +3,12 @@ use crate::{ config::{Config, CONFIG_FILE}, error::CliError, }; -use clap::Parser; +use clap::{Command, CommandFactory, Parser, ValueHint}; +use clap_complete::{ + generate, generate_to, + shells::{Bash, Zsh}, + Generator, Shell, +}; pub enum ConfigState { InitialisedButUnAuthenticated(Config), @@ -16,8 +21,21 @@ pub struct App { pub cli: ClapApp, } +fn print_completions(gen: G, cmd: &mut Command) { + generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout()); +} + impl App { pub fn new<'a>() -> Result> { + let cli = ClapApp::parse(); + let mut cmd = ClapApp::command(); + + cmd.set_bin_name("wukong"); + + let outdir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("completions/"); + generate_to::(Bash, &mut cmd, "wukong", &outdir); + generate_to::(Zsh, &mut cmd, "wukong", &outdir); + let config_file = CONFIG_FILE .as_ref() .expect("Unable to identify user's home directory"); diff --git a/src/clap_app.rs b/src/clap_app.rs index e4c775f76..5334fd009 100644 --- a/src/clap_app.rs +++ b/src/clap_app.rs @@ -1,10 +1,15 @@ use crate::commands::CommandGroup; use clap::Parser; +use clap_complete::{generate, Generator, Shell}; /// A Swiss-army Knife CLI For Mindvalley Developers #[derive(Debug, Parser)] #[clap(version, author)] pub struct ClapApp { + /// If provided, outputs the completion file for given shell + #[clap(long = "generate", arg_enum)] + pub generator: Option, + #[clap(subcommand)] pub command_group: CommandGroup, diff --git a/src/main.rs b/src/main.rs index 5c8754f95..b5c0adeb6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ mod app; mod auth; -mod clap_app; +pub mod clap_app; mod commands; mod config; mod error; From 51e8b5f1ee588676268ad34150ec2c588afce411 Mon Sep 17 00:00:00 2001 From: jk-gan Date: Thu, 18 Aug 2022 16:20:26 +0800 Subject: [PATCH 2/4] feat: add completions command --- Cargo.toml | 4 +-- completions/{ => bash}/wukong.bash | 27 ++++++++++++++++-- completions/fish/wukong.fish | 44 ++++++++++++++++++++++++++++++ completions/{ => zsh}/_wukong | 16 ++++++++++- generate-completions.sh | 26 ++++++++++++++++++ src/app.rs | 20 +------------- src/clap_app.rs | 5 ---- src/commands/completions.rs | 12 ++++++++ src/commands/mod.rs | 7 +++++ src/main.rs | 5 +++- 10 files changed, 135 insertions(+), 31 deletions(-) rename completions/{ => bash}/wukong.bash (92%) create mode 100644 completions/fish/wukong.fish rename completions/{ => zsh}/_wukong (93%) create mode 100644 generate-completions.sh create mode 100644 src/commands/completions.rs diff --git a/Cargo.toml b/Cargo.toml index 6bb72bc23..6403606c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,13 @@ name = "wukong" version = "0.0.1" edition = "2021" +default-run = "wukong" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] clap = { version = "3.2.5", features = ["derive"] } +clap_complete = { version = "3.2.4" } dirs-next = "2.0.0" indicatif = "0.17.0" lazy_static = "1.4.0" @@ -36,6 +38,4 @@ openidconnect = "2.3.2" url = "2.2.2" webbrowser = "0.7.1" env_config = { package = "config", version = "0.13.2"} -clap_complete = "3.2.4" -[build-dependencies] diff --git a/completions/wukong.bash b/completions/bash/wukong.bash similarity index 92% rename from completions/wukong.bash rename to completions/bash/wukong.bash index 173662df0..c67dfd812 100644 --- a/completions/wukong.bash +++ b/completions/bash/wukong.bash @@ -15,6 +15,9 @@ _wukong() { ci-status) cmd+="__ci__status" ;; + completions) + cmd+="__completions" + ;; config) cmd+="__config" ;; @@ -49,16 +52,34 @@ _wukong() { case "${cmd}" in wukong) - opts="-h -V -a --help --version --generate --application init pipeline config login help" + opts="-h -V -a --help --version --application init pipeline config login completions help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in - --generate) - COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}")) + --application) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -a) + COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + wukong__completions) + opts="-h -a --help --application bash elvish fish powershell zsh" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in --application) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/completions/fish/wukong.fish b/completions/fish/wukong.fish new file mode 100644 index 000000000..e379e1109 --- /dev/null +++ b/completions/fish/wukong.fish @@ -0,0 +1,44 @@ +complete -c wukong -n "__fish_use_subcommand" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_use_subcommand" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_use_subcommand" -s V -l version -d 'Print version information' +complete -c wukong -n "__fish_use_subcommand" -f -a "init" -d 'Initialize Wukong\'s configurations' +complete -c wukong -n "__fish_use_subcommand" -f -a "pipeline" -d 'This contains the commands to view & interact with an application’s pipeline' +complete -c wukong -n "__fish_use_subcommand" -f -a "config" -d 'This contains the commands to view & interact with Wukong\'s configurations' +complete -c wukong -n "__fish_use_subcommand" -f -a "login" -d 'Login to start using wukong command' +complete -c wukong -n "__fish_use_subcommand" -f -a "completions" -d 'Generate wukong cli completions for your shell to stdout' +complete -c wukong -n "__fish_use_subcommand" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c wukong -n "__fish_seen_subcommand_from init" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from init" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from ci-status; and not __fish_seen_subcommand_from help" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from ci-status; and not __fish_seen_subcommand_from help" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from ci-status; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List the current pipelines of the application' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from ci-status; and not __fish_seen_subcommand_from help" -f -a "describe" -d 'Show the details of a pipeline' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from ci-status; and not __fish_seen_subcommand_from help" -f -a "ci-status" -d 'Show the build status and (possible) errors on branch ci pipeline' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from ci-status; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from list" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from describe" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from describe" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from ci-status" -l repo-url -d 'Repository url' -r +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from ci-status" -l branch -d 'Branch name' -r +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from ci-status" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from ci-status" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from pipeline; and __fish_seen_subcommand_from help" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from set; and not __fish_seen_subcommand_from get; and not __fish_seen_subcommand_from help" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from set; and not __fish_seen_subcommand_from get; and not __fish_seen_subcommand_from help" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from set; and not __fish_seen_subcommand_from get; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List the configurations' +complete -c wukong -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from set; and not __fish_seen_subcommand_from get; and not __fish_seen_subcommand_from help" -f -a "set" -d 'Set the value of a configuration' +complete -c wukong -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from set; and not __fish_seen_subcommand_from get; and not __fish_seen_subcommand_from help" -f -a "get" -d 'Print the value of a configuration' +complete -c wukong -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from set; and not __fish_seen_subcommand_from get; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c wukong -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from list" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from set" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from set" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from get" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from get" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from help" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from login" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from login" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from completions" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r +complete -c wukong -n "__fish_seen_subcommand_from completions" -s h -l help -d 'Print help information' +complete -c wukong -n "__fish_seen_subcommand_from help" -s a -l application -d 'Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config' -r diff --git a/completions/_wukong b/completions/zsh/_wukong similarity index 93% rename from completions/_wukong rename to completions/zsh/_wukong index 75feb3b6a..dc9ea37a9 100644 --- a/completions/_wukong +++ b/completions/zsh/_wukong @@ -15,7 +15,6 @@ _wukong() { local context curcontext="$curcontext" state line _arguments "${_arguments_options[@]}" \ -'--generate=[If provided, outputs the completion file for given shell]:GENERATOR:(bash elvish fish powershell zsh)' \ '-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ '--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ '-h[Print help information]' \ @@ -155,6 +154,15 @@ _arguments "${_arguments_options[@]}" \ '--help[Print help information]' \ && ret=0 ;; +(completions) +_arguments "${_arguments_options[@]}" \ +'-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'--application=[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +':shell:(bash elvish fish powershell zsh)' \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" \ '-a+[Override the application name that the CLI will perform the command against. If the flag is not used, then the CLI will use the default application name from the config]:APPLICATION: ' \ @@ -174,6 +182,7 @@ _wukong_commands() { 'pipeline:This contains the commands to view & interact with an application’s pipeline' \ 'config:This contains the commands to view & interact with Wukong'\''s configurations' \ 'login:Login to start using wukong command' \ +'completions:Generate wukong cli completions for your shell to stdout' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'wukong commands' commands "$@" @@ -183,6 +192,11 @@ _wukong__pipeline__ci-status_commands() { local commands; commands=() _describe -t commands 'wukong pipeline ci-status commands' commands "$@" } +(( $+functions[_wukong__completions_commands] )) || +_wukong__completions_commands() { + local commands; commands=() + _describe -t commands 'wukong completions commands' commands "$@" +} (( $+functions[_wukong__config_commands] )) || _wukong__config_commands() { local commands; commands=( diff --git a/generate-completions.sh b/generate-completions.sh new file mode 100644 index 000000000..12acb56fd --- /dev/null +++ b/generate-completions.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +if ! command -v wukong >/dev/null 2>&1; then + if ! command -v ./target/release/wukong > /dev/null 2>&1; then + echo "ERROR: wukong command is not available." + exit 1 + else + wukong_command=./target/release/wukong + fi +else + wukong_command=wukong +fi + +cd "$(dirname "$0")" +mkdir -p completions + +gen() { + outdir="completions/$1" + mkdir -p ./completions/$1 + $wukong_command completions $1 > ./completions/$1/$2 + echo "$1 completions generated successfully at $( cd "$(dirname "$0")" ; pwd -P )/completions/$1/$2." +} + +gen bash wukong.bash +gen zsh _wukong +gen fish wukong.fish diff --git a/src/app.rs b/src/app.rs index e529da0b0..8ba76ba51 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,12 +3,7 @@ use crate::{ config::{Config, CONFIG_FILE}, error::CliError, }; -use clap::{Command, CommandFactory, Parser, ValueHint}; -use clap_complete::{ - generate, generate_to, - shells::{Bash, Zsh}, - Generator, Shell, -}; +use clap::Parser; pub enum ConfigState { InitialisedButUnAuthenticated(Config), @@ -21,21 +16,8 @@ pub struct App { pub cli: ClapApp, } -fn print_completions(gen: G, cmd: &mut Command) { - generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout()); -} - impl App { pub fn new<'a>() -> Result> { - let cli = ClapApp::parse(); - let mut cmd = ClapApp::command(); - - cmd.set_bin_name("wukong"); - - let outdir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("completions/"); - generate_to::(Bash, &mut cmd, "wukong", &outdir); - generate_to::(Zsh, &mut cmd, "wukong", &outdir); - let config_file = CONFIG_FILE .as_ref() .expect("Unable to identify user's home directory"); diff --git a/src/clap_app.rs b/src/clap_app.rs index 5334fd009..e4c775f76 100644 --- a/src/clap_app.rs +++ b/src/clap_app.rs @@ -1,15 +1,10 @@ use crate::commands::CommandGroup; use clap::Parser; -use clap_complete::{generate, Generator, Shell}; /// A Swiss-army Knife CLI For Mindvalley Developers #[derive(Debug, Parser)] #[clap(version, author)] pub struct ClapApp { - /// If provided, outputs the completion file for given shell - #[clap(long = "generate", arg_enum)] - pub generator: Option, - #[clap(subcommand)] pub command_group: CommandGroup, diff --git a/src/commands/completions.rs b/src/commands/completions.rs new file mode 100644 index 000000000..0e84622d0 --- /dev/null +++ b/src/commands/completions.rs @@ -0,0 +1,12 @@ +use crate::{clap_app::ClapApp, error::CliError, GlobalContext}; +use clap::CommandFactory; +use clap_complete::{generate, Shell}; + +pub fn handle_completions<'a>(_context: GlobalContext, shell: Shell) -> Result> { + let mut cmd = ClapApp::command(); + cmd.set_bin_name("wukong"); + + generate(shell, &mut cmd, "wukong", &mut std::io::stdout().lock()); + + Ok(true) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 74dd0eaff..a1aec98c6 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -2,8 +2,10 @@ pub mod config; pub mod init; pub mod login; pub mod pipeline; +pub mod completions; use clap::Subcommand; +use clap_complete::Shell; #[derive(Debug, Subcommand)] pub enum CommandGroup { @@ -15,4 +17,9 @@ pub enum CommandGroup { Config(config::Config), /// Login to start using wukong command Login, + /// Generate wukong cli completions for your shell to stdout + Completions { + #[clap(arg_enum)] + shell: Shell, + }, } diff --git a/src/main.rs b/src/main.rs index b5c0adeb6..03a149a78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,9 @@ mod settings; // mod logger; use chrono::{DateTime, Local}; -use commands::{init::handle_init, login::handle_login, CommandGroup}; +use commands::{ + completions::handle_completions, init::handle_init, login::handle_login, CommandGroup, +}; use config::{Config, CONFIG_FILE}; use error::{handle_error, CliError}; // use logger::Logger; @@ -146,6 +148,7 @@ async fn run<'a>() -> Result> { CommandGroup::Config(config) => must_init!(app.config, config.handle_command(context)), CommandGroup::Login => must_init!(app.config, handle_login(context).await), CommandGroup::Init => handle_init(context, existing_config).await, + CommandGroup::Completions { shell } => handle_completions(context, shell), } } From 213bd127302b0e352518ca6d76836fc045bf1c3e Mon Sep 17 00:00:00 2001 From: jk-gan Date: Thu, 18 Aug 2022 16:56:27 +0800 Subject: [PATCH 3/4] chore: remove unused files --- Cargo.toml | 1 - build.rs | 10 ---------- generate-completions.sh | 0 3 files changed, 11 deletions(-) delete mode 100644 build.rs mode change 100644 => 100755 generate-completions.sh diff --git a/Cargo.toml b/Cargo.toml index 6403606c1..d2d118c2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ name = "wukong" version = "0.0.1" edition = "2021" -default-run = "wukong" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/build.rs b/build.rs deleted file mode 100644 index 09236b841..000000000 --- a/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -// use crate::commands::CommandGroup; -// use clap::{Clap, CommandFactory, Parser}; - -// include!("src/clap_app.rs"); - -fn main() { - // let mut app = ClapApp::command(); - - // todo!("generate the completion scripts!"); -} diff --git a/generate-completions.sh b/generate-completions.sh old mode 100644 new mode 100755 From f59b94401bb1d142b3eb37c4ca64d350171e4914 Mon Sep 17 00:00:00 2001 From: jk-gan Date: Mon, 22 Aug 2022 13:04:36 +0800 Subject: [PATCH 4/4] fix: fix the zsh completion --- completions/zsh/_wukong | 2 +- generate-completions.sh | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/completions/zsh/_wukong b/completions/zsh/_wukong index dc9ea37a9..fc88f372a 100644 --- a/completions/zsh/_wukong +++ b/completions/zsh/_wukong @@ -268,4 +268,4 @@ _wukong__config__set_commands() { _describe -t commands 'wukong config set commands' commands "$@" } -_wukong "$@" +compdef _wukong wukong diff --git a/generate-completions.sh b/generate-completions.sh index 12acb56fd..6fc4e3bbb 100755 --- a/generate-completions.sh +++ b/generate-completions.sh @@ -17,7 +17,13 @@ mkdir -p completions gen() { outdir="completions/$1" mkdir -p ./completions/$1 - $wukong_command completions $1 > ./completions/$1/$2 + if [ $1 = 'zsh' ]; then + # replace the last line so the zsh autocompletion is source-able + # https://github.com/starship/starship/issues/2806 + $wukong_command completions $1 | sed '$s/_wukong "$@"/compdef _wukong wukong/' > ./completions/$1/$2 + else + $wukong_command completions $1 > ./completions/$1/$2 + fi echo "$1 completions generated successfully at $( cd "$(dirname "$0")" ; pwd -P )/completions/$1/$2." }