# How Superfile Renders File Previews with Syntax Highlighting Using Chroma and Bat

> Discover how Superfile renders file previews with syntax highlighting using Chroma and Bat. Learn about runtime file type detection and highlighting fallback mechanisms.

- Repository: [Yorukot/superfile](https://github.com/yorukot/superfile)
- Tags: internals
- Published: 2026-07-26

---

**Superfile determines file type at runtime and routes content through either a pure‑Go Chroma highlighter or an external `bat` process, falling back to plain text when no lexer matches.**

Superfile’s preview pane supports syntax highlighting for source code through a pluggable architecture that balances portability with performance. The implementation lives primarily in the `src/internal/ui/preview` package and uses either the Go-native Chroma library or the external `bat` CLI tool, depending on user configuration.

## The Rendering Pipeline

The entry point for all preview generation is `RenderWithPath` inside [`src/internal/ui/preview/render.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/preview/render.go). This function acts as a router that first inspects the file’s mode, extension, and MIME type before deciding whether to render a directory listing, an image, or text with optional syntax highlighting.

The pipeline follows four distinct phases: **detection**, **content acquisition**, **highlighting engine selection**, and **final rendering**. This separation allows the UI code to remain agnostic of the underlying highlighter while supporting both offline-first (Chroma) and external tool (Bat) workflows.

## File Type Detection and Routing

Before any content is read, Superfile categorizes the path:

- **Unsupported formats** are filtered early via `common.UnsupportedPreviewFormats`
- **Directories** are handed to `renderDirectoryPreview`, which returns a file listing with icons
- **Images** are detected by `isImageFile` (defined in [`render_utils.go`](https://github.com/yorukot/superfile/blob/main/render_utils.go)) and processed by an `imagePreviewer` that converts them to ANSI or Kitty graphics protocol
- **Regular text files** proceed to `renderTextPreview`

For text files, the system first attempts lexer detection using Chroma’s `lexers.Match(filepath.Base(itemPath))`. If Chroma returns no lexer, Superfile runs `common.IsTextFile` to perform a binary‑text heuristic before deciding whether to render the content as plain text.

## Syntax Highlighting Engines

Once a file is confirmed as text with a valid lexer, Superfile checks `common.Config.CodePreviewer` to decide which highlighting engine to invoke.

### The Built‑in Chroma Engine (ansichroma)

When `CodePreviewer` is set to `"ansichroma"` (or when `bat` is unavailable), Superfile uses its own wrapper around the Chroma library.

The file content is first loaded via `utils.ReadFileContent`, then passed to:

```go
fileContent, err = ansichroma.HightlightString(
    fileContent, format.Config().Name,
    common.Theme.CodeSyntaxHighlightTheme, background)

```

This call tokenizes the content using the detected lexer and renders it to ANSI escape codes using the theme defined in `common.Theme.CodeSyntaxHighlightTheme`. The `ansichroma` package (internal to Superfile) adapts Chroma’s token stream into terminal‑ready strings, respecting the configured `background` color when `transparentBackground` is disabled.

### The External Bat Integration

If `common.Config.CodePreviewer` equals `"bat"` and the executable exists (detected via `CheckBatCmd` which resolves to `bat` or `batcat`), Superfile delegates highlighting to the external tool.

The implementation in [`src/internal/ui/preview/render_utils.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/preview/render_utils.go) constructs the command via `getBatSyntaxHighlightedContent`:

```go
batArgs := []string{itemPath, "--plain", "--force-colorization",
                    "--line-range", fmt.Sprintf(":%d", previewLine)}
cmd := exec.CommandContext(ctx, batCmd, batArgs...)

```

The `--plain` flag disables Bat’s default pagination and headers, while `--force-colorization` ensures ANSI codes are emitted even when piped. The `--line-range` argument limits output to the available preview height.

After execution, the output optionally passes through `setBatBackground` to inject solid‑color background ANSI sequences when the terminal theme requires it. This matches the behavior of the Chroma path for consistent UI appearance.

## Rendering the Final Output

Regardless of which engine produces the highlighted string, completion follows the same path. The content is added to the renderer instance via `r.AddLines(fileContent)`, which buffers the lines for display. Finally, `r.Render()` emits the complete ANSI string (or Kitty image data) that the Bubble Tea framework draws into the preview pane.

`RenderWithPath` also manages dimension calculations for optional borders and clears previous Kitty images using `m.imagePreviewer.GetKittyClearRaw()` to prevent ghosting when navigating between files.

## Code Examples

### Render a Source File with Default Chroma

```go
// Inside the UI model:
previewStr, _ := model.RenderWithPath(
    "/path/to/main.go",   // itemPath
    80,                   // previewWidth (columns)
    20,                   // previewHeight (rows)
    120,                  // fullModelWidth
)
fmt.Println(previewStr) // ANSI‑colored preview shown in the panel

```

### Force Bat for Syntax Highlighting

```go
common.Config.CodePreviewer = "bat"
common.Config.BatCmd = preview.CheckBatCmd() // resolves to "bat" or "batcat"

previewStr, _ := model.RenderWithPath(
    "/path/to/main.go", 80, 20, 120,
)
fmt.Println(previewStr) // output produced by the external `bat` command

```

### Preview a Directory

```go
previewStr, _ := model.RenderWithPath(
    "/path/to/dir", 30, 10, 80,
)
fmt.Println(previewStr) // lists files with icons

```

### Preview an Image (Kitty Protocol)

```go
previewStr, raw := model.RenderWithPath(
    "/path/to/image.png", 40, 15, 100,
)
// `raw` contains the Kitty image data that must be sent via tea.Raw()

```

## Summary

- **Routing logic** in [`src/internal/ui/preview/render.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/preview/render.go) (`RenderWithPath`) separates file type detection from rendering logic.
- **Chroma integration** uses `lexers.Match` for detection and `ansichroma.HightlightString` for pure‑Go highlighting without external dependencies.
- **Bat integration** shells out to `bat` or `batcat` with `--plain` and `--force-colorization` flags for fast, high‑fidelity highlighting.
- **Configuration** is controlled by `common.Config.CodePreviewer` and theme settings in `common.Theme.CodeSyntaxHighlightTheme`.
- **Extensibility** is built‑in: new highlighters can be added by implementing the content acquisition → highlight → `r.AddLines` pattern.

## Frequently Asked Questions

### What happens if a file has no recognized syntax?

If Chroma’s `lexers.Match` returns nil and `common.IsTextFile` confirms the content is text, Superfile renders the file as plain text without color codes. Binary files are rejected from the preview pane entirely.

### Can I use `bat` on systems where it is installed as `batcat`?

Yes. Superfile’s `CheckBatCmd` function (in [`render_utils.go`](https://github.com/yorukot/superfile/blob/main/render_utils.go)) probes the system path and automatically resolves the correct executable name, storing it in `common.Config.BatCmd` for subsequent calls.

### How does Superfile limit the amount of file content read for large files?

Both the Chroma and Bat paths respect the `previewLine` limit. The Bat integration passes `--line-range` to restrict output, while the Chroma path reads content via `utils.ReadFileContent` which internally handles buffering and truncation to avoid loading multi‑gigabyte files into memory.

### Where is the color theme for syntax highlighting defined?

The theme name is stored in `common.Theme.CodeSyntaxHighlightTheme` (defined in the style configuration) and is passed directly to `ansichroma.HightlightString`. When using Bat, the theme is controlled by Bat’s own configuration, though Superfile still manages the background color via `setBatBackground` to ensure consistency with the terminal UI.