# How bat's Printer Renders Output to the Terminal: A Deep Dive into the Rendering Pipeline

> Explore bat's printer rendering pipeline. Learn how InteractivePrinter and SimplePrinter handle syntax highlighting, decorations, and raw output for your terminal.

- Repository: [David Peter/bat](https://github.com/sharkdp/bat)
- Tags: deep-dive
- Published: 2026-03-06

---

**bat renders file contents through a trait-based pipeline where `InteractivePrinter` handles syntax highlighting, decorations, and ANSI escape sequences, while `SimplePrinter` provides raw output for plain mode.**

The `bat` command-line tool (sharkdp/bat) enhances file viewing with syntax highlighting, Git integration, and decorative borders. At the core of this functionality lies the **Printer** trait, which defines the contract for transforming file bytes into terminal-ready output through a sophisticated multi-stage pipeline.

## Printer Architecture and Selection

The rendering process originates in [`src/controller.rs`](https://github.com/sharkdp/bat/blob/main/src/controller.rs), where the `Controller` determines which printer implementation to instantiate based on user configuration.

### The Printer Trait and Implementations

All terminal output in bat flows through the `Printer` trait defined in [`src/printer.rs`](https://github.com/sharkdp/bat/blob/main/src/printer.rs). This trait specifies four required operations: `print_header`, `print_footer`, `print_snip`, and `print_line`. Two distinct implementations satisfy this interface:

- **`SimplePrinter`**: Activated when `config.loop_through` evaluates to true (triggered by `--plain` or `--color=never`). This implementation writes raw bytes directly to stdout without color processing, decorations, or syntax highlighting.
- **`InteractivePrinter`**: The full-featured renderer handling complex styling, syntax highlighting, line numbers, grid borders, and Git change markers.

According to `src/controller.rs:92-95`, the selection logic examines `config.loop_through` to determine which printer to instantiate before processing begins.

## The Line Rendering Pipeline

Once initialized, `Controller::print_file_ranges` (src/controller.rs:52-66) drives the rendering loop, reading files into a sliding buffer and invoking printer methods for each line or range marker.

### 1. Header Composition

Before content processing, `InteractivePrinter::print_header` (src/printer.rs:50-62) assembles metadata components using active `StyleComponent`s. This stage generates optional rule lines, file-type labels, file-size indicators, and border elements that frame the content.

### 2. Line Pre-processing

For every line in the input, `InteractivePrinter::print_line` (src/printer.rs:120-148) transforms raw bytes into displayable strings. This stage handles multiple encoding and formatting concerns:

- UTF-16 decoding for non-UTF-8 files
- Replacement of non-printable control characters
- Stripping existing ANSI sequences (when configured)
- Tab expansion to spaces

### 3. Syntax Highlighting

If syntax highlighting is enabled, the pre-processed line passes to `highlight_regions_for_line` (src/printer.rs:15-29), which executes `syntect::HighlightLines` to tokenize content and generate styled regions. For extremely long inputs, bat optimizes performance by substituting a dummy line to prevent excessive processing.

### 4. Decoration Generation

Decorations such as line numbers, Git change markers, and grid borders implement a shared `Decoration` trait. Each active decoration generates its text representation via a `generate` method and reserves terminal width (tracked internally as `cursor_max`) before content rendering, as shown in src/printer.rs:94-104.

### 5. ANSI Escape Sequence Generation

The `EscapeSequenceIterator` processes highlighted regions, converting syntect style definitions into terminal escape codes via `as_terminal_escaped`. This function applies:

- Foreground and background colors (supporting true-color)
- Bold and italic font attributes
- Underline styles for highlighted lines
- Passthrough for existing ANSI sequences

Implementation details appear in src/printer.rs:66-84.

### 6. Line Wrapping

When `config.wrapping_mode` is not set to `NoWrapping`, bat performs word- or character-wrapping (src/printer.rs:84-126). The wrapping engine inserts appropriate line breaks while preserving decoration prefixes, ensuring line numbers and grid borders align correctly across wrapped visual lines.

### 7. Finalization

After writing each line, the printer resets background highlights and disables underline escapes if using the "ansi" theme (src/printer.rs:122-130). Finally, `print_footer` outputs closing grid lines or section terminators to complete the output frame.

## Practical Usage Examples

The `PrettyPrinter` API in [`src/pretty_printer.rs`](https://github.com/sharkdp/bat/blob/main/src/pretty_printer.rs) provides the public interface that configures the `Controller` and manages printer selection.

### Example 1: Standard Interactive Output

```rust
use bat::PrettyPrinter;

fn main() -> bat::Result<()> {
    // Creates an InteractivePrinter with default decorations
    let mut printer = PrettyPrinter::new();
    printer.input_file("src/printer.rs");
    printer.print()?;
    Ok(())
}

```

This code instantiates an `InteractivePrinter`, enabling full syntax highlighting and decorative borders.

### Example 2: Plain Mode with SimplePrinter

```rust
use bat::PrettyPrinter;

let _ = PrettyPrinter::new()
    .colored_output(false)      // Disables color processing
    .grid(false)                // Removes border decorations
    .line_numbers(false)        // Hides line numbers
    .input_file("src/printer.rs")
    .print();

```

Setting `colored_output(false)` triggers `config.loop_through`, forcing the `Controller` to select `SimplePrinter` which streams raw bytes without processing overhead.

### Example 3: Ranges and Highlighted Lines

```rust
use bat::{PrettyPrinter, LineRanges};

let mut printer = PrettyPrinter::new();
printer
    .input_file("src/controller.rs")
    .line_ranges(LineRanges::from(vec![1..=5, 10..=12]))
    .highlight(3);  // Underlines line 3 in ansi theme

printer.print()?;

```

`Controller::print_file_ranges` invokes `print_snip` between disjoint ranges, while `InteractivePrinter` applies underline styling specifically to line 3.

## Summary

- bat uses a **trait-based architecture** with `SimplePrinter` for raw output and `InteractivePrinter` for styled rendering.
- The **Controller** orchestrates printer selection and line iteration based on the `config.loop_through` flag.
- **Pre-processing** handles encoding conversion, tab expansion, and non-printable character replacement before syntax highlighting.
- **Decorations** (line numbers, Git markers) reserve terminal width via `cursor_max` before content rendering.
- **ANSI escape sequences** are generated through `as_terminal_escaped`, supporting true-color, bold, italic, and underline styles.
- **Line wrapping** preserves decoration alignment across visual breaks when enabled via `config.wrapping_mode`.

## Frequently Asked Questions

### What is the difference between SimplePrinter and InteractivePrinter?

`SimplePrinter` provides zero-overhead plain text output, writing raw bytes directly without color processing or decorations. `InteractivePrinter` implements the complete rendering pipeline including syntax highlighting via syntect, Git integration, grid borders, and ANSI escape sequence generation for styled terminal output.

### How does bat handle syntax highlighting for different file types?

The `InteractivePrinter` uses `syntect::HighlightLines` within `highlight_regions_for_line` (src/printer.rs:15-29) to tokenize each line and apply theme-defined styles. The resulting regions are converted to ANSI codes via `as_terminal_escaped`, supporting both 256-color and true-color terminal emulators.

### Can I use bat's Printer implementation in my own Rust project?

Yes. The `PrettyPrinter` struct in [`src/pretty_printer.rs`](https://github.com/sharkdp/bat/blob/main/src/pretty_printer.rs) provides a public API for programmatic use. You can configure output options, select input files or strings, and call `print()` to render through the same `Controller` and printer pipeline used by the CLI.

### How does line wrapping affect decorations like line numbers?

When wrapping is enabled (any mode except `NoWrapping`), the printer calculates wrapped segments and repeats the decoration prefix—including line numbers and grid borders—for each visual line. This ensures alignment is maintained across wrapped lines while preserving the original line numbering schema, as implemented in src/printer.rs:84-126.