Skip to content

Help Rendering: Dynamic Layouts and Custom Styles

Unlike many CLI frameworks that hardcode text templates or construct static string blocks, clish generates all help displays dynamically at runtime. This ensures that help text remains fully synchronized with the underlying types, validations, and default values.

This page explains how the help engine measures terminal column widths, resolves layout elements, and formats the output with configurable styles.


The Metadata Source

The help engine relies on the static list of ParamEntry structs generated for each command. Every parameter property is parsed at startup:

pub struct ParamEntry {
    pub name: &'static str,
    pub short: char,
    pub help: &'static str,
    pub details: &'static str,
    pub kind: &'static str,
    pub placeholder: &'static str,
    ...
}

Dynamic Layout & Column Alignment

To prevent jagged text layouts, clish computes column margins dynamically based on the longest parameter description:

flowchart TD
    A[Compile Help Elements] --> B[Calculate Label Widths]
    B --> C[Positional\ne.g. '<TARGET>']
    B --> D[Named Option\ne.g. '-p, --port <P>']
    B --> E[Flag\ne.g. '-f, --force']
    C --> F[Find Max Label String Length\nmax_width]
    D --> F
    E --> F
    F --> G[Format Table Column]
    G --> H["Print: [Label] + [Padding to max_width] + [Help] + [Hints]"]

1. Label Formatting (param_label)

First, the engine translates each parameter's properties into a display label. * Positional parameters: * Required: <NAME> (e.g. <TARGET>) * Optional: [<NAME>] (e.g. [<TARGET>]) * Variadic: <NAME>... (e.g. <TARGET>...) * Named parameters: * Without short alias: --port <PORT> * With short alias: -p, --port <PORT> * Flag parameters: * Without short alias: --force * With short alias: -f, --force

The generic name inside the brackets is determined by the placeholder attribute, falling back to the uppercase parameter name.

2. Alignment Calculation

The engine loops through all parameter display labels to determine the length of the longest string. Let's call this max_width.

When printing, the label column is padded to max_width using spaces, followed by two spaces of buffer, and then the parameter's help description. This creates a clean vertical alignment across all lines:

Options:
  -p, --port <PORT>  Port number
  -f, --force        Force deployment
  --verbose          Print detailed logs
  ^~~~~~~~~~~~~~~~~^  ^
  label (padded)      description

3. Compact Usage Collapse

If a command has many named options and flags, the usage line can become excessively long. The App::compact_usage_limit field controls when the usage line collapses to [OPTIONS]:

  • None -- never collapse
  • Some(0) -- always collapse
  • Some(n) -- collapse when named + flags > n

The collapse only affects the usage line. The full Arguments and Options sections below it always show every parameter with its metadata.

The default threshold is Some(5), which means commands with 6 or more options/flags get a compact usage line.


The Styling Engine: farben & anstyle

clish supports a customizable styling system through the AppStyles structure. Each style field (like header or brand) is represented by the AppStyle enum:

pub enum AppStyle {
    Markup(&'static str),
    Anstyle(anstyle::Style),
}

This hybrid model gives developers the flexibility to choose their styling paradigm:

1. Farben Markup

farben is a text markup library. It uses embedded tags inside strings to define colors:

let my_style: AppStyle = "[bold cyan]".into();
When printed, farben translates "[bold cyan]" into standard ANSI escape codes (\x1b[1;36m) and automatically resets the terminal coloring at the end of the text.

2. anstyle Structs

anstyle is a lightweight, zero-dependency styling specification crate. It is the modern standard for writing CLI tool colors in Rust:

use anstyle::{Style, AnsiColor};
let my_style: AppStyle = Style::new()
    .fg_color(Some(AnsiColor::Cyan.into()))
    .bold()
    .into();


Detailed vs. Short Help

clish automatically manages two levels of help display detail:

flowchart LR
    S["Short Help\n(-h)"] --> S1[App Name, Version and Description]
    S --> S2[Parameter Labels and Short Help]
    S --> S3[Omits long details]

    D["Detailed Help\n(--help)"] --> D1[Everything in Short Help]
    D --> D2[Long App and Command Details]
    D --> D3[Parameter-specific Details]
    D --> D4["Parameter Fallback Hints\n(Env, Defaults)"]

Short Help (-h)

Triggered by running <command> -h or <app> -h. * Displays the application name, version, and primary description. * Lists parameters with their standard display labels and short help summaries. * Omits the long command details and parameter details to keep output compact.

Detailed Help (--help)

Triggered by running <command> --help or <app> --help. * Includes everything from the short help. * Appends the command's long details block below the primary description. * Appends parameter-specific details below their respective parameter rows. * Displays argument fallback hints (choices, environment variables, and defaults) inside parentheses:

-p, --port <PORT>  Port number (env: PORT, default: 8080)


Next, let's explore App Dispatch to see how clish initializes the program and resolves subcommand routing.