Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/uu/false/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ false-about = Returns false, an unsuccessful exit status.
will try to write the help or version text. Any IO error during this operation is diagnosed, yet
the program will also return 1.

false-usage = false [ignored command line arguments]
or: false OPTION

false-help-text = Print help information
false-version-text = Print version information
4 changes: 4 additions & 0 deletions src/uu/false/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ false-about = Renvoie false, un code de sortie indiquant un échec.
Retourne immédiatement avec le code de sortie 1. Lorsqu'il est invoqué avec l'une des options reconnues,
il tente d'afficher l'aide ou la version. Toute erreur d'entrée/sortie pendant cette opération est signalée,
mais le programme retourne également 1.

false-usage = false [arguments de ligne de commande ignorés]
ou: false OPTION

false-help-text = Afficher l'aide
false-version-text = Afficher les informations de version
5 changes: 3 additions & 2 deletions src/uu/false/src/false.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// file that was distributed with this source code.
use clap::{Arg, ArgAction, Command};
use std::io::Write;
use uucore::{crate_version, translate};
use uucore::{crate_version, format_usage, translate};

// uucore::main does not support no-result
pub fn uumain(mut args: impl uucore::Args) -> i32 {
Expand Down Expand Up @@ -33,7 +33,8 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
pub fn uu_app() -> Command {
Command::new("false")
.version(crate_version!())
.help_template(uucore::localized_help_template("false"))
.help_template(uucore::localized_help_template_usage_first("false"))
.override_usage(format_usage(&translate!("false-usage")))
.about(translate!("false-about"))
// We provide our own help and version options, to ensure maximum compatibility with GNU.
.disable_help_flag(true)
Expand Down
3 changes: 3 additions & 0 deletions src/uu/true/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ true-about = Returns true, a successful exit status.
options. In those cases it will try to write the help or version text. Any IO error during this
operation causes the program to return 1 instead.

true-usage = true [ignored command line arguments]
or: true OPTION

true-help-text = Print help information
true-version-text = Print version information
4 changes: 4 additions & 0 deletions src/uu/true/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@ true-about = Renvoie true, un code de sortie indiquant le succès.
Retourne immédiatement avec le code de sortie 0, sauf si l'une des options reconnues est utilisée.
Dans ce cas, il essaiera d'afficher l'aide ou la version. Une erreur d'entrée/sortie pendant cette
opération entraîne un retour avec le code de sortie 1.

true-usage = true [arguments de ligne de commande ignorés]
ou: true OPTION

true-help-text = Afficher l'aide
true-version-text = Afficher les informations de version
5 changes: 3 additions & 2 deletions src/uu/true/src/true.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// file that was distributed with this source code.
use clap::{Arg, ArgAction, Command};
use std::io::Write;
use uucore::{crate_version, translate};
use uucore::{crate_version, format_usage, translate};

// uucore::main does not support no-result
pub fn uumain(mut args: impl uucore::Args) -> i32 {
Expand Down Expand Up @@ -38,7 +38,8 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
pub fn uu_app() -> Command {
Command::new("true")
.version(crate_version!())
.help_template(uucore::localized_help_template("true"))
.help_template(uucore::localized_help_template_usage_first("true"))
.override_usage(format_usage(&translate!("true-usage")))
.about(translate!("true-about"))
// We provide our own help and version options, to ensure maximum compatibility with GNU.
.disable_help_flag(true)
Expand Down
66 changes: 47 additions & 19 deletions src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,26 +282,45 @@ pub fn format_usage(s: &str) -> String {
/// .help_template(localized_help_template("myutil"));
/// ```
pub fn localized_help_template(util_name: &str) -> clap::builder::StyledStr {
use std::io::IsTerminal;
localized_help_template_with_colors(util_name, detect_colors_enabled())
}

// Determine if colors should be enabled - same logic as configure_localized_command
let colors_enabled = if std::env::var("NO_COLOR").is_ok() {
false
} else if std::env::var("CLICOLOR_FORCE").is_ok() || std::env::var("FORCE_COLOR").is_ok() {
true
} else {
IsTerminal::is_terminal(&std::io::stdout())
&& std::env::var("TERM").unwrap_or_default() != "dumb"
};
/// Like [`localized_help_template`], but places the localized "Usage:" line
/// *before* the `--about` text instead of after it.
///
/// GNU utilities print the usage synopsis first (e.g. `Usage: false
/// [ignored command line arguments]`) followed by the description. Most
/// uutils utilities keep the description first since their usage line is
/// less informative on its own, but for utilities like `true`/`false` where
/// the usage line itself communicates useful information, matching GNU's
/// ordering is clearer.
pub fn localized_help_template_usage_first(util_name: &str) -> clap::builder::StyledStr {
localized_help_template_with_order(util_name, detect_colors_enabled(), true)
}

localized_help_template_with_colors(util_name, colors_enabled)
/// Determine whether colored output should be used, based on the same
/// environment variables and TTY checks as `configure_localized_command`.
fn detect_colors_enabled() -> bool {
clap_localization::should_use_color_for_stream(&std::io::stdout())
}
Comment on lines +301 to 305

/// Create a localized help template with explicit color control
/// This ensures color detection consistency between clap and our template
pub fn localized_help_template_with_colors(
util_name: &str,
colors_enabled: bool,
) -> clap::builder::StyledStr {
localized_help_template_with_order(util_name, colors_enabled, false)
}

/// Shared implementation behind [`localized_help_template_with_colors`] and
/// [`localized_help_template_usage_first`]. When `usage_first` is `true`,
/// the "Usage:" line is placed before `{about-with-newline}` rather than
/// after it.
fn localized_help_template_with_order(
util_name: &str,
colors_enabled: bool,
usage_first: bool,
) -> clap::builder::StyledStr {
use std::fmt::Write;

Expand All @@ -311,21 +330,30 @@ pub fn localized_help_template_with_colors(
// Get the localized "Usage" label
let usage_label = crate::locale::translate!("common-usage");

// Build the styled usage header (bold + underline like clap's default)
let mut usage_line = String::new();
if colors_enabled {
write!(
usage_line,
"\x1b[1m\x1b[4m{usage_label}:\x1b[0m {{usage}}\n\n"
)
.unwrap();
} else {
write!(usage_line, "{usage_label}: {{usage}}\n\n").unwrap();
}

// Create a styled template
let mut template = clap::builder::StyledStr::new();

// Add the basic template parts
writeln!(template, "{{before-help}}{{about-with-newline}}").unwrap();

// Add styled usage header (bold + underline like clap's default)
if colors_enabled {
write!(
if usage_first {
writeln!(
template,
"\x1b[1m\x1b[4m{usage_label}:\x1b[0m {{usage}}\n\n"
"{{before-help}}{usage_line}{{about-with-newline}}"
)
.unwrap();
} else {
write!(template, "{usage_label}: {{usage}}\n\n").unwrap();
writeln!(template, "{{before-help}}{{about-with-newline}}").unwrap();
write!(template, "{usage_line}").unwrap();
}

// Add the rest
Expand Down
2 changes: 1 addition & 1 deletion src/uucore/src/lib/mods/clap_localization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn get_color_choice() -> clap::ColorChoice {
}

/// Generic helper to check if colors should be enabled for a given stream
fn should_use_color_for_stream<S: std::io::IsTerminal>(stream: &S) -> bool {
pub(crate) fn should_use_color_for_stream<S: std::io::IsTerminal>(stream: &S) -> bool {
match get_color_choice() {
clap::ColorChoice::Always => true,
clap::ColorChoice::Never => false,
Expand Down
12 changes: 12 additions & 0 deletions tests/by-util/test_false.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ fn test_help() {
.stdout_contains("false");
}

#[test]
fn test_help_starts_with_usage() {
// GNU's `false --help` starts with a "Usage:" line; ensure ours does too.
// NO_COLOR avoids ANSI styling so the raw stdout comparison is stable.
let result = new_ucmd!().env("NO_COLOR", "1").args(&["--help"]).fails();
assert!(
result
.stdout_str()
.starts_with("Usage: false [ignored command line arguments]")
);
Comment on lines +32 to +39
}

#[test]
fn test_short_options() {
for option in ["-h", "-V"] {
Expand Down
15 changes: 15 additions & 0 deletions tests/by-util/test_true.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ fn test_help() {
.stdout_contains("true");
}

#[test]
fn test_help_starts_with_usage() {
// GNU's `true --help` starts with a "Usage:" line; ensure ours does too.
// NO_COLOR avoids ANSI styling so the raw stdout comparison is stable.
let result = new_ucmd!()
.env("NO_COLOR", "1")
.args(&["--help"])
.succeeds();
assert!(
result
.stdout_str()
.starts_with("Usage: true [ignored command line arguments]")
);
Comment on lines +35 to +45
}

#[test]
fn test_short_options() {
for option in ["-h", "-V"] {
Expand Down