diff --git a/Cargo.lock b/Cargo.lock index 4e06e43ac..a9f48b3e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,6 +406,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +[[package]] +name = "clap_mangen" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ea63a92086df93893164221ad4f24142086d535b3a0957b9b9bea2dc86301" +dependencies = [ + "clap", + "roff", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -1549,6 +1559,7 @@ dependencies = [ "chrono", "clap", "clap_complete", + "clap_mangen", "console", "directories", "dockworker", @@ -1766,6 +1777,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "roff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" + [[package]] name = "rust_decimal" version = "1.40.0" diff --git a/Cargo.toml b/Cargo.toml index 7e61552ab..e61010a22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ anyhow = "1.0" byte-unit = "5.2" clap = {version = "4.4", features = ["derive"]} clap_complete = "4.4" +clap_mangen = "0.2" console = "0.16.2" chrono = {version = "0.4.44", default-features = false, features = ["clock"]} directories = "6.0.0" @@ -57,6 +58,7 @@ unicode-width = "0.2" anyhow = "1.0" clap = {version = "4.4", features = ["derive"]} clap_complete = "4.4" +clap_mangen = "0.2" [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] pager = "0.16.1" diff --git a/build.rs b/build.rs index dd9266065..d6be255d8 100644 --- a/build.rs +++ b/build.rs @@ -1,13 +1,30 @@ +use clap::CommandFactory; + include!("src/opt.rs"); fn main() { if let Ok(path) = std::env::var("COMPLETION_PATH") { let out_dir = PathBuf::from(path); - gen_completion(Shell::Bash, &out_dir).unwrap(); - gen_completion(Shell::Elvish, &out_dir).unwrap(); - gen_completion(Shell::Fish, &out_dir).unwrap(); - gen_completion(Shell::PowerShell, &out_dir).unwrap(); - gen_completion(Shell::Zsh, &out_dir).unwrap(); + gen_completion(Shell::Bash, &out_dir, &mut Opt::command()).unwrap(); + gen_completion(Shell::Elvish, &out_dir, &mut Opt::command()).unwrap(); + gen_completion(Shell::Fish, &out_dir, &mut Opt::command()).unwrap(); + gen_completion(Shell::PowerShell, &out_dir, &mut Opt::command()).unwrap(); + gen_completion(Shell::Zsh, &out_dir, &mut Opt::command()).unwrap(); + } + + // MAN_PATH=./man cargo build + if let Ok(path) = std::env::var("MAN_PATH") { + let out_dir = std::path::Path::new(&path); + std::fs::create_dir_all(out_dir).unwrap(); + let cmd = Opt::command(); + let man = clap_mangen::Man::new(cmd); + let mut buf = Vec::new(); + man.render(&mut buf).unwrap(); + std::fs::write(out_dir.join("procs.1"), buf).unwrap(); + println!( + "man page is generated: {}", + out_dir.join("procs.1").display() + ); } } diff --git a/src/main.rs b/src/main.rs index 771efa047..6ea5afee2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,9 @@ use crate::view::View; use crate::watcher::Watcher; use anyhow::{Context, Error}; use clap::{CommandFactory, Parser}; +use clap_mangen::Man; use console::Term; +use once_cell::sync::Lazy; use std::cmp; use std::collections::HashMap; use std::fs; @@ -31,6 +33,29 @@ use unicode_width::UnicodeWidthStr; // Functions // --------------------------------------------------------------------------------------------------------------------- +static KIND_NAMES_LOWER: Lazy> = Lazy::new(|| { + KIND_LIST + .iter() + .map(|(_, (name, _))| name.to_lowercase()) + .collect() +}); + +fn command_with_kind_values() -> clap::Command { + let kind_values: Vec = KIND_LIST + .iter() + .zip(KIND_NAMES_LOWER.iter()) + .map(|((_, (_, desc)), lower)| { + clap::builder::PossibleValue::new(lower.as_str()).help(*desc) + }) + .collect(); + let parser = clap::builder::PossibleValuesParser::new(kind_values); + Opt::command() + .mut_arg("sorta", |a| a.value_parser(parser.clone())) + .mut_arg("sortd", |a| a.value_parser(parser.clone())) + .mut_arg("insert", |a| a.value_parser(parser.clone())) + .mut_arg("only", |a| a.value_parser(parser)) +} + fn get_config(opt: &Opt) -> Result { let dot_cfg_path = directories::BaseDirs::new() .map(|base| base.home_dir().join(".procs.toml")) @@ -122,10 +147,19 @@ fn run() -> Result<(), Error> { run_list(); Ok(()) } else if let Some(shell) = opt.gen_completion { - gen_completion(shell, "./") + gen_completion(shell, "./", &mut command_with_kind_values()) } else if let Some(shell) = opt.gen_completion_out { - //Opt::clap().gen_completions_to("procs", shell, &mut stdout()); - clap_complete::generate(shell, &mut Opt::command(), "procs", &mut stdout()); + clap_complete::generate( + shell, + &mut command_with_kind_values(), + "procs", + &mut stdout(), + ); + Ok(()) + } else if opt.gen_man_page { + let cmd = command_with_kind_values(); + let man = Man::new(cmd); + man.render(&mut stdout())?; Ok(()) } else { let config = get_config(&opt)?; diff --git a/src/opt.rs b/src/opt.rs index 0de2d4913..c7e37ddb3 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -1,5 +1,4 @@ use anyhow::{Error, anyhow}; -use clap::CommandFactory; use clap::builder::styling::{AnsiColor, Effects, Styles}; use clap::{Parser, ValueEnum}; use clap_complete::Shell; @@ -173,6 +172,10 @@ pub struct Opt { #[clap(long = "gen-completion-out", value_name = "shell")] pub gen_completion_out: Option, + /// Generate man page and write to stdout + #[clap(long = "gen-man-page")] + pub gen_man_page: bool, + /// Suppress header #[clap(long = "no-header")] pub no_header: bool, @@ -186,10 +189,13 @@ pub struct Opt { pub debug: bool, } -pub fn gen_completion>(shell: Shell, path: P) -> Result<(), Error> { +pub fn gen_completion>( + shell: Shell, + path: P, + cmd: &mut clap::Command, +) -> Result<(), Error> { let path_str = path.as_ref().as_os_str(); - //Opt::clap().gen_completions("procs", shell, path_str); - clap_complete::generate_to(shell, &mut Opt::command(), "procs", path_str)?; + clap_complete::generate_to(shell, cmd, "procs", path_str)?; let filename = match shell { Shell::Bash => "procs.bash", Shell::Elvish => "procs.elv",