From f5a090baaea8aca3310b9f1081e7077ae14c94e7 Mon Sep 17 00:00:00 2001 From: Barrett Ruth <62671086+barrettruth@users.noreply.github.com> Date: Mon, 9 Feb 2026 12:39:13 -0500 Subject: [PATCH 1/4] perf: cache repo root and harden async paths (#100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem `get_repo_root()` shells out to `git rev-parse` on every call, causing 4-6 redundant subprocesses per `gdiff_file()` invocation. Three other minor issues: `highlight_vim_syntax()` leaks a scratch buffer if `nvim_buf_call` errors, `lib.ensure()` silently drops callbacks during download so hunks highlighted mid-download permanently miss intra-line highlights, and the debounce timer callback can operate on a deleted buffer. ## Solution Cache `get_repo_root()` results by parent directory — repo roots don't change within a session. Wrap `nvim_buf_call` and `nvim_buf_delete` in pcall so the scratch buffer is always cleaned up. Replace the early `callback(nil)` in `lib.ensure()` with a pending callback queue that fires once the download completes. Guard the debounce timer callback with `nvim_buf_is_valid`. --- lua/diffs/git.lua | 6 ++++++ lua/diffs/highlight.lua | 4 ++-- lua/diffs/init.lua | 4 +++- lua/diffs/lib.lua | 30 +++++++++++++++++++----------- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/lua/diffs/git.lua b/lua/diffs/git.lua index 7695283..1aa6328 100644 --- a/lua/diffs/git.lua +++ b/lua/diffs/git.lua @@ -1,13 +1,19 @@ local M = {} +local repo_root_cache = {} + ---@param filepath string ---@return string? function M.get_repo_root(filepath) local dir = vim.fn.fnamemodify(filepath, ':h') + if repo_root_cache[dir] ~= nil then + return repo_root_cache[dir] + end local result = vim.fn.systemlist({ 'git', '-C', dir, 'rev-parse', '--show-toplevel' }) if vim.v.shell_error ~= 0 then return nil end + repo_root_cache[dir] = result[1] return result[1] end diff --git a/lua/diffs/highlight.lua b/lua/diffs/highlight.lua index 306b0e5..6cdfe92 100644 --- a/lua/diffs/highlight.lua +++ b/lua/diffs/highlight.lua @@ -238,7 +238,7 @@ local function highlight_vim_syntax(bufnr, ns, hunk, code_lines, covered_lines, local spans = {} - vim.api.nvim_buf_call(scratch, function() + pcall(vim.api.nvim_buf_call, scratch, function() vim.cmd('setlocal syntax=' .. ft) vim.cmd('redraw') @@ -256,7 +256,7 @@ local function highlight_vim_syntax(bufnr, ns, hunk, code_lines, covered_lines, spans = M.coalesce_syntax_spans(query_fn, code_lines) end) - vim.api.nvim_buf_delete(scratch, { force = true }) + pcall(vim.api.nvim_buf_delete, scratch, { force = true }) local hunk_line_count = #hunk.lines local extmark_count = 0 diff --git a/lua/diffs/init.lua b/lua/diffs/init.lua index cdc29d1..69a5cf9 100644 --- a/lua/diffs/init.lua +++ b/lua/diffs/init.lua @@ -200,7 +200,9 @@ local function create_debounced_highlight(bufnr) timer = nil t:close() end - highlight_buffer(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + highlight_buffer(bufnr) + end end) ) end diff --git a/lua/diffs/lib.lua b/lua/diffs/lib.lua index 5b3254b..8376925 100644 --- a/lua/diffs/lib.lua +++ b/lua/diffs/lib.lua @@ -8,6 +8,9 @@ local cached_handle = nil ---@type boolean local download_in_progress = false +---@type fun(handle: table?)[] +local pending_callbacks = {} + ---@return string local function get_os() local os_name = jit.os:lower() @@ -164,9 +167,10 @@ function M.ensure(callback) return end + table.insert(pending_callbacks, callback) + if download_in_progress then - dbg('download already in progress') - callback(nil) + dbg('download already in progress, queued callback') return end @@ -192,21 +196,25 @@ function M.ensure(callback) vim.system(cmd, {}, function(result) download_in_progress = false vim.schedule(function() + local handle = nil if result.code ~= 0 then vim.notify('[diffs] failed to download libvscode_diff', vim.log.levels.WARN) dbg('curl failed: %s', result.stderr or '') - callback(nil) - return + else + local f = io.open(version_path(), 'w') + if f then + f:write(EXPECTED_VERSION) + f:close() + end + vim.notify('[diffs] libvscode_diff downloaded', vim.log.levels.INFO) + handle = M.load() end - local f = io.open(version_path(), 'w') - if f then - f:write(EXPECTED_VERSION) - f:close() + local cbs = pending_callbacks + pending_callbacks = {} + for _, cb in ipairs(cbs) do + cb(handle) end - - vim.notify('[diffs] libvscode_diff downloaded', vim.log.levels.INFO) - callback(M.load()) end) end) end From 6e1a053bc48865ec867c5318bcc1f9666dc2154d Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Mon, 9 Feb 2026 13:05:37 -0500 Subject: [PATCH 2/4] feat(conflict): add virtual text formatting and action lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: conflict resolution virtual text only showed plain "current" / "incoming" labels with no keymap hints, and users had no way to discover available keymaps without reading docs. Solution: add keymap hints to default labels ("current — doo"), expose format_virtual_text config for custom label formatting, and add show_actions option for codelens-style action lines above conflict markers. Also add hunk hints in merge diff views. --- doc/diffs.nvim.txt | 38 ++++++++- lua/diffs/conflict.lua | 61 ++++++++++++-- lua/diffs/init.lua | 6 ++ lua/diffs/merge.lua | 39 +++++++++ spec/conflict_spec.lua | 182 +++++++++++++++++++++++++++++++++++++++++ spec/merge_spec.lua | 60 ++++++++++++++ 6 files changed, 375 insertions(+), 11 deletions(-) diff --git a/doc/diffs.nvim.txt b/doc/diffs.nvim.txt index 6bd848c..4d96f33 100644 --- a/doc/diffs.nvim.txt +++ b/doc/diffs.nvim.txt @@ -85,6 +85,7 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads: enabled = true, disable_diagnostics = true, show_virtual_text = true, + show_actions = false, keymaps = { ours = 'doo', theirs = 'dot', @@ -416,6 +417,7 @@ Configuration: ~ enabled = true, disable_diagnostics = true, show_virtual_text = true, + show_actions = false, keymaps = { ours = 'doo', theirs = 'dot', @@ -442,9 +444,35 @@ Configuration: ~ diagnostics alone. {show_virtual_text} (boolean, default: true) - Show virtual text labels (" current" and - " incoming") at the end of `<<<<<<<` and - `>>>>>>>` marker lines. + Show virtual text labels at the end of + `<<<<<<<` and `>>>>>>>` marker lines. + Default labels include keymap hints: + `(current — doo)` and `(incoming — dot)`. + If a keymap is `false`, the hint is omitted. + Also controls hunk hints in merge diff + views. + + {format_virtual_text} (function|nil, default: nil) + Custom formatter for virtual text labels. + Receives `(side, keymap)` where `side` is + `"ours"` or `"theirs"` and `keymap` is the + configured keymap string or `false`. Return + a string (label text without parens) or + `nil` to hide the label. Example: >lua + format_virtual_text = function(side, keymap) + if keymap then + return side .. ' [' .. keymap .. ']' + end + return side + end +< + + {show_actions} (boolean, default: false) + Show a codelens-style action line above each + `<<<<<<<` marker listing available resolution + keymaps. Renders as virtual lines using the + `DiffsConflictActions` highlight group. + Only keymaps that are not `false` appear. {keymaps} (table, default: see above) Buffer-local keymaps for conflict resolution @@ -687,6 +715,10 @@ Conflict highlights: ~ *DiffsConflictBaseNr* DiffsConflictBaseNr Line number for base content lines (diff3). + *DiffsConflictActions* + DiffsConflictActions Dimmed foreground (no bold) for the codelens-style + action line shown when `show_actions` is true. + Diff mode window highlights: ~ These are used for |winhighlight| remapping in `&diff` windows. diff --git a/lua/diffs/conflict.lua b/lua/diffs/conflict.lua index 894c0b8..e2a5298 100644 --- a/lua/diffs/conflict.lua +++ b/lua/diffs/conflict.lua @@ -92,6 +92,21 @@ local function parse_buffer(bufnr) return M.parse(lines) end +---@param side string +---@param config diffs.ConflictConfig +---@return string? +local function get_virtual_text_label(side, config) + local keymap = side == 'ours' and config.keymaps.ours or config.keymaps.theirs + if config.format_virtual_text then + return config.format_virtual_text(side, keymap) + end + local label = side == 'ours' and 'current' or 'incoming' + if keymap then + return ('%s \226\128\148 %s'):format(label, keymap) + end + return label +end + ---@param bufnr integer ---@param regions diffs.ConflictRegion[] ---@param config diffs.ConflictConfig @@ -107,10 +122,37 @@ local function apply_highlights(bufnr, regions, config) }) if config.show_virtual_text then - pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, region.marker_ours, 0, { - virt_text = { { ' (current)', 'DiffsConflictMarker' } }, - virt_text_pos = 'eol', - }) + local ours_label = get_virtual_text_label('ours', config) + if ours_label then + pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, region.marker_ours, 0, { + virt_text = { { ' (' .. ours_label .. ')', 'DiffsConflictMarker' } }, + virt_text_pos = 'eol', + }) + end + end + + if config.show_actions then + local parts = {} + local actions = { + { 'Current', config.keymaps.ours }, + { 'Incoming', config.keymaps.theirs }, + { 'Both', config.keymaps.both }, + { 'None', config.keymaps.none }, + } + for _, action in ipairs(actions) do + if action[2] then + if #parts > 0 then + table.insert(parts, { ' \226\148\130 ', 'DiffsConflictActions' }) + end + table.insert(parts, { ('%s (%s)'):format(action[1], action[2]), 'DiffsConflictActions' }) + end + end + if #parts > 0 then + pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, region.marker_ours, 0, { + virt_lines = { parts }, + virt_lines_above = true, + }) + end end for line = region.ours_start, region.ours_end - 1 do @@ -176,10 +218,13 @@ local function apply_highlights(bufnr, regions, config) }) if config.show_virtual_text then - pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, region.marker_theirs, 0, { - virt_text = { { ' (incoming)', 'DiffsConflictMarker' } }, - virt_text_pos = 'eol', - }) + local theirs_label = get_virtual_text_label('theirs', config) + if theirs_label then + pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, region.marker_theirs, 0, { + virt_text = { { ' (' .. theirs_label .. ')', 'DiffsConflictMarker' } }, + virt_text_pos = 'eol', + }) + end end end end diff --git a/lua/diffs/init.lua b/lua/diffs/init.lua index cdc29d1..597beb0 100644 --- a/lua/diffs/init.lua +++ b/lua/diffs/init.lua @@ -41,6 +41,8 @@ ---@field enabled boolean ---@field disable_diagnostics boolean ---@field show_virtual_text boolean +---@field format_virtual_text? fun(side: string, keymap: string|false): string? +---@field show_actions boolean ---@field keymaps diffs.ConflictKeymaps ---@class diffs.Config @@ -128,6 +130,7 @@ local default_config = { enabled = true, disable_diagnostics = true, show_virtual_text = true, + show_actions = false, keymaps = { ours = 'doo', theirs = 'dot', @@ -274,6 +277,7 @@ local function compute_highlight_groups() vim.api.nvim_set_hl(0, 'DiffsConflictTheirs', { default = true, bg = blended_theirs }) vim.api.nvim_set_hl(0, 'DiffsConflictBase', { default = true, bg = blended_base }) vim.api.nvim_set_hl(0, 'DiffsConflictMarker', { default = true, fg = 0x808080, bold = true }) + vim.api.nvim_set_hl(0, 'DiffsConflictActions', { default = true, fg = 0x808080 }) vim.api.nvim_set_hl( 0, 'DiffsConflictOursNr', @@ -388,6 +392,8 @@ local function init() ['conflict.enabled'] = { opts.conflict.enabled, 'boolean', true }, ['conflict.disable_diagnostics'] = { opts.conflict.disable_diagnostics, 'boolean', true }, ['conflict.show_virtual_text'] = { opts.conflict.show_virtual_text, 'boolean', true }, + ['conflict.format_virtual_text'] = { opts.conflict.format_virtual_text, 'function', true }, + ['conflict.show_actions'] = { opts.conflict.show_actions, 'boolean', true }, ['conflict.keymaps'] = { opts.conflict.keymaps, 'table', true }, }) diff --git a/lua/diffs/merge.lua b/lua/diffs/merge.lua index 9bb9d68..4332220 100644 --- a/lua/diffs/merge.lua +++ b/lua/diffs/merge.lua @@ -338,6 +338,43 @@ function M.goto_prev(bufnr) vim.api.nvim_win_set_cursor(0, { candidates[#candidates].start_line + 1, 0 }) end +---@param bufnr integer +---@param config diffs.ConflictConfig +local function apply_hunk_hints(bufnr, config) + if not config.show_virtual_text then + return + end + + local hunks = M.parse_hunks(bufnr) + for _, hunk in ipairs(hunks) do + if M.is_resolved(bufnr, hunk.index) then + add_resolved_virtual_text(bufnr, hunk) + else + local parts = {} + local actions = { + { 'current', config.keymaps.ours }, + { 'incoming', config.keymaps.theirs }, + { 'both', config.keymaps.both }, + { 'none', config.keymaps.none }, + } + for _, action in ipairs(actions) do + if action[2] then + if #parts > 0 then + table.insert(parts, { ' | ', 'Comment' }) + end + table.insert(parts, { ('%s: %s'):format(action[2], action[1]), 'Comment' }) + end + end + if #parts > 0 then + pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, hunk.start_line, 0, { + virt_text = parts, + virt_text_pos = 'eol', + }) + end + end + end +end + ---@param bufnr integer ---@param config diffs.ConflictConfig function M.setup_keymaps(bufnr, config) @@ -358,6 +395,8 @@ function M.setup_keymaps(bufnr, config) end end + apply_hunk_hints(bufnr, config) + vim.api.nvim_create_autocmd('BufWipeout', { buffer = bufnr, callback = function() diff --git a/spec/conflict_spec.lua b/spec/conflict_spec.lua index 75eac23..a64911a 100644 --- a/spec/conflict_spec.lua +++ b/spec/conflict_spec.lua @@ -6,6 +6,7 @@ local function default_config(overrides) enabled = true, disable_diagnostics = false, show_virtual_text = true, + show_actions = false, keymaps = { ours = 'doo', theirs = 'dot', @@ -685,4 +686,185 @@ describe('conflict', function() helpers.delete_buffer(bufnr) end) end) + + describe('virtual text formatting', function() + after_each(function() + conflict.detach(vim.api.nvim_get_current_buf()) + end) + + it('includes keymap hints in default virtual text', function() + local bufnr = create_file_buffer({ + '<<<<<<< HEAD', + 'local x = 1', + '=======', + 'local x = 2', + '>>>>>>> feature', + }) + + conflict.attach(bufnr, default_config()) + + local extmarks = get_extmarks(bufnr) + local labels = {} + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_text then + table.insert(labels, mark[4].virt_text[1][1]) + end + end + assert.are.equal(2, #labels) + assert.is_truthy(labels[1]:find('current')) + assert.is_truthy(labels[1]:find('doo')) + assert.is_truthy(labels[2]:find('incoming')) + assert.is_truthy(labels[2]:find('dot')) + + helpers.delete_buffer(bufnr) + end) + + it('omits keymap from label when keymap is false', function() + local bufnr = create_file_buffer({ + '<<<<<<< HEAD', + 'local x = 1', + '=======', + 'local x = 2', + '>>>>>>> feature', + }) + + conflict.attach(bufnr, default_config({ keymaps = { ours = false, theirs = false } })) + + local extmarks = get_extmarks(bufnr) + local labels = {} + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_text then + table.insert(labels, mark[4].virt_text[1][1]) + end + end + assert.are.equal(2, #labels) + assert.are.equal(' (current)', labels[1]) + assert.are.equal(' (incoming)', labels[2]) + + helpers.delete_buffer(bufnr) + end) + + it('uses custom format_virtual_text function', function() + local bufnr = create_file_buffer({ + '<<<<<<< HEAD', + 'local x = 1', + '=======', + 'local x = 2', + '>>>>>>> feature', + }) + + conflict.attach( + bufnr, + default_config({ + format_virtual_text = function(side) + return side == 'ours' and 'OURS' or 'THEIRS' + end, + }) + ) + + local extmarks = get_extmarks(bufnr) + local labels = {} + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_text then + table.insert(labels, mark[4].virt_text[1][1]) + end + end + assert.are.equal(2, #labels) + assert.are.equal(' (OURS)', labels[1]) + assert.are.equal(' (THEIRS)', labels[2]) + + helpers.delete_buffer(bufnr) + end) + + it('hides label when format_virtual_text returns nil', function() + local bufnr = create_file_buffer({ + '<<<<<<< HEAD', + 'local x = 1', + '=======', + 'local x = 2', + '>>>>>>> feature', + }) + + conflict.attach( + bufnr, + default_config({ + format_virtual_text = function() + return nil + end, + }) + ) + + local extmarks = get_extmarks(bufnr) + local virt_text_count = 0 + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_text then + virt_text_count = virt_text_count + 1 + end + end + assert.are.equal(0, virt_text_count) + + helpers.delete_buffer(bufnr) + end) + end) + + describe('action lines', function() + after_each(function() + conflict.detach(vim.api.nvim_get_current_buf()) + end) + + it('adds virt_lines when show_actions is true', function() + local bufnr = create_file_buffer({ + '<<<<<<< HEAD', + 'local x = 1', + '=======', + 'local x = 2', + '>>>>>>> feature', + }) + + conflict.attach(bufnr, default_config({ show_actions = true })) + + local extmarks = get_extmarks(bufnr) + local virt_lines_count = 0 + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_lines then + virt_lines_count = virt_lines_count + 1 + end + end + assert.are.equal(1, virt_lines_count) + + helpers.delete_buffer(bufnr) + end) + + it('omits disabled keymaps from action line', function() + local bufnr = create_file_buffer({ + '<<<<<<< HEAD', + 'local x = 1', + '=======', + 'local x = 2', + '>>>>>>> feature', + }) + + conflict.attach( + bufnr, + default_config({ show_actions = true, keymaps = { both = false, none = false } }) + ) + + local extmarks = get_extmarks(bufnr) + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_lines then + local line = mark[4].virt_lines[1] + local text = '' + for _, chunk in ipairs(line) do + text = text .. chunk[1] + end + assert.is_truthy(text:find('Current')) + assert.is_truthy(text:find('Incoming')) + assert.is_falsy(text:find('Both')) + assert.is_falsy(text:find('None')) + end + end + + helpers.delete_buffer(bufnr) + end) + end) end) diff --git a/spec/merge_spec.lua b/spec/merge_spec.lua index 2f788a6..7d5019b 100644 --- a/spec/merge_spec.lua +++ b/spec/merge_spec.lua @@ -6,6 +6,7 @@ local function default_config(overrides) enabled = true, disable_diagnostics = false, show_virtual_text = true, + show_actions = false, keymaps = { ours = 'doo', theirs = 'dot', @@ -617,6 +618,65 @@ describe('merge', function() end) end) + describe('hunk hints', function() + it('adds keymap hints on hunk header lines', function() + local d_bufnr = create_diff_buffer({ + 'diff --git a/file.lua b/file.lua', + '--- a/file.lua', + '+++ b/file.lua', + '@@ -1,1 +1,1 @@', + '-local x = 1', + '+local x = 2', + }) + + merge.setup_keymaps(d_bufnr, default_config()) + + local extmarks = + vim.api.nvim_buf_get_extmarks(d_bufnr, merge.get_namespace(), 0, -1, { details = true }) + local hint_marks = {} + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_text then + local text = '' + for _, chunk in ipairs(mark[4].virt_text) do + text = text .. chunk[1] + end + table.insert(hint_marks, { line = mark[2], text = text }) + end + end + assert.are.equal(1, #hint_marks) + assert.are.equal(3, hint_marks[1].line) + assert.is_truthy(hint_marks[1].text:find('doo')) + assert.is_truthy(hint_marks[1].text:find('dot')) + + helpers.delete_buffer(d_bufnr) + end) + + it('does not add hints when show_virtual_text is false', function() + local d_bufnr = create_diff_buffer({ + 'diff --git a/file.lua b/file.lua', + '--- a/file.lua', + '+++ b/file.lua', + '@@ -1,1 +1,1 @@', + '-local x = 1', + '+local x = 2', + }) + + merge.setup_keymaps(d_bufnr, default_config({ show_virtual_text = false })) + + local extmarks = + vim.api.nvim_buf_get_extmarks(d_bufnr, merge.get_namespace(), 0, -1, { details = true }) + local virt_text_count = 0 + for _, mark in ipairs(extmarks) do + if mark[4] and mark[4].virt_text then + virt_text_count = virt_text_count + 1 + end + end + assert.are.equal(0, virt_text_count) + + helpers.delete_buffer(d_bufnr) + end) + end) + describe('fugitive integration', function() it('parse_file_line returns status for unmerged files', function() local fugitive = require('diffs.fugitive') From 861f83600abbdcf671e2b7eec976bc6dffba7277 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Mon, 9 Feb 2026 13:27:09 -0500 Subject: [PATCH 3/4] refactor(conflict): keep default virtual text labels minimal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: default labels included keymap hints ("current — doo") which is an anti-pattern for a vim plugin — discoverability belongs in docs and opt-in config, not baked into the default UI. Solution: default labels return to plain "(current)" / "(incoming)". Keymap hints are only shown when users provide a format_virtual_text function or enable show_actions. --- doc/diffs.nvim.txt | 11 ++++------- lua/diffs/conflict.lua | 8 ++------ spec/conflict_spec.lua | 29 +---------------------------- 3 files changed, 7 insertions(+), 41 deletions(-) diff --git a/doc/diffs.nvim.txt b/doc/diffs.nvim.txt index 4d96f33..f199d31 100644 --- a/doc/diffs.nvim.txt +++ b/doc/diffs.nvim.txt @@ -444,13 +444,10 @@ Configuration: ~ diagnostics alone. {show_virtual_text} (boolean, default: true) - Show virtual text labels at the end of - `<<<<<<<` and `>>>>>>>` marker lines. - Default labels include keymap hints: - `(current — doo)` and `(incoming — dot)`. - If a keymap is `false`, the hint is omitted. - Also controls hunk hints in merge diff - views. + Show `(current)` and `(incoming)` labels at + the end of `<<<<<<<` and `>>>>>>>` marker + lines. Also controls hunk hints in merge + diff views. {format_virtual_text} (function|nil, default: nil) Custom formatter for virtual text labels. diff --git a/lua/diffs/conflict.lua b/lua/diffs/conflict.lua index e2a5298..ce3618d 100644 --- a/lua/diffs/conflict.lua +++ b/lua/diffs/conflict.lua @@ -96,15 +96,11 @@ end ---@param config diffs.ConflictConfig ---@return string? local function get_virtual_text_label(side, config) - local keymap = side == 'ours' and config.keymaps.ours or config.keymaps.theirs if config.format_virtual_text then + local keymap = side == 'ours' and config.keymaps.ours or config.keymaps.theirs return config.format_virtual_text(side, keymap) end - local label = side == 'ours' and 'current' or 'incoming' - if keymap then - return ('%s \226\128\148 %s'):format(label, keymap) - end - return label + return side == 'ours' and 'current' or 'incoming' end ---@param bufnr integer diff --git a/spec/conflict_spec.lua b/spec/conflict_spec.lua index a64911a..b163960 100644 --- a/spec/conflict_spec.lua +++ b/spec/conflict_spec.lua @@ -692,7 +692,7 @@ describe('conflict', function() conflict.detach(vim.api.nvim_get_current_buf()) end) - it('includes keymap hints in default virtual text', function() + it('default labels show current and incoming without keymaps', function() local bufnr = create_file_buffer({ '<<<<<<< HEAD', 'local x = 1', @@ -703,33 +703,6 @@ describe('conflict', function() conflict.attach(bufnr, default_config()) - local extmarks = get_extmarks(bufnr) - local labels = {} - for _, mark in ipairs(extmarks) do - if mark[4] and mark[4].virt_text then - table.insert(labels, mark[4].virt_text[1][1]) - end - end - assert.are.equal(2, #labels) - assert.is_truthy(labels[1]:find('current')) - assert.is_truthy(labels[1]:find('doo')) - assert.is_truthy(labels[2]:find('incoming')) - assert.is_truthy(labels[2]:find('dot')) - - helpers.delete_buffer(bufnr) - end) - - it('omits keymap from label when keymap is false', function() - local bufnr = create_file_buffer({ - '<<<<<<< HEAD', - 'local x = 1', - '=======', - 'local x = 2', - '>>>>>>> feature', - }) - - conflict.attach(bufnr, default_config({ keymaps = { ours = false, theirs = false } })) - local extmarks = get_extmarks(bufnr) local labels = {} for _, mark in ipairs(extmarks) do From 2834b588f113750dd5ed3bd0b7fa875a1ddb635f Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Mon, 9 Feb 2026 13:36:56 -0500 Subject: [PATCH 4/4] docs: trim readme features list --- README.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f0a04a8..996ee27 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,12 @@ syntax highlighting. ## Features -- Treesitter syntax highlighting in `:Git` diffs and commit views -- Diff header highlighting (`diff --git`, `index`, `---`, `+++`) -- `:Gdiffsplit` / `:Gvdiffsplit` syntax through diff backgrounds -- `:Gdiff` unified diff against any git revision with syntax highlighting -- Fugitive status buffer keymaps (`du`/`dU`) for unified diffs -- Background-only diff colors for any `&diff` buffer (`:diffthis`, `vimdiff`) -- Vim syntax fallback for languages without a treesitter parser -- Hunk header context highlighting (`@@ ... @@ function foo()`) -- Character-level (intra-line) diff highlighting for changed characters -- Inline merge conflict detection, highlighting, and resolution keymaps -- Configurable debouncing, max lines, diff prefix concealment, blend alpha, and - highlight overrides +- Treesitter syntax highlighting in fugitive diffs and commit views +- Character-level intra-line diff highlighting +- `:Gdiff` unified diff against any revision +- Background-only diff colors for `&diff` buffers +- Inline merge conflict detection, highlighting, and resolution +- Vim syntax fallback, context padding, configurable blend/debounce ## Requirements