From 0270aa8672d57885a53466b0f0ddde34862f6d8f Mon Sep 17 00:00:00 2001 From: Luis Useche Date: Sat, 28 Jun 2025 22:16:15 -0700 Subject: [PATCH 1/6] style(diff): format the `diff.lua` file using `lua_ls` --- lua/mini/diff.lua | 57 ++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/lua/mini/diff.lua b/lua/mini/diff.lua index 574b6a8a..d0d8a637 100644 --- a/lua/mini/diff.lua +++ b/lua/mini/diff.lua @@ -923,8 +923,8 @@ H.operator_cache = {} -- Common extmark data for supported styles --stylua: ignore H.style_extmark_data = { - sign = { hl_group_prefix = 'MiniDiffSign', field = 'sign_hl_group' }, - number = { hl_group_prefix = 'MiniDiffSign', field = 'number_hl_group' }, + sign = { hl_group_prefix = 'MiniDiffSign', field = 'sign_hl_group' }, + number = { hl_group_prefix = 'MiniDiffSign', field = 'number_hl_group' }, } -- Suffix for overlay virtual lines to be highlighted as full line @@ -943,9 +943,12 @@ H.vimdiff_opts = { result_type = 'indices', ctxlen = 0, interhunkctxlen = 0 } -- reduce noisiness (chosen as slightly less than average English word length) --stylua: ignore H.worddiff_opts = { - algorithm = 'minimal', result_type = 'indices', - ctxlen = 0, interhunkctxlen = 4, - indent_heuristic = false, linematch = 0 + algorithm = 'minimal', + result_type = 'indices', + ctxlen = 0, + interhunkctxlen = 4, + indent_heuristic = false, + linematch = 0 } -- BOM bytes prepended to buffer text if 'bomb' is enabled. See `:h bom-bytes`. @@ -1017,14 +1020,14 @@ H.apply_config = function(config) H.map(modes, mappings.textobject, 'lua MiniDiff.textobject()', { desc = 'Hunk range textobject' }) --stylua: ignore start - H.map({ 'n', 'x' }, mappings.goto_first, "lua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) - H.map('o', mappings.goto_first, "Vlua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) - H.map({ 'n', 'x' }, mappings.goto_prev, "lua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) - H.map('o', mappings.goto_prev, "Vlua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) - H.map({ 'n', 'x' }, mappings.goto_next, "lua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) - H.map('o', mappings.goto_next, "Vlua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) - H.map({ 'n', 'x' }, mappings.goto_last, "lua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) - H.map('o', mappings.goto_last, "Vlua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) + H.map({ 'n', 'x' }, mappings.goto_first, "lua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) + H.map('o', mappings.goto_first, "Vlua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) + H.map({ 'n', 'x' }, mappings.goto_prev, "lua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) + H.map('o', mappings.goto_prev, "Vlua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) + H.map({ 'n', 'x' }, mappings.goto_next, "lua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) + H.map('o', mappings.goto_next, "Vlua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) + H.map({ 'n', 'x' }, mappings.goto_last, "lua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) + H.map('o', mappings.goto_last, "Vlua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) --stylua: ignore end -- Register decoration provider which actually makes visualization @@ -1054,15 +1057,15 @@ H.create_default_hl = function() end local has_core_diff_hl = vim.fn.has('nvim-0.10') == 1 - hi('MiniDiffSignAdd', { link = has_core_diff_hl and 'Added' or 'diffAdded' }) - hi('MiniDiffSignChange', { link = has_core_diff_hl and 'Changed' or 'diffChanged' }) - hi('MiniDiffSignDelete', { link = has_core_diff_hl and 'Removed' or 'diffRemoved' }) - hi('MiniDiffOverAdd', { link = 'DiffAdd' }) - hi('MiniDiffOverChange', { link = 'DiffText' }) - hi('MiniDiffOverChangeBuf', { link = 'MiniDiffOverChange'}) - hi('MiniDiffOverContext', { link = 'DiffChange' }) + hi('MiniDiffSignAdd', { link = has_core_diff_hl and 'Added' or 'diffAdded' }) + hi('MiniDiffSignChange', { link = has_core_diff_hl and 'Changed' or 'diffChanged' }) + hi('MiniDiffSignDelete', { link = has_core_diff_hl and 'Removed' or 'diffRemoved' }) + hi('MiniDiffOverAdd', { link = 'DiffAdd' }) + hi('MiniDiffOverChange', { link = 'DiffText' }) + hi('MiniDiffOverChangeBuf', { link = 'MiniDiffOverChange' }) + hi('MiniDiffOverContext', { link = 'DiffChange' }) hi('MiniDiffOverContextBuf', {}) - hi('MiniDiffOverDelete', { link = 'DiffDelete' }) + hi('MiniDiffOverDelete', { link = 'DiffDelete' }) end H.is_disabled = function(buf_id) @@ -1208,7 +1211,7 @@ H.convert_view_to_extmark_opts = function(view) local field, hl_group_prefix = extmark_data.field, extmark_data.hl_group_prefix --stylua: ignore return { - add = { [field] = hl_group_prefix .. 'Add', sign_text = signs.add, priority = view.priority, invalidate = H.extmark_invalidate }, + add = { [field] = hl_group_prefix .. 'Add', sign_text = signs.add, priority = view.priority, invalidate = H.extmark_invalidate }, change = { [field] = hl_group_prefix .. 'Change', sign_text = signs.change, priority = view.priority, invalidate = H.extmark_invalidate }, delete = { [field] = hl_group_prefix .. 'Delete', sign_text = signs.delete, priority = view.priority, invalidate = H.extmark_invalidate }, } @@ -1394,7 +1397,7 @@ H.append_overlay_change = function(overlay_lines, hunk, ref_lines, buf_lines, pr -- Defer actually computing word diff until in decoration provider as it -- will compute only for displayed lines local data = - { type = 'change_worddiff', ref_line = ref_lines[ref_n], buf_line = buf_lines[buf_n], priority = priority } + { type = 'change_worddiff', ref_line = ref_lines[ref_n], buf_line = buf_lines[buf_n], priority = priority } H.append_overlay(overlay_lines, buf_n, data) end return @@ -1435,7 +1438,7 @@ H.draw_overlay_line = function(buf_id, ns_id, row, data) -- "Change"/"Delete" hunks show affected reference range as virtual lines opts.virt_lines, opts.virt_lines_above, opts.virt_lines_overflow = - data.lines, data.show_above, H.extmark_virt_lines_overflow + data.lines, data.show_above, H.extmark_virt_lines_overflow H.set_extmark(buf_id, ns_id, row, 0, opts) end @@ -1456,7 +1459,9 @@ H.draw_overlay_line_worddiff = function(buf_id, ns_id, row, data) --stylua: ignore local ref_opts = { - virt_lines = { virt_line }, virt_lines_above = true, virt_lines_overflow = H.extmark_virt_lines_overflow, + virt_lines = { virt_line }, + virt_lines_above = true, + virt_lines_overflow = H.extmark_virt_lines_overflow, priority = priority, } H.set_extmark(buf_id, ns_id, row, 0, ref_opts) @@ -1470,7 +1475,7 @@ H.draw_overlay_line_worddiff = function(buf_id, ns_id, row, data) H.set_extmark(buf_id, ns_id, row, part[1] - 1 - off, buf_opts) end local context_opts = - { end_row = row + 1, end_col = 0, hl_group = 'MiniDiffOverContextBuf', hl_eol = true, priority = priority - 1 } + { end_row = row + 1, end_col = 0, hl_group = 'MiniDiffOverContextBuf', hl_eol = true, priority = priority - 1 } H.set_extmark(buf_id, ns_id, row, 0, context_opts) end From d563d570c76f0d4f8b24e37e5799fa155a2de46c Mon Sep 17 00:00:00 2001 From: Luis Useche Date: Sat, 28 Jun 2025 22:12:43 -0700 Subject: [PATCH 2/6] refactor(diff): parameterize `git_*` methods for added flexibility the final idea is to add support for `mercurial`, that has a similar structure to git's. Parameterizing these functions makes the addition of mercurial trivial. The patch renames those functions as `s/git_/dvcs_/` to reflect their more generic nature. --- lua/mini/diff.lua | 88 ++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/lua/mini/diff.lua b/lua/mini/diff.lua index d0d8a637..c38639cf 100644 --- a/lua/mini/diff.lua +++ b/lua/mini/diff.lua @@ -647,19 +647,28 @@ MiniDiff.gen_source = {} MiniDiff.gen_source.git = function() local attach = function(buf_id) -- Try attaching to a buffer only once - if H.git_cache[buf_id] ~= nil then return false end + if H.dvcs_cache[buf_id] ~= nil then return false end -- - Possibly resolve symlinks to get data from the original repo local path = H.get_buf_realpath(buf_id) if path == '' then return false end - H.git_cache[buf_id] = {} - H.git_start_watching_index(buf_id, path) + H.dvcs_cache[buf_id] = {} + local watch_index_opts = { + command = 'git', + dvcs_dir_cmd_args = { 'rev-parse', '--path-format=absolute', '--git-dir' }, + index_name = 'index', + dvcs_file_test_spawn_args_fn = function(local_path) + local basename = vim.fn.fnamemodify(local_path, ':t') + return { 'show', ':0:./' .. basename } + end + } + H.dvcs_start_watching_index(buf_id, path, watch_index_opts) end local detach = function(buf_id) - local cache = H.git_cache[buf_id] - H.git_cache[buf_id] = nil - H.git_invalidate_cache(cache) + local cache = H.dvcs_cache[buf_id] + H.dvcs_cache[buf_id] = nil + H.dvcs_invalidate_cache(cache) end local apply_hunks = function(buf_id, hunks) @@ -915,7 +924,7 @@ H.bufs_to_update = {} H.cache = {} -- Cache per buffer for attached `git` source -H.git_cache = {} +H.dvcs_cache = {} -- Cache for operator H.operator_cache = {} @@ -1674,25 +1683,28 @@ H.export_qf = function(opts) return res end --- Git ------------------------------------------------------------------------ -H.git_start_watching_index = function(buf_id, path) +-- DVCS ------------------------------------------------------------------------ +H.dvcs_start_watching_index = function(buf_id, path, watch_index_opts) -- NOTE: Watching single 'index' file is not enough as staging by Git is done -- via "create fresh 'index.lock' file, apply modifications, change file name -- to 'index'". Hence watch the whole '.git' (first level) and react only if -- change was in 'index' file. local stdout = vim.loop.new_pipe() - local args = { 'rev-parse', '--path-format=absolute', '--git-dir' } - local spawn_opts = { args = args, cwd = vim.fn.fnamemodify(path, ':h'), stdio = { nil, stdout, nil } } + local spawn_opts = { + args = watch_index_opts.dvcs_dir_cmd_args, + cwd = vim.fn.fnamemodify(path, ':h'), + stdio = { nil, stdout, nil } + } - -- If path is not in Git, disable buffer but make sure that it will not try + -- If path is not in DVCS, disable buffer but make sure that it will not try -- to re-attach until buffer is properly disabled - local on_not_in_git = vim.schedule_wrap(function() + local on_not_in_dvcs = vim.schedule_wrap(function() if not vim.api.nvim_buf_is_valid(buf_id) then H.cache[buf_id] = nil return end MiniDiff.fail_attach(buf_id) - H.git_cache[buf_id] = {} + H.dvcs_cache[buf_id] = {} end) local process, stdout_feed = nil, {} @@ -1700,48 +1712,51 @@ H.git_start_watching_index = function(buf_id, path) process:close() -- Watch index only if there was no error retrieving path to it - if exit_code ~= 0 or stdout_feed[1] == nil then return on_not_in_git() end + if exit_code ~= 0 or stdout_feed[1] == nil then return on_not_in_dvcs() end -- Set up index watching - local git_dir_path = table.concat(stdout_feed, ''):gsub('\n+$', '') - H.git_setup_index_watch(buf_id, git_dir_path) + local dvcs_dir_path = table.concat(stdout_feed, ''):gsub('\n+$', '') + H.dvcs_setup_index_watch(buf_id, dvcs_dir_path, watch_index_opts) -- Set reference text immediately - H.git_set_ref_text(buf_id) + H.dvcs_set_ref_text(buf_id, watch_index_opts) end - process = vim.loop.spawn('git', spawn_opts, on_exit) - H.git_read_stream(stdout, stdout_feed) + process = vim.loop.spawn(watch_index_opts.command, spawn_opts, on_exit) + H.dvcs_read_stream(stdout, stdout_feed) end -H.git_setup_index_watch = function(buf_id, git_dir_path) +H.dvcs_setup_index_watch = function(buf_id, dvcs_dir_path, watch_index_opts) local buf_fs_event, timer = vim.loop.new_fs_event(), vim.loop.new_timer() - local buf_git_set_ref_text = function() H.git_set_ref_text(buf_id) end + local buf_dvcs_set_ref_text = function() H.dvcs_set_ref_text(buf_id, watch_index_opts) end local watch_index = function(_, filename, _) - if filename ~= 'index' then return end + if filename ~= watch_index_opts.index_name then return end -- Debounce to not overload during incremental staging (like in script) timer:stop() - timer:start(50, 0, buf_git_set_ref_text) + timer:start(50, 0, buf_dvcs_set_ref_text) end - buf_fs_event:start(git_dir_path, { recursive = false }, watch_index) + buf_fs_event:start(dvcs_dir_path, { recursive = false }, watch_index) - H.git_invalidate_cache(H.git_cache[buf_id]) - H.git_cache[buf_id] = { fs_event = buf_fs_event, timer = timer } + H.dvcs_invalidate_cache(H.dvcs_cache[buf_id]) + H.dvcs_cache[buf_id] = { fs_event = buf_fs_event, timer = timer } end -H.git_set_ref_text = vim.schedule_wrap(function(buf_id) +H.dvcs_set_ref_text = vim.schedule_wrap(function(buf_id, watch_index_opts) if not vim.api.nvim_buf_is_valid(buf_id) then return end local buf_set_ref_text = vim.schedule_wrap(function(text) pcall(MiniDiff.set_ref_text, buf_id, text) end) -- NOTE: Do not cache buffer's name to react to its possible rename local path = H.get_buf_realpath(buf_id) if path == '' then return buf_set_ref_text({}) end - local cwd, basename = vim.fn.fnamemodify(path, ':h'), vim.fn.fnamemodify(path, ':t') -- Set local stdout = vim.loop.new_pipe() - local spawn_opts = { args = { 'show', ':0:./' .. basename }, cwd = cwd, stdio = { nil, stdout, nil } } + local spawn_opts = { + args = watch_index_opts.dvcs_file_test_spawn_args_fn(path), + cwd = vim.fn.fnamemodify(path, ':h'), + stdio = { nil, stdout, nil } + } local process, stdout_feed = nil, {} local on_exit = function(exit_code) @@ -1752,7 +1767,7 @@ H.git_set_ref_text = vim.schedule_wrap(function(buf_id) -- - 'Not in index' files (new, ignored, etc.). -- - 'Neither in index nor on disk' files (after checking out commit which -- does not yet have file created). - -- - 'Relative can not be used outside working tree' (when opening file + -- - 'Relative can not be used outside working tree' (e.g. when opening file -- inside '.git' directory). if exit_code ~= 0 or stdout_feed[1] == nil then return buf_set_ref_text({}) end @@ -1761,10 +1776,11 @@ H.git_set_ref_text = vim.schedule_wrap(function(buf_id) buf_set_ref_text(text) end - process = vim.loop.spawn('git', spawn_opts, on_exit) - H.git_read_stream(stdout, stdout_feed) + process = vim.loop.spawn(watch_index_opts.command, spawn_opts, on_exit) + H.dvcs_read_stream(stdout, stdout_feed) end) +-- Git ------------------------------------------------------------------------ H.git_get_path_data = function(path) -- Get path data needed for proper patch header local cwd, basename = vim.fn.fnamemodify(path, ':h'), vim.fn.fnamemodify(path, ':t') @@ -1784,7 +1800,7 @@ H.git_get_path_data = function(path) end process = vim.loop.spawn('git', spawn_opts, on_exit) - H.git_read_stream(stdout, stdout_feed) + H.dvcs_read_stream(stdout, stdout_feed) vim.wait(1000, function() return did_exit end, 1) return res end @@ -1835,7 +1851,7 @@ H.git_apply_patch = function(path_data, patch) stdin:shutdown(function() stdin:close() end) end -H.git_read_stream = function(stream, feed) +H.dvcs_read_stream = function(stream, feed) local callback = function(err, data) if data ~= nil then return table.insert(feed, data) end if err then feed[1] = nil end @@ -1844,7 +1860,7 @@ H.git_read_stream = function(stream, feed) stream:read_start(callback) end -H.git_invalidate_cache = function(cache) +H.dvcs_invalidate_cache = function(cache) if cache == nil then return end pcall(vim.loop.fs_event_stop, cache.fs_event) pcall(vim.loop.timer_stop, cache.timer) From 7d05863717e1ac54365d70fc2195e12976626978 Mon Sep 17 00:00:00 2001 From: Luis Useche Date: Sun, 29 Jun 2025 11:15:43 -0700 Subject: [PATCH 3/6] feat(diff): add mercurial support with `source_gen.hg` --- doc/mini-diff.txt | 29 ++++++++++++++++++++++++- lua/mini/diff.lua | 54 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/doc/mini-diff.txt b/doc/mini-diff.txt index b480655b..ccd8cbf8 100644 --- a/doc/mini-diff.txt +++ b/doc/mini-diff.txt @@ -26,6 +26,13 @@ Features: - "Hunk range under cursor" textobject to be used as operator target. - Navigate to first/previous/next/last hunk. See |MiniDiff.goto_hunk()|. +- Supports three diff sources: + - |MiniDiff.gen_source.git()|: Use git repository as the source to diff + the current buffer. + - |MiniDiff.gen_source.hg()|: Use mercurial repository as the source to + diff the current buffer. + - |MiniDiff.gen_source.save()|: Diff with respect to the file on disk. + What it doesn't do: - Provide functionality to work directly with Git outside of visualizing @@ -533,7 +540,11 @@ Examples: >lua diff.setup({ source = diff.gen_source.save() }) -- Multiple sources (attempted to attach in order) - diff.setup({ source = { diff.gen_source.git(), diff.gen_source.save() } }) + diff.setup({ source = { + diff.gen_source.git(), + diff.gen_source.hg(), + diff.gen_source.save() + } }) < ------------------------------------------------------------------------------ *MiniDiff.gen_source.git()* @@ -553,6 +564,22 @@ Notes: Return ~ `(table)` Source. See |MiniDiff-source-specification|. +------------------------------------------------------------------------------ + *MiniDiff.gen_source.hg()* + `MiniDiff.gen_source.hg`() +Merurial/hg source + +Uses file text from mercurial's `dirstate` as reference. This results in: +- "Add" hunks represent text present in current buffer, but not in hg repo. +- "Change" hunks represent modified text not in hg repo. +- "Delete" hunks represent text deleted from repo. + +Notes: +- Requires Git version at least 6.9.4. + +Return ~ +`(table)` Source. See |MiniDiff-source-specification|. + ------------------------------------------------------------------------------ *MiniDiff.gen_source.none()* `MiniDiff.gen_source.none`() diff --git a/lua/mini/diff.lua b/lua/mini/diff.lua index c38639cf..6b71f866 100644 --- a/lua/mini/diff.lua +++ b/lua/mini/diff.lua @@ -26,6 +26,13 @@ --- - "Hunk range under cursor" textobject to be used as operator target. --- - Navigate to first/previous/next/last hunk. See |MiniDiff.goto_hunk()|. --- +--- - Supports three diff sources: +--- - |MiniDiff.gen_source.git()|: Use git repository as the source to diff +--- the current buffer. +--- - |MiniDiff.gen_source.hg()|: Use mercurial repository as the source to +--- diff the current buffer. +--- - |MiniDiff.gen_source.save()|: Diff with respect to the file on disk. +--- --- What it doesn't do: --- --- - Provide functionality to work directly with Git outside of visualizing @@ -627,7 +634,11 @@ end --- diff.setup({ source = diff.gen_source.save() }) --- --- -- Multiple sources (attempted to attach in order) ---- diff.setup({ source = { diff.gen_source.git(), diff.gen_source.save() } }) +--- diff.setup({ source = { +--- diff.gen_source.git(), +--- diff.gen_source.hg(), +--- diff.gen_source.save() +--- } }) --- < MiniDiff.gen_source = {} @@ -681,6 +692,47 @@ MiniDiff.gen_source.git = function() return { name = 'git', attach = attach, detach = detach, apply_hunks = apply_hunks } end +--- Merurial/hg source +--- +--- Uses file text from mercurial's `dirstate` as reference. This results in: +--- - "Add" hunks represent text present in current buffer, but not in hg repo. +--- - "Change" hunks represent modified text not in hg repo. +--- - "Delete" hunks represent text deleted from repo. +--- +--- Notes: +--- - Requires Git version at least 6.9.4. +--- +---@return table Source. See |MiniDiff-source-specification|. +MiniDiff.gen_source.hg = function() + local attach = function(buf_id) + -- Try attaching to a buffer only once + if H.dvcs_cache[buf_id] ~= nil then return false end + -- - Possibly resolve symlinks to get data from the original repo + local path = H.get_buf_realpath(buf_id) + if path == '' then return false end + + H.dvcs_cache[buf_id] = {} + local watch_index_opts = { + command = 'hg', + dvcs_dir_cmd_args = { 'root', '--template', '{reporoot}/.hg' }, + index_name = 'dirstate', + dvcs_file_test_spawn_args_fn = function(local_path) + local basename = vim.fn.fnamemodify(local_path, ':t') + return { 'cat', basename } + end + } + H.dvcs_start_watching_index(buf_id, path, watch_index_opts) + end + + local detach = function(buf_id) + local cache = H.dvcs_cache[buf_id] + H.dvcs_cache[buf_id] = nil + H.dvcs_invalidate_cache(cache) + end + + return { name = 'hg', attach = attach, detach = detach } +end + --- "Do nothing" source --- --- Allows buffers to be enabled while not setting any reference text. From cf75d303f4b51a8540b20be69ee263dc9965fe47 Mon Sep 17 00:00:00 2001 From: Luis Useche Date: Sun, 29 Jun 2025 13:41:11 -0700 Subject: [PATCH 4/6] fix(diff): nil cache before calling `fail_attach` so that next time we try to use `dvcs_cache`, we can start from scratch. This is important when other `gen_sources` are using `dvcs_cache` as well. --- lua/mini/diff.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/mini/diff.lua b/lua/mini/diff.lua index 6b71f866..0058682b 100644 --- a/lua/mini/diff.lua +++ b/lua/mini/diff.lua @@ -950,7 +950,7 @@ MiniDiff.fail_attach = function(buf_id) -- Try attaching next source buf_cache.source_id = buf_cache.source_id + 1 - local attach_output = H.get_active_source(H.cache[buf_id]).attach(buf_id) + local attach_output = H.get_active_source(buf_cache).attach(buf_id) if attach_output == false then MiniDiff.fail_attach(buf_id) end end @@ -1755,8 +1755,8 @@ H.dvcs_start_watching_index = function(buf_id, path, watch_index_opts) H.cache[buf_id] = nil return end + H.dvcs_cache[buf_id] = nil MiniDiff.fail_attach(buf_id) - H.dvcs_cache[buf_id] = {} end) local process, stdout_feed = nil, {} From 59660fa69e0a8030e4d05bb5fb84ba0f3cace62a Mon Sep 17 00:00:00 2001 From: Luis Useche Date: Sun, 29 Jun 2025 18:47:04 -0700 Subject: [PATCH 5/6] Revert "style(diff): format the `diff.lua` file using `lua_ls`" This reverts commit 0270aa8672d57885a53466b0f0ddde34862f6d8f. --- lua/mini/diff.lua | 57 +++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/lua/mini/diff.lua b/lua/mini/diff.lua index 0058682b..9ab52927 100644 --- a/lua/mini/diff.lua +++ b/lua/mini/diff.lua @@ -984,8 +984,8 @@ H.operator_cache = {} -- Common extmark data for supported styles --stylua: ignore H.style_extmark_data = { - sign = { hl_group_prefix = 'MiniDiffSign', field = 'sign_hl_group' }, - number = { hl_group_prefix = 'MiniDiffSign', field = 'number_hl_group' }, + sign = { hl_group_prefix = 'MiniDiffSign', field = 'sign_hl_group' }, + number = { hl_group_prefix = 'MiniDiffSign', field = 'number_hl_group' }, } -- Suffix for overlay virtual lines to be highlighted as full line @@ -1004,12 +1004,9 @@ H.vimdiff_opts = { result_type = 'indices', ctxlen = 0, interhunkctxlen = 0 } -- reduce noisiness (chosen as slightly less than average English word length) --stylua: ignore H.worddiff_opts = { - algorithm = 'minimal', - result_type = 'indices', - ctxlen = 0, - interhunkctxlen = 4, - indent_heuristic = false, - linematch = 0 + algorithm = 'minimal', result_type = 'indices', + ctxlen = 0, interhunkctxlen = 4, + indent_heuristic = false, linematch = 0 } -- BOM bytes prepended to buffer text if 'bomb' is enabled. See `:h bom-bytes`. @@ -1081,14 +1078,14 @@ H.apply_config = function(config) H.map(modes, mappings.textobject, 'lua MiniDiff.textobject()', { desc = 'Hunk range textobject' }) --stylua: ignore start - H.map({ 'n', 'x' }, mappings.goto_first, "lua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) - H.map('o', mappings.goto_first, "Vlua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) - H.map({ 'n', 'x' }, mappings.goto_prev, "lua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) - H.map('o', mappings.goto_prev, "Vlua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) - H.map({ 'n', 'x' }, mappings.goto_next, "lua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) - H.map('o', mappings.goto_next, "Vlua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) - H.map({ 'n', 'x' }, mappings.goto_last, "lua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) - H.map('o', mappings.goto_last, "Vlua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) + H.map({ 'n', 'x' }, mappings.goto_first, "lua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) + H.map('o', mappings.goto_first, "Vlua MiniDiff.goto_hunk('first')", { desc = 'First hunk' }) + H.map({ 'n', 'x' }, mappings.goto_prev, "lua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) + H.map('o', mappings.goto_prev, "Vlua MiniDiff.goto_hunk('prev')", { desc = 'Previous hunk' }) + H.map({ 'n', 'x' }, mappings.goto_next, "lua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) + H.map('o', mappings.goto_next, "Vlua MiniDiff.goto_hunk('next')", { desc = 'Next hunk' }) + H.map({ 'n', 'x' }, mappings.goto_last, "lua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) + H.map('o', mappings.goto_last, "Vlua MiniDiff.goto_hunk('last')", { desc = 'Last hunk' }) --stylua: ignore end -- Register decoration provider which actually makes visualization @@ -1118,15 +1115,15 @@ H.create_default_hl = function() end local has_core_diff_hl = vim.fn.has('nvim-0.10') == 1 - hi('MiniDiffSignAdd', { link = has_core_diff_hl and 'Added' or 'diffAdded' }) - hi('MiniDiffSignChange', { link = has_core_diff_hl and 'Changed' or 'diffChanged' }) - hi('MiniDiffSignDelete', { link = has_core_diff_hl and 'Removed' or 'diffRemoved' }) - hi('MiniDiffOverAdd', { link = 'DiffAdd' }) - hi('MiniDiffOverChange', { link = 'DiffText' }) - hi('MiniDiffOverChangeBuf', { link = 'MiniDiffOverChange' }) - hi('MiniDiffOverContext', { link = 'DiffChange' }) + hi('MiniDiffSignAdd', { link = has_core_diff_hl and 'Added' or 'diffAdded' }) + hi('MiniDiffSignChange', { link = has_core_diff_hl and 'Changed' or 'diffChanged' }) + hi('MiniDiffSignDelete', { link = has_core_diff_hl and 'Removed' or 'diffRemoved' }) + hi('MiniDiffOverAdd', { link = 'DiffAdd' }) + hi('MiniDiffOverChange', { link = 'DiffText' }) + hi('MiniDiffOverChangeBuf', { link = 'MiniDiffOverChange'}) + hi('MiniDiffOverContext', { link = 'DiffChange' }) hi('MiniDiffOverContextBuf', {}) - hi('MiniDiffOverDelete', { link = 'DiffDelete' }) + hi('MiniDiffOverDelete', { link = 'DiffDelete' }) end H.is_disabled = function(buf_id) @@ -1272,7 +1269,7 @@ H.convert_view_to_extmark_opts = function(view) local field, hl_group_prefix = extmark_data.field, extmark_data.hl_group_prefix --stylua: ignore return { - add = { [field] = hl_group_prefix .. 'Add', sign_text = signs.add, priority = view.priority, invalidate = H.extmark_invalidate }, + add = { [field] = hl_group_prefix .. 'Add', sign_text = signs.add, priority = view.priority, invalidate = H.extmark_invalidate }, change = { [field] = hl_group_prefix .. 'Change', sign_text = signs.change, priority = view.priority, invalidate = H.extmark_invalidate }, delete = { [field] = hl_group_prefix .. 'Delete', sign_text = signs.delete, priority = view.priority, invalidate = H.extmark_invalidate }, } @@ -1458,7 +1455,7 @@ H.append_overlay_change = function(overlay_lines, hunk, ref_lines, buf_lines, pr -- Defer actually computing word diff until in decoration provider as it -- will compute only for displayed lines local data = - { type = 'change_worddiff', ref_line = ref_lines[ref_n], buf_line = buf_lines[buf_n], priority = priority } + { type = 'change_worddiff', ref_line = ref_lines[ref_n], buf_line = buf_lines[buf_n], priority = priority } H.append_overlay(overlay_lines, buf_n, data) end return @@ -1499,7 +1496,7 @@ H.draw_overlay_line = function(buf_id, ns_id, row, data) -- "Change"/"Delete" hunks show affected reference range as virtual lines opts.virt_lines, opts.virt_lines_above, opts.virt_lines_overflow = - data.lines, data.show_above, H.extmark_virt_lines_overflow + data.lines, data.show_above, H.extmark_virt_lines_overflow H.set_extmark(buf_id, ns_id, row, 0, opts) end @@ -1520,9 +1517,7 @@ H.draw_overlay_line_worddiff = function(buf_id, ns_id, row, data) --stylua: ignore local ref_opts = { - virt_lines = { virt_line }, - virt_lines_above = true, - virt_lines_overflow = H.extmark_virt_lines_overflow, + virt_lines = { virt_line }, virt_lines_above = true, virt_lines_overflow = H.extmark_virt_lines_overflow, priority = priority, } H.set_extmark(buf_id, ns_id, row, 0, ref_opts) @@ -1536,7 +1531,7 @@ H.draw_overlay_line_worddiff = function(buf_id, ns_id, row, data) H.set_extmark(buf_id, ns_id, row, part[1] - 1 - off, buf_opts) end local context_opts = - { end_row = row + 1, end_col = 0, hl_group = 'MiniDiffOverContextBuf', hl_eol = true, priority = priority - 1 } + { end_row = row + 1, end_col = 0, hl_group = 'MiniDiffOverContextBuf', hl_eol = true, priority = priority - 1 } H.set_extmark(buf_id, ns_id, row, 0, context_opts) end From de22f9c77f3d99829f88d653595798fa74a766b1 Mon Sep 17 00:00:00 2001 From: Luis Useche Date: Mon, 30 Jun 2025 09:27:48 -0700 Subject: [PATCH 6/6] style(diff): Address commments #issuecomment-3018572261 in particular, this patch fixes: 1. `s/watch_index_opts/opts/` 2. `s/dvcs/vcs/` 3. `s/hg/mercurial/` 4. Format with `stylua` --- doc/mini-diff.txt | 17 +++---- lua/mini/diff.lua | 115 +++++++++++++++++++++++----------------------- 2 files changed, 67 insertions(+), 65 deletions(-) diff --git a/doc/mini-diff.txt b/doc/mini-diff.txt index ccd8cbf8..54613234 100644 --- a/doc/mini-diff.txt +++ b/doc/mini-diff.txt @@ -29,8 +29,8 @@ Features: - Supports three diff sources: - |MiniDiff.gen_source.git()|: Use git repository as the source to diff the current buffer. - - |MiniDiff.gen_source.hg()|: Use mercurial repository as the source to - diff the current buffer. + - |MiniDiff.gen_source.mercurial()|: Use mercurial repository as the + source to diff the current buffer. - |MiniDiff.gen_source.save()|: Diff with respect to the file on disk. What it doesn't do: @@ -542,7 +542,7 @@ Examples: >lua -- Multiple sources (attempted to attach in order) diff.setup({ source = { diff.gen_source.git(), - diff.gen_source.hg(), + diff.gen_source.mercurial(), diff.gen_source.save() } }) < @@ -565,13 +565,14 @@ Return ~ `(table)` Source. See |MiniDiff-source-specification|. ------------------------------------------------------------------------------ - *MiniDiff.gen_source.hg()* - `MiniDiff.gen_source.hg`() -Merurial/hg source + *MiniDiff.gen_source.mercurial()* + `MiniDiff.gen_source.mercurial`() +Merurial source Uses file text from mercurial's `dirstate` as reference. This results in: -- "Add" hunks represent text present in current buffer, but not in hg repo. -- "Change" hunks represent modified text not in hg repo. +- "Add" hunks represent text present in current buffer, but not in + mercurial repo. +- "Change" hunks represent modified text not in mercurial repo. - "Delete" hunks represent text deleted from repo. Notes: diff --git a/lua/mini/diff.lua b/lua/mini/diff.lua index 9ab52927..e6ebc8d6 100644 --- a/lua/mini/diff.lua +++ b/lua/mini/diff.lua @@ -29,8 +29,8 @@ --- - Supports three diff sources: --- - |MiniDiff.gen_source.git()|: Use git repository as the source to diff --- the current buffer. ---- - |MiniDiff.gen_source.hg()|: Use mercurial repository as the source to ---- diff the current buffer. +--- - |MiniDiff.gen_source.mercurial()|: Use mercurial repository as the +--- source to diff the current buffer. --- - |MiniDiff.gen_source.save()|: Diff with respect to the file on disk. --- --- What it doesn't do: @@ -636,7 +636,7 @@ end --- -- Multiple sources (attempted to attach in order) --- diff.setup({ source = { --- diff.gen_source.git(), ---- diff.gen_source.hg(), +--- diff.gen_source.mercurial(), --- diff.gen_source.save() --- } }) --- < @@ -658,28 +658,28 @@ MiniDiff.gen_source = {} MiniDiff.gen_source.git = function() local attach = function(buf_id) -- Try attaching to a buffer only once - if H.dvcs_cache[buf_id] ~= nil then return false end + if H.vcs_cache[buf_id] ~= nil then return false end -- - Possibly resolve symlinks to get data from the original repo local path = H.get_buf_realpath(buf_id) if path == '' then return false end - H.dvcs_cache[buf_id] = {} - local watch_index_opts = { + H.vcs_cache[buf_id] = {} + local opts = { command = 'git', - dvcs_dir_cmd_args = { 'rev-parse', '--path-format=absolute', '--git-dir' }, + vcs_dir_cmd_args = { 'rev-parse', '--path-format=absolute', '--git-dir' }, index_name = 'index', - dvcs_file_test_spawn_args_fn = function(local_path) + vcs_file_test_spawn_args_fn = function(local_path) local basename = vim.fn.fnamemodify(local_path, ':t') return { 'show', ':0:./' .. basename } - end + end, } - H.dvcs_start_watching_index(buf_id, path, watch_index_opts) + H.vcs_start_watching_index(buf_id, path, opts) end local detach = function(buf_id) - local cache = H.dvcs_cache[buf_id] - H.dvcs_cache[buf_id] = nil - H.dvcs_invalidate_cache(cache) + local cache = H.vcs_cache[buf_id] + H.vcs_cache[buf_id] = nil + H.vcs_invalidate_cache(cache) end local apply_hunks = function(buf_id, hunks) @@ -692,45 +692,46 @@ MiniDiff.gen_source.git = function() return { name = 'git', attach = attach, detach = detach, apply_hunks = apply_hunks } end ---- Merurial/hg source +--- Merurial source --- --- Uses file text from mercurial's `dirstate` as reference. This results in: ---- - "Add" hunks represent text present in current buffer, but not in hg repo. ---- - "Change" hunks represent modified text not in hg repo. +--- - "Add" hunks represent text present in current buffer, but not in +--- mercurial repo. +--- - "Change" hunks represent modified text not in mercurial repo. --- - "Delete" hunks represent text deleted from repo. --- --- Notes: --- - Requires Git version at least 6.9.4. --- ---@return table Source. See |MiniDiff-source-specification|. -MiniDiff.gen_source.hg = function() +MiniDiff.gen_source.mercurial = function() local attach = function(buf_id) -- Try attaching to a buffer only once - if H.dvcs_cache[buf_id] ~= nil then return false end + if H.vcs_cache[buf_id] ~= nil then return false end -- - Possibly resolve symlinks to get data from the original repo local path = H.get_buf_realpath(buf_id) if path == '' then return false end - H.dvcs_cache[buf_id] = {} - local watch_index_opts = { + H.vcs_cache[buf_id] = {} + local opts = { command = 'hg', - dvcs_dir_cmd_args = { 'root', '--template', '{reporoot}/.hg' }, + vcs_dir_cmd_args = { 'root', '--template', '{reporoot}/.hg' }, index_name = 'dirstate', - dvcs_file_test_spawn_args_fn = function(local_path) + vcs_file_test_spawn_args_fn = function(local_path) local basename = vim.fn.fnamemodify(local_path, ':t') return { 'cat', basename } - end + end, } - H.dvcs_start_watching_index(buf_id, path, watch_index_opts) + H.vcs_start_watching_index(buf_id, path, opts) end local detach = function(buf_id) - local cache = H.dvcs_cache[buf_id] - H.dvcs_cache[buf_id] = nil - H.dvcs_invalidate_cache(cache) + local cache = H.vcs_cache[buf_id] + H.vcs_cache[buf_id] = nil + H.vcs_invalidate_cache(cache) end - return { name = 'hg', attach = attach, detach = detach } + return { name = 'mercurial', attach = attach, detach = detach } end --- "Do nothing" source @@ -976,7 +977,7 @@ H.bufs_to_update = {} H.cache = {} -- Cache per buffer for attached `git` source -H.dvcs_cache = {} +H.vcs_cache = {} -- Cache for operator H.operator_cache = {} @@ -1730,27 +1731,27 @@ H.export_qf = function(opts) return res end --- DVCS ------------------------------------------------------------------------ -H.dvcs_start_watching_index = function(buf_id, path, watch_index_opts) +-- VCS ------------------------------------------------------------------------ +H.vcs_start_watching_index = function(buf_id, path, opts) -- NOTE: Watching single 'index' file is not enough as staging by Git is done -- via "create fresh 'index.lock' file, apply modifications, change file name -- to 'index'". Hence watch the whole '.git' (first level) and react only if -- change was in 'index' file. local stdout = vim.loop.new_pipe() local spawn_opts = { - args = watch_index_opts.dvcs_dir_cmd_args, + args = opts.vcs_dir_cmd_args, cwd = vim.fn.fnamemodify(path, ':h'), - stdio = { nil, stdout, nil } + stdio = { nil, stdout, nil }, } - -- If path is not in DVCS, disable buffer but make sure that it will not try + -- If path is not in VCS, disable buffer but make sure that it will not try -- to re-attach until buffer is properly disabled - local on_not_in_dvcs = vim.schedule_wrap(function() + local on_not_in_vcs = vim.schedule_wrap(function() if not vim.api.nvim_buf_is_valid(buf_id) then H.cache[buf_id] = nil return end - H.dvcs_cache[buf_id] = nil + H.vcs_cache[buf_id] = nil MiniDiff.fail_attach(buf_id) end) @@ -1759,37 +1760,37 @@ H.dvcs_start_watching_index = function(buf_id, path, watch_index_opts) process:close() -- Watch index only if there was no error retrieving path to it - if exit_code ~= 0 or stdout_feed[1] == nil then return on_not_in_dvcs() end + if exit_code ~= 0 or stdout_feed[1] == nil then return on_not_in_vcs() end -- Set up index watching - local dvcs_dir_path = table.concat(stdout_feed, ''):gsub('\n+$', '') - H.dvcs_setup_index_watch(buf_id, dvcs_dir_path, watch_index_opts) + local vcs_dir_path = table.concat(stdout_feed, ''):gsub('\n+$', '') + H.vcs_setup_index_watch(buf_id, vcs_dir_path, opts) -- Set reference text immediately - H.dvcs_set_ref_text(buf_id, watch_index_opts) + H.vcs_set_ref_text(buf_id, opts) end - process = vim.loop.spawn(watch_index_opts.command, spawn_opts, on_exit) - H.dvcs_read_stream(stdout, stdout_feed) + process = vim.loop.spawn(opts.command, spawn_opts, on_exit) + H.vcs_read_stream(stdout, stdout_feed) end -H.dvcs_setup_index_watch = function(buf_id, dvcs_dir_path, watch_index_opts) +H.vcs_setup_index_watch = function(buf_id, vcs_dir_path, opts) local buf_fs_event, timer = vim.loop.new_fs_event(), vim.loop.new_timer() - local buf_dvcs_set_ref_text = function() H.dvcs_set_ref_text(buf_id, watch_index_opts) end + local buf_vcs_set_ref_text = function() H.vcs_set_ref_text(buf_id, opts) end local watch_index = function(_, filename, _) - if filename ~= watch_index_opts.index_name then return end + if filename ~= opts.index_name then return end -- Debounce to not overload during incremental staging (like in script) timer:stop() - timer:start(50, 0, buf_dvcs_set_ref_text) + timer:start(50, 0, buf_vcs_set_ref_text) end - buf_fs_event:start(dvcs_dir_path, { recursive = false }, watch_index) + buf_fs_event:start(vcs_dir_path, { recursive = false }, watch_index) - H.dvcs_invalidate_cache(H.dvcs_cache[buf_id]) - H.dvcs_cache[buf_id] = { fs_event = buf_fs_event, timer = timer } + H.vcs_invalidate_cache(H.vcs_cache[buf_id]) + H.vcs_cache[buf_id] = { fs_event = buf_fs_event, timer = timer } end -H.dvcs_set_ref_text = vim.schedule_wrap(function(buf_id, watch_index_opts) +H.vcs_set_ref_text = vim.schedule_wrap(function(buf_id, opts) if not vim.api.nvim_buf_is_valid(buf_id) then return end local buf_set_ref_text = vim.schedule_wrap(function(text) pcall(MiniDiff.set_ref_text, buf_id, text) end) @@ -1800,9 +1801,9 @@ H.dvcs_set_ref_text = vim.schedule_wrap(function(buf_id, watch_index_opts) -- Set local stdout = vim.loop.new_pipe() local spawn_opts = { - args = watch_index_opts.dvcs_file_test_spawn_args_fn(path), + args = opts.vcs_file_test_spawn_args_fn(path), cwd = vim.fn.fnamemodify(path, ':h'), - stdio = { nil, stdout, nil } + stdio = { nil, stdout, nil }, } local process, stdout_feed = nil, {} @@ -1823,8 +1824,8 @@ H.dvcs_set_ref_text = vim.schedule_wrap(function(buf_id, watch_index_opts) buf_set_ref_text(text) end - process = vim.loop.spawn(watch_index_opts.command, spawn_opts, on_exit) - H.dvcs_read_stream(stdout, stdout_feed) + process = vim.loop.spawn(opts.command, spawn_opts, on_exit) + H.vcs_read_stream(stdout, stdout_feed) end) -- Git ------------------------------------------------------------------------ @@ -1847,7 +1848,7 @@ H.git_get_path_data = function(path) end process = vim.loop.spawn('git', spawn_opts, on_exit) - H.dvcs_read_stream(stdout, stdout_feed) + H.vcs_read_stream(stdout, stdout_feed) vim.wait(1000, function() return did_exit end, 1) return res end @@ -1898,7 +1899,7 @@ H.git_apply_patch = function(path_data, patch) stdin:shutdown(function() stdin:close() end) end -H.dvcs_read_stream = function(stream, feed) +H.vcs_read_stream = function(stream, feed) local callback = function(err, data) if data ~= nil then return table.insert(feed, data) end if err then feed[1] = nil end @@ -1907,7 +1908,7 @@ H.dvcs_read_stream = function(stream, feed) stream:read_start(callback) end -H.dvcs_invalidate_cache = function(cache) +H.vcs_invalidate_cache = function(cache) if cache == nil then return end pcall(vim.loop.fs_event_stop, cache.fs_event) pcall(vim.loop.timer_stop, cache.timer)