How Bat Style Components Work: From CLI Flags to Terminal Rendering

Bat style components are defined in src/style.rs as a Rust enum where variants like Grid, LineNumbers, and Header expand into sub-component sets via the components() method, allowing the --style CLI flag to add, remove, or override visual decorations with specific precedence rules.

The sharkdp/bat repository renders files with syntax highlighting and rich visual decorations controlled by a modular style system. These bat style components translate user-friendly CLI arguments into concrete rendering instructions through a type-safe Rust architecture that handles expansion, parsing, and runtime checks.

Style Component Architecture

The StyleComponent Enum

At the core of the system lies the StyleComponent enum defined in src/style.rs. This enum represents every visual feature available in the terminal output:

pub enum StyleComponent {
    Auto,
    #[cfg(feature = "git")] Changes,
    Grid,
    Rule,
    Header,
    HeaderFilename,
    HeaderFilesize,
    LineNumbers,
    Snip,
    Full,
    Default,
    Plain,
}

(source: StyleComponent enum)

Each variant corresponds to a specific decoration: Grid draws border lines, LineNumbers enables line numbering, HeaderFilename displays the file name in a header, and Plain disables all visual features. The #[cfg(feature = "git")] attribute conditionally includes Git change indicators only when the git feature is compiled.

Sub-Component Expansion

Style components frequently represent aggregate sets of decorations rather than single features. The components() method in src/style.rs (lines 24-62) resolves these variants into concrete sub-component lists based on terminal interactivity:

pub fn components(self, interactive_terminal: bool) -> &'static [StyleComponent] {
    match self {
        StyleComponent::Auto => {
            if interactive_terminal {
                StyleComponent::Default.components(interactive_terminal)
            } else {
                StyleComponent::Plain.components(interactive_terminal)
            }
        }
        StyleComponent::Full => &[
            #[cfg(feature = "git")] StyleComponent::Changes,
            StyleComponent::Grid,
            StyleComponent::HeaderFilename,
            StyleComponent::HeaderFilesize,
            StyleComponent::LineNumbers,
            StyleComponent::Snip,
        ],
        StyleComponent::Default => &[
            #[cfg(feature = "git")] StyleComponent::Changes,
            StyleComponent::Grid,
            StyleComponent::HeaderFilename,
            StyleComponent::LineNumbers,
            StyleComponent::Snip,
        ],
        StyleComponent::Plain => &[],
        // single-ton components return themselves
        _ => &[self],
    }
}

(source: components() implementation)

This method implements the expansion logic:

  • Auto selects Default for interactive terminals and Plain for non-interactive environments (like pipes)
  • Full expands to include file size headers (HeaderFilesize) alongside the default set
  • Plain returns an empty slice, disabling all decorations
  • Individual components like Grid or Rule return themselves as single-element slices

Parsing and Precedence Rules

CLI Argument Parsing

The --style flag accepts comma-separated component names with optional prefixes to modify the active set. In src/style.rs, the ComponentAction enum defines three operations:

enum ComponentAction { Override, Add, Remove }

impl ComponentAction {
    fn extract_from_str(s: &str) -> (ComponentAction, &str) {
        match s.chars().next() {
            Some('-') => (ComponentAction::Remove, s.strip_prefix('-').unwrap()),
            Some('+') => (ComponentAction::Add,    s.strip_prefix('+').unwrap()),
            _          => (ComponentAction::Override, s),
        }
    }
}

(source: ComponentAction)

Prefixing a component with + adds it to the current set, - removes it, and no prefix replaces the entire configuration.

Component Set Construction

The StyleComponentList::to_components() method (referenced in src/style.rs lines 82-88) applies these actions with strict precedence rules:

  1. Override entries clear the existing set before applying new components
  2. Add/Remove actions merge into the current set without clearing previous selections

For example, the combination [grid] + [numbers] results in only {LineNumbers} because the second list's override behavior replaces the first.

Runtime Rendering Integration

During file rendering, the finalized StyleComponents set drives decoration decisions in src/terminal.rs. The code checks specific component availability to conditionally emit visual elements:

if style.grid()    { /* draw grid */ }
if style.numbers() { /* print line numbers */ }
if style.header()  { /* show header (filename/size) */ }

(source: Terminal style checks)

These boolean methods on the StyleComponents struct provide efficient runtime checks that determine whether to draw grid borders, print line numbers, or display file metadata headers.

Practical Command-Line Examples

You can combine style components using comma-separated values with prefix modifiers:


# Show a grid and line numbers

bat --style=grid,numbers file.rs

# Start from the default set and remove the grid

bat --style=default,-grid file.rs

# Add rule lines on top of a previous style definition

bat --style=full,+rule file.rs

The default keyword initializes the standard component set (Changes, Grid, HeaderFilename, LineNumbers, Snip), allowing you to selectively disable specific features using the - prefix.

Summary

  • Bat style components are defined in src/style.rs as the StyleComponent enum, with variants representing visual decorations like Grid, LineNumbers, and Header.
  • The components() method expands aggregate variants (Full, Default, Auto, Plain) into static slices of concrete sub-components based on terminal interactivity.
  • CLI parsing uses ComponentAction with +, -, and no-prefix modifiers to add, remove, or override components, with override actions taking precedence by clearing existing sets.
  • Runtime rendering checks in src/terminal.rs query the finalized component set via methods like style.grid() and style.numbers() to conditionally emit decorations.

Frequently Asked Questions

What bat style components are available in the CLI?

The available components are Auto, Changes (with git feature), Grid, Rule, Header, HeaderFilename, HeaderFilesize, LineNumbers, Snip, Full, Default, and Plain. You can view these by running bat --help or examining the StyleComponent enum in src/style.rs lines 8-22.

How does the Auto style component determine which decorations to show?

The Auto component delegates to components() in src/style.rs, which checks the interactive_terminal parameter. If the terminal is interactive (TTY), it expands to the Default component set; otherwise, it expands to Plain, disabling all decorations for pipe-friendly output.

Can I mix additive and subtractive style arguments in one bat command?

Yes. The --style flag accepts comma-separated values where +component adds to the set and -component removes from it. However, if any component appears without a prefix (an override), it clears all previous selections. For example, bat --style=numbers,-grid works, but bat --style=numbers,grid replaces any inherited styles with just those two components.

Where does bat resolve the final list of active style components?

Final resolution happens in src/style.rs within the StyleComponentList::to_components() method, which processes the parsed CLI arguments according to precedence rules. The resulting StyleComponents set is then consumed by rendering logic in src/terminal.rs to conditionally display decorations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →