Comprehensive architecture, configuration, deployment, and development reference for bat -- a cat(1) clone with syntax highlighting, git integration, and automatic paging. Written in Rust, dual-licensed under MIT/Apache-2.0.
Executive Summary
bat is a mature, production-quality command-line tool and Rust library that replaces cat(1) with syntax-highlighted, git-aware file display. The codebase is approximately 876 files across 19 significant modules, built with Rust edition 2021 and a minimum supported Rust version (MSRV) of 1.88.
Key Metrics
| Metric | Value |
|---|---|
| Version | 0.26.1 |
| Language | Rust (edition 2021) |
| MSRV | 1.88 |
| License | MIT OR Apache-2.0 |
| Source files | ~876 |
| Core modules | 19 (src/) |
| Built-in themes | 27 |
| Dependencies | ~40 direct |
| Cargo features | 10 |
| Public API | PrettyPrinter, Input, SyntaxMapping, MappingTarget |
Repository Structure
bat/
src/ Core library and binary
assets.rs Syntax/theme loading, caching, detection
config.rs Config struct (25+ fields), env resolution
controller.rs Main execution pipeline, pager orchestration
decorations.rs Line numbers, git markers, grid rendering
diff.rs Git diff integration (via git2)
error.rs Error types and handling
input.rs File/stdin/reader input, encoding detection
less.rs Less version detection
lessopen.rs LESSOPEN/LESSCLOSE preprocessing
lib.rs Public API exports
line_range.rs Line range parsing and evaluation
macros.rs bat_warning! macro
nonprintable_notation.rs Non-printable character display modes
output.rs Output type management (pager/stdout/builtin)
pager.rs Pager selection, source tracking, kind detection
paging.rs PagingMode enum
preprocessor.rs Tab expansion, ANSI stripping, overstrike removal
pretty_printer.rs Public library API (PrettyPrinter)
printer.rs InteractivePrinter (core rendering)
style.rs StyleComponent parsing and resolution
syntax_mapping.rs File-to-syntax mapping (glob patterns)
syntax_mapping/
ignored_suffixes.rs Suffix stripping for syntax detection
terminal.rs Color conversion (syntect -> nu_ansi_term)
theme.rs Theme resolution, color scheme detection
vscreen.rs Virtual screen, ANSI escape sequence parsing
wrapping.rs WrappingMode enum
examples/ 7 Rust examples demonstrating PrettyPrinter API
tests/ Integration tests, snapshot tests, syntax tests
assets/ Syntax definitions, themes, completions, man page
completions/ Shell completions (bash, zsh, fish, PowerShell)
manual/ Man page source
syntaxes/ Sublime Text .sublime-syntax files
themes/ .tmTheme theme files
doc/ Documentation, translated READMEs
Architecture
System Architecture Overview
┌─────────────┐
│ CLI/API │
│ (clap 4.x) │
└──────┬──────┘
│
┌──────▼──────┐
│ Config │
│ (25+ fields)│
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
┌──────▼──────┐ ┌──▼───┐ ┌──────▼──────┐
│HighlightingAssets│ │Input│ │ SyntaxMapping│
│(syntect cache)│ │ │ │(glob→syntax)│
└──────┬──────┘ └──┬───┘ └──────┬──────┘
│ │ │
└────────────┼────────────┘
│
┌──────▼──────┐
│ Controller │
│(orchestrate)│
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
┌──────▼──────┐ ┌──▼────────┐ ┌─▼──────┐
│InteractivePrinter│ │Decorations│ │OutputType│
│(render lines)│ │(sidebar) │ │(pager) │
└─────────────┘ └───────────┘ └────────┘
Core Components
Config (src/config.rs)
The central configuration struct that drives all behavior. All CLI flags, environment variables, and config file settings resolve into this struct.
pub struct Config<'a> {
pub language: Option<&'a str>,
pub show_nonprintable: bool,
pub wrapping_mode: WrappingMode,
pub colored_output: bool,
pub true_color: bool,
pub term_width: usize,
pub tab_width: usize,
pub theme: String,
pub style_components: StyleComponents,
pub pager: Option<&'a str>,
pub use_italic_text: bool,
pub visible_lines: VisibleLines,
pub highlighted_lines: HighlightedLineRanges,
pub syntax_mapping: SyntaxMapping<'a>,
pub paging_mode: PagingMode,
pub strip_ansi: StripAnsiMode,
pub squeeze_empty_lines: Option<usize>,
// ... additional fields
}
The diff_mode() method returns true when the --diff flag is active.
Note
Config is lifetime-bound (
'a) because it borrows string references from the CLI argument parser and syntax mapping data.
HighlightingAssets (src/assets.rs)
Manages syntax definitions and themes. Two loading modes:
- Binary (compiled-in) --
HighlightingAssets::from_binary()loads the bundled, serialized asset data. This is the default for normal operation. - Cache (user-customized) --
HighlightingAssets::from_cache(path)loads from a user-built cache directory containing custom syntaxes and themes.
Key methods:
| Method | Purpose |
|---|---|
get_syntax_set() |
Returns the compiled SyntaxSet |
syntaxes() |
Returns all available syntax references |
themes() |
Iterator over available theme names |
get_theme(name) |
Returns a specific theme |
set_fallback_theme(theme) |
Sets the fallback theme for unknown names |
get_acknowledgements() |
Returns bundled license text |
Syntax detection order (implemented in assets.rs):
- User-defined
--map-syntaxglob mappings - Exact file name match
- Hidden file extension matching (e.g.,
.bashrc) - File extension
- First-line content analysis (shebangs, modelines)
Controller (src/controller.rs)
The top-level orchestrator. Controller::new(config, assets) creates the execution context. The controller:
- Resolves syntax for each input
- Creates the output type (pager or stdout)
- Instantiates the
InteractivePrinterwith decorations - Iterates through input lines, applying highlighting and decorations
- Manages pager lifecycle
Input (src/input.rs)
Represents a single input source with metadata.
pub struct Input<'a> {
kind: InputKind<'a>,
metadata: InputMetadata,
description: InputDescription,
}
Input sources:
| Method | Source |
|---|---|
Input::ordinary_file(path) |
File on disk |
Input::stdin() |
Standard input |
Input::from_reader(reader) |
Custom Read implementation |
The InputDescription struct holds the display name, kind (e.g., "File"), title, and summary for the header decoration.
Encoding detection: bat detects UTF-16 (BOM-based) and handles the conversion to UTF-8 internally. It also detects ZIP files by magic bytes and refuses to display them.
SyntaxMapping (src/syntax_mapping.rs)
Maps file names/paths to syntax definitions using glob patterns.
pub struct SyntaxMapping<'a> {
mappings: Vec<(GlobMatcher, MappingTarget<'a>)>,
// ...
}
MappingTarget variants:
MapTo(syntax_name)-- Use the named syntaxMapToUnknown-- Force plain textMapExtensionToUnknown-- Ignore file extension, use plain text
The IgnoredSuffixes sub-module (syntax_mapping/ignored_suffixes.rs) strips known suffixes (e.g., .bak, .orig) before syntax detection, so file.rs.bak is detected as Rust.
Output (src/output.rs)
Three output types:
| Variant | Description |
|---|---|
OutputType::Pager(Child) |
External pager process (e.g., less) |
OutputType::BuiltinPager(BuiltinPager) |
Built-in pager (via minus crate) |
OutputType::Stdout |
Direct stdout |
The BuiltinPager uses minus::dynamic_paging() on a background thread. It registers Home/End key bindings for navigation.
OutputHandle abstracts over io::Write and fmt::Write to support both external pagers (byte-oriented) and the built-in pager (string-oriented).
Pager Selection (src/pager.rs)
Priority chain for pager resolution:
--pager / config file → PagerSource::Config
BAT_PAGER env var → PagerSource::EnvVarBatPager
PAGER env var → PagerSource::EnvVarPager
"less" (default) → PagerSource::Default
Known pager kinds:
| PagerKind | Behavior |
|---|---|
Less |
Gets automatic -R, -F, -K flags when from PAGER/default |
More |
Silently replaced with less when from PAGER |
Most |
Silently replaced with less when from PAGER |
Bat |
Error (InvalidPagerValueBat) if from BAT_PAGER/--pager; replaced with less if from PAGER |
Builtin |
Uses the internal minus-based pager |
Unknown |
Used as-is with user-provided arguments |
Important
When the pager is
lessand comes fromPAGER(notBAT_PAGER), bat replaces the user's arguments with its own defaults. This is intentional: the genericPAGERvariable often contains flags incompatible with ANSI colors.BAT_PAGERarguments are always preserved.
Less Version Detection (src/less.rs)
bat detects the less version to adjust behavior:
| Version | Behavior |
|---|---|
| < 530 | Adds --no-init to fix --quit-if-one-screen bug |
| >= 530 (Unix) | Omits --no-init (enables mouse wheel) |
| >= 558 (Windows) | Omits --no-init |
| BusyBox | Omits -K (not supported) |
Detection via less --version output parsing.
Preprocessor (src/preprocessor.rs)
Three preprocessing operations:
- Tab expansion (
expand_tabs) -- Converts tabs to spaces respecting tab stops. Handles ANSI escape sequences correctly (cursor position is not affected by escapes). - ANSI stripping (
strip_ansi) -- Removes ANSI escape sequences from input. - Overstrike removal (
strip_overstrike) -- Strips man-page-style bold (X\x08X) and underline (_\x08X) formatting.
The StripAnsiMode enum controls when ANSI stripping occurs: Never, Always, or Auto (strip only when input is not plain text).
Virtual Screen (src/vscreen.rs)
Parses and tracks ANSI escape sequences for correct terminal state management. Two tracker types:
EscapeSequenceTracker-- Tracks basic state across escape sequencesEscapeSequenceOffsetsIterator-- Zero-allocation parser that yields offset ranges for text vs. escape sequence segments
LESSOPEN Preprocessing (src/lessopen.rs)
Note
Only available when the
lessopencargo feature is enabled (included indefaultfeatures).
Implements the same LESSOPEN/LESSCLOSE protocol as less:
| LESSOPEN Prefix | Mode | Behavior |
|---|---|---|
| (none) | TempFile |
Preprocessor writes output to a temp file |
| |
PipedIgnoreExitCode |
Output piped; falls back to original if empty AND non-zero exit |
|| |
Piped |
Output piped; falls back to original only if empty |
- prefix |
stdin processing | Also preprocesses stdin input |
The Preprocessed struct implements Drop to run LESSCLOSE cleanup on deallocation.
Warning
When the pager is
lessand LESSOPEN is enabled, bat passes--no-lessopentolessto prevent double-preprocessing.
Decorations (src/decorations.rs)
Sidebar decorations are implemented as trait objects that produce formatted output for each line:
- LineNumberDecoration -- Renders line numbers
- GridBorderDecoration -- Renders vertical grid lines
- GitChangeDecoration -- Renders
+,~,-markers from git diff data - SnipDecoration -- Renders
...markers between non-contiguous line ranges
Diff Integration (src/diff.rs)
Uses git2 (libgit2 bindings) to compute line-level changes:
pub fn get_git_diff(filename: &Path) -> Option<LineChanges>
Returns a HashMap<usize, LineChange> mapping line numbers to change types. Only available when the git cargo feature is enabled.
Theme Resolution (src/theme.rs)
The theme() function resolves the active theme:
pub fn theme(options: ThemeOptions) -> ThemeResult
ThemeOptions includes:
theme-- User-requested theme name (may be "auto")color_scheme-- Detected terminal color scheme (dark/light/unknown)
When theme is "auto" or references a dark/light variant, the function uses terminal-colorsaurus to detect the terminal background color and selects an appropriate theme.
Style Components (src/style.rs)
StyleComponents is a set-based container for active visual components:
pub struct StyleComponents(pub HashSet<StyleComponent>);
Parsing supports:
- Comma-separated component names
- Negation with
-prefix (e.g.,default,-grid) - Meta-components that expand to multiple components (
full,default,auto,header) - Multiple
--stylearguments that merge (later values take precedence)
Requirements
Build Requirements
| Requirement | Version |
|---|---|
| Rust | >= 1.88 |
| Cargo | (bundled with Rust) |
| C compiler | Required for libgit2 (unless vendored-libgit2) |
| pkg-config | Linux: for finding system libgit2 |
| cmake | When building vendored libgit2 |
Runtime Requirements
| Requirement | Required | Notes |
|---|---|---|
| Terminal | Yes | Any terminal supporting ANSI escape sequences |
less |
Recommended | Default pager; bat works without it (falls back to stdout) |
git |
No | Only needed for git2 feature at compile time |
| Visual C++ Runtime | Windows only | Required on Windows |
COLORTERM env |
Recommended | Set to truecolor or 24bit for best color output |
Supported Platforms
Pre-built binaries are provided for:
| Platform | Architectures |
|---|---|
| Linux | x86_64 (glibc, musl), aarch64, armhf, i686 |
| macOS | x86_64, aarch64 (Apple Silicon) |
| Windows | x86_64, i686 |
| FreeBSD | x86_64 |
Additional platforms supported via source compilation (any Rust target with std support).
Configuration Reference
Config Struct Fields
Complete reference of all Config struct fields and their corresponding CLI/env sources:
| Field | Type | CLI Flag | Env Var | Default |
|---|---|---|---|---|
language |
Option<&str> |
--language, -l |
-- | None (auto-detect) |
show_nonprintable |
bool |
--show-all, -A |
-- | false |
wrapping_mode |
WrappingMode |
--wrap |
-- | NoWrapping(false) |
colored_output |
bool |
--color |
-- | true (interactive) |
true_color |
bool |
-- | COLORTERM |
Auto-detected |
term_width |
usize |
--terminal-width |
-- | Terminal width |
tab_width |
usize |
--tabs |
-- | 4 |
theme |
String |
--theme |
BAT_THEME |
Platform default |
style_components |
StyleComponents |
--style |
BAT_STYLE |
default |
pager |
Option<&str> |
--pager |
BAT_PAGER, PAGER |
None (use default chain) |
use_italic_text |
bool |
--italic-text |
-- | false |
visible_lines |
VisibleLines |
--line-range, -r, --diff |
-- | All lines |
highlighted_lines |
HighlightedLineRanges |
--highlight-line, -H |
-- | None |
syntax_mapping |
SyntaxMapping |
--map-syntax |
-- | Empty |
paging_mode |
PagingMode |
--paging |
-- | QuitIfOneScreen |
strip_ansi |
StripAnsiMode |
--strip-ansi |
-- | Auto |
squeeze_empty_lines |
Option<usize> |
--squeeze-limit |
-- | None |
WrappingMode Values
| Value | CLI | Description |
|---|---|---|
NoWrapping(false) |
--wrap=never |
No wrapping |
NoWrapping(true) |
--wrap=never with less |
No wrapping + -S flag to less |
Character |
--wrap=character |
Wrap at character boundaries |
PagingMode Values
| Value | CLI | Description |
|---|---|---|
Always |
--paging=always |
Always use pager |
QuitIfOneScreen |
--paging=auto |
Pager only if output exceeds terminal |
Never |
--paging=never |
Never use pager |
StyleComponent Values
| Component | Description | Expansion |
|---|---|---|
auto |
Platform-aware defaults | (varies) |
full |
All components | All below |
default |
Same as auto |
(varies) |
plain |
No decorations | (empty set) |
changes |
Git change markers | -- |
header |
File header | header-filename + header-filesize |
header-filename |
File name in header | -- |
header-filesize |
File size in header | -- |
grid |
Vertical/horizontal grid lines | -- |
rule |
Horizontal rule between files | -- |
numbers |
Line numbers | -- |
snip |
Snip markers between ranges | -- |
Cargo Features
bat uses Cargo features to control optional functionality:
| Feature | Default | Description |
|---|---|---|
default |
Yes | Enables application |
application |
Yes | Full CLI binary (includes git, paging, lessopen, build-assets) |
git |
Yes (via application) |
Git diff integration via git2 |
paging |
Yes (via application) |
Pager support |
lessopen |
Yes (via application) |
LESSOPEN/LESSCLOSE preprocessing |
build-assets |
Yes (via application) |
Asset compilation for custom syntaxes/themes |
regex-onig |
No | Use Oniguruma regex engine (faster, requires C lib) |
regex-fancy |
No | Use pure-Rust fancy-regex engine |
minimal-application |
No | Minimal binary without git/paging/lessopen |
vendored-libgit2 |
No | Compile libgit2 from source instead of using system lib |
Important
Library consumers must enable either
regex-onigorregex-fancy. Neither is enabled by default for the library crate -- theapplicationfeature selects one automatically for the binary.
Feature Combinations for Library Use
# Recommended for library usage (pure Rust, no C dependencies)
[dependencies]
bat = { version = "0.26", default-features = false, features = ["regex-fancy"] }
# With git support
bat = { version = "0.26", default-features = false, features = ["regex-fancy", "git"] }
# With paging support
bat = { version = "0.26", default-features = false, features = ["regex-fancy", "paging"] }
Dependencies
Key Direct Dependencies
| Crate | Version | Purpose |
|---|---|---|
syntect |
5.3.0 | Syntax highlighting engine (Sublime Text compatible) |
clap |
4.5.60 | CLI argument parser (compile-time generated) |
git2 |
0.20 | Git integration (libgit2 bindings) |
nu-ansi-term |
0.50.3 | ANSI color/style formatting |
content_inspector |
0.2.4 | Binary file detection |
encoding_rs |
0.8 | Character encoding conversion |
terminal-colorsaurus |
0.4.7 | Terminal color scheme detection |
minus |
5.6 | Built-in pager |
console |
0.15 | Terminal width detection |
shell-words |
1.1 | Shell command parsing |
grep-cli |
0.1 | Binary path resolution |
globset |
0.4 | Glob pattern matching for syntax mapping |
once_cell |
1.21 | Lazy initialization |
itertools |
0.14.0 | Iterator utilities |
Build Dependencies
| Crate | Purpose |
|---|---|
clap_complete |
Shell completion generation |
Test Dependencies
| Crate | Purpose |
|---|---|
assert_cmd |
CLI integration testing |
predicates |
Assertion helpers |
serial_test |
Serialized test execution (for env-var-dependent tests) |
tempfile |
Temporary file/directory creation |
wait-timeout |
Process timeout handling |
Building and Development
Building from Source
# Clone with all submodules (syntax test files)
git clone --recursive https://github.com/sharkdp/bat
cd bat
# Debug build
cargo build --bins
# Release build
cargo build --release --bins
# Install release binary
cargo install --locked
Running Tests
# All tests
cargo test
# Unit tests only
cargo test --lib
# Integration tests only
cargo test --test integration_tests
Note
Some tests use
serial_testbecause they read/write environment variables. These tests must not run in parallel. The test harness handles this automatically.
Building Custom Assets
To include modified syntaxes and themes in the binary:
bash assets/create.sh
cargo install --locked --force
Snapshot Testing
bat uses snapshot-based testing for output verification. The test infrastructure is in tests/snapshots/:
# Generate snapshots
python tests/snapshots/generate_snapshots.py
# Syntax-specific highlighted versions
python tests/syntax-tests/create_highlighted_versions.py
python tests/syntax-tests/compare_highlighted_versions.py
Test Utilities
Located in tests/utils/:
command.rs-- Helper functions to create bat command instances (bat(),bat_with_config(),bat_raw_command())mocked_pagers.rs-- Creates temporary mocked pager executables for testing pager behavior
Security
Input Validation
Binary File Detection
bat uses the content_inspector crate to detect binary files by inspecting the initial bytes. When binary content is detected, bat prints a warning and does not display the content. This prevents:
- Terminal corruption from binary escape sequences
- Rendering of executable content as text
- Accidental processing of binary data through the pager
Path Traversal
bat reads files specified on the command line. It does not follow symlinks in any special way -- standard OS file resolution applies. On Windows, Cygwin-style paths are not natively supported.
Encoding Safety
bat natively handles UTF-8 and UTF-16 (with BOM detection). It uses the encoding_rs crate for character encoding, which is a well-audited, spec-compliant implementation.
ZIP file detection (by magic bytes) prevents accidental display of archive contents.
LESSOPEN Security Considerations
Warning
The LESSOPEN/LESSCLOSE mechanism executes arbitrary shell commands specified in environment variables. This is by design (matching
lessbehavior) but represents a potential security risk if environment variables are controlled by an attacker. bat does not sanitize or sandbox LESSOPEN commands.
When LESSOPEN processing is enabled and the pager is less, bat passes --no-lessopen to less to prevent the input from being preprocessed twice.
Pager Security
bat spawns the configured pager as a child process with stdin piped. The pager command is parsed using shell-words, which handles shell quoting but does not perform shell expansion. This means:
- Pager commands are split by whitespace with quote handling
- No shell metacharacters are expanded
- The binary is resolved via
grep_cli::resolve_binary(), which searchesPATH
Dependencies and Supply Chain
bat's dependencies are locked via Cargo.lock. Key security-sensitive dependencies:
| Dependency | Risk Surface |
|---|---|
git2 (libgit2) |
Native C code via FFI; system or vendored |
syntect |
Complex parser; processes untrusted syntax definitions |
encoding_rs |
Well-audited Mozilla project |
shell-words |
Simple parser; no shell execution |
Denial of Service Considerations
- Large files: bat reads and processes entire files into memory for syntax highlighting. Very large files may consume significant memory. Use
--line-rangeto limit. - Complex syntax definitions: Pathological syntax definitions could cause excessive backtracking in the regex engine.
- Regex engine:
regex-onig(Oniguruma, C) orregex-fancy(pure Rust) -- both handle untrusted patterns but may exhibit worst-case performance.
Operations
Configuration Directories
| Platform | Config Dir | Cache Dir |
|---|---|---|
| Linux | ~/.config/bat/ |
~/.cache/bat/ |
| macOS | ~/Library/Application Support/bat/ |
~/Library/Caches/bat/ |
| Windows | %APPDATA%\bat\ |
%LOCALAPPDATA%\bat\ |
Cache Management
The syntax/theme cache is a serialized binary file. It must be rebuilt when:
- Custom syntaxes are added/removed/updated
- Custom themes are added/removed/updated
- bat is updated to a new version (binary format may change)
# Build cache from custom syntaxes/themes
bat cache --build
# Clear cache (revert to built-in assets)
bat cache --clear
# Show cache directory
bat --cache-dir
Performance Characteristics
| Operation | Performance Notes |
|---|---|
| Startup | ~5-15ms (binary asset loading, no file I/O for built-in assets) |
| Syntax detection | O(n) glob matching against mappings; fast for typical mapping counts |
| Highlighting | Line-by-line; proportional to file size and syntax complexity |
| Git diff | One git2 call per file; reads .git directory |
| Tab expansion | Single-pass with ANSI-awareness |
| Pager startup | Process spawn; ~5ms for less |
Monitoring and Diagnostics
bat does not provide built-in monitoring or logging. Diagnostic approaches:
# Show detected syntax for a file
bat --language --list-languages
# Verify theme availability
bat --list-themes | grep "ThemeName"
# Test color support
bat --color=always test.rs | cat -v
# Debug pager behavior
BAT_PAGER="cat" bat file.rs # Bypass pager entirely
# Check bat version and features
bat --version
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Invalid pager value (bat set as its own pager) |
Troubleshooting (Administrator)
Build Failures
git2 compilation fails
Cause: Missing C compiler or libgit2 dependencies.
Solution:
# Ubuntu/Debian
sudo apt install cmake pkg-config libssl-dev
# Or use vendored libgit2
cargo build --features vendored-libgit2
regex-onig compilation fails
Cause: Missing Oniguruma C library.
Solution: Use regex-fancy instead:
cargo build --no-default-features --features minimal-application,regex-fancy,git,paging
MSRV violation
Cause: Rust version < 1.88.
Solution: Update Rust:
rustup update stable
Runtime Issues
Syntax cache corruption
Symptoms: Panics or garbled highlighting after update.
Solution:
bat cache --clear
bat cache --build
Memory usage with large files
bat loads entire files for syntax highlighting. For very large files:
# Use line ranges
bat -r :1000 large.log
# Use plain mode (reduces memory from decoration tracking)
bat -pp large.log
# Use cat for files that don't need highlighting
cat large.log
Pager process orphaned
If bat crashes or is killed, the pager child process may become orphaned. The OutputType::Drop implementation waits for the pager to exit, but this only works during normal cleanup.
Solution: Kill orphaned less processes manually if they persist.
Environment variable conflicts
| Variable | Conflict Scenario |
|---|---|
PAGER=bat |
Infinite recursion (bat detects and errors) |
PAGER=most |
No ANSI color support (bat substitutes less) |
BAT_OPTS with conflicting flags |
Later values override earlier ones |
LESSOPEN |
May preprocess bat input unexpectedly |
Packaging Notes
Debian/Ubuntu naming conflict
The bat binary conflicts with another Debian package. The Debian package installs the binary as batcat:
# Standard Debian installation
apt install bat
# Binary is at /usr/bin/batcat
# Users create symlink
ln -s /usr/bin/batcat ~/.local/bin/bat
Package maintainers should be aware of this naming convention.
Shell completion installation
Completions are generated during build and can also be generated at runtime:
bat --completion bash > /usr/share/bash-completion/completions/bat
bat --completion zsh > /usr/share/zsh/vendor-completions/_bat
bat --completion fish > /usr/share/fish/vendor_completions.d/bat.fish
Man page installation
The man page source is in assets/manual/. It should be installed to $PREFIX/share/man/man1/bat.1.
Quick Reference
Environment Variables
| Variable | Purpose | Scope |
|---|---|---|
BAT_THEME |
Set syntax highlighting theme | Global |
BAT_STYLE |
Set output style components | Global |
BAT_PAGER |
Override pager (highest priority after --pager) | Global |
BAT_OPTS |
Additional CLI options | Global |
BAT_CONFIG_PATH |
Override config file path | Global |
BAT_CONFIG_DIR |
Override config directory path | Global |
PAGER |
Default pager (lower priority than BAT_PAGER) | System |
COLORTERM |
Terminal color capability (truecolor or 24bit) |
System |
LESSOPEN |
Input preprocessor command | System |
LESSCLOSE |
Preprocessor cleanup command | System |
LESSCHARSET |
Set by bat to UTF-8 when invoking less |
Set by bat |
File Locations
| File | Platform | Default Path |
|---|---|---|
| Config file | Linux | ~/.config/bat/config |
| Config file | macOS | ~/Library/Application Support/bat/config |
| Config file | Windows | %APPDATA%\bat\config |
| Custom syntaxes | All | $(bat --config-dir)/syntaxes/ |
| Custom themes | All | $(bat --config-dir)/themes/ |
| Binary cache | Linux | ~/.cache/bat/ |
| Binary cache | macOS | ~/Library/Caches/bat/ |
| Binary cache | Windows | %LOCALAPPDATA%\bat\ |
Pager Priority
1. --pager flag (CLI or config file) → PagerSource::Config
2. BAT_PAGER env var → PagerSource::EnvVarBatPager
3. PAGER env var → PagerSource::EnvVarPager
4. "less" (built-in default) → PagerSource::Default
Less Flags (Auto-configured)
When bat uses less as the pager with default or PAGER-sourced arguments:
| Flag | Purpose | Condition |
|---|---|---|
-R |
Enable ANSI color pass-through | Always |
-F |
Quit if content fits one screen | When paging mode = QuitIfOneScreen |
-S |
Chop long lines (horizontal scroll) | When wrapping = never |
-K |
Quit on interrupt (Ctrl+C) | Unless BusyBox less |
--no-init |
Fix quit-if-one-screen bug | Only less < 530 (Unix) or < 558 (Windows) |
--no-lessopen |
Prevent double preprocessing | When lessopen feature is enabled |
Public Library API
| Type | Module | Description |
|---|---|---|
PrettyPrinter |
bat::PrettyPrinter |
Main library entry point (builder pattern) |
Input |
bat::Input |
Input source wrapper |
Syntax |
bat::Syntax |
Syntax definition metadata |
SyntaxMapping |
bat::SyntaxMapping |
File-to-syntax mapping |
MappingTarget |
bat::MappingTarget |
Syntax mapping target |
WrappingMode |
bat::WrappingMode |
Text wrapping mode |
PagingMode |
bat::PagingMode |
Pager behavior mode |
StripAnsiMode |
bat::StripAnsiMode |
ANSI stripping mode |
NonprintableNotation |
bat::NonprintableNotation |
Non-printable display mode |
BinaryBehavior |
bat::BinaryBehavior |
Binary content handling mode |
Class/Enum Inventory
Key types in the codebase (46 total classes/enums):
| Type | Kind | Module | Purpose |
|---|---|---|---|
Config |
Struct | config.rs |
Central configuration |
HighlightingAssets |
Struct | assets.rs |
Syntax/theme cache |
Controller |
Struct | controller.rs |
Execution orchestrator |
Input |
Struct | input.rs |
Input source |
InputDescription |
Struct | input.rs |
Input metadata |
InputKind |
Enum | input.rs |
Input source variant |
SyntaxMapping |
Struct | syntax_mapping.rs |
Glob-to-syntax map |
MappingTarget |
Enum | syntax_mapping.rs |
Mapping destination |
IgnoredSuffixes |
Struct | ignored_suffixes.rs |
Suffix stripping |
OutputType |
Enum | output.rs |
Output destination |
OutputHandle |
Enum | output.rs |
Write abstraction |
BuiltinPager |
Struct | output.rs |
minus-based pager |
Pager |
Struct | pager.rs |
Pager metadata |
PagerSource |
Enum | pager.rs |
Pager origin |
PagerKind |
Enum | pager.rs |
Known pager type |
PagingMode |
Enum | paging.rs |
Paging behavior |
PrettyPrinter |
Struct | pretty_printer.rs |
Public API |
StyleComponent |
Enum | style.rs |
Visual component |
StyleComponents |
Struct | style.rs |
Component set |
WrappingMode |
Enum | wrapping.rs |
Wrapping behavior |
StripAnsiMode |
Enum | preprocessor.rs |
ANSI stripping mode |
NonprintableNotation |
Enum | nonprintable_notation.rs |
Display mode |
BinaryBehavior |
Enum | nonprintable_notation.rs |
Binary handling |
LineRange |
Struct | line_range.rs |
Range specification |
LineRanges |
Struct | line_range.rs |
Range collection |
RangeBound |
Enum | line_range.rs |
Absolute or relative bound |
HighlightedLineRanges |
Struct | line_range.rs |
Highlighted ranges |
RangeCheckResult |
Enum | line_range.rs |
Range check outcome |
VisibleLines |
Enum | config.rs |
Visible line specification |
ThemeOptions |
Struct | theme.rs |
Theme resolution input |
ThemeResult |
Struct | theme.rs |
Theme resolution output |
LessVersion |
Enum | less.rs |
Detected less variant |
LessOpenPreprocessor |
Struct | lessopen.rs |
LESSOPEN handler |
LessOpenKind |
Enum | lessopen.rs |
LESSOPEN mode |
Preprocessed |
Struct | lessopen.rs |
Preprocessed input |
PreprocessedKind |
Enum | lessopen.rs |
Piped vs temp file |
Internals
Execution Flow
1. CLI parsing (clap)
↓
2. Config construction
- Merge: CLI flags > BAT_* env > PAGER env > defaults
- Resolve theme (color scheme detection if "auto")
- Parse style components
- Build syntax mappings
↓
3. Asset loading
- Try cache (from_cache) → fallback to binary (from_binary)
↓
4. For each input:
a. Open input (file/stdin/reader)
b. Detect syntax (mapping → filename → extension → first line)
c. Detect encoding (UTF-8/UTF-16/binary)
d. If LESSOPEN enabled: preprocess
↓
5. Create output type
- Resolve pager (Config → BAT_PAGER → PAGER → "less")
- Spawn pager process or use stdout
↓
6. Create InteractivePrinter
- Configure decorations (line numbers, git markers, grid)
- Set up syntax highlighting state
↓
7. For each line:
a. Strip overstrike if present
b. Expand tabs (ANSI-aware)
c. Apply syntax highlighting (syntect)
d. Render decorations (sidebar)
e. Apply wrapping
f. Write to output handle
↓
8. Cleanup
- Close input
- Flush output
- Wait for pager process to exit (Drop impl)
- Run LESSCLOSE if applicable (Drop impl)
Syntax Highlighting Pipeline
Raw input line
↓
Strip overstrike (if backspace found)
↓
Expand tabs → spaces (ANSI-aware, respects tab stops)
↓
Strip ANSI (if StripAnsiMode != Never && not plain text)
↓
syntect::highlight_line() → styled spans
↓
Convert syntect Color → nu_ansi_term::Color
↓
Render with decorations (line number, git markers, grid)
↓
Apply wrapping (if enabled)
↓
Output to pager/stdout
Line Range Evaluation
The LineRange system supports both absolute and relative-from-end bounds:
enum RangeBound {
Absolute(usize), // Line N
OffsetFromEnd(usize), // N lines from end
}
For relative-from-end bounds, bat buffers lines since the final line count is unknown until EOF. The MaxBufferedLineNumber enum tracks whether the current maximum is Tentative (still reading) or Final (EOF reached), and range checks return conservative results for tentative values.
Asset Compilation
bat's built-in assets (syntaxes and themes) are compiled at build time into a binary blob using syntect's serialization:
assets/create.shcollects.sublime-syntaxand.tmThemefilessyntectcompiles them intoSyntaxSetandThemeSet- These are serialized and embedded in the binary via
include_bytes! - At runtime,
HighlightingAssets::from_binary()deserializes them
User custom assets follow the same path but are compiled at runtime with bat cache --build and stored in the cache directory.
Error Handling
bat defines its own Error type in src/error.rs with a default_error_handler:
pub fn default_error_handler(error: &Error, output: &mut dyn Write)
Error handling is done via Result<T> (aliased to bat::error::Result<T>). The bat_warning! macro in src/macros.rs outputs yellow-formatted warnings to stderr:
bat_warning!("Pager '{}' not found, outputting to stdout instead", pager.bin);
Module Visibility
| Module | Visibility | Notes |
|---|---|---|
assets |
pub |
Public API |
config |
pub |
Public API |
controller |
pub |
Public API |
decorations |
private | Internal |
diff |
private | Internal |
error |
pub |
Public API |
input |
pub |
Public API |
less |
private | Internal |
lessopen |
private | Internal, feature-gated |
line_range |
pub |
Public API |
output |
pub |
Public API |
pager |
private | Internal, feature-gated |
paging |
pub(crate) |
Crate-internal, feature-gated |
preprocessor |
private | Internal |
pretty_printer |
private (re-exported) | Public types via pub use |
printer |
pub(crate) |
Crate-internal |
style |
pub |
Public API |
syntax_mapping |
pub(crate) |
Crate-internal (re-exported) |
terminal |
private | Internal |
theme |
pub |
Public API |
vscreen |
private | Internal |
wrapping |
pub(crate) |
Crate-internal (re-exported) |
Potential Improvements
Architecture Improvements
-
Streaming output for large files -- Currently bat loads entire lines for syntax highlighting. A streaming mode could reduce memory for files that don't need full syntax context.
-
Async pager integration -- The built-in pager uses a background thread. An async architecture could simplify the
BuiltinPagerimplementation and improve shutdown behavior. -
Pluggable syntax engines -- Currently tightly coupled to syntect. An abstraction layer would allow alternative highlighting engines (e.g., tree-sitter).
Feature Gaps
-
No official Docker image -- #3633 -- Would benefit CI/CD pipeline integration.
-
No riscv64 pre-built binaries -- #3463 -- Growing platform demand.
-
DOS newline handling -- #3556 -- Explicit CR/LF handling could improve cross-platform behavior.
-
Tclsh shebang detection -- #3513 -- Missing first-line detection for Tcl scripts.
-
Caddyfile syntax support -- #3644 -- Growing demand for Caddy web server config highlighting.
Code Quality
-
clapargument definitions are compile-time generated -- The CLI surface is not available for AST extraction tools. Consider generating a machine-readable CLI specification. -
Test serialization -- LESSOPEN tests require
serial_testdue to environment variable mutation. Consider test isolation via subprocess or separate environment. -
Man page syntax still in development -- The
Manpage.sublime-syntaxdefinition inassets/syntaxes/02_Extra/is noted as still being developed. May produce suboptimal highlighting for some man page formats.