diff --git a/Cargo.lock b/Cargo.lock index a880080..49e0aad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,15 @@ dependencies = [ "termcolor", ] +[[package]] +name = "clap_complete" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0012995dc3a54314f4710f5631d74767e73c534b8757221708303e48eef7a19b" +dependencies = [ + "clap", +] + [[package]] name = "clap_lex" version = "0.3.1" @@ -212,6 +221,7 @@ dependencies = [ "alpm", "anyhow", "clap", + "clap_complete", "const_format", "pacdef_macros", "path-absolutize", diff --git a/_completion.zsh b/_completion.zsh deleted file mode 100644 index a9beae9..0000000 --- a/_completion.zsh +++ /dev/null @@ -1,112 +0,0 @@ -#compdef pacdef - -_pacdef() { - integer ret=1 - local line - - if [ -z "${XDG_CONFIG_HOME}" ]; then - GROUPDIR="~/.config/pacdef/groups" - else - GROUPDIR="${XDG_CONFIG_HOME}/pacdef/groups" - fi - - function _subcommands { - local -a subcommands - subcommands=( - 'group:manage groups (alias: g)' - 'package:manage packages (alias: p)' - 'version:show version info (alias: v)' - ) - _describe 'subcommand' subcommands - } - - function _group_actions { - local -a group_actions - group_actions=( - 'edit:edit an imported group file (alias: e)' - 'list:show names of imported groups (alias: l)' - 'import:import a new group file (alias: i)' - 'new:create a new group file (alias: n)' - 'remove:remove a group file (alias: r)' - 'show:show packages under an imported group (alias: s)' - ) - _describe 'group action' group_actions - } - - - function _package_actions { - local -a package_actions - package_actions=( - 'clean:uninstall packages not managed by pacdef (alias: c)' - 'review:review unmanaged packages (alias: r)' - 'search:show the group containing a package (alias: se)' - 'sync:install all packages from imported groups (alias: sy)' - 'unmanaged:show explicitly installed packages not managed by pacdef (alias: u)' - ) - _describe 'package action' package_actions - } - - _arguments -C \ - "1: :_subcommands" \ - "*::arg:->args" \ - && ret=0 - - case $state in - (args) - case $line[1] in - package) - case $line[2] in - search) - _arguments \ - "2:regex:" && ret=0 - ;; - (clean|review|sync|unmanaged|help) - _message "no more arguments" && ret=0 - ;; - *) - _arguments \ - "1: :_package_actions" \ - "*::arg:->args" && ret=0 - ;; - esac - ;; - group) - case $line[2] in - list) - _message "no more arguments" && ret=0 - ;; - (edit|remove|show) - _arguments "*:group file:_files -W '$GROUPDIR'" && ret=0 - - ;; - import) - _arguments "*:new group file(s):_files" && ret=0 - ;; - new) - _arguments \ - {-e,--edit}"[edit group file after creating them]" \ - "*:new group name(s):" \ - && ret=0 - ;; - *) _arguments \ - "1: :_group_actions" \ - "*::arg:->args" && ret=0 - ;; - esac - ;; - version) - _message "no more arguments" && ret=0 - ;; - *) - _message "unknown subcommand" && ret=1 - ;; - esac - ;; - esac - - return ret -} - -_pacdef - - diff --git a/crates/pacdef_core/Cargo.toml b/crates/pacdef_core/Cargo.toml index d783ba6..0bef842 100644 --- a/crates/pacdef_core/Cargo.toml +++ b/crates/pacdef_core/Cargo.toml @@ -27,3 +27,4 @@ serde_derive = "1.0" serde = "1.0" pacdef_macros = { path = "../pacdef_macros", version = "0.1" } +clap_complete = "4.1.3" diff --git a/crates/pacdef_core/src/action.rs b/crates/pacdef_core/src/action.rs index 31ce1fa..74c1c6b 100644 --- a/crates/pacdef_core/src/action.rs +++ b/crates/pacdef_core/src/action.rs @@ -5,6 +5,7 @@ use pacdef_macros::Action; #[derive(Debug, Action)] pub enum Actions { Clean, + Completion, Edit, Import, List, diff --git a/crates/pacdef_core/src/args.rs b/crates/pacdef_core/src/args.rs index 75a2bc8..40fae52 100644 --- a/crates/pacdef_core/src/args.rs +++ b/crates/pacdef_core/src/args.rs @@ -9,17 +9,18 @@ use crate::core::get_version_string; /// Build the `pacdef` argument parser, with subcommands for `version`, /// `group` and `package`. -fn get_arg_parser() -> Command { +pub fn build_cli() -> Command { let package_cmd = get_package_cmd(); let group_cmd = get_group_cmd(); let version_cmd = Command::new(VERSION).about("show version info"); + let completion_cmd = Command::new(COMPLETION).about("generate shell completion"); Command::new("pacdef") .about("declarative package manager for Linux") .version(get_version_string()) .subcommand_required(true) .arg_required_else_help(true) - .subcommands([package_cmd, group_cmd, version_cmd]) + .subcommands([package_cmd, group_cmd, version_cmd, completion_cmd]) } /// Build the `pacdef group` subcommand. @@ -67,7 +68,7 @@ fn get_group_cmd() -> Command { Command::new("group") .arg_required_else_help(true) - .about("TODO????") + .about("manage groups") .visible_alias("g") .subcommand_required(true) .subcommands([edit, import, list, new, remove, show]) @@ -95,7 +96,7 @@ fn get_package_cmd() -> Command { Command::new("package") .arg_required_else_help(true) - .about("TODO????") + .about("manage packages") .visible_alias("p") .subcommand_required(true) .subcommands([clean, review, unmanaged, search, sync]) @@ -104,7 +105,7 @@ fn get_package_cmd() -> Command { /// Get and parse the CLI arguments. #[must_use] pub fn get() -> clap::ArgMatches { - get_arg_parser().get_matches() + build_cli().get_matches() } /// For each file argument, return the absolute path to the file. diff --git a/crates/pacdef_core/src/core.rs b/crates/pacdef_core/src/core.rs index 8092ca2..d2d1595 100644 --- a/crates/pacdef_core/src/core.rs +++ b/crates/pacdef_core/src/core.rs @@ -5,6 +5,7 @@ use std::path::Path; use anyhow::{anyhow, bail, ensure, Context, Result}; use clap::ArgMatches; +use clap_complete::{generate, shells::Zsh}; use const_format::formatcp; use crate::action::*; @@ -84,6 +85,7 @@ impl Pacdef { }, Some((VERSION, _)) => Ok(self.show_version()), + Some((COMPLETION, _)) => Ok(self.generate_shell_completion()), Some((_, _)) => panic!("{ACTION_NOT_MATCHED}"), None => unreachable!("{UNREACHABLE_ARM}"), @@ -299,6 +301,16 @@ impl Pacdef { Ok(()) } + + #[allow(clippy::unused_self)] + fn generate_shell_completion(&self) { + generate( + Zsh, + &mut crate::args::build_cli(), + "pacdef", + &mut std::io::stdout(), + ); + } } /// For the provided CLI arguments, get the path to each corresponding group file.