From 63653af274f759bcfd70244f0fb5dc474c72b1ab Mon Sep 17 00:00:00 2001 From: lnc3l0t Date: Fri, 16 Jun 2023 12:12:34 +0200 Subject: [PATCH] feat(actions): preview inline diff on hover By calling `Gitsigns automatic_hunk_inline` or by enabling `Config.automatic_hunk_inline` when the cursor is on an hunk (added, modified or removed) `preview_hunk_inline` is called automatically. If the cursor moves inside the hunk's bounds the extmark doesn't flicker, if it exits the hunk the extmarks are cleared automatically. It works via an autocommand triggering on CursorMoved. It takes `_inline2` into consideration. `Config.automatic_hunk_inline` is an option to enable the functionality by default on new buffers. --- README.md | 4 ++- doc/gitsigns.txt | 18 +++++++++++- lua/gitsigns/actions.lua | 61 +++++++++++++++++++++++++++++++--------- lua/gitsigns/attach.lua | 4 +++ lua/gitsigns/config.lua | 13 +++++++++ 5 files changed, 85 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a8ce1a159..3c4af5d58 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ require('gitsigns').setup { ignore_whitespace = false, }, current_line_blame_formatter = ', - ', + automatic_hunk_inline = false, -- `:h gitsigns-config-automatic_hunk_inline` sign_priority = 6, update_debounce = 100, status_formatter = nil, -- Use default @@ -177,6 +178,7 @@ require('gitsigns').setup { map('n', 'hu', 'Gitsigns undo_stage_hunk') map('n', 'hR', 'Gitsigns reset_buffer') map('n', 'hp', 'Gitsigns preview_hunk') + map('n', 'hP', 'Gitsigns automatic_hunk_inline') map('n', 'hb', 'lua require"gitsigns".blame_line{full=true}') map('n', 'tb', 'Gitsigns toggle_current_line_blame') map('n', 'hd', 'Gitsigns diffthis') @@ -231,7 +233,7 @@ Ensures signs are always up to date | :white_check_mark: * Never saves the buffer | :white_check_mark: | :white_check_mark: :heavy_exclamation_mark: * | * Writes [buffer](https://github.com/airblade/vim-gitgutter/blob/0f98634b92da9a35580b618c11a6d2adc42d9f90/autoload/gitgutter/diff.vim#L106) (and index) to short lived temp files Quick jumping between hunks | :white_check_mark: | :white_check_mark: | Stage/reset/preview individual hunks | :white_check_mark: | :white_check_mark: | -Preview hunks directly in the buffer (inline) | :white_check_mark: * | | * Via `preview_hunk_inline` +Preview hunks directly in the buffer (inline) | :white_check_mark: * | | * Via `preview_hunk_inline` on demand or on hover via `automatic_hunk_inline` Stage/reset hunks in range/selection | :white_check_mark: | :white_check_mark: :heavy_exclamation_mark: * | * Only stage Stage/reset all hunks in buffer | :white_check_mark: | | Undo staged hunks | :white_check_mark: | | diff --git a/doc/gitsigns.txt b/doc/gitsigns.txt index 5f058d326..79e4f0cab 100644 --- a/doc/gitsigns.txt +++ b/doc/gitsigns.txt @@ -32,7 +32,7 @@ For basic setup with all batteries included: Configuration can be passed to the setup function. Here is an example with most of the default settings: -> +>lua require('gitsigns').setup { signs = { add = { text = '│' }, @@ -59,6 +59,7 @@ of the default settings: ignore_whitespace = false, }, current_line_blame_formatter = ', - ', + automatic_hunk_inline = false, -- `:h gitsigns-config-automatic_hunk_inline` sign_priority = 6, update_debounce = 100, status_formatter = nil, -- Use default @@ -334,6 +335,13 @@ select_hunk() *gitsigns.select_hunk()* preview_hunk_inline() *gitsigns.preview_hunk_inline()* Preview the hunk at the cursor position inline in the buffer. +automatic_hunk_inline() *gitsigns.automatic_hunk_inline()* + Preview the hunk at the current cursor position inline in the + buffer automatically every time the CursorMoved event is + triggered. This function enables or disables the inline diff + functionality based on whether the cursor is inside or outside + of a hunk. + preview_hunk() *gitsigns.preview_hunk()* Preview the hunk at the cursor position in a floating window. If the preview is already open, calling this @@ -919,6 +927,14 @@ debug_mode *gitsigns-config-debug_mode* Enables debug logging and makes the following functions available: `dump_cache`, `debug_messages`, `clear_debug`. +automatic_hunk_inline *gitsigns-config-automatic_hunk_inline* + Type: `boolean`, Default: `false` + + The `automatic_hunk_inline` function enables automatic inline preview of + a hunk's diff when the cursor is inside the hunk. The preview is removed + when the cursor moves outside the hunk. This option ensures that any + newly attached buffer will have this functionality enabled by default, + without the need to manually call `Gitsigns automatic_hunk_inline`. ============================================================================== HIGHLIGHT GROUPS *gitsigns-highlight-groups* diff --git a/lua/gitsigns/actions.lua b/lua/gitsigns/actions.lua index 411cb4323..213946ccb 100644 --- a/lua/gitsigns/actions.lua +++ b/lua/gitsigns/actions.lua @@ -675,7 +675,13 @@ M.preview_hunk = noautocmd(function() popup.create(lines_spec, config.preview_config, 'hunk') end) +local preview_hunk_state = { prev_hunk = nil, floating = nil, keep = false } + local function clear_preview_inline(bufnr) + preview_hunk_state.prev_hunk = nil + if preview_hunk_state.floating then + pcall(api.nvim_win_close, preview_hunk_state.floating, true) + end api.nvim_buf_clear_namespace(bufnr, ns_inline, 0, -1) end @@ -686,32 +692,34 @@ M.preview_hunk_inline = function() local hunk = get_cursor_hunk(bufnr) if not hunk then + clear_preview_inline(bufnr) return end + if hunk == preview_hunk_state.prev_hunk then return end + clear_preview_inline(bufnr) + preview_hunk_state.prev_hunk = hunk - local winid ---@type integer manager.show_added(bufnr, ns_inline, hunk) if config._inline2 then if hunk.removed.count > 0 then - winid = manager.show_deleted_in_float(bufnr, ns_inline, hunk) + preview_hunk_state.floating = manager.show_deleted_in_float(bufnr, ns_inline, hunk) end else manager.show_deleted(bufnr, ns_inline, hunk) end - api.nvim_create_autocmd({ 'CursorMoved', 'InsertEnter' }, { - buffer = bufnr, - desc = 'Clear gitsigns inline preview', - callback = function() - if winid then - pcall(api.nvim_win_close, winid, true) - end - clear_preview_inline(bufnr) - end, - once = true, - }) + if not preview_hunk_state.keep then + api.nvim_create_autocmd({ 'CursorMoved', 'InsertEnter' }, { + buffer = bufnr, + desc = 'Clear gitsigns inline preview', + callback = function() + clear_preview_inline(bufnr) + end, + once = true, + }) + end -- Virtual lines will be hidden if cursor is on the top row, so automatically -- scroll the viewport. @@ -722,6 +730,33 @@ M.preview_hunk_inline = function() end end + +--- Preview the hunk at the current cursor position inline in the buffer automatically +--- every time the CursorMoved event is triggered. This function enables or disables the inline +--- diff functionality based on whether the cursor is inside or outside of a hunk. +M.automatic_hunk_inline = noautocmd(function() + local bufnr = current_buf() + if not cache[bufnr] then return end + + local group = api.nvim_create_augroup("gitsigns_automatic_hunk_inline", { clear = false }) + + -- If the autocmd group already has registered autocmds, clear them and disable the preview + if #api.nvim_get_autocmds({ group = group, buffer = bufnr }) > 0 then + api.nvim_clear_autocmds({ group = group, buffer = bufnr }) + clear_preview_inline(bufnr) + preview_hunk_state = {} + else + -- Enable the preview + preview_hunk_state.keep = true + api.nvim_create_autocmd("CursorMoved", { + group = group, + buffer = bufnr, + callback = M.preview_hunk_inline, + desc = "Automatically show inline diff on hover" + }) + end +end) + --- Select the hunk under the cursor. M.select_hunk = function() local hunk = get_cursor_hunk() diff --git a/lua/gitsigns/attach.lua b/lua/gitsigns/attach.lua index b6e304e82..458a9aedc 100644 --- a/lua/gitsigns/attach.lua +++ b/lua/gitsigns/attach.lua @@ -356,6 +356,10 @@ local attach_throttled = throttle_by_id(function(cbuf, ctx, aucmd) if config.keymaps and not vim.tbl_isempty(config.keymaps) then require('gitsigns.mappings')(config.keymaps, cbuf) end + + if config.automatic_hunk_inline then + require('gitsigns.actions').automatic_hunk_inline() + end end) --- Detach Gitsigns from all buffers it is attached to. diff --git a/lua/gitsigns/config.lua b/lua/gitsigns/config.lua index bd791dfe5..7d36be1af 100644 --- a/lua/gitsigns/config.lua +++ b/lua/gitsigns/config.lua @@ -77,6 +77,7 @@ end --- @field worktrees {toplevel: string, gitdir: string}[] --- @field word_diff boolean --- @field keymaps table +--- @field automatic_hunk_inline boolean --- -- Undocumented --- @field _refresh_staged_on_update boolean --- @field _blame_cache boolean @@ -762,6 +763,18 @@ M.schema = { ]], }, + automatic_hunk_inline = { + type = 'boolean', + default = false, + description = [[ + The `automatic_hunk_inline` function enables automatic inline preview of + a hunk's diff when the cursor is inside the hunk. The preview is removed + when the cursor moves outside the hunk. This option ensures that any + newly attached buffer will have this functionality enabled by default, + without the need to manually call `Gitsigns automatic_hunk_inline`. + ]], + }, + _refresh_staged_on_update = { type = 'boolean', default = false,