Improve help output after switch to clap-derive

Make it more like it used to be.
This commit is contained in:
Thayne McCombs 2022-07-18 02:19:11 -06:00
parent 4e7b403c1f
commit b7f5f4ac7d
2 changed files with 60 additions and 41 deletions

View file

@ -4,8 +4,8 @@ use std::time::Duration;
#[cfg(feature = "completions")] #[cfg(feature = "completions")]
use anyhow::anyhow; use anyhow::anyhow;
use clap::{ use clap::{
builder::RangedU64ValueParser, value_parser, AppSettings, Arg, ArgAction, ArgEnum, ArgGroup, builder::RangedU64ValueParser, value_parser, AppSettings, Arg, ArgAction, ArgGroup, ArgMatches,
ArgMatches, Command, ErrorKind, Parser, Command, ErrorKind, Parser, ValueEnum,
}; };
#[cfg(feature = "completions")] #[cfg(feature = "completions")]
use clap_complete::Shell; use clap_complete::Shell;
@ -92,7 +92,7 @@ pub struct Opts {
/// Include hidden directories and files in the search results (default: /// Include hidden directories and files in the search results (default:
/// hidden files and directories are skipped). Files and directories are considered /// hidden files and directories are skipped). Files and directories are considered
/// to be hidden if their name starts with a `.` sign (dot). /// to be hidden if their name starts with a `.` sign (dot).
/// The flag can be overriden with --no-hidden. /// The flag can be overridden with --no-hidden.
#[clap(long, short = 'H', action, overrides_with = "hidden")] #[clap(long, short = 'H', action, overrides_with = "hidden")]
pub hidden: bool, pub hidden: bool,
/// Do not respect .(git|fd)ignore files /// Do not respect .(git|fd)ignore files
@ -187,7 +187,7 @@ pub struct Opts {
/// ///
/// By default, fd does not descend into symlinked directories. Using this /// By default, fd does not descend into symlinked directories. Using this
/// flag, symbolic links are also traversed. /// flag, symbolic links are also traversed.
/// Flag can be overriden with --no-follow. /// Flag can be overridden with --no-follow.
#[clap( #[clap(
long, long,
short = 'L', short = 'L',
@ -200,9 +200,16 @@ pub struct Opts {
/// ///
/// By default, the search pattern is only matched against the filename (or /// By default, the search pattern is only matched against the filename (or
/// directory name). Using this flag, the pattern is matched against the full /// directory name). Using this flag, the pattern is matched against the full
/// (absolute) path. Example: /// (absolute) path.
/// Example:
/// fd --glob -p '**/.git/config' /// fd --glob -p '**/.git/config'
#[clap(long, short = 'p', action, overrides_with("full-path"))] #[clap(
long,
short = 'p',
action,
overrides_with("full-path"),
verbatim_doc_comment
)]
pub full_path: bool, pub full_path: bool,
/// Separate results by the null character /// Separate results by the null character
/// ///
@ -247,8 +254,8 @@ pub struct Opts {
/// you want to exclude specific directories, use the '--exclude=…' option. /// you want to exclude specific directories, use the '--exclude=…' option.
#[clap(long, hide_short_help = true, action, conflicts_with_all(&["size", "exact-depth"]))] #[clap(long, hide_short_help = true, action, conflicts_with_all(&["size", "exact-depth"]))]
pub prune: bool, pub prune: bool,
/// Filter by type: file (f), directory (d), symlink (l),\nexecutable (x), /// Filter by type: file (f), directory (d), symlink (l),
/// empty (e), socket (s), pipe (p)) /// executable (x), empty (e), socket (s), pipe (p)
/// ///
/// Filter the search by type: /// Filter the search by type:
/// ///
@ -286,13 +293,12 @@ pub struct Opts {
/// fd --type empty --type directory /// fd --type empty --type directory
/// fd -te -td" /// fd -te -td"
#[clap(long = "type", short = 't', value_name = "filetype", hide_possible_values = true, #[clap(long = "type", short = 't', value_name = "filetype", hide_possible_values = true,
arg_enum, action = ArgAction::Append, number_of_values = 1)] arg_enum, action = ArgAction::Append, number_of_values = 1, verbatim_doc_comment)]
pub filetype: Option<Vec<FileType>>, pub filetype: Option<Vec<FileType>>,
/// Filter by file extension /// Filter by file extension
/// ///
/// (Additionally) filter search results by their file extension. Multiple /// (Additionally) filter search results by their file extension. Multiple
/// allowable file extensions can be specified. /// allowable file extensions can be specified.
///
/// If you want to search for files without extension, /// If you want to search for files without extension,
/// you can use the regex '^[^.]+$' as a normal search pattern. /// you can use the regex '^[^.]+$' as a normal search pattern.
#[clap(long = "extension", short = 'e', value_name = "ext", action = ArgAction::Append, number_of_values = 1)] #[clap(long = "extension", short = 'e', value_name = "ext", action = ArgAction::Append, number_of_values = 1)]
@ -320,14 +326,13 @@ pub struct Opts {
pub batch_size: usize, pub batch_size: usize,
/// Exclude entries that match the given glob pattern /// Exclude entries that match the given glob pattern
/// ///
/// "Exclude files/directories that match the given glob pattern. This /// Exclude files/directories that match the given glob pattern. This overrides any other
/// overrides any other ignore logic. Multiple exclude patterns can be /// ignore logic. Multiple exclude patterns can be specified.
/// specified.
/// ///
/// Examples: /// Examples:
/// --exclude '*.pyc' /// --exclude '*.pyc'
/// --exclude node_modules /// --exclude node_modules
#[clap(long, short = 'E', value_name = "pattern", action = ArgAction::Append, number_of_values = 1)] #[clap(long, short = 'E', value_name = "pattern", action = ArgAction::Append, number_of_values = 1, verbatim_doc_comment)]
pub exclude: Vec<String>, pub exclude: Vec<String>,
/// Add custom ignore-file in '.gitignore' format /// Add custom ignore-file in '.gitignore' format
/// ///
@ -336,12 +341,18 @@ pub struct Opts {
#[clap(long, value_name = "path", action = ArgAction::Append, number_of_values = 1, hide_short_help = true)] #[clap(long, value_name = "path", action = ArgAction::Append, number_of_values = 1, hide_short_help = true)]
pub ignore_file: Vec<PathBuf>, pub ignore_file: Vec<PathBuf>,
/// When to use colors /// When to use colors
///
/// 'auto': show colors if the output goes to an interactive console (default)
/// 'never': do not use colorized output
/// 'always': always use colorized output
#[clap( #[clap(
long, long,
short = 'c', short = 'c',
arg_enum, arg_enum,
default_value = "auto", default_value = "auto",
value_name = "when" value_name = "when",
hide_possible_values = true,
verbatim_doc_comment
)] )]
pub color: ColorWhen, pub color: ColorWhen,
/// Set number of threads /// Set number of threads
@ -368,7 +379,7 @@ pub struct Opts {
/// 'mi': mebibytes /// 'mi': mebibytes
/// 'gi': gibibytes /// 'gi': gibibytes
/// 'ti': tebibytes /// 'ti': tebibytes
#[clap(long, short = 'S', number_of_values = 1, value_parser = SizeFilter::from_string, allow_hyphen_values = true, action = ArgAction::Append)] #[clap(long, short = 'S', number_of_values = 1, value_parser = SizeFilter::from_string, allow_hyphen_values = true, action = ArgAction::Append, verbatim_doc_comment)]
pub size: Vec<SizeFilter>, pub size: Vec<SizeFilter>,
/// Milliseconds to buffer before streaming search results to console /// Milliseconds to buffer before streaming search results to console
/// ///
@ -378,8 +389,8 @@ pub struct Opts {
pub max_buffer_time: Option<Duration>, pub max_buffer_time: Option<Duration>,
/// Filter by file modification time (newer than) /// Filter by file modification time (newer than)
/// ///
/// Filter results based on the file modification time. The argument can be provided /// The argument can be provided as a specific point in time (YYYY-MM-DD HH:MM:SS)
/// as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). /// or as a duration (10h, 1d, 35min).
/// If the time is not specified, it defaults to 00:00:00. /// If the time is not specified, it defaults to 00:00:00.
/// '--change-newer-than' or '--newer' can be used as aliases. /// '--change-newer-than' or '--newer' can be used as aliases.
/// Examples: /// Examples:
@ -392,13 +403,14 @@ pub struct Opts {
alias("newer"), alias("newer"),
value_name = "date|dur", value_name = "date|dur",
number_of_values = 1, number_of_values = 1,
verbatim_doc_comment,
action action
)] )]
pub changed_within: Option<String>, pub changed_within: Option<String>,
/// Filter by file modification time (older than) /// Filter by file modification time (older than)
/// ///
/// Filter results based on the file modification time. The argument can be provided /// The argument can be provided as a specific point in time (YYYY-MM-DD HH:MM:SS)
/// as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). /// or as a duration (10h, 1d, 35min).
/// '--change-older-than' or '--older' can be used as aliases. /// '--change-older-than' or '--older' can be used as aliases.
/// ///
/// Examples: /// Examples:
@ -411,6 +423,7 @@ pub struct Opts {
alias("older"), alias("older"),
value_name = "date|dur", value_name = "date|dur",
number_of_values = 1, number_of_values = 1,
verbatim_doc_comment,
action action
)] )]
pub changed_before: Option<String>, pub changed_before: Option<String>,
@ -419,9 +432,8 @@ pub struct Opts {
/// Limit the number of search results to 'count' and quit immediately. /// Limit the number of search results to 'count' and quit immediately.
#[clap(long, value_name = "count", hide_short_help = true, value_parser)] #[clap(long, value_name = "count", hide_short_help = true, value_parser)]
max_results: Option<usize>, max_results: Option<usize>,
/// Limit search to a single result /// Limit search to a single result and quit immediately
/// ///
/// Limit the search to a single result and quit immediately.
/// This is an alias for '--max-results=1'. /// This is an alias for '--max-results=1'.
#[clap( #[clap(
short = '1', short = '1',
@ -437,7 +449,14 @@ pub struct Opts {
/// exit code will be 1. /// exit code will be 1.
/// ///
/// '--has-results' can be used as an alias. /// '--has-results' can be used as an alias.
#[clap(long, short = 'q', alias = "has-results", hide_short_help = true, conflicts_with("max-results"), action)] #[clap(
long,
short = 'q',
alias = "has-results",
hide_short_help = true,
conflicts_with("max-results"),
action
)]
pub quiet: bool, pub quiet: bool,
/// Show filesystem errors /// Show filesystem errors
/// ///
@ -466,7 +485,7 @@ pub struct Opts {
/// pattern (if --glob is used). If no pattern has been specified, every entry /// pattern (if --glob is used). If no pattern has been specified, every entry
/// is considered a match. If your pattern starts with a dash (-), make sure to /// is considered a match. If your pattern starts with a dash (-), make sure to
/// pass '--' first, or it will be considered as a flag (fd -- '-foo'). /// pass '--' first, or it will be considered as a flag (fd -- '-foo').
#[clap(value_parser, default_value = "")] #[clap(value_parser, default_value = "", hide_default_value = true)]
pub pattern: String, pub pattern: String,
/// Set path separator when printing file paths /// Set path separator when printing file paths
/// Set the path separator to use when printing file paths. The default is /// Set the path separator to use when printing file paths. The default is
@ -475,15 +494,13 @@ pub struct Opts {
pub path_separator: Option<String>, pub path_separator: Option<String>,
/// the root directories for the filesystem search (optional) /// the root directories for the filesystem search (optional)
/// ///
/// The directories where the filesystem search is rooted (optional). /// The directories where the filesystem search is rooted.
/// If omitted, search the current working directory. /// If omitted, search the current working directory.
#[clap(action = ArgAction::Append)] #[clap(action = ArgAction::Append)]
path: Vec<PathBuf>, path: Vec<PathBuf>,
/// Provides paths to search as an alternative to the positional <path> /// Provides paths to search as an alternative to the positional <path> argument
/// ///
/// Provide paths to search as an alternative to the positional <path> /// Changes the usage to `fd [OPTIONS] --search-path <path> --search-path <path2> [<pattern>]`
/// argument. Changes the usage to `fd [OPTIONS] --search-path <path>
/// --search-path <path2> [<pattern>]`
#[clap(long, conflicts_with("path"), action = ArgAction::Append, hide_short_help = true, number_of_values = 1)] #[clap(long, conflicts_with("path"), action = ArgAction::Append, hide_short_help = true, number_of_values = 1)]
search_path: Vec<PathBuf>, search_path: Vec<PathBuf>,
/// strip './' prefix from non-tty outputs /// strip './' prefix from non-tty outputs
@ -494,16 +511,15 @@ pub struct Opts {
pub strip_cwd_prefix: bool, pub strip_cwd_prefix: bool,
/// Filter by owning user and/or group /// Filter by owning user and/or group
/// ///
/// Filter files by their user and/or group. /// Filter files by their user and/or group. Format: [(user|uid)][:(group|gid)].
/// Format: [(user|uid)][:(group|gid)]. Either side is optional. /// Either side is optional. Precede either side with a '!' to exclude files instead.
/// Precede either side with a '!' to exclude files instead.
/// ///
/// Examples: /// Examples:
/// --owner john /// --owner john
/// --owner :students /// --owner :students
/// --owner '!john:students' /// --owner '!john:students'
#[cfg(unix)] #[cfg(unix)]
#[clap(long, short = 'o', value_parser = OwnerFilter::from_string, value_name = "user:group")] #[clap(long, short = 'o', value_parser = OwnerFilter::from_string, value_name = "user:group", verbatim_doc_comment)]
pub owner: Option<OwnerFilter>, pub owner: Option<OwnerFilter>,
/// Do not descend into a different file system /// Do not descend into a different file system
/// ///
@ -581,7 +597,9 @@ impl Opts {
} }
pub fn max_results(&self) -> Option<usize> { pub fn max_results(&self) -> Option<usize> {
self.max_results.filter(|&m| m > 0).or_else(|| self.max_one_result.then(|| 1)) self.max_results
.filter(|&m| m > 0)
.or_else(|| self.max_one_result.then(|| 1))
} }
#[cfg(feature = "completions")] #[cfg(feature = "completions")]
@ -599,7 +617,8 @@ impl Opts {
#[cfg(feature = "completions")] #[cfg(feature = "completions")]
fn guess_shell() -> anyhow::Result<Shell> { fn guess_shell() -> anyhow::Result<Shell> {
let env_shell = std::env::var_os("SHELL").map(PathBuf::from); let env_shell = std::env::var_os("SHELL").map(PathBuf::from);
let shell = env_shell.as_ref() let shell = env_shell
.as_ref()
.and_then(|s| s.file_name()) .and_then(|s| s.file_name())
.and_then(|s| s.to_str()) .and_then(|s| s.to_str())
.ok_or_else(|| anyhow!("Unable to get shell from environment"))?; .ok_or_else(|| anyhow!("Unable to get shell from environment"))?;
@ -608,7 +627,7 @@ fn guess_shell() -> anyhow::Result<Shell> {
.map_err(|_| anyhow!("Unknown shell {}", shell)) .map_err(|_| anyhow!("Unknown shell {}", shell))
} }
#[derive(Copy, Clone, PartialEq, Eq, ArgEnum)] #[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
pub enum FileType { pub enum FileType {
#[clap(alias = "f")] #[clap(alias = "f")]
File, File,
@ -626,7 +645,7 @@ pub enum FileType {
Pipe, Pipe,
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug, ArgEnum)] #[derive(Copy, Clone, PartialEq, Eq, Debug, ValueEnum)]
pub enum ColorWhen { pub enum ColorWhen {
/// show colors if the output goes to an interactive console (default) /// show colors if the output goes to an interactive console (default)
Auto, Auto,

View file

@ -18,7 +18,7 @@ use std::time;
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use atty::Stream; use atty::Stream;
use clap::{CommandFactory,Parser}; use clap::{CommandFactory, Parser};
use globset::GlobBuilder; use globset::GlobBuilder;
use lscolors::LsColors; use lscolors::LsColors;
use regex::bytes::{RegexBuilder, RegexSetBuilder}; use regex::bytes::{RegexBuilder, RegexSetBuilder};