feat: add neogit support (#117)
## TODO 1. docs (vimdoc + readme) - this is a non-trivial feature 2. push luarocks version ## Problem diffs.nvim only activates on `fugitive`, `git`, and `gitcommit` filetypes. Neogit uses its own custom filetypes (`NeogitStatus`, `NeogitCommitView`, `NeogitDiffView`) and doesn't set `b:git_dir`, so the plugin never attaches and repo root resolution fails for filetype detection within diff hunks. ## Solution Two changes: 1. **`lua/diffs/init.lua`** — Add the three Neogit filetypes to the default `filetypes` list. The `FileType` autocmd in `plugin/diffs.lua` already handles them correctly since the `is_fugitive_buffer` guard only applies to the `git` filetype. 2. **`lua/diffs/parser.lua`** — Add a CWD-based fallback in `get_repo_root()`. After the existing `b:diffs_repo_root` and `b:git_dir` checks, fall back to `vim.fn.getcwd()` via `git.get_repo_root()` (already cached). Without this, the parser can't resolve filetypes for files in Neogit buffers. Neogit's expanded diffs use standard unified diff format, so the parser handles them without modification. Closes #110.
This commit is contained in:
parent
5d3bbc3631
commit
3d640c207b
8 changed files with 314 additions and 56 deletions
22
README.md
22
README.md
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
**Syntax highlighting for diffs in Neovim**
|
**Syntax highlighting for diffs in Neovim**
|
||||||
|
|
||||||
Enhance `vim-fugitive` and Neovim's built-in diff mode with language-aware
|
Enhance [vim-fugitive](https://github.com/tpope/vim-fugitive),
|
||||||
syntax highlighting.
|
[Neogit](https://github.com/NeogitOrg/neogit), and Neovim's built-in diff mode
|
||||||
|
with language-aware syntax highlighting.
|
||||||
|
|
||||||
<video src="https://github.com/user-attachments/assets/24574916-ecb2-478e-a0ea-e4cdc971e310" width="100%" controls></video>
|
<video src="https://github.com/user-attachments/assets/24574916-ecb2-478e-a0ea-e4cdc971e310" width="100%" controls></video>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Treesitter syntax highlighting in fugitive diffs and commit views
|
- Treesitter syntax highlighting in vim-fugitive, Neogit, and `diff` filetype
|
||||||
- Character-level intra-line diff highlighting (with optional
|
- Character-level intra-line diff highlighting (with optional
|
||||||
[vscode-diff](https://github.com/esmuellert/codediff.nvim) FFI backend for
|
[vscode-diff](https://github.com/esmuellert/codediff.nvim) FFI backend for
|
||||||
word-level accuracy)
|
word-level accuracy)
|
||||||
|
|
@ -37,6 +38,21 @@ luarocks install diffs.nvim
|
||||||
:help diffs.nvim
|
:help diffs.nvim
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**Does diffs.nvim support vim-fugitive/Neogit?**
|
||||||
|
|
||||||
|
Yes. Enable it in your config:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
vim.g.diffs = {
|
||||||
|
fugitive = true,
|
||||||
|
neogit = true,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the documentation for more information.
|
||||||
|
|
||||||
## Known Limitations
|
## Known Limitations
|
||||||
|
|
||||||
- **Incomplete syntax context**: Treesitter parses each diff hunk in isolation.
|
- **Incomplete syntax context**: Treesitter parses each diff hunk in isolation.
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ License: MIT
|
||||||
INTRODUCTION *diffs.nvim*
|
INTRODUCTION *diffs.nvim*
|
||||||
|
|
||||||
diffs.nvim adds syntax highlighting to diff views. It overlays language-aware
|
diffs.nvim adds syntax highlighting to diff views. It overlays language-aware
|
||||||
highlights on top of default diff highlighting in vim-fugitive and Neovim's
|
highlights on top of default diff highlighting in vim-fugitive, Neogit, and
|
||||||
built-in diff mode.
|
Neovim's built-in diff mode.
|
||||||
|
|
||||||
Features: ~
|
Features: ~
|
||||||
- Syntax highlighting in |:Git| summary diffs and commit detail views
|
- Syntax highlighting in |:Git| summary diffs and commit detail views
|
||||||
|
|
@ -53,7 +53,9 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
||||||
vim.g.diffs = {
|
vim.g.diffs = {
|
||||||
debug = false,
|
debug = false,
|
||||||
hide_prefix = false,
|
hide_prefix = false,
|
||||||
filetypes = { 'fugitive', 'git', 'gitcommit' },
|
fugitive = false,
|
||||||
|
neogit = false,
|
||||||
|
extra_filetypes = {},
|
||||||
highlights = {
|
highlights = {
|
||||||
background = true,
|
background = true,
|
||||||
gutter = true,
|
gutter = true,
|
||||||
|
|
@ -83,10 +85,6 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
||||||
},
|
},
|
||||||
overrides = {},
|
overrides = {},
|
||||||
},
|
},
|
||||||
fugitive = {
|
|
||||||
horizontal = 'du',
|
|
||||||
vertical = 'dU',
|
|
||||||
},
|
|
||||||
conflict = {
|
conflict = {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
disable_diagnostics = true,
|
disable_diagnostics = true,
|
||||||
|
|
@ -116,14 +114,36 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
||||||
is also enabled, the overlay inherits the line's
|
is also enabled, the overlay inherits the line's
|
||||||
background color.
|
background color.
|
||||||
|
|
||||||
{filetypes} (table, default: {'fugitive','git','gitcommit'})
|
{fugitive} (boolean|table, default: false)
|
||||||
List of filetypes that trigger attachment. Add
|
Enable vim-fugitive integration. Accepts
|
||||||
`'diff'` to enable highlighting in plain `.diff`
|
`true`, `false`, or a table with sub-options
|
||||||
and `.patch` files: >lua
|
(see |diffs.FugitiveConfig|). When enabled,
|
||||||
|
the `fugitive` filetype is active and status
|
||||||
|
buffer keymaps are registered. >lua
|
||||||
|
vim.g.diffs = { fugitive = true }
|
||||||
vim.g.diffs = {
|
vim.g.diffs = {
|
||||||
filetypes = {
|
fugitive = { horizontal = 'dd' },
|
||||||
'fugitive', 'git', 'gitcommit', 'diff',
|
}
|
||||||
},
|
<
|
||||||
|
|
||||||
|
{neogit} (boolean|table, default: false)
|
||||||
|
Enable Neogit integration. Accepts `true`,
|
||||||
|
`false`, or `{ enabled = false }`. When
|
||||||
|
enabled, `NeogitStatus`, `NeogitCommitView`,
|
||||||
|
and `NeogitDiffView` filetypes are active and
|
||||||
|
Neogit highlight overrides are applied. See
|
||||||
|
|diffs-neogit|. >lua
|
||||||
|
vim.g.diffs = { neogit = false }
|
||||||
|
<
|
||||||
|
|
||||||
|
{extra_filetypes} (table, default: {})
|
||||||
|
Additional filetypes to attach to, beyond the
|
||||||
|
built-in `git`, `gitcommit`, and any enabled
|
||||||
|
integration filetypes. Use this to enable
|
||||||
|
highlighting in plain `.diff` / `.patch`
|
||||||
|
files: >lua
|
||||||
|
vim.g.diffs = {
|
||||||
|
extra_filetypes = { 'diff' },
|
||||||
}
|
}
|
||||||
<
|
<
|
||||||
|
|
||||||
|
|
@ -131,10 +151,6 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
||||||
Controls which highlight features are enabled.
|
Controls which highlight features are enabled.
|
||||||
See |diffs.Highlights| for fields.
|
See |diffs.Highlights| for fields.
|
||||||
|
|
||||||
{fugitive} (table, default: see below)
|
|
||||||
Fugitive status buffer keymap options.
|
|
||||||
See |diffs.FugitiveConfig| for fields.
|
|
||||||
|
|
||||||
{conflict} (table, default: see below)
|
{conflict} (table, default: see below)
|
||||||
Inline merge conflict resolution options.
|
Inline merge conflict resolution options.
|
||||||
See |diffs.ConflictConfig| for fields.
|
See |diffs.ConflictConfig| for fields.
|
||||||
|
|
@ -422,12 +438,20 @@ Configuration: ~
|
||||||
>lua
|
>lua
|
||||||
vim.g.diffs = {
|
vim.g.diffs = {
|
||||||
fugitive = {
|
fugitive = {
|
||||||
|
enabled = true, -- false to disable fugitive integration entirely
|
||||||
horizontal = 'du', -- keymap for horizontal split, false to disable
|
horizontal = 'du', -- keymap for horizontal split, false to disable
|
||||||
vertical = 'dU', -- keymap for vertical split, false to disable
|
vertical = 'dU', -- keymap for vertical split, false to disable
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
<
|
<
|
||||||
Fields: ~
|
Fields: ~
|
||||||
|
{enabled} (boolean, default: false)
|
||||||
|
Enable fugitive integration. When false, the
|
||||||
|
`fugitive` filetype is excluded and no status
|
||||||
|
buffer keymaps are registered. Shorthand:
|
||||||
|
`fugitive = false` is equivalent to
|
||||||
|
`fugitive = { enabled = false }`.
|
||||||
|
|
||||||
{horizontal} (string|false, default: 'du')
|
{horizontal} (string|false, default: 'du')
|
||||||
Keymap for unified diff in horizontal split.
|
Keymap for unified diff in horizontal split.
|
||||||
Set to `false` to disable.
|
Set to `false` to disable.
|
||||||
|
|
@ -579,6 +603,31 @@ The working file buffer is modified in place; save it when ready.
|
||||||
Phase 1 inline conflict highlights (see |diffs-conflict|) are refreshed
|
Phase 1 inline conflict highlights (see |diffs-conflict|) are refreshed
|
||||||
automatically after each resolution.
|
automatically after each resolution.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
NEOGIT *diffs-neogit*
|
||||||
|
|
||||||
|
diffs.nvim works with Neogit (https://github.com/NeogitOrg/neogit) out of
|
||||||
|
the box. Enable Neogit support in your config: >lua
|
||||||
|
vim.g.diffs = { neogit = true }
|
||||||
|
<
|
||||||
|
|
||||||
|
When a diff is expanded in a Neogit buffer (e.g., via TAB on a file in the
|
||||||
|
status view), diffs.nvim applies treesitter syntax highlighting and
|
||||||
|
intra-line diffs to the hunk lines, just as it does for fugitive.
|
||||||
|
|
||||||
|
Neogit highlight overrides: ~
|
||||||
|
On first attach to a Neogit buffer, diffs.nvim overrides Neogit's diff
|
||||||
|
highlight groups (`NeogitDiffAdd*`, `NeogitDiffDelete*`,
|
||||||
|
`NeogitDiffContext*`, `NeogitHunkHeader*`, `NeogitDiffHeader*`, etc.) by
|
||||||
|
setting them to empty (`{}`). This gives diffs.nvim sole control of diff
|
||||||
|
line visuals. The overrides are reapplied on `ColorScheme` since Neogit
|
||||||
|
re-defines its groups then. When `neogit = false`, no highlight overrides
|
||||||
|
are applied.
|
||||||
|
|
||||||
|
Deprecated: ~
|
||||||
|
The `filetypes` config key still works but is deprecated and will be
|
||||||
|
removed in 0.3.0. Use `fugitive`, `neogit`, and `extra_filetypes` instead.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
API *diffs-api*
|
API *diffs-api*
|
||||||
|
|
||||||
|
|
@ -600,7 +649,7 @@ refresh({bufnr}) *diffs.refresh()*
|
||||||
IMPLEMENTATION *diffs-implementation*
|
IMPLEMENTATION *diffs-implementation*
|
||||||
|
|
||||||
Summary / commit detail views: ~
|
Summary / commit detail views: ~
|
||||||
1. `FileType` autocmd for configured filetypes (see {filetypes}) triggers
|
1. `FileType` autocmd for computed filetypes (see |diffs-config|) triggers
|
||||||
|diffs.attach()|. For `git` buffers, only `fugitive://` URIs are attached.
|
|diffs.attach()|. For `git` buffers, only `fugitive://` URIs are attached.
|
||||||
2. The buffer is parsed to detect file headers (`M path/to/file`,
|
2. The buffer is parsed to detect file headers (`M path/to/file`,
|
||||||
`diff --git a/... b/...`) and hunk headers (`@@ -10,3 +10,4 @@`)
|
`diff --git a/... b/...`) and hunk headers (`@@ -10,3 +10,4 @@`)
|
||||||
|
|
|
||||||
|
|
@ -443,18 +443,11 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts.highlights.background and is_diff_line then
|
if opts.highlights.background and is_diff_line then
|
||||||
pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, buf_line, 0, {
|
local ext = { line_hl_group = line_hl, priority = p.line_bg }
|
||||||
end_row = buf_line + 1,
|
|
||||||
hl_group = line_hl,
|
|
||||||
hl_eol = true,
|
|
||||||
priority = p.line_bg,
|
|
||||||
})
|
|
||||||
if opts.highlights.gutter then
|
if opts.highlights.gutter then
|
||||||
pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, buf_line, 0, {
|
ext.number_hl_group = number_hl
|
||||||
number_hl_group = number_hl,
|
|
||||||
priority = p.line_bg,
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, buf_line, 0, ext)
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_marker and line_len > pw then
|
if is_marker and line_len > pw then
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,13 @@
|
||||||
---@field priorities diffs.PrioritiesConfig
|
---@field priorities diffs.PrioritiesConfig
|
||||||
|
|
||||||
---@class diffs.FugitiveConfig
|
---@class diffs.FugitiveConfig
|
||||||
|
---@field enabled boolean
|
||||||
---@field horizontal string|false
|
---@field horizontal string|false
|
||||||
---@field vertical string|false
|
---@field vertical string|false
|
||||||
|
|
||||||
|
---@class diffs.NeogitConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
|
||||||
---@class diffs.ConflictKeymaps
|
---@class diffs.ConflictKeymaps
|
||||||
---@field ours string|false
|
---@field ours string|false
|
||||||
---@field theirs string|false
|
---@field theirs string|false
|
||||||
|
|
@ -56,9 +60,11 @@
|
||||||
---@class diffs.Config
|
---@class diffs.Config
|
||||||
---@field debug boolean|string
|
---@field debug boolean|string
|
||||||
---@field hide_prefix boolean
|
---@field hide_prefix boolean
|
||||||
---@field filetypes string[]
|
---@field filetypes? string[] @deprecated use fugitive, neogit, extra_filetypes
|
||||||
|
---@field extra_filetypes string[]
|
||||||
---@field highlights diffs.Highlights
|
---@field highlights diffs.Highlights
|
||||||
---@field fugitive diffs.FugitiveConfig
|
---@field fugitive diffs.FugitiveConfig
|
||||||
|
---@field neogit diffs.NeogitConfig
|
||||||
---@field conflict diffs.ConflictConfig
|
---@field conflict diffs.ConflictConfig
|
||||||
|
|
||||||
---@class diffs
|
---@class diffs
|
||||||
|
|
@ -108,7 +114,7 @@ end
|
||||||
local default_config = {
|
local default_config = {
|
||||||
debug = false,
|
debug = false,
|
||||||
hide_prefix = false,
|
hide_prefix = false,
|
||||||
filetypes = { 'fugitive', 'git', 'gitcommit' },
|
extra_filetypes = {},
|
||||||
highlights = {
|
highlights = {
|
||||||
background = true,
|
background = true,
|
||||||
gutter = true,
|
gutter = true,
|
||||||
|
|
@ -137,9 +143,13 @@ local default_config = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fugitive = {
|
fugitive = {
|
||||||
|
enabled = false,
|
||||||
horizontal = 'du',
|
horizontal = 'du',
|
||||||
vertical = 'dU',
|
vertical = 'dU',
|
||||||
},
|
},
|
||||||
|
neogit = {
|
||||||
|
enabled = false,
|
||||||
|
},
|
||||||
conflict = {
|
conflict = {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
disable_diagnostics = true,
|
disable_diagnostics = true,
|
||||||
|
|
@ -188,6 +198,31 @@ function M.is_fugitive_buffer(bufnr)
|
||||||
return vim.api.nvim_buf_get_name(bufnr):match('^fugitive://') ~= nil
|
return vim.api.nvim_buf_get_name(bufnr):match('^fugitive://') ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param opts table
|
||||||
|
---@return string[]
|
||||||
|
function M.compute_filetypes(opts)
|
||||||
|
if opts.filetypes then
|
||||||
|
return opts.filetypes
|
||||||
|
end
|
||||||
|
local fts = { 'git', 'gitcommit' }
|
||||||
|
local fug = opts.fugitive
|
||||||
|
if fug == true or (type(fug) == 'table' and fug.enabled ~= false) then
|
||||||
|
table.insert(fts, 'fugitive')
|
||||||
|
end
|
||||||
|
local neo = opts.neogit
|
||||||
|
if neo == true or (type(neo) == 'table' and neo.enabled ~= false) then
|
||||||
|
table.insert(fts, 'NeogitStatus')
|
||||||
|
table.insert(fts, 'NeogitCommitView')
|
||||||
|
table.insert(fts, 'NeogitDiffView')
|
||||||
|
end
|
||||||
|
if type(opts.extra_filetypes) == 'table' then
|
||||||
|
for _, ft in ipairs(opts.extra_filetypes) do
|
||||||
|
table.insert(fts, ft)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return fts
|
||||||
|
end
|
||||||
|
|
||||||
local dbg = log.dbg
|
local dbg = log.dbg
|
||||||
|
|
||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
|
|
@ -387,6 +422,34 @@ local function compute_highlight_groups()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local neogit_attached = false
|
||||||
|
|
||||||
|
local neogit_hl_groups = {
|
||||||
|
'NeogitDiffAdd',
|
||||||
|
'NeogitDiffAddCursor',
|
||||||
|
'NeogitDiffAddHighlight',
|
||||||
|
'NeogitDiffDelete',
|
||||||
|
'NeogitDiffDeleteCursor',
|
||||||
|
'NeogitDiffDeleteHighlight',
|
||||||
|
'NeogitDiffContext',
|
||||||
|
'NeogitDiffContextCursor',
|
||||||
|
'NeogitDiffContextHighlight',
|
||||||
|
'NeogitDiffHeader',
|
||||||
|
'NeogitDiffHeaderHighlight',
|
||||||
|
'NeogitHunkHeader',
|
||||||
|
'NeogitHunkHeaderCursor',
|
||||||
|
'NeogitHunkHeaderHighlight',
|
||||||
|
'NeogitHunkMergeHeader',
|
||||||
|
'NeogitHunkMergeHeaderCursor',
|
||||||
|
'NeogitHunkMergeHeaderHighlight',
|
||||||
|
}
|
||||||
|
|
||||||
|
local function override_neogit_highlights()
|
||||||
|
for _, name in ipairs(neogit_hl_groups) do
|
||||||
|
vim.api.nvim_set_hl(0, name, {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function init()
|
local function init()
|
||||||
if initialized then
|
if initialized then
|
||||||
return
|
return
|
||||||
|
|
@ -395,6 +458,35 @@ local function init()
|
||||||
|
|
||||||
local opts = vim.g.diffs or {}
|
local opts = vim.g.diffs or {}
|
||||||
|
|
||||||
|
if opts.filetypes then
|
||||||
|
vim.deprecate(
|
||||||
|
'vim.g.diffs.filetypes',
|
||||||
|
'fugitive, neogit, and extra_filetypes',
|
||||||
|
'0.3.0',
|
||||||
|
'diffs.nvim'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.fugitive == true then
|
||||||
|
opts.fugitive = { enabled = true }
|
||||||
|
elseif opts.fugitive == false then
|
||||||
|
opts.fugitive = { enabled = false }
|
||||||
|
elseif opts.fugitive == nil then
|
||||||
|
opts.fugitive = nil
|
||||||
|
elseif type(opts.fugitive) == 'table' and opts.fugitive.enabled == nil then
|
||||||
|
opts.fugitive.enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.neogit == true then
|
||||||
|
opts.neogit = { enabled = true }
|
||||||
|
elseif opts.neogit == false then
|
||||||
|
opts.neogit = { enabled = false }
|
||||||
|
elseif opts.neogit == nil then
|
||||||
|
opts.neogit = nil
|
||||||
|
elseif type(opts.neogit) == 'table' and opts.neogit.enabled == nil then
|
||||||
|
opts.neogit.enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
vim.validate({
|
vim.validate({
|
||||||
debug = {
|
debug = {
|
||||||
opts.debug,
|
opts.debug,
|
||||||
|
|
@ -404,7 +496,9 @@ local function init()
|
||||||
'boolean or string (file path)',
|
'boolean or string (file path)',
|
||||||
},
|
},
|
||||||
hide_prefix = { opts.hide_prefix, 'boolean', true },
|
hide_prefix = { opts.hide_prefix, 'boolean', true },
|
||||||
filetypes = { opts.filetypes, 'table', true },
|
fugitive = { opts.fugitive, 'table', true },
|
||||||
|
neogit = { opts.neogit, 'table', true },
|
||||||
|
extra_filetypes = { opts.extra_filetypes, 'table', true },
|
||||||
highlights = { opts.highlights, 'table', true },
|
highlights = { opts.highlights, 'table', true },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -472,23 +566,30 @@ local function init()
|
||||||
|
|
||||||
if opts.fugitive then
|
if opts.fugitive then
|
||||||
vim.validate({
|
vim.validate({
|
||||||
|
['fugitive.enabled'] = { opts.fugitive.enabled, 'boolean', true },
|
||||||
['fugitive.horizontal'] = {
|
['fugitive.horizontal'] = {
|
||||||
opts.fugitive.horizontal,
|
opts.fugitive.horizontal,
|
||||||
function(v)
|
function(v)
|
||||||
return v == false or type(v) == 'string'
|
return v == nil or v == false or type(v) == 'string'
|
||||||
end,
|
end,
|
||||||
'string or false',
|
'string or false',
|
||||||
},
|
},
|
||||||
['fugitive.vertical'] = {
|
['fugitive.vertical'] = {
|
||||||
opts.fugitive.vertical,
|
opts.fugitive.vertical,
|
||||||
function(v)
|
function(v)
|
||||||
return v == false or type(v) == 'string'
|
return v == nil or v == false or type(v) == 'string'
|
||||||
end,
|
end,
|
||||||
'string or false',
|
'string or false',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if opts.neogit then
|
||||||
|
vim.validate({
|
||||||
|
['neogit.enabled'] = { opts.neogit.enabled, 'boolean', true },
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
if opts.conflict then
|
if opts.conflict then
|
||||||
vim.validate({
|
vim.validate({
|
||||||
['conflict.enabled'] = { opts.conflict.enabled, 'boolean', true },
|
['conflict.enabled'] = { opts.conflict.enabled, 'boolean', true },
|
||||||
|
|
@ -583,6 +684,9 @@ local function init()
|
||||||
vim.api.nvim_create_autocmd('ColorScheme', {
|
vim.api.nvim_create_autocmd('ColorScheme', {
|
||||||
callback = function()
|
callback = function()
|
||||||
compute_highlight_groups()
|
compute_highlight_groups()
|
||||||
|
if neogit_attached then
|
||||||
|
vim.schedule(override_neogit_highlights)
|
||||||
|
end
|
||||||
for bufnr, _ in pairs(attached_buffers) do
|
for bufnr, _ in pairs(attached_buffers) do
|
||||||
invalidate_cache(bufnr)
|
invalidate_cache(bufnr)
|
||||||
end
|
end
|
||||||
|
|
@ -701,6 +805,11 @@ function M.attach(bufnr)
|
||||||
end
|
end
|
||||||
attached_buffers[bufnr] = true
|
attached_buffers[bufnr] = true
|
||||||
|
|
||||||
|
if not neogit_attached and config.neogit.enabled and vim.bo[bufnr].filetype:match('^Neogit') then
|
||||||
|
neogit_attached = true
|
||||||
|
vim.schedule(override_neogit_highlights)
|
||||||
|
end
|
||||||
|
|
||||||
dbg('attaching to buffer %d', bufnr)
|
dbg('attaching to buffer %d', bufnr)
|
||||||
|
|
||||||
ensure_cache(bufnr)
|
ensure_cache(bufnr)
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,9 @@ local function get_repo_root(bufnr)
|
||||||
return vim.fn.fnamemodify(git_dir, ':h')
|
return vim.fn.fnamemodify(git_dir, ':h')
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
local cwd = vim.fn.getcwd()
|
||||||
|
local git = require('diffs.git')
|
||||||
|
return git.get_repo_root(cwd .. '/.')
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
|
|
@ -194,7 +196,13 @@ function M.parse_buffer(bufnr)
|
||||||
|
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
local diff_git_file = line:match('^diff %-%-git a/.+ b/(.+)$')
|
local diff_git_file = line:match('^diff %-%-git a/.+ b/(.+)$')
|
||||||
local filename = line:match('^[MADRCU%?!]%s+(.+)$') or diff_git_file
|
local neogit_file = line:match('^modified%s+(.+)$')
|
||||||
|
or line:match('^new file%s+(.+)$')
|
||||||
|
or line:match('^deleted%s+(.+)$')
|
||||||
|
or line:match('^renamed%s+(.+)$')
|
||||||
|
or line:match('^copied%s+(.+)$')
|
||||||
|
local bare_file = not hunk_start and line:match('^([^%s]+%.[^%s]+)$')
|
||||||
|
local filename = line:match('^[MADRCU%?!]%s+(.+)$') or diff_git_file or neogit_file or bare_file
|
||||||
if filename then
|
if filename then
|
||||||
is_unified_diff = diff_git_file ~= nil
|
is_unified_diff = diff_git_file ~= nil
|
||||||
flush_hunk()
|
flush_hunk()
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ vim.g.loaded_diffs = 1
|
||||||
require('diffs.commands').setup()
|
require('diffs.commands').setup()
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd('FileType', {
|
vim.api.nvim_create_autocmd('FileType', {
|
||||||
pattern = (vim.g.diffs or {}).filetypes or { 'fugitive', 'git', 'gitcommit' },
|
pattern = require('diffs').compute_filetypes(vim.g.diffs or {}),
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
local diffs = require('diffs')
|
local diffs = require('diffs')
|
||||||
if args.match == 'git' and not diffs.is_fugitive_buffer(args.buf) then
|
if args.match == 'git' and not diffs.is_fugitive_buffer(args.buf) then
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ describe('highlight', function()
|
||||||
local extmarks = get_extmarks(bufnr)
|
local extmarks = get_extmarks(bufnr)
|
||||||
local has_diff_add = false
|
local has_diff_add = false
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
if mark[4] and mark[4].hl_group == 'DiffsAdd' then
|
if mark[4] and mark[4].line_hl_group == 'DiffsAdd' then
|
||||||
has_diff_add = true
|
has_diff_add = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
@ -320,7 +320,7 @@ describe('highlight', function()
|
||||||
local extmarks = get_extmarks(bufnr)
|
local extmarks = get_extmarks(bufnr)
|
||||||
local has_diff_delete = false
|
local has_diff_delete = false
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
if mark[4] and mark[4].hl_group == 'DiffsDelete' then
|
if mark[4] and mark[4].line_hl_group == 'DiffsDelete' then
|
||||||
has_diff_delete = true
|
has_diff_delete = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
@ -386,7 +386,7 @@ describe('highlight', function()
|
||||||
local extmarks = get_extmarks(bufnr)
|
local extmarks = get_extmarks(bufnr)
|
||||||
local has_diff_add = false
|
local has_diff_add = false
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
if mark[4] and mark[4].hl_group == 'DiffsAdd' then
|
if mark[4] and mark[4].line_hl_group == 'DiffsAdd' then
|
||||||
has_diff_add = true
|
has_diff_add = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
@ -500,7 +500,7 @@ describe('highlight', function()
|
||||||
local extmarks = get_extmarks(bufnr)
|
local extmarks = get_extmarks(bufnr)
|
||||||
local has_diff_add = false
|
local has_diff_add = false
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
if mark[4] and mark[4].hl_group == 'DiffsAdd' then
|
if mark[4] and mark[4].line_hl_group == 'DiffsAdd' then
|
||||||
has_diff_add = true
|
has_diff_add = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
@ -560,7 +560,7 @@ describe('highlight', function()
|
||||||
delete_buffer(bufnr)
|
delete_buffer(bufnr)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('uses hl_group not line_hl_group for line backgrounds', function()
|
it('uses line_hl_group for line backgrounds', function()
|
||||||
local bufnr = create_buffer({
|
local bufnr = create_buffer({
|
||||||
'@@ -1,2 +1,1 @@',
|
'@@ -1,2 +1,1 @@',
|
||||||
'-local x = 1',
|
'-local x = 1',
|
||||||
|
|
@ -582,17 +582,19 @@ describe('highlight', function()
|
||||||
)
|
)
|
||||||
|
|
||||||
local extmarks = get_extmarks(bufnr)
|
local extmarks = get_extmarks(bufnr)
|
||||||
|
local found = false
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
local d = mark[4]
|
local d = mark[4]
|
||||||
if d and (d.hl_group == 'DiffsAdd' or d.hl_group == 'DiffsDelete') then
|
if d and (d.line_hl_group == 'DiffsAdd' or d.line_hl_group == 'DiffsDelete') then
|
||||||
assert.is_true(d.hl_eol == true)
|
found = true
|
||||||
assert.is_nil(d.line_hl_group)
|
assert.is_nil(d.hl_eol)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
assert.is_true(found)
|
||||||
delete_buffer(bufnr)
|
delete_buffer(bufnr)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('hl_eol background extmarks are multiline so hl_eol takes effect', function()
|
it('line_hl_group background extmarks are single-line', function()
|
||||||
local bufnr = create_buffer({
|
local bufnr = create_buffer({
|
||||||
'@@ -1,2 +1,1 @@',
|
'@@ -1,2 +1,1 @@',
|
||||||
'-local x = 1',
|
'-local x = 1',
|
||||||
|
|
@ -616,8 +618,8 @@ describe('highlight', function()
|
||||||
local extmarks = get_extmarks(bufnr)
|
local extmarks = get_extmarks(bufnr)
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
local d = mark[4]
|
local d = mark[4]
|
||||||
if d and (d.hl_group == 'DiffsAdd' or d.hl_group == 'DiffsDelete') then
|
if d and (d.line_hl_group == 'DiffsAdd' or d.line_hl_group == 'DiffsDelete') then
|
||||||
assert.is_true(d.end_row > mark[2])
|
assert.is_nil(d.end_row)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
delete_buffer(bufnr)
|
delete_buffer(bufnr)
|
||||||
|
|
@ -771,7 +773,7 @@ describe('highlight', function()
|
||||||
if d then
|
if d then
|
||||||
if d.hl_group == 'DiffsClear' then
|
if d.hl_group == 'DiffsClear' then
|
||||||
table.insert(priorities.clear, d.priority)
|
table.insert(priorities.clear, d.priority)
|
||||||
elseif d.hl_group == 'DiffsAdd' or d.hl_group == 'DiffsDelete' then
|
elseif d.line_hl_group == 'DiffsAdd' or d.line_hl_group == 'DiffsDelete' then
|
||||||
table.insert(priorities.line_bg, d.priority)
|
table.insert(priorities.line_bg, d.priority)
|
||||||
elseif d.hl_group == 'DiffsAddText' or d.hl_group == 'DiffsDeleteText' then
|
elseif d.hl_group == 'DiffsAddText' or d.hl_group == 'DiffsDeleteText' then
|
||||||
table.insert(priorities.char_bg, d.priority)
|
table.insert(priorities.char_bg, d.priority)
|
||||||
|
|
@ -871,8 +873,8 @@ describe('highlight', function()
|
||||||
local extmarks = get_extmarks(bufnr)
|
local extmarks = get_extmarks(bufnr)
|
||||||
local line_bgs = {}
|
local line_bgs = {}
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
if mark[4] and mark[4].hl_eol then
|
if mark[4] and mark[4].line_hl_group then
|
||||||
line_bgs[mark[2]] = mark[4].hl_group
|
line_bgs[mark[2]] = mark[4].line_hl_group
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert.is_nil(line_bgs[1])
|
assert.is_nil(line_bgs[1])
|
||||||
|
|
@ -1063,8 +1065,8 @@ describe('highlight', function()
|
||||||
local marker_text = {}
|
local marker_text = {}
|
||||||
for _, mark in ipairs(extmarks) do
|
for _, mark in ipairs(extmarks) do
|
||||||
local d = mark[4]
|
local d = mark[4]
|
||||||
if d and d.hl_eol then
|
if d and d.line_hl_group then
|
||||||
line_bgs[mark[2]] = d.hl_group
|
line_bgs[mark[2]] = d.line_hl_group
|
||||||
end
|
end
|
||||||
if d and d.number_hl_group then
|
if d and d.number_hl_group then
|
||||||
gutter_hls[mark[2]] = d.number_hl_group
|
gutter_hls[mark[2]] = d.number_hl_group
|
||||||
|
|
|
||||||
|
|
@ -587,5 +587,86 @@ describe('parser', function()
|
||||||
assert.are.equal('/tmp/test-repo', hunks[1].repo_root)
|
assert.are.equal('/tmp/test-repo', hunks[1].repo_root)
|
||||||
delete_buffer(bufnr)
|
delete_buffer(bufnr)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('detects neogit modified prefix', function()
|
||||||
|
local bufnr = create_buffer({
|
||||||
|
'modified hello.lua',
|
||||||
|
'@@ -1,2 +1,3 @@',
|
||||||
|
' local M = {}',
|
||||||
|
'+local x = 1',
|
||||||
|
' return M',
|
||||||
|
})
|
||||||
|
local hunks = parser.parse_buffer(bufnr)
|
||||||
|
|
||||||
|
assert.are.equal(1, #hunks)
|
||||||
|
assert.are.equal('hello.lua', hunks[1].filename)
|
||||||
|
assert.are.equal('lua', hunks[1].ft)
|
||||||
|
assert.are.equal(3, #hunks[1].lines)
|
||||||
|
delete_buffer(bufnr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('detects neogit new file prefix', function()
|
||||||
|
local bufnr = create_buffer({
|
||||||
|
'new file hello.lua',
|
||||||
|
'@@ -0,0 +1,2 @@',
|
||||||
|
'+local M = {}',
|
||||||
|
'+return M',
|
||||||
|
})
|
||||||
|
local hunks = parser.parse_buffer(bufnr)
|
||||||
|
|
||||||
|
assert.are.equal(1, #hunks)
|
||||||
|
assert.are.equal('hello.lua', hunks[1].filename)
|
||||||
|
assert.are.equal('lua', hunks[1].ft)
|
||||||
|
assert.are.equal(2, #hunks[1].lines)
|
||||||
|
delete_buffer(bufnr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('detects neogit deleted prefix', function()
|
||||||
|
local bufnr = create_buffer({
|
||||||
|
'deleted hello.lua',
|
||||||
|
'@@ -1,2 +0,0 @@',
|
||||||
|
'-local M = {}',
|
||||||
|
'-return M',
|
||||||
|
})
|
||||||
|
local hunks = parser.parse_buffer(bufnr)
|
||||||
|
|
||||||
|
assert.are.equal(1, #hunks)
|
||||||
|
assert.are.equal('hello.lua', hunks[1].filename)
|
||||||
|
assert.are.equal('lua', hunks[1].ft)
|
||||||
|
assert.are.equal(2, #hunks[1].lines)
|
||||||
|
delete_buffer(bufnr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('detects bare filename for untracked files', function()
|
||||||
|
local bufnr = create_buffer({
|
||||||
|
'newfile.rs',
|
||||||
|
'@@ -0,0 +1,3 @@',
|
||||||
|
'+fn main() {',
|
||||||
|
'+ println!("hello");',
|
||||||
|
'+}',
|
||||||
|
})
|
||||||
|
local hunks = parser.parse_buffer(bufnr)
|
||||||
|
|
||||||
|
assert.are.equal(1, #hunks)
|
||||||
|
assert.are.equal('newfile.rs', hunks[1].filename)
|
||||||
|
assert.are.equal(3, #hunks[1].lines)
|
||||||
|
delete_buffer(bufnr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('does not match section headers as bare filenames', function()
|
||||||
|
local bufnr = create_buffer({
|
||||||
|
'Untracked files (1)',
|
||||||
|
'newfile.rs',
|
||||||
|
'@@ -0,0 +1,3 @@',
|
||||||
|
'+fn main() {',
|
||||||
|
'+ println!("hello");',
|
||||||
|
'+}',
|
||||||
|
})
|
||||||
|
local hunks = parser.parse_buffer(bufnr)
|
||||||
|
|
||||||
|
assert.are.equal(1, #hunks)
|
||||||
|
assert.are.equal('newfile.rs', hunks[1].filename)
|
||||||
|
delete_buffer(bufnr)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue