How bat Handles Paging for Long Output: A Deep Dive into the Rust Source Code
bat determines whether to pipe output through a pager by checking the PagingMode configuration (Always, QuitIfOneScreen, or Never), then delegates to OutputType::from_mode in src/output.rs to spawn either an external pager (like less) with carefully crafted arguments or a built-in pager using the minus crate.
The bat command-line tool by sharkdp/bat enhances the standard cat command with syntax highlighting and Git integration. Understanding how bat handles paging for long output reveals a sophisticated system that balances user configuration, environment detection, and intelligent pager selection to ensure optimal viewing experience across different terminal environments.
The Three Paging Modes in bat
At the core of bat's paging logic is the PagingMode enum defined in src/paging.rs. This enum specifies exactly when bat should engage a pager:
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum PagingMode {
Always,
QuitIfOneScreen,
#[default] Never,
}
Never(default): bat writes directly to stdout without invoking a pager.QuitIfOneScreen: bat invokes the pager with flags that cause it to exit immediately if the entire output fits on one screen.Always: bat always pipes output through the pager, regardless of length.
How bat Decides When to Page
The decision logic resides in Controller::run within src/controller.rs. This method inspects the runtime Config to determine the appropriate paging strategy:
let mut paging_mode = self.config.paging_mode;
// ... logic to force Never mode when no input files exist ...
output_type_opt = Some(OutputType::from_mode(
paging_mode,
wrapping_mode,
self.config.pager,
)?);
If no input files are provided (for example, when reading from stdin with no tty), bat forces PagingMode::Never to prevent hanging on interactive pager prompts. Otherwise, it constructs an OutputType that encapsulates either a spawned pager process or direct stdout access.
Selecting and Configuring the Pager
The OutputType::from_mode function in src/output.rs serves as the factory for pager instances. It delegates to try_pager with specific SingleScreenAction instructions based on the paging mode:
match paging_mode {
Always => OutputType::try_pager(SingleScreenAction::Nothing, …)?,
QuitIfOneScreen => OutputType::try_pager(SingleScreenAction::Quit, …)?,
_ => OutputType::stdout(),
}
External Pager Selection
The try_pager method relies on src/pager.rs to resolve which pager binary to execute. The resolution follows a strict priority hierarchy:
--pagerCLI flagBAT_PAGERenvironment variablePAGERenvironment variable- Default to
less
As implemented in src/pager.rs:
let (cmd, source) = match (config_pager, &bat_pager, &pager) {
(Some(config_pager), _, _) => (config_pager, PagerSource::Config),
(_, Ok(bat_pager), _) => (bat_pager.as_str(), PagerSource::EnvVarBatPager),
(_, _, Ok(pager)) => (pager.as_str(), PagerSource::EnvVarPager),
_ => ("less", PagerSource::Default),
};
Argument Handling for less
When using the generic PAGER environment variable, bat sanitizes arguments to ensure proper behavior. It injects flags such as:
-R(or--RAW-CONTROL-CHARS): Preserves ANSI color sequences-F(or--quit-if-one-screen): Exits immediately if output fits on one screen (used withQuitIfOneScreenmode)-S(or--chop-long-lines): Truncates long lines rather than wrapping--no-init: Prevents clearing the screen on exit
Built-in Pager Fallback
If the resolved pager command is exactly builtin, bat instantiates an internal pager using the minus crate. This provides a pure-Rust paging solution that requires no external binaries, useful in constrained environments or when less is unavailable.
Practical Examples
Configure bat's paging behavior using CLI flags or environment variables:
# Default behavior – use less, quit if output fits on one screen
bat src/main.rs
# Force paging always, even for short output
bat --paging=always src/main.rs
# Disable paging entirely – pipe-friendly for scripts
bat --paging=never src/main.rs
# Use a custom pager via environment variable
export BAT_PAGER="most -R"
bat src/main.rs
# Use the built-in Rust pager
export BAT_PAGER="builtin"
bat src/main.rs
For programmatic use in Rust applications using bat as a library:
// Requires bat = "0.24"
use bat::{ConfigBuilder, PagingMode};
let config = ConfigBuilder::new()
.paging_mode(PagingMode::QuitIfOneScreen)
.build()
.unwrap();
let printer = bat::PrettyPrinter::new(&config);
printer.print_source("fn main() {}", "rust").unwrap();
Summary
- Three paging policies:
Never(default),QuitIfOneScreen, andAlways, defined insrc/paging.rs. - Decision logic:
Controller::runinsrc/controller.rsevaluatesconfig.paging_modeand forcesNeverwhen no input files exist. - Pager instantiation:
OutputType::from_modeinsrc/output.rsspawns external pagers or falls back to stdout based on the mode. - Pager resolution:
src/pager.rsselects binaries via--pager,BAT_PAGER,PAGER, or defaults toless, sanitizing arguments for color and quit behavior. - Built-in option: Setting the pager to
builtinuses the minus crate for a pure-Rust paging implementation.
Frequently Asked Questions
How do I disable paging in bat completely?
Set the paging mode to never using the --paging=never CLI flag or configure it in your bat configuration file. This forces bat to write directly to stdout without invoking any pager process, making it ideal for piping output to other commands or scripts.
Why does bat sometimes quit automatically when output is short?
When using the default QuitIfOneScreen paging mode (or when --paging=auto is set), bat passes the -F flag to less, which instructs the pager to exit immediately if the entire output fits on one screen. This behavior prevents unnecessary interactive prompts for short files.
Can I use a custom pager like most or delta with bat?
Yes, bat respects the BAT_PAGER environment variable and the --pager CLI option, allowing you to specify any executable. For example, export BAT_PAGER="most -R" or bat --pager="delta" will route bat's formatted output through your chosen pager with the specified arguments.
What happens if less is not installed on my system?
If less is unavailable and no custom pager is configured via BAT_PAGER or PAGER, bat can fall back to a built-in pager by setting BAT_PAGER="builtin". This uses the minus crate to provide a pure-Rust paging implementation that requires no external binaries.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →