# How bat Maps File Extensions or Names to Syntaxes: Complete Technical Guide

> Discover how bat maps file extensions or names to syntaxes using glob patterns and TOML definitions. Learn syntax highlighting with this technical guide.

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

---

**bat determines syntax highlighting by matching file paths against a precedence-ordered table of glob patterns, combining compile-time TOML definitions with runtime `--map-syntax` overrides to resolve either a specific syntax or a fallback to first-line detection.**

The `bat` command-line tool by sharkdp/bat provides syntax highlighting for files by intelligently mapping file extensions and names to specific language syntaxes. Understanding how bat maps file extensions or names to syntaxes reveals a hybrid architecture that blends built-in definitions with user-customizable rules. This mapping system relies on the `SyntaxMapping` struct in [`src/syntax_mapping.rs`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping.rs) to orchestrate the resolution pipeline.

## The Syntax Mapping Architecture

At the core of bat's file-type detection is a **syntax-mapping table** that associates glob patterns with specific targets. Each mapping entry consists of a pattern (e.g., `*.c`, `Dockerfile`) and a **MappingTarget** that determines how to interpret the match.

### Mapping Targets and Glob Patterns

The `MappingTarget` enum defined in [`src/syntax_mapping.rs`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping.rs) (lines 30-47) supports three distinct resolution strategies:

- **MapTo("syntax name")** — Forces bat to use the specified syntax immediately.
- **MapToUnknown** — Treats the file name as a hint only, causing bat to fall back to first-line detection (shebang or modeline parsing).
- **MapExtensionToUnknown** — Similar to `MapToUnknown`, but applies exclusively when the pattern matches a file extension (e.g., `*.conf`), deferring to content-based detection if no syntax matches.

### Built-in Mappings from TOML Files

During compilation, a build script scans all `*.toml` files under `src/syntax_mapping/builtins/` and generates a static array named `BUILTIN_MAPPINGS` in [`src/syntax_mapping/builtin.rs`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping/builtin.rs). Each TOML entry lists a target syntax and a list of glob matchers.

For example, [`src/syntax_mapping/builtins/common/50-git.toml`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping/builtins/common/50-git.toml) contains:

```toml
[mappings]
"Git Ignore" = ["*.gitignore", "**/.gitignore"]

```

This generates a `MapTo("Git Ignore")` entry for both patterns. The build-time code generator in [`build/syntax_mapping.rs`](https://github.com/sharkdp/bat/blob/main/build/syntax_mapping.rs) converts these declarative tables into Rust code using `MappingDefModel`, `Matcher`, and `MappingTarget` structures.

## Runtime Resolution and Precedence

When bat initializes, it constructs a `SyntaxMapping` object that merges user-defined rules with the compiled-in defaults.

### Adding Custom Mappings via CLI

The `--map-syntax` flag allows users to inject custom mappings at runtime. Internally, this invokes `SyntaxMapping::insert` (defined in [`src/syntax_mapping.rs`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping.rs) lines 99-103), which adds a `(GlobMatcher, MappingTarget)` pair to the `custom_mappings` vector. For example:

```bash

# Treat any *.myext files as C source

bat --map-syntax="*.myext=C" myfile.myext

```

This command parses the input string and calls `SyntaxMapping::insert("*.myext", MappingTarget::MapTo("C"))`, as implemented in [`src/bin/bat/app.rs`](https://github.com/sharkdp/bat/blob/main/src/bin/bat/app.rs) around line 349.

### Lookup Order in get_syntax_for

The `SyntaxMapping::get_syntax_for` method (lines 148-166 in [`src/syntax_mapping.rs`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping.rs)) iterates over all mappings in strict precedence: **custom mappings first, then built-in mappings**. It uses the `globset` crate's `GlobMatcher` to test both the full candidate path and the isolated filename:

```rust
pub fn get_syntax_for(&self, path: impl AsRef<Path>) -> Option<MappingTarget<'a>> {
    // try custom + builtin mappings
    for (glob, syntax) in self.all_mappings() {
        if glob.is_match_candidate(&candidate) ||
           candidate_filename.as_ref().map_or(false, |f| glob.is_match_candidate(f)) {
            return Some(*syntax);
        }
    }
    // fallback to ignored-suffix stripping ...
}

```

If no mapping matches, bat strips ignored suffixes (like `.bak` or `.orig`) and repeats the lookup.

## Applying Mappings to Syntax Selection

Once `get_syntax_for` returns a `MappingTarget`, `HighlightingAssets::get_syntax_for_path` (in [`src/assets.rs`](https://github.com/sharkdp/bat/blob/main/src/assets.rs), lines 58-84) applies the mapping to the actual syntax set:

- **MapTo(syntax_name)** — Looks up the syntax name in the bundled `syntect` syntax set and applies it immediately.
- **MapToUnknown** or **MapExtensionToUnknown** — Returns `Error::UndetectedSyntax`, triggering the caller to perform first-line detection (shebang parsing).

This separation between mapping resolution and syntax application allows bat to distinguish between "force this syntax" and "use this as a hint only" scenarios.

## Practical Code Examples

### Override Built-in Rules with Custom Mappings

Force bat to treat a specific filename as Docker syntax, regardless of extension:

```bash
bat --map-syntax="Dockerfile=Dockerfile" ./my-docker-file

```

The glob pattern `"Dockerfile"` matches the filename portion before any extension handling occurs.

### Enable First-Line Detection for Extensions

Map an extension to unknown, allowing bat to examine the shebang line:

```bash

# *.conf files should first be examined for a shebang / modeline

bat --map-syntax="*.conf=MapExtensionToUnknown" config.conf

```

Using `MapExtensionToUnknown` tells bat to attempt extension-based mapping, but defer to first-line detection if the syntax cannot be determined (see [`src/assets.rs`](https://github.com/sharkdp/bat/blob/main/src/assets.rs) lines 78-84).

### Programmatically Inspect Mappings

To list all active mappings using bat's internal API:

```rust
use bat::syntax_mapping::SyntaxMapping;

fn main() {
    let mapping = SyntaxMapping::new(); // loads built-ins only
    for (matcher, target) in mapping.all_mappings() {
        println!("{} → {:?}", matcher.glob(), target);
    }
}

```

## Summary

- **bat maps file extensions or names to syntaxes** using a hybrid table of glob patterns and mapping targets defined in [`src/syntax_mapping.rs`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping.rs).
- **Built-in mappings** are generated at compile time from TOML files in `src/syntax_mapping/builtins/` and stored in `BUILTIN_MAPPINGS`.
- **Custom mappings** added via `--map-syntax` take precedence over built-ins and are inserted via `SyntaxMapping::insert`.
- **Resolution order** processes custom mappings first, then built-ins, using `GlobMatcher` to test paths and filenames.
- **Mapping targets** determine behavior: `MapTo` forces a syntax, while `MapToUnknown` and `MapExtensionToUnknown` enable fallback to first-line detection handled by `HighlightingAssets::get_syntax_for_path` in [`src/assets.rs`](https://github.com/sharkdp/bat/blob/main/src/assets.rs).

## Frequently Asked Questions

### How do I override a built-in syntax mapping in bat?

Use the `--map-syntax` CLI flag to inject a custom mapping with higher precedence. For example, `bat --map-syntax="*.txt=Markdown" file.txt` forces bat to treat all `.txt` files as Markdown, overriding the default Plain Text mapping. These custom rules are stored in the `custom_mappings` vector and checked before built-in rules during `SyntaxMapping::get_syntax_for`.

### What happens if bat cannot determine a syntax from the file extension?

If no mapping matches or if the mapping target is `MapToUnknown` or `MapExtensionToUnknown`, bat returns `Error::UndetectedSyntax` from `HighlightingAssets::get_syntax_for_path`. This triggers a fallback mechanism that examines the file's first line for shebangs (`#!`) or modelines (e.g., `vim: set ft=python:`) to determine the appropriate syntax.

### Where are the built-in syntax mappings defined in the bat source code?

Built-in mappings are defined in TOML files located under `src/syntax_mapping/builtins/`, organized by category (e.g., `common/`, `git/`). During the build process, [`build/syntax_mapping.rs`](https://github.com/sharkdp/bat/blob/main/build/syntax_mapping.rs) parses these files and generates the `BUILTIN_MAPPINGS` static array in [`src/syntax_mapping/builtin.rs`](https://github.com/sharkdp/bat/blob/main/src/syntax_mapping/builtin.rs), which is then compiled into the binary.

### How does bat handle file names without extensions, like Dockerfile?

bat uses glob patterns that match the full filename, not just extensions. In `src/syntax_mapping/builtins/`, entries like `"Dockerfile" = ["Dockerfile", "**/Dockerfile"]` create `MapTo` targets that match the literal filename. During lookup in `SyntaxMapping::get_syntax_for`, the matcher tests both the full path and the isolated filename against these patterns, enabling detection of extension-less files like `Makefile` or `Dockerfile`.