mirror of
https://github.com/gay-pizza/jaarg.git
synced 2025-12-19 07:20:18 +00:00
Partially use standard formatter alignment for aligning option help text
This commit is contained in:
@@ -74,17 +74,16 @@ impl<ID> core::fmt::Display for StandardFullHelpWriter<'_, ID> {
|
|||||||
writeln!(f, "{description}")?;
|
writeln!(f, "{description}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_left_pad<ID: 'static>(option: &Opt<ID>) -> usize {
|
// Determine the alignment width from the longest option parameter
|
||||||
|
fn calculate_option_line_length<ID: 'static>(option: &Opt<ID>) -> usize {
|
||||||
(match option.names {
|
(match option.names {
|
||||||
OptIdentifier::Single(name) => name.chars().count(),
|
OptIdentifier::Single(name) => name.chars().count(),
|
||||||
OptIdentifier::Multi(names) => (names.len() - 1) * 3 + names.iter()
|
OptIdentifier::Multi(names) => (names.len() - 1) * 3 + names.iter()
|
||||||
.fold(0, |accum, name| accum + name.chars().count()),
|
.fold(0, |accum, name| accum + name.chars().count()),
|
||||||
}) + option.value_name.map_or(0, |v| v.len() + 3)
|
}) + option.value_name.map_or(0, |v| v.len() + 3)
|
||||||
}
|
}
|
||||||
|
let align_width = 3 + self.0.options.iter()
|
||||||
// Determine the alignment width from the longest option parameter
|
.map(|o| calculate_option_line_length(o)).max().unwrap_or(0);
|
||||||
let align_width = 2 + self.0.options.iter()
|
|
||||||
.map(|o| calculate_left_pad(o)).max().unwrap_or(0);
|
|
||||||
|
|
||||||
// Write positional argument descriptions
|
// Write positional argument descriptions
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
@@ -97,15 +96,64 @@ impl<ID> core::fmt::Display for StandardFullHelpWriter<'_, ID> {
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write positional argument line
|
// Write positional argument line (name + optional aligned help text)
|
||||||
write!(f, " {name}", name = option.first_name())?;
|
let name = option.first_name();
|
||||||
|
write!(f, " {name}")?;
|
||||||
if let Some(help_text) = option.help_string {
|
if let Some(help_text) = option.help_string {
|
||||||
write!(f, " {:.<width$} {help_text}", "",
|
write!(f, " {:.<width$} {help_text}", "",
|
||||||
width = align_width - calculate_left_pad(option))?;
|
width = align_width - name.chars().count() - 1)?;
|
||||||
}
|
}
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formatter for option usage lines.
|
||||||
|
struct OptionUsageLine<'a, ID>(&'a Opt<ID>);
|
||||||
|
impl<ID> core::fmt::Display for OptionUsageLine<'_, ID> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
use core::fmt::Write;
|
||||||
|
let mut length = 0;
|
||||||
|
|
||||||
|
// Write option flag name(s)
|
||||||
|
match self.0.names {
|
||||||
|
OptIdentifier::Single(name) => {
|
||||||
|
write!(f, "{name}")?;
|
||||||
|
length = name.chars().count();
|
||||||
|
}
|
||||||
|
OptIdentifier::Multi(names) => {
|
||||||
|
for (i, name) in names.iter().enumerate() {
|
||||||
|
if i == 0 {
|
||||||
|
write!(f, "{name}")?;
|
||||||
|
length += name.chars().count();
|
||||||
|
} else {
|
||||||
|
write!(f, " | {name}")?;
|
||||||
|
length += 3 + name.chars().count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write value argument for value options parameters
|
||||||
|
if let Some(value_name) = self.0.value_name {
|
||||||
|
write!(f, " <{value_name}>")?;
|
||||||
|
length += 2 + value_name.chars().count() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write padding if requested
|
||||||
|
match (f.align(), f.width().unwrap_or(0).checked_sub(length)) {
|
||||||
|
(Some(core::fmt::Alignment::Left), Some(width)) if width > 0 => {
|
||||||
|
let fill = f.fill();
|
||||||
|
// First padding char is *always* a space
|
||||||
|
f.write_char(' ')?;
|
||||||
|
for _ in 1..width {
|
||||||
|
f.write_char(fill)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write option parameter argument descriptions
|
// Write option parameter argument descriptions
|
||||||
first = true;
|
first = true;
|
||||||
for option in self.0.options.iter()
|
for option in self.0.options.iter()
|
||||||
@@ -117,25 +165,12 @@ impl<ID> core::fmt::Display for StandardFullHelpWriter<'_, ID> {
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write option flag name(s)
|
// Write line for option, with aligned help text if needed
|
||||||
match option.names {
|
let line = OptionUsageLine(&option);
|
||||||
OptIdentifier::Single(name) => {
|
|
||||||
write!(f, " {name}")?;
|
|
||||||
}
|
|
||||||
OptIdentifier::Multi(names) => for (i, name) in names.iter().enumerate() {
|
|
||||||
write!(f, "{prefix}{name}", prefix = if i == 0 { " " } else { " | " })?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write value argument for value options parameters
|
|
||||||
if let Some(value_name) = option.value_name {
|
|
||||||
write!(f, " <{value_name}>")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write padding and help text
|
|
||||||
if let Some(help_text) = option.help_string {
|
if let Some(help_text) = option.help_string {
|
||||||
write!(f, " {:.<width$} {help_text}", "",
|
write!(f, " {line:.<align_width$} {help_text}")?;
|
||||||
width = align_width - calculate_left_pad(option))?;
|
} else {
|
||||||
|
write!(f, " {line}")?;
|
||||||
}
|
}
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ impl<ID> Opt<ID> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
impl<ID: 'static> Opt<ID> {
|
impl<ID: 'static> Opt<ID> {
|
||||||
/// Get the first name of the option.
|
/// Get the first name of the option.
|
||||||
pub const fn first_name(&self) -> &str {
|
pub const fn first_name(&self) -> &str {
|
||||||
|
|||||||
Reference in New Issue
Block a user