Skip to content

How to integrate dog

Learn how to integrate dog with interactive previewers such as fzf, tv, and yazi — along with less, shell pipelines, and tab completions. Make sure dog is installed and available on your PATH.

With fzf

fzf is the go-to fuzzy finder. Point its preview pane at dog and every file you scroll through renders highlighted.

Basic file preview

sh
fzf --preview 'dog --color=always {}'

The preview pane renders each file through dog in the default theme; exit with Esc.

The first flag, --color=always, forces ANSI colors through the pipe, while {} is fzf's placeholder for the selected filename.

With a specific theme

sh
fzf --preview 'dog --color=always --wrap=never --theme "Catppuccin Mocha" {}' --preview-window=noinfo

Pin any theme dog can resolve. See Theming for the list.

Prefer that theme everywhere? Save it as your default once and drop the --theme flag from preview commands.

Wrap behavior in previews

dog auto-detects fzf's preview pane and defaults to no-wrap, so long lines get cut off at the pane edge instead of double-wrapping. Prefer wrapped lines? Have dog wrap them itself at the pane width:

sh
fzf --preview 'dog --color=always --wrap=auto --terminal-width=$FZF_PREVIEW_COLUMNS {}' --preview-window=noinfo

--preview-window=noinfo hides fzf's scroll indicator, which counts wrapped rows rather than file lines. Skip fzf's own --preview-window=wrap — it wraps dog's finished output, so the wrapped rows lose their alignment with the line-number gutter.

Matching the preview width

dog also auto-detects the preview width, but you can be explicit if you'd like — pass --terminal-width with fzf's preview-width env var:

sh
fzf --preview 'dog --color=always --terminal-width=$FZF_PREVIEW_COLUMNS {}'

See Forcing a width for the general case.

Scoping to a file list

You can also pipe from other tools into fzf — find, ripgrep, git ls-files, etc. Here, rg lists the Swift files that mention TODO, and fzf lets you fuzzy-pick among them with a dog preview of each:

sh
rg -l --type swift 'TODO' | fzf --preview 'dog -p --color=always --wrap=never {}'

With tv

tv (television) is a TUI fuzzy finder driven by per-channel TOML configs.

One-shot preview

sh
tv files -p 'dog --color=always --wrap=never {}'

-p sets the preview command inline. The --color=always flag forces ANSI colors through the pipe, while --wrap=never truncates long lines at the pane edge instead of wrapping.

Set dog as your default for existing channels

Each tv channel is a TOML file in ~/.config/television/cable/. In the following example we set dog as the previewer for the files channel (the default tv channel):

toml
# ~/.config/television/cable/files.toml
[preview]
command = "dog --color=always '{}'"

TIP

Consult tv's docs for the exact schema — it changes between releases.

Wrap behavior

tv pages output inside a bordered pane. dog detects the pipe and defaults to no-wrap — long lines get truncated at the pane edge rather than double-wrapping.

TIP

If tv's pane width isn't autodetected correctly, pin it with --terminal-width. See Forcing a width for examples.

With yazi

yazi is a TUI file manager with a plugin system for custom previewers. Simple setup with the dog.yazi plugin.

Install the plugin

sh
ya pkg add edden27/dog
sh
git clone https://github.com/edden27/dog.yazi ~/.config/yazi/plugins/dog.yazi
sh
# Download main.lua from https://github.com/edden27/dog.yazi
# and place it at ~/.config/yazi/plugins/dog.yazi/main.lua

Register dog

Copy this into ~/.config/yazi/yazi.toml under [plugin] (optionally adjust it for specific files, etc.):

toml
[plugin]
prepend_previewers = [
  { mime = "text/*",              run = "dog" },
  { mime = "application/json",    run = "dog" },
  { mime = "application/x-yaml",  run = "dog" },
  { mime = "application/toml",    run = "dog" },
  { url = "*.swift",  run = "dog" },
  { url = "*.rs",     run = "dog" },
  { url = "*.go",     run = "dog" },
  { url = "*.py",     run = "dog" },
  { url = "*.js",     run = "dog" },
  { url = "*.mjs",    run = "dog" },
  { url = "*.cjs",    run = "dog" },
  { url = "*.ts",     run = "dog" },
  { url = "*.tsx",    run = "dog" },
  { url = "*.jsx",    run = "dog" },
  { url = "*.rb",     run = "dog" },
  { url = "*.lua",    run = "dog" },
  { url = "*.sh",     run = "dog" },
  { url = "*.bash",   run = "dog" },
  { url = "*.zsh",    run = "dog" },
  { url = "*.c",      run = "dog" },
  { url = "*.cpp",    run = "dog" },
  { url = "*.cc",     run = "dog" },
  { url = "*.h",      run = "dog" },
  { url = "*.hpp",    run = "dog" },
  { url = "*.css",    run = "dog" },
  { url = "*.html",   run = "dog" },
  { url = "*.htm",    run = "dog" },
  { url = "*.md",     run = "dog" },
  { url = "*.markdown", run = "dog" },
]

TIP

Mime rules match first; URL globs catch extensions where mime detection returns a non-text type for valid source.

Theme dog in yazi

You can set a theme for dog's previews in ~/.config/yazi/init.lua:

lua
require("dog"):setup({
  theme = "Catppuccin Mocha",
  theme_dir = "~/.config/zed/themes",  -- optional
})

TIP

Both fields are optional. Omit theme to use dog's default theme. Theme names with spaces and parens work as-is.

Technical details

  • First hover renders the whole file once through dog -P --color=always --wrap=never --terminal-width <W> and caches the ANSI output.
  • Subsequent scrolls slice the cache — no re-render.
  • Cache key includes pane width, theme, theme_dir, and dog --version, so resizing, changing theme, or upgrading dog invalidates stale renders.
  • If for some reason there is a failure, the preview pane shows dog failed (exit N): <stderr> instead of a blank.

Using with less (pager)

When dog defaults to less

When stdout is a TTY and output exceeds the terminal height, dog auto-pipes through less -R. No flag needed. This keeps long files from flooding your terminal's scrollback, and gives you scrolling and search (/) for free — press q to quit.

Disable the pager

If you prefer to see the full output printed in your terminal, you can disable the pager:

sh
dog -P large-file.rs
dog --paging=never large-file.rs

See the Configuration reference or run dog --help for more info on these flags.

Force the pager

If you prefer to have output in less even when it would not exceed the height of your terminal:

sh
dog --paging=always short-file.rs

Disable pager & print plain output

sh
dog -pp large-file.rs

A shortcut to -p and -P is -pp, which both skips the pager and outputs without line numbers/gutter.

If you want dog to wrap lines at terminal width before handing off to less -R (rather than letting less scroll horizontally), add --wrap=auto.

$PAGER is not honored

dog calls less -R directly. The $PAGER env var has no effect in 0.1. Track the open issue in the Configuration reference.

Shell pipes

Force color through a pipe

sh
dog --color=always --wrap=auto --terminal-width=$COLUMNS src/main.rs | head -40

Without --color=always, dog auto-detects the pipe and strips ANSI codes. | head, | less -R, | tee, and any other pipe target that understands ANSI need the explicit flag. --wrap=auto with --terminal-width=$COLUMNS has dog wrap long lines itself at your terminal's width, so they don't mess up the line numbers/gutter when your terminal wraps them — in a pipe, the wrap flag only engages when a width is given explicitly.

Strip decorations for downstream tooling

-p disables line numbers so piped output doesn't carry 1: 2: 3: prefixes. dog also defaults to no-wrap when piped, so one source line stays one pipe line for tools that parse line-by-line or try to determine current/total line counts.

Combine with find or fd

For a single file at an unknown location, you don't even need to use a pipe — $(...) hands find's result to dog as the file path:

sh
dog "$(find . -name 'Package.swift' -print -quit)"

dog takes one file at a time; -print -quit stops find at the first match, so dog gets exactly one path even when the name shows up again deeper in the tree — dependency checkouts love shipping their own Package.swift. The quotes keep paths with spaces in one piece.

For a set of files, pipe the paths through xargs — here, every Swift file that mentions TODO:

sh
find . -name '*.swift' -print0 | xargs -0 grep -l 'TODO' | xargs -I{} dog --color=always --wrap=never --terminal-width=$COLUMNS {}

find lists the paths, grep -l keeps only the ones that contain a match, and xargs -I{} runs dog once per surviving path — piping the paths straight into dog would render the list itself, not the files. The -print0/-0 pair hands the paths over NUL-separated, so a folder name with a space in it doesn't get split into two broken paths.

fd does the same with less typing — -e swift filters by extension:

sh
fd -e swift -0 | xargs -0 grep -l 'TODO' | xargs -I{} dog --color=always --wrap=never --terminal-width=$COLUMNS {}

Combine with ripgrep

Render every file that contains a match — here, every Swift file mentioning TODO:

sh
rg -l --type swift 'TODO' | xargs -I{} dog --color=always --wrap=never --terminal-width=$COLUMNS {}

rg -l lists just the paths of the matching files, --type swift scopes the search to Swift files, and xargs -I{} runs dog once per path — {} stands in for each filename, the same placeholder idea as fzf.

The long form below does the same thing through ripgrep's JSON output — a starting point for when you want match details (line numbers, match text) to filter on before rendering. It needs jq installed:

sh
rg --json --type swift 'TODO' | jq -r '.data.path.text // empty' | sort -u | while read -r f; do
  dog --color=always --wrap=never --terminal-width=$COLUMNS "$f"
done

--type swift scopes it to Swift files again, jq -r '.data.path.text // empty' pulls the file path out of each JSON event (// empty skips events that don't carry one), and sort -u dedupes so each file renders once.

Remap cat/bat to dog

sh
alias cat='dog'
alias bat='dog'

Drop into your shell rc. Your cat muscle memory keeps working — you just get dog's output. dog passes through stdin as plain text when no language is detected, so piping non-code (logs, JSON manifests, etc.) still works.

Forcing a width

Detection order

dog resolves terminal width in this order:

  1. --terminal-width N flag (explicit override)
  2. $FZF_PREVIEW_COLUMNS env var (only together with $FZF_PREVIEW_LINES — fzf sets both)
  3. ioctl(stdout)
  4. ioctl(stderr)
  5. Fallback: 80

dog does not read $COLUMNS — use --terminal-width when stdout isn't a TTY and you aren't inside fzf.

Forcing a width

If wrapping or truncation looks wrong, pass --terminal-width:

sh
dog --color=always --terminal-width=$COLUMNS file.rs | head -40

$COLUMNS is set by most interactive shells even though dog doesn't read it directly — passing it via --terminal-width is the idiomatic way to forward the shell's width into piped commands.

--terminal-width pairs with --wrap. With wrap on, the value sets where lines break. With wrap off, it sets where long lines get truncated.

sh
dog --wrap=auto --terminal-width=40 file.txt   # long lines break at column 40
dog --wrap=never --terminal-width=40 file.txt   # long lines cut off at column 40

Stdin and shebang detection

Piping code

sh
echo 'struct Woof { let barks: Int, goodBoy: Bool = true }' | dog -l swift

When reading stdin, dog has no filename to detect from. Pass -l with the language name or alias.

Shebang autodetection

If the stdin starts with a recognized shebang, dog detects the language without -l:

sh
# Preview the dog install script using dog
curl -fsSL https://sh.dog/install | dog

The shebang parser handles /usr/bin/env, version suffixes (python3.11python3), and flags (-S, -u). Supported interpreters are listed in Configuration → Detection cascade.

No detection, no shebang

If stdin has neither, dog prints the input as plain text. Pass -l to force highlighting.

Shell completions

dog generates its own tab-completion script for bash, zsh, and fish. dog -<Tab> shows every flag available, --theme <Tab> and --set-default-theme <Tab> fill in the themes actually installed in your themes directory (read live from disk, so a theme you drop in today shows up today), and -l <Tab> lists every supported language. The file and --theme-dir arguments complete paths.

If you installed with the install script or Homebrew, completion setup should be set up already or have prompted you during installation to complete it. Otherwise, read on to learn how to manually set them up based on your shell.

zsh

Write the script into a directory zsh loads completions from — the filename must be _dog:

sh
dog --generate-completion-script zsh | sudo tee /usr/local/share/zsh/site-functions/_dog > /dev/null

With oh-my-zsh, skip the sudo and use its own completions directory instead:

sh
dog --generate-completion-script zsh > ~/.oh-my-zsh/completions/_dog

If neither directory works for your setup, make one, generate the script into it, and add it to your fpath in ~/.zshrc above the compinit line:

sh
mkdir -p ~/.zsh/completion
dog --generate-completion-script zsh > ~/.zsh/completion/_dog
sh
# ~/.zshrc
fpath=(~/.zsh/completion $fpath)
autoload -Uz compinit
compinit

bash

Generate the script, then source it from ~/.bashrc (or ~/.bash_profile — on macOS, Terminal starts bash as a login shell and reads that one instead):

sh
mkdir -p ~/.bash_completions
dog --generate-completion-script bash > ~/.bash_completions/dog.bash
echo 'source ~/.bash_completions/dog.bash' >> ~/.bashrc

If you use the bash-completion package, drop the file into its completions directory instead ($(brew --prefix)/etc/bash_completion.d/ on Homebrew) and skip the source line. If you installed dog from Homebrew, this is handled for you already.

fish

One command — fish watches its completions directory and picks the script up in new shells automatically:

sh
dog --generate-completion-script fish > ~/.config/fish/completions/dog.fish

Advanced combination recipe for dog

A more advanced workflow involving fzf, dog, no pager, and a theme — set as a shell function so you can easily run fzdog in your terminal:

sh
fzdog() {
  fzf --preview 'dog --color=always --wrap=never --theme "Catppuccin Mocha" {}' --preview-window=noinfo
}

Now fzdog (or swap fzdog to whatever you prefer) works as a command anywhere in your shell.

Add --theme-dir if your themes live outside ~/.config/dog/themes:

sh
fzdog() {
  fzf --preview 'dog --color=always --wrap=never \
    --theme-dir ~/.config/zed/themes \
    --theme "Catppuccin Mocha" {}' --preview-window=noinfo
}