Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Gelio/difft.nvim

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

difft.nvim

A Neovim frontend for Difftastic.

Neovim Lua

Screen.Recording.2025-10-16.at.13.49.52.mov

Features

  • Parse and display difftastic output with full ANSI color support
  • Navigate between file changes with keybindings
  • Jump to changed files directly from diff view
  • Customizable window layouts (buffer, float, ivy-style)
  • File header customization support

Requirements

difft --color=always $left $right

Installation

lazy.nvim

return {
  "ahkohd/difft.nvim",
  config = function()
    require("difft").setup()
  end,
}

Quick Setup

--luacheck: globals Difft

return {
  "ahkohd/difft.nvim",
  keys = {
    {
      "<leader>d",
      function()
        if Difft.is_visible() then
          Difft.hide()
        else
          Difft.diff()
        end
      end,
      desc = "Toggle Difft",
    },
  },
  config = function()
    require("difft").setup({
      command = "jj diff --no-pager",  -- or "git diff"
      layout = "float",  -- nil (buffer), "float", or "ivy_taller"
    })
  end,
}

Example Setup

--luacheck: globals Difft

return {
  "ahkohd/difft.nvim",
  keys = {
    {
      "<leader>d",
      function()
        if Difft.is_visible() then
          Difft.hide()
        else
          Difft.diff()
        end
      end,
      desc = "Toggle Difft",
    },
  },
  config = function()
    require("difft").setup({
      layout = "ivy_taller",
      no_diff_message = "All clean! No changes detected.",
      loading_message = "Loading diff...",
      window = {
        number = false,
        relativenumber = false,
        border = "rounded",
      },
    --- Custom header content with webdev icons
      header = {
        content = function(filename, step, _language)
          local devicons = require("nvim-web-devicons")
          local basename = vim.fn.fnamemodify(filename, ":t")
          local icon, hl = devicons.get_icon(basename)

          -- Get the bg from FloatTitle (what DifftFileHeader links to)
          local header_hl = vim.api.nvim_get_hl(0, { name = "FloatTitle", link = false })

          -- Create custom highlight with devicon fg + header bg
          local icon_hl = hl
          if hl and header_hl.bg then
            local devicon_colors = vim.api.nvim_get_hl(0, { name = hl })
            if devicon_colors.fg then
              local custom_hl_name = "DifftIcon_" .. hl
              vim.api.nvim_set_hl(0, custom_hl_name, {
                fg = devicon_colors.fg,
                bg = header_hl.bg,
              })
              icon_hl = custom_hl_name
            end
          end

          local result = {}
          table.insert(result, { " " })
          table.insert(result, { icon and (icon .. " ") or "", icon_hl })
          table.insert(result, { filename })
          table.insert(result, { " " })

          if step then
            table.insert(result, { "" })
            table.insert(result, { tostring(step.current) })
            table.insert(result, { "/" })
            table.insert(result, { tostring(step.of) })
            table.insert(result, { " " })
          end

          return result
        end,
        highlight = {
          link = "FloatTitle",
          full_width = true,
        },
      },
    })
  end,
}

Configuration

Layout Options

layout = nil        -- Open in current buffer
layout = "float"    -- Centered floating window
layout = "ivy_taller" -- Bottom window (ivy-style)

Window Options

window = {
  width = 0.9,           -- Float window width (0-1)
  height = 0.8,          -- Float window height (0-1)
  title = " Difft ",     -- Window title
  number = false,        -- Show line numbers
  relativenumber = false,
  border = "rounded",    -- Border style: "none", "single", "double", "rounded", "solid", "shadow", or custom array
}

Keymaps

keymaps = {
  next = "<Down>",   -- Next file change
  prev = "<Up>",     -- Previous file change
  close = "q",       -- Close diff window (float only)
  refresh = "r",     -- Refresh diff
  first = "gg",      -- First file change
  last = "G",        -- Last file change
}

Jump Configuration

jump = {
  enabled = true,       -- Enable file jumping
  ["<CR>"] = "edit",    -- Open file in current window
  ["<C-v>"] = "vsplit", -- Open file in vertical split
  ["<C-x>"] = "split",  -- Open file in horizontal split
  ["<C-t>"] = "tabedit", -- Open file in new tab
}

Header Customization

Simple Header

header = {
  content = function(filename, step, language)
    if step then
      return string.format("[%d/%d] %s (%s)", step.current, step.of, filename, language)
    end
    return string.format("%s (%s)", filename, language)
  end,
  highlight = {
    link = "FloatTitle",
    full_width = true,
  },
}

Header with Icons

header = {
  content = function(filename, step, language)
    local devicons = require("nvim-web-devicons")
    local basename = vim.fn.fnamemodify(filename, ":t")
    local icon, hl = devicons.get_icon(basename)

    local result = {}
    table.insert(result, { " " })
    table.insert(result, { icon and (icon .. " ") or "", hl })
    table.insert(result, { filename })

    if step then
      table.insert(result, { "" })
      table.insert(result, { tostring(step.current) })
      table.insert(result, { "/" })
      table.insert(result, { tostring(step.of) })
    end

    return result
  end,
  highlight = {
    link = "FloatTitle",
    full_width = true,
  },
}

Header Highlight Options

-- Link to existing highlight group
highlight = {
  link = "FloatTitle",
  full_width = false,
}

-- Custom colors
highlight = {
  fg = "#ffffff",
  bg = "#5c6370",
  full_width = true,
}

-- Link colors separately
highlight = {
  fg = { link = "Statement" },
  bg = { link = "Visual" },
  full_width = false,
}

Other Options

command = "jj diff --no-pager"  -- Diff command to execute
auto_jump = true                 -- Jump to first change on open
no_diff_message = "No changes found"
loading_message = "Loading diff..."
refresh_on_resize = true         -- Auto-refresh on window resize

Usage

Basic Commands

-- Open diff
require("difft").diff()

-- Open with custom command
require("difft").diff({ cmd = "git diff" })

-- Close diff
require("difft").close()

-- Hide diff (float only, keeps buffer)
require("difft").hide()

-- Refresh current diff
require("difft").refresh()

-- Check if diff exists
if require("difft").exists() then
  -- ...
end

-- Check if diff is visible
if require("difft").is_visible() then
  -- ...
end

Global API

The plugin also exposes a global Difft table:

Difft.diff()
Difft.close()
Difft.hide()
Difft.refresh()
Difft.exists()
Difft.is_visible()

Example Keybinding

vim.keymap.set("n", "<leader>d", function()
  if Difft.is_visible() then
    Difft.hide()
  else
    Difft.diff()
  end
end, { desc = "Toggle difft" })

Navigation

When viewing a diff:

  • <Down> / <Up> - Navigate between file changes
  • gg / G - Jump to first/last change
  • <CR> - Open file at cursor (jump to changed line)
  • <C-v> / <C-x> / <C-t> - Open file in split/tab
  • r - Refresh diff
  • q - Close diff (floating windows only)

Highlight Groups

The plugin uses these highlight groups for diff content:

  • DiffAdd - Added lines
  • DiffDelete - Deleted lines
  • DiffChange - Changed lines
  • DifftFileHeader - File headers (customizable)
  • DifftFileHeaderBg - Header background for full-width mode

Testing

Run tests with:

nvim -l tests/run.lua

See tests/README.md for details.

About

A Neovim frontend for Difftastic

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Lua 100.0%