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
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
fzf --preview 'dog --color=always --wrap=never --theme "Catppuccin Mocha" {}' --preview-window=noinfoPin 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:
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:
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:
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
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):
# ~/.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
ya pkg add edden27/doggit clone https://github.com/edden27/dog.yazi ~/.config/yazi/plugins/dog.yazi# Download main.lua from https://github.com/edden27/dog.yazi
# and place it at ~/.config/yazi/plugins/dog.yazi/main.luaRegister dog
Copy this into ~/.config/yazi/yazi.toml under [plugin] (optionally adjust it for specific files, etc.):
[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:
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, anddog --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:
dog -P large-file.rs
dog --paging=never large-file.rsSee 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:
dog --paging=always short-file.rsDisable pager & print plain output
dog -pp large-file.rsA 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
dog --color=always --wrap=auto --terminal-width=$COLUMNS src/main.rs | head -40Without --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:
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:
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:
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:
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:
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
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:
--terminal-width Nflag (explicit override)$FZF_PREVIEW_COLUMNSenv var (only together with$FZF_PREVIEW_LINES— fzf sets both)ioctl(stdout)ioctl(stderr)- 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:
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.
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 40Stdin and shebang detection
Piping code
echo 'struct Woof { let barks: Int, goodBoy: Bool = true }' | dog -l swiftWhen 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:
# Preview the dog install script using dog
curl -fsSL https://sh.dog/install | dogThe shebang parser handles /usr/bin/env, version suffixes (python3.11 → python3), 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:
dog --generate-completion-script zsh | sudo tee /usr/local/share/zsh/site-functions/_dog > /dev/nullWith oh-my-zsh, skip the sudo and use its own completions directory instead:
dog --generate-completion-script zsh > ~/.oh-my-zsh/completions/_dogIf 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:
mkdir -p ~/.zsh/completion
dog --generate-completion-script zsh > ~/.zsh/completion/_dog# ~/.zshrc
fpath=(~/.zsh/completion $fpath)
autoload -Uz compinit
compinitbash
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):
mkdir -p ~/.bash_completions
dog --generate-completion-script bash > ~/.bash_completions/dog.bash
echo 'source ~/.bash_completions/dog.bash' >> ~/.bashrcIf 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:
dog --generate-completion-script fish > ~/.config/fish/completions/dog.fishAdvanced 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:
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:
fzdog() {
fzf --preview 'dog --color=always --wrap=never \
--theme-dir ~/.config/zed/themes \
--theme "Catppuccin Mocha" {}' --preview-window=noinfo
}