feat(neojj): add neojj (jujutsu) integration
Problem: diffs.nvim has no support for neojj, the neogit-like TUI for jujutsu VCS. Users switching to jj get no syntax highlighting in neojj status/diff buffers. Solution: Mirror the existing neogit integration pattern for neojj. Register `NeojjStatus`, `NeojjCommitView`, `NeojjDiffView` filetypes, set `vim.b.neojj_disable_hunk_highlight` on attach, listen for `User NeojjDiffLoaded` events, and detect jj repo root via `neojj.lib.jj.repo.worktree_root`. Add parser patterns for neojj's `added`, `updated`, `changed`, and `unmerged` filename labels.
This commit is contained in:
parent
de04381298
commit
5da3929480
5 changed files with 265 additions and 6 deletions
16
README.md
16
README.md
|
|
@ -9,7 +9,10 @@ highlighting driven by treesitter.
|
|||
|
||||
## Features
|
||||
|
||||
- Treesitter syntax highlighting in vim-fugitive, Neogit, and `diff` filetype
|
||||
- Treesitter syntax highlighting in
|
||||
[vim-fugitive](https://github.com/tpope/vim-fugitive),
|
||||
[Neogit](https://github.com/NeogitOrg/neogit), builtin `diff` filetype, and
|
||||
more!
|
||||
- Character-level intra-line diff highlighting (with optional
|
||||
[vscode-diff](https://github.com/esmuellert/codediff.nvim) FFI backend for
|
||||
word-level accuracy)
|
||||
|
|
@ -58,15 +61,18 @@ luarocks install diffs.nvim
|
|||
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/gitsigns?**
|
||||
**Q: Does diffs.nvim support vim-fugitive/Neogit/neojj/gitsigns?**
|
||||
|
||||
Yes. Enable integrations in your config:
|
||||
|
||||
```lua
|
||||
vim.g.diffs = {
|
||||
fugitive = true,
|
||||
neogit = true,
|
||||
gitsigns = true,
|
||||
integrations = {
|
||||
fugitive = true,
|
||||
neogit = true,
|
||||
neojj = true,
|
||||
gitsigns = true,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ CONTENTS *diffs-contents*
|
|||
7. Integrations ..................................... |diffs-integrations|
|
||||
Fugitive .......................................... |diffs-fugitive|
|
||||
Neogit .............................................. |diffs-neogit|
|
||||
Neojj ............................................... |diffs-neojj|
|
||||
Gitsigns .......................................... |diffs-gitsigns|
|
||||
Telescope ........................................ |diffs-telescope|
|
||||
8. Conflict Resolution .................................... |diffs-conflict|
|
||||
|
|
@ -84,6 +85,7 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
integrations = {
|
||||
fugitive = false,
|
||||
neogit = false,
|
||||
neojj = false,
|
||||
gitsigns = false,
|
||||
committia = false,
|
||||
telescope = false,
|
||||
|
|
@ -180,6 +182,14 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
integrations = { neogit = true }
|
||||
<
|
||||
|
||||
{neojj} (boolean|table, default: false)
|
||||
Enable neojj integration. When active,
|
||||
`NeojjStatus`, `NeojjCommitView`, and
|
||||
`NeojjDiffView` filetypes are registered.
|
||||
See |diffs-neojj|. >lua
|
||||
integrations = { neojj = true }
|
||||
<
|
||||
|
||||
{gitsigns} (boolean|table, default: false)
|
||||
Enable gitsigns.nvim blame popup highlighting.
|
||||
See |diffs-gitsigns|. >lua
|
||||
|
|
@ -536,6 +546,7 @@ each integration's filetypes and attaches automatically.
|
|||
integrations = {
|
||||
fugitive = true,
|
||||
neogit = true,
|
||||
neojj = true,
|
||||
gitsigns = true,
|
||||
},
|
||||
}
|
||||
|
|
@ -624,6 +635,17 @@ Expanding a diff in a Neogit buffer (e.g., TAB on a file in the status
|
|||
view) applies treesitter syntax highlighting and intra-line diffs to the
|
||||
hunk lines.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
NEOJJ *diffs-neojj*
|
||||
|
||||
Enable neojj (https://github.com/NicholasZolton/neojj) support: >lua
|
||||
vim.g.diffs = { integrations = { neojj = true } }
|
||||
<
|
||||
|
||||
Expanding a diff in a neojj buffer (e.g., TAB on a file in the status
|
||||
view) applies treesitter syntax highlighting and intra-line diffs to the
|
||||
hunk lines.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
GITSIGNS *diffs-gitsigns*
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
---@class diffs.NeogitConfig
|
||||
|
||||
---@class diffs.NeojjConfig
|
||||
|
||||
---@class diffs.GitsignsConfig
|
||||
|
||||
---@class diffs.CommittiaConfig
|
||||
|
|
@ -65,6 +67,7 @@
|
|||
---@class diffs.IntegrationsConfig
|
||||
---@field fugitive diffs.FugitiveConfig|false
|
||||
---@field neogit diffs.NeogitConfig|false
|
||||
---@field neojj diffs.NeojjConfig|false
|
||||
---@field gitsigns diffs.GitsignsConfig|false
|
||||
---@field committia diffs.CommittiaConfig|false
|
||||
---@field telescope diffs.TelescopeConfig|false
|
||||
|
|
@ -77,6 +80,7 @@
|
|||
---@field integrations diffs.IntegrationsConfig
|
||||
---@field fugitive? diffs.FugitiveConfig|false deprecated: use integrations.fugitive
|
||||
---@field neogit? diffs.NeogitConfig|false deprecated: use integrations.neogit
|
||||
---@field neojj? diffs.NeojjConfig|false deprecated: use integrations.neojj
|
||||
---@field gitsigns? diffs.GitsignsConfig|false deprecated: use integrations.gitsigns
|
||||
---@field committia? diffs.CommittiaConfig|false deprecated: use integrations.committia
|
||||
---@field telescope? diffs.TelescopeConfig|false deprecated: use integrations.telescope
|
||||
|
|
@ -161,6 +165,7 @@ local default_config = {
|
|||
integrations = {
|
||||
fugitive = false,
|
||||
neogit = false,
|
||||
neojj = false,
|
||||
gitsigns = false,
|
||||
committia = false,
|
||||
telescope = false,
|
||||
|
|
@ -239,6 +244,15 @@ function M.compute_filetypes(opts)
|
|||
table.insert(fts, 'NeogitCommitView')
|
||||
table.insert(fts, 'NeogitDiffView')
|
||||
end
|
||||
local njj = intg.neojj
|
||||
if njj == nil then
|
||||
njj = opts.neojj
|
||||
end
|
||||
if njj == true or type(njj) == 'table' then
|
||||
table.insert(fts, 'NeojjStatus')
|
||||
table.insert(fts, 'NeojjCommitView')
|
||||
table.insert(fts, 'NeojjDiffView')
|
||||
end
|
||||
if type(opts.extra_filetypes) == 'table' then
|
||||
for _, ft in ipairs(opts.extra_filetypes) do
|
||||
table.insert(fts, ft)
|
||||
|
|
@ -610,7 +624,7 @@ local function compute_highlight_groups(is_default)
|
|||
end
|
||||
end
|
||||
|
||||
local integration_keys = { 'fugitive', 'neogit', 'gitsigns', 'committia', 'telescope' }
|
||||
local integration_keys = { 'fugitive', 'neogit', 'neojj', 'gitsigns', 'committia', 'telescope' }
|
||||
|
||||
local function migrate_integrations(opts)
|
||||
if opts.integrations then
|
||||
|
|
@ -674,6 +688,10 @@ local function init()
|
|||
intg.neogit = {}
|
||||
end
|
||||
|
||||
if intg.neojj == true then
|
||||
intg.neojj = {}
|
||||
end
|
||||
|
||||
if intg.gitsigns == true then
|
||||
intg.gitsigns = {}
|
||||
end
|
||||
|
|
@ -1032,6 +1050,21 @@ function M.attach(bufnr)
|
|||
})
|
||||
end
|
||||
|
||||
local neojj_augroup = nil
|
||||
if config.integrations.neojj and vim.bo[bufnr].filetype:match('^Neojj') then
|
||||
vim.b[bufnr].neojj_disable_hunk_highlight = true
|
||||
neojj_augroup = vim.api.nvim_create_augroup('diffs_neojj_' .. bufnr, { clear = true })
|
||||
vim.api.nvim_create_autocmd('User', {
|
||||
pattern = 'NeojjDiffLoaded',
|
||||
group = neojj_augroup,
|
||||
callback = function()
|
||||
if vim.api.nvim_buf_is_valid(bufnr) and attached_buffers[bufnr] then
|
||||
M.refresh(bufnr)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
dbg('attaching to buffer %d', bufnr)
|
||||
|
||||
ensure_cache(bufnr)
|
||||
|
|
@ -1045,6 +1078,9 @@ function M.attach(bufnr)
|
|||
if neogit_augroup then
|
||||
pcall(vim.api.nvim_del_augroup_by_id, neogit_augroup)
|
||||
end
|
||||
if neojj_augroup then
|
||||
pcall(vim.api.nvim_del_augroup_by_id, neojj_augroup)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
|
@ -1101,6 +1137,12 @@ function M.get_fugitive_config()
|
|||
return config.integrations.fugitive
|
||||
end
|
||||
|
||||
---@return diffs.NeojjConfig|false
|
||||
function M.get_neojj_config()
|
||||
init()
|
||||
return config.integrations.neojj
|
||||
end
|
||||
|
||||
---@return diffs.CommittiaConfig|false
|
||||
function M.get_committia_config()
|
||||
init()
|
||||
|
|
|
|||
|
|
@ -129,6 +129,18 @@ local function get_repo_root(bufnr)
|
|||
return vim.fn.fnamemodify(neogit_git_dir, ':h')
|
||||
end
|
||||
|
||||
if vim.bo[bufnr].filetype:match('^Neojj') then
|
||||
local jj_ok, jj_mod = pcall(require, 'neojj.lib.jj')
|
||||
if jj_ok then
|
||||
local rok, repo = pcall(function()
|
||||
return jj_mod.repo
|
||||
end)
|
||||
if rok and repo and repo.worktree_root then
|
||||
return repo.worktree_root
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cwd = vim.fn.getcwd()
|
||||
local git = require('diffs.git')
|
||||
return git.get_repo_root(cwd .. '/.')
|
||||
|
|
@ -244,6 +256,10 @@ function M.parse_buffer(bufnr)
|
|||
or (not logical:match('^deleted file mode') and logical:match('^deleted%s+(.+)$'))
|
||||
or logical:match('^renamed%s+(.+)$')
|
||||
or logical:match('^copied%s+(.+)$')
|
||||
or logical:match('^added%s+(.+)$')
|
||||
or logical:match('^updated%s+(.+)$')
|
||||
or logical:match('^changed%s+(.+)$')
|
||||
or logical:match('^unmerged%s+(.+)$')
|
||||
local bare_file = not hunk_start and logical:match('^([^%s]+%.[^%s]+)$')
|
||||
local filename = logical:match('^[MADRCU%?!]%s+(.+)$')
|
||||
or diff_git_file
|
||||
|
|
|
|||
173
spec/neojj_integration_spec.lua
Normal file
173
spec/neojj_integration_spec.lua
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
require('spec.helpers')
|
||||
|
||||
vim.g.diffs = { integrations = { neojj = true } }
|
||||
|
||||
local diffs = require('diffs')
|
||||
local parser = require('diffs.parser')
|
||||
|
||||
local function create_buffer(lines)
|
||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines or {})
|
||||
return bufnr
|
||||
end
|
||||
|
||||
local function delete_buffer(bufnr)
|
||||
if vim.api.nvim_buf_is_valid(bufnr) then
|
||||
vim.api.nvim_buf_delete(bufnr, { force = true })
|
||||
end
|
||||
end
|
||||
|
||||
describe('neojj_integration', function()
|
||||
describe('neojj_disable_hunk_highlight', function()
|
||||
it('sets neojj_disable_hunk_highlight on NeojjStatus buffer after attach', function()
|
||||
local bufnr = create_buffer({
|
||||
'modified test.lua',
|
||||
'@@ -1,1 +1,2 @@',
|
||||
' local x = 1',
|
||||
'+local y = 2',
|
||||
})
|
||||
vim.api.nvim_set_option_value('filetype', 'NeojjStatus', { buf = bufnr })
|
||||
diffs.attach(bufnr)
|
||||
|
||||
assert.is_true(vim.b[bufnr].neojj_disable_hunk_highlight)
|
||||
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('does not set neojj_disable_hunk_highlight on non-Neojj buffer', function()
|
||||
local bufnr = create_buffer({})
|
||||
vim.api.nvim_set_option_value('filetype', 'git', { buf = bufnr })
|
||||
diffs.attach(bufnr)
|
||||
|
||||
assert.is_not_true(vim.b[bufnr].neojj_disable_hunk_highlight)
|
||||
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('NeojjStatus buffer attach', function()
|
||||
it('populates hunk_cache for NeojjStatus buffer with diff content', function()
|
||||
local bufnr = create_buffer({
|
||||
'modified hello.lua',
|
||||
'@@ -1,2 +1,3 @@',
|
||||
' local M = {}',
|
||||
'+local x = 1',
|
||||
' return M',
|
||||
})
|
||||
vim.api.nvim_set_option_value('filetype', 'NeojjStatus', { buf = bufnr })
|
||||
diffs.attach(bufnr)
|
||||
local entry = diffs._test.hunk_cache[bufnr]
|
||||
assert.is_not_nil(entry)
|
||||
assert.is_table(entry.hunks)
|
||||
assert.are.equal(1, #entry.hunks)
|
||||
assert.are.equal('hello.lua', entry.hunks[1].filename)
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('populates hunk_cache for NeojjDiffView buffer', function()
|
||||
local bufnr = create_buffer({
|
||||
'new file newmod.lua',
|
||||
'@@ -0,0 +1,2 @@',
|
||||
'+local M = {}',
|
||||
'+return M',
|
||||
})
|
||||
vim.api.nvim_set_option_value('filetype', 'NeojjDiffView', { buf = bufnr })
|
||||
diffs.attach(bufnr)
|
||||
local entry = diffs._test.hunk_cache[bufnr]
|
||||
assert.is_not_nil(entry)
|
||||
assert.is_table(entry.hunks)
|
||||
assert.are.equal(1, #entry.hunks)
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('parser neojj patterns', function()
|
||||
it('detects added prefix via parser', function()
|
||||
local bufnr = create_buffer({
|
||||
'added utils.py',
|
||||
'@@ -0,0 +1,2 @@',
|
||||
'+def hello():',
|
||||
'+ pass',
|
||||
})
|
||||
local hunks = parser.parse_buffer(bufnr)
|
||||
assert.are.equal(1, #hunks)
|
||||
assert.are.equal('utils.py', hunks[1].filename)
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('detects updated prefix via parser', function()
|
||||
local bufnr = create_buffer({
|
||||
'updated config.toml',
|
||||
'@@ -1,2 +1,3 @@',
|
||||
' [section]',
|
||||
'+key = "val"',
|
||||
' other = 1',
|
||||
})
|
||||
local hunks = parser.parse_buffer(bufnr)
|
||||
assert.are.equal(1, #hunks)
|
||||
assert.are.equal('config.toml', hunks[1].filename)
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('detects changed prefix via parser', function()
|
||||
local bufnr = create_buffer({
|
||||
'changed main.rs',
|
||||
'@@ -1,1 +1,2 @@',
|
||||
' fn main() {}',
|
||||
'+fn helper() {}',
|
||||
})
|
||||
local hunks = parser.parse_buffer(bufnr)
|
||||
assert.are.equal(1, #hunks)
|
||||
assert.are.equal('main.rs', hunks[1].filename)
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('detects unmerged prefix via parser', function()
|
||||
local bufnr = create_buffer({
|
||||
'unmerged conflict.lua',
|
||||
'@@ -1,1 +1,2 @@',
|
||||
' local x = 1',
|
||||
'+local y = 2',
|
||||
})
|
||||
local hunks = parser.parse_buffer(bufnr)
|
||||
assert.are.equal(1, #hunks)
|
||||
assert.are.equal('conflict.lua', hunks[1].filename)
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('parses multi-file neojj buffer with modified and added', function()
|
||||
local bufnr = create_buffer({
|
||||
'modified test.lua',
|
||||
'@@ -1,2 +1,3 @@',
|
||||
' local M = {}',
|
||||
'+local x = 1',
|
||||
' return M',
|
||||
'added utils.py',
|
||||
'@@ -0,0 +1,2 @@',
|
||||
'+def hello():',
|
||||
'+ pass',
|
||||
})
|
||||
local hunks = parser.parse_buffer(bufnr)
|
||||
assert.are.equal(2, #hunks)
|
||||
assert.are.equal('test.lua', hunks[1].filename)
|
||||
assert.are.equal('utils.py', hunks[2].filename)
|
||||
delete_buffer(bufnr)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('compute_filetypes', function()
|
||||
it('includes Neojj filetypes when neojj integration is enabled', function()
|
||||
local fts = diffs.compute_filetypes({ integrations = { neojj = true } })
|
||||
assert.is_true(vim.tbl_contains(fts, 'NeojjStatus'))
|
||||
assert.is_true(vim.tbl_contains(fts, 'NeojjCommitView'))
|
||||
assert.is_true(vim.tbl_contains(fts, 'NeojjDiffView'))
|
||||
end)
|
||||
|
||||
it('excludes Neojj filetypes when neojj integration is disabled', function()
|
||||
local fts = diffs.compute_filetypes({ integrations = { neojj = false } })
|
||||
assert.is_false(vim.tbl_contains(fts, 'NeojjStatus'))
|
||||
assert.is_false(vim.tbl_contains(fts, 'NeojjCommitView'))
|
||||
assert.is_false(vim.tbl_contains(fts, 'NeojjDiffView'))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
Loading…
Add table
Add a link
Reference in a new issue