No description
Find a file
Barrett Ruth 0a10f3882d
fix(init): prevent infinite filetype retry loop on did_filetype
Problem: the deferred filetype retry in ensure_cache created an infinite
invalidation loop. did_filetype() stays 1 for the lifetime of a buffer
opened via FileType autocmd — it never resets in vim.schedule callbacks.
Each retry reparsed, found has_nil_ft=true again, and scheduled another
retry. Each iteration cleared all extmarks via pending_clear, causing the
deferred syntax pass to bail on its tick check. Result: language syntax
highlighting never settled on buffers containing files with
function-handled extensions (.sh, .bash, .conf, etc).

Solution: add ft_retry_pending guard table. Set before scheduling the
retry, check before scheduling again. The flag is set while the callback
is pending and during the synchronous redraw inside it, preventing the
reparse from scheduling another iteration. Cleared after the callback
completes. Cleaned up on BufWipeout.
2026-03-04 13:44:57 -05:00
.github ci: migrate to nix 2026-02-23 18:14:05 -05:00
doc fix(conflict)!: change default nav keymaps from ]x/[x to ]c/[c (#132) 2026-02-24 12:07:54 -05:00
lua/diffs fix(init): prevent infinite filetype retry loop on did_filetype 2026-03-04 13:44:57 -05:00
plugin feat: add neogit support (#117) 2026-02-14 17:12:01 -05:00
spec fix: clear stale gutter extmarks after fugitive section toggle (#139) 2026-02-25 13:20:59 -05:00
.busted feat(test): testing infrastructure 2026-02-01 23:09:05 -05:00
.editorconfig feat: initial setup files 2026-02-01 16:30:24 -05:00
.gitignore ci: migrate to nix 2026-02-23 18:14:05 -05:00
.luarc.json ci: migrate to nix 2026-02-23 18:14:05 -05:00
.pre-commit-config.yaml fix: use yaml, not yml YAML file extension 2026-02-02 22:14:04 -05:00
.prettierignore feat: initial setup files 2026-02-01 16:30:24 -05:00
.prettierrc fix: use yaml, not yml YAML file extension 2026-02-02 22:14:04 -05:00
diffs.nvim-scm-1.rockspec feat: rename everything 2026-02-02 22:09:13 -05:00
flake.lock performance improvements (#116) 2026-02-12 16:59:13 -05:00
flake.nix build(flake): add lua-language-server to devShell 2026-02-23 17:35:22 -05:00
LICENSE feat: initial setup files 2026-02-01 16:30:24 -05:00
README.md fix(doc): improve q&a format 2026-02-21 23:02:39 -05:00
selene.toml ci: migrate to nix 2026-02-23 18:14:05 -05:00
stylua.toml feat: initial setup files 2026-02-01 16:30:24 -05:00
vim.yaml ci: add bit luajit global 2026-02-23 18:18:30 -05:00

diffs.nvim

Syntax highlighting for diffs in Neovim

Enhance vim-fugitive, Neogit, and Neovim's built-in diff mode with language-aware syntax highlighting.

Features

  • Treesitter syntax highlighting in vim-fugitive, Neogit, and diff filetype
  • Character-level intra-line diff highlighting (with optional vscode-diff FFI backend for word-level accuracy)
  • :Gdiff unified diff against any revision
  • Background-only diff colors for &diff buffers
  • Inline merge conflict detection, highlighting, and resolution
  • Vim syntax fallback, configurable blend/priorities

Requirements

  • Neovim 0.9.0+

Installation

Install with your package manager of choice or via luarocks:

luarocks install diffs.nvim

Documentation

:help diffs.nvim

FAQ

Q: How do I install with lazy.nvim?

{
  'barrettruth/diffs.nvim',
  init = function()
    vim.g.diffs = {
      ...
    }
  end,
}

Do not lazy load diffs.nvim with event, lazy, ft, config, or keys to control loading - diffs.nvim lazy-loads itself.

Q: Does diffs.nvim support vim-fugitive/Neogit?

Yes. Enable it in your config:

vim.g.diffs = {
  fugitive = true,
  neogit = true,
}

See the documentation for more information.

Known Limitations

  • Incomplete syntax context: Treesitter parses each diff hunk in isolation. Context lines within the hunk provide syntactic context for the parser. In rare cases, hunks that start or end mid-expression may produce imperfect highlights due to treesitter error recovery.

  • Syntax "flashing": diffs.nvim hooks into the FileType fugitive event triggered by vim-fugitive, at which point the buffer is preliminarily painted. The decoration provider applies highlights on the next redraw cycle, causing a brief visual "flash".

  • Cold Start: Treesitter grammar loading (~10ms) and query compilation (~4ms) are one-time costs per language per Neovim session. Each language pays this cost on first encounter, which may cause a brief stutter when a diff containing a new language first enters the viewport.

  • Vim syntax fallback is deferred: The vim syntax fallback (for languages without a treesitter parser) cannot run inside the decoration provider's redraw cycle due to Neovim's restriction on buffer mutations. Vim syntax highlights for these hunks appear slightly delayed.

  • Conflicting diff plugins: diffs.nvim may not interact well with other plugins that modify diff highlighting. Known plugins that may conflict:

    • diffview.nvim - provides its own diff highlighting and conflict resolution UI
    • mini.diff - visualizes buffer differences with its own highlighting system
    • gitsigns.nvim - generally compatible, but both plugins modifying line highlights may produce unexpected results
    • git-conflict.nvim - diffs.nvim now includes built-in conflict resolution; disable one or the other to avoid overlap

Acknowledgements