refactor: Port to clap3

Ths does remove the specialization of version's description.  The way
this is done (internally through `mut_arg`) doesn't play well with
subcommands.  Clap tries to force this version of `version` into the
subcommand despite not being needed.  Clap v4 dramatically changes how
version customization works.

clap also does more error checks now to prevent programmer mistake, so
we can't have a conflict with an argument that is conditionally there,
so I swapped the condition.
This commit is contained in:
Ed Page 2022-09-01 15:41:07 -05:00 committed by David Peter
parent 49875d6ce7
commit 2323804f7e
5 changed files with 61 additions and 56 deletions

55
Cargo.lock generated
View file

@ -193,18 +193,28 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.34.0"
version = "3.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"clap_lex",
"indexmap",
"once_cell",
"strsim",
"term_size",
"termcolor",
"terminal_size",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
@ -715,6 +725,12 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "os_str_bytes"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]]
name = "parking_lot"
version = "0.11.2"
@ -1045,9 +1061,9 @@ checksum = "8207e78455ffdf55661170876f88daf85356e4edd54e0a3dbc79586ca1e50cbe"
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
@ -1108,16 +1124,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "term_size"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.2"
@ -1145,12 +1151,11 @@ checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16"
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
dependencies = [
"term_size",
"unicode-width",
"terminal_size",
]
[[package]]
@ -1243,12 +1248,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"

View file

@ -77,10 +77,10 @@ default-features = false
features = ["parsing"]
[dependencies.clap]
version = "2.34"
version = "3.2.20"
optional = true
default-features = false
features = ["suggestions", "color", "wrap_help"]
features = ["std", "suggestions", "color", "wrap_help", "cargo"]
[dev-dependencies]
assert_cmd = "2.0.4"
@ -93,7 +93,7 @@ tempfile = "3.3.0"
nix = { version = "0.24.2", default-features = false, features = ["term"] }
[build-dependencies]
clap = { version = "2.34", optional = true }
clap = { version = "3.2.20", optional = true }
[profile.release]
lto = true

View file

@ -32,7 +32,7 @@ fn is_truecolor_terminal() -> bool {
}
pub struct App {
pub matches: ArgMatches<'static>,
pub matches: ArgMatches,
interactive_output: bool,
}
@ -49,7 +49,7 @@ impl App {
})
}
fn matches(interactive_output: bool) -> Result<ArgMatches<'static>> {
fn matches(interactive_output: bool) -> Result<ArgMatches> {
let args = if wild::args_os().nth(1) == Some("cache".into())
|| wild::args_os().any(|arg| arg == "--no-config")
{

View file

@ -16,7 +16,7 @@ static VERSION: Lazy<String> = Lazy::new(|| {
}
});
pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
pub fn build_app(interactive_output: bool) -> ClapApp<'static> {
let clap_color_setting = if interactive_output && env::var_os("NO_COLOR").is_none() {
AppSettings::ColoredHelp
} else {
@ -32,7 +32,6 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.setting(AppSettings::ArgsNegateSubcommands)
.setting(AppSettings::AllowExternalSubcommands)
.setting(AppSettings::DisableHelpSubcommand)
.setting(AppSettings::VersionlessSubcommands)
.max_term_width(100)
.about(
"A cat(1) clone with wings.\n\n\
@ -50,14 +49,16 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
"File(s) to print / concatenate. Use a dash ('-') or no argument at all \
to read from standard input.",
)
.takes_value(true)
.multiple(true)
.empty_values(false),
.empty_values(false)
.allow_invalid_utf8(true),
)
.arg(
Arg::with_name("show-all")
.long("show-all")
.alias("show-nonprintable")
.short("A")
.short('A')
.conflicts_with("language")
.help("Show non-printable characters (space, tab, newline, ..).")
.long_help(
@ -70,9 +71,9 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
Arg::with_name("plain")
.overrides_with("plain")
.overrides_with("number")
.short("p")
.short('p')
.long("plain")
.multiple(true)
.multiple_occurrences(true)
.help("Show plain style (alias for '--style=plain').")
.long_help(
"Only show plain style, no decorations. This is an alias for \
@ -82,7 +83,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
)
.arg(
Arg::with_name("language")
.short("l")
.short('l')
.long("language")
.overrides_with("language")
.help("Set the language for syntax highlighting.")
@ -97,7 +98,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.arg(
Arg::with_name("highlight-line")
.long("highlight-line")
.short("H")
.short('H')
.takes_value(true)
.number_of_values(1)
.multiple(true)
@ -120,6 +121,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.number_of_values(1)
.multiple(true)
.value_name("name")
.allow_invalid_utf8(true)
.help("Specify the name to display for a file.")
.long_help(
"Specify the name to display for a file. Useful when piping \
@ -135,7 +137,8 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.arg(
Arg::with_name("diff")
.long("diff")
.short("d")
.short('d')
.conflicts_with("line-range")
.help("Only show lines that have been added/removed/modified.")
.long_help(
"Only show lines that have been added/removed/modified with respect \
@ -226,7 +229,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
Arg::with_name("number")
.long("number")
.overrides_with("number")
.short("n")
.short('n')
.help("Show line numbers (alias for '--style=numbers').")
.long_help(
"Only show line numbers, no other decorations. This is an alias for \
@ -280,7 +283,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.arg(
Arg::with_name("force-colorization")
.long("force-colorization")
.short("f")
.short('f')
.conflicts_with("color")
.conflicts_with("decorations")
.overrides_with("force-colorization")
@ -309,7 +312,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
)
.arg(
Arg::with_name("no-paging")
.short("P")
.short('P')
.long("no-paging")
.alias("no-pager")
.overrides_with("no-paging")
@ -334,7 +337,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
)
.arg(
Arg::with_name("map-syntax")
.short("m")
.short('m')
.long("map-syntax")
.multiple(true)
.takes_value(true)
@ -450,12 +453,11 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.arg(
Arg::with_name("line-range")
.long("line-range")
.short("r")
.short('r')
.multiple(true)
.takes_value(true)
.number_of_values(1)
.value_name("N:M")
.conflicts_with("diff")
.help("Only print the lines from N to M.")
.long_help(
"Only print the specified range of lines for each file. \
@ -470,14 +472,14 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.arg(
Arg::with_name("list-languages")
.long("list-languages")
.short("L")
.short('L')
.conflicts_with("list-themes")
.help("Display all supported languages.")
.long_help("Display a list of supported languages for syntax highlighting."),
)
.arg(
Arg::with_name("unbuffered")
.short("u")
.short('u')
.long("unbuffered")
.hidden_short_help(true)
.long_help(
@ -539,8 +541,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.hidden_short_help(true)
.help("Show acknowledgements."),
)
.help_message("Print this help message.")
.version_message("Show version information.");
.help_message("Print this help message.");
// Check if the current directory contains a file name cache. Otherwise,
// enable the 'bat cache' subcommand.
@ -553,7 +554,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.arg(
Arg::with_name("build")
.long("build")
.short("b")
.short('b')
.help("Initialize (or update) the syntax/theme cache.")
.long_help(
"Initialize (or update) the syntax/theme cache by loading from \
@ -563,7 +564,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.arg(
Arg::with_name("clear")
.long("clear")
.short("c")
.short('c')
.help("Remove the cached syntax definitions and themes."),
)
.group(
@ -607,3 +608,8 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
)
}
}
#[test]
fn verify_app() {
build_app(false).debug_assert();
}

View file

@ -293,11 +293,11 @@ fn run() -> Result<bool> {
}
match app.matches.subcommand() {
("cache", Some(cache_matches)) => {
Some(("cache", cache_matches)) => {
// If there is a file named 'cache' in the current working directory,
// arguments for subcommand 'cache' are not mandatory.
// If there are non-zero arguments, execute the subcommand cache, else, open the file cache.
if !cache_matches.args.is_empty() {
if cache_matches.args_present() {
run_cache_subcommand(cache_matches)?;
Ok(true)
} else {