Neovim sign gutter, designed to be mostly VCS agnostic
at experimental_diffthis 108 lines 3.3 kB view raw
1local M = {} 2 3local diff = require "vcsigns.diff" 4local hunkops = require "vcsigns.hunkops" 5 6local HIGHLIGHT_PRIORITY = 3 7 8local function _highlights_namespace() 9 return vim.api.nvim_create_namespace "vcsigns_highlights" 10end 11 12local function put_virtual_hunk(bufnr, ns, hunk) 13 local intra_diff = diff.intra_diff(hunk) 14 local deletion_at_top = hunk.plus_start == 0 and hunk.plus_count == 0 15 16 local line = hunk.plus_start - 1 17 local virt_lines = {} 18 -- Long string with spaces to get virt_lines highlights to eol. 19 -- Surely there is a better way... 20 local spacer = string.rep(" ", 1000) 21 22 for i, l in ipairs(hunk.minus_lines) do 23 local chunks = {} 24 local intervals = intra_diff.minus_intervals[i] or {} 25 local start = 0 26 for _, interval in ipairs(intervals) do 27 if start < interval[1] then 28 -- Add a chunk for the text before the interval. 29 chunks[#chunks + 1] = 30 { l:sub(start + 1, interval[1]), { "VcsignsDiffDelete" } } 31 end 32 -- Add a chunk for the interval. 33 chunks[#chunks + 1] = { 34 l:sub(interval[1] + 1, interval[2]), 35 { "VcsignsDiffDelete", "VcsignsDiffTextDelete" }, 36 } 37 start = interval[2] 38 end 39 -- Add a chunk for the text after the last interval. 40 chunks[#chunks + 1] = { l:sub(start + 1), { "VcsignsDiffDelete" } } 41 42 chunks[#chunks + 1] = { spacer, { "VcsignsDiffDelete" } } 43 virt_lines[#virt_lines + 1] = chunks 44 end 45 46 if deletion_at_top then 47 -- We can't put virtual lines below non-existent line -1. 48 vim.api.nvim_buf_set_extmark( 49 bufnr, 50 ns, 51 0, 52 0, 53 { virt_lines = virt_lines, virt_lines_above = true } 54 ) 55 else 56 vim.api.nvim_buf_set_extmark( 57 bufnr, 58 ns, 59 line + hunkops.hunk_visual_size(hunk) - 1, 60 0, 61 { virt_lines = virt_lines } 62 ) 63 end 64 if hunk.plus_count > 0 then 65 -- Working around issue with line_hl_group not working with hl_group. 66 -- Workaround from: 67 -- https://github.com/lewis6991/gitsigns.nvim/issues/1115#issuecomment-2319497559 68 -- 69 -- vim.api.nvim_buf_set_extmark(bufnr, ns, line, 0, { 70 -- line_hl_group = "VcsignsDiffAdd", 71 -- end_row = line + hunk.plus_count - 1, 72 -- }) 73 vim.api.nvim_buf_set_extmark(bufnr, ns, line, 0, { 74 end_line = line + hunk.plus_count, 75 hl_group = "VcsignsDiffAdd", 76 priority = HIGHLIGHT_PRIORITY, 77 end_col = 0, 78 hl_eol = true, 79 strict = false, 80 }) 81 end 82 83 -- Fine grained diff. 84 local plus_intervals = intra_diff.plus_intervals 85 for offset, intervals in pairs(plus_intervals) do 86 for _, interval in ipairs(intervals) do 87 local interval_line = line + offset - 1 88 vim.api.nvim_buf_set_extmark(bufnr, ns, interval_line, interval[1], { 89 end_col = interval[2], 90 hl_group = "VcsignsDiffTextAdd", 91 strict = false, 92 }) 93 end 94 end 95end 96 97---@param bufnr integer The buffer number. 98---@param hunks Hunk[] A list of hunks to highlight. 99function M.highlight_hunks(bufnr, hunks) 100 local ns = _highlights_namespace() 101 vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) 102 -- Reverse order, this seems to create the right order for same line hunks. 103 for hunk in vim.iter(hunks):rev() do 104 put_virtual_hunk(bufnr, ns, hunk) 105 end 106end 107 108return M