My nvim configuration
| 1 | local M = { |
| 2 | --- Map original buffer to diff buffer |
| 3 | diff_buf_map = {}, |
| 4 | |
| 5 | --- Filetypes to allow diff to stay open on BufEnter events. |
| 6 | allowed_filetypes = {}, |
| 7 | |
| 8 | --- Default settings |
| 9 | --- @class git-extensions.settings |
| 10 | defaults = { |
| 11 | git_bcommit_diff_map = "<C-d>", |
| 12 | leader = "<leader>g", |
| 13 | }, |
| 14 | |
| 15 | --- Final settings |
| 16 | --- @type git-extensions.settings? |
| 17 | settings = nil, |
| 18 | |
| 19 | open_previous_commits = ":Telescope git-extensions previous_commit<cr>", |
| 20 | } |
| 21 | --- Finds the commit for last time the current file was changed. |
| 22 | M.current_file_last_change_commit_hash = function() |
| 23 | local file = vim.fn.expand("%:p") |
| 24 | local diff = vim.fn.system("git diff " .. file) |
| 25 | local has_changed_from_current = vim.trim(diff) ~= "" |
| 26 | local ref = vim.fn.system("git log -1 --format=%H -- " .. vim.fn.shellescape(file)):gsub("%s+", "") |
| 27 | if not has_changed_from_current then |
| 28 | ref = ref .. "~1" |
| 29 | end |
| 30 | return ref |
| 31 | end |
| 32 | |
| 33 | --- Toggle gvdiff from current status to last time the file was changed. |
| 34 | --- Example: The file was last changed 5 commits ago. |
| 35 | M.toggle_last_gvdiff = function() |
| 36 | local original_buf = vim.api.nvim_get_current_buf() |
| 37 | if M.diff_buf_map[original_buf] == nil then |
| 38 | local rev = M.current_file_last_change_commit_hash() |
| 39 | vim.cmd(":Gvdiff " .. rev) |
| 40 | M.diff_buf_map[original_buf] = vim.api.nvim_get_current_buf() |
| 41 | vim.cmd("wincmd l") |
| 42 | vim.defer_fn(function() |
| 43 | vim.cmd("diffupdate") |
| 44 | end, 50) |
| 45 | else |
| 46 | vim.api.nvim_buf_delete(M.diff_buf_map[original_buf], {}) |
| 47 | M.diff_buf_map[original_buf] = nil |
| 48 | end |
| 49 | end |
| 50 | |
| 51 | --- Set quickfix list with global hunks and go the next one. |
| 52 | M.next_global_hunk = function() |
| 53 | vim.cmd("Gitsigns setqflist all") |
| 54 | vim.defer_fn(function() |
| 55 | local ok, _ = pcall(vim.cmd.cnext) |
| 56 | if not ok then |
| 57 | pcall(vim.cmd.cfirst) |
| 58 | end |
| 59 | pcall(vim.cmd.cclose) |
| 60 | end, 50) |
| 61 | end |
| 62 | |
| 63 | --- Set quickfix list with global hunks and go to the previous one. |
| 64 | M.prev_global_hunk = function() |
| 65 | local ok, _ = vim.cmd("Gitsigns setqflist all") |
| 66 | vim.defer_fn(function() |
| 67 | if not ok then |
| 68 | pcall(vim.cmd.clast) |
| 69 | end |
| 70 | pcall(vim.cmd.cclose) |
| 71 | end, 50) |
| 72 | end |
| 73 | |
| 74 | local |
| 75 | for _, win in ipairs(vim.api.nvim_list_wins()) do |
| 76 | if vim.api.nvim_win_get_buf(win) == buf then |
| 77 | return true |
| 78 | end |
| 79 | end |
| 80 | return false |
| 81 | end |
| 82 | |
| 83 | --- Clear diff buffer when changing buffers. |
| 84 | --- Example: A gitdiff is open and I open a new file. I don't want to see the diff |
| 85 | --- on the old file anymore. |
| 86 | M.clear_diff_bufs = function() |
| 87 | local current_buf = vim.api.nvim_get_current_buf() |
| 88 | for o, buf in pairs(M.diff_buf_map) do |
| 89 | if (o ~= current_buf and buf ~= current_buf) or not is_buf_visible(buf) then |
| 90 | vim.api.nvim_buf_delete(buf, {}) |
| 91 | M.diff_buf_map[o] = nil |
| 92 | end |
| 93 | end |
| 94 | end |
| 95 | |
| 96 | --- Telescope git_bcommit with Gvdiff with settings.git_bcommit_diff_map |
| 97 | M.extended_bcommit_picker = function() |
| 98 | require("telescope.builtin").git_bcommits({ |
| 99 | attach_mappings = function(_, map) |
| 100 | local keymap = M.settings and M.settings.git_bcommit_diff_map or M.defaults.git_bcommit_diff_map |
| 101 | local actions = require("telescope.actions") |
| 102 | local action_state = require("telescope.actions.state") |
| 103 | map("i", keymap, function(prompt_bufnr) |
| 104 | M.opening_diff_buf = true |
| 105 | local entry = action_state.get_selected_entry() |
| 106 | actions.close(prompt_bufnr) |
| 107 | vim.cmd("Gvdiff " .. entry.value) |
| 108 | local diff_buf = vim.api.nvim_get_current_buf() |
| 109 | vim.cmd("wincmd l") |
| 110 | vim.defer_fn(function() |
| 111 | vim.cmd("diffupdate") |
| 112 | end, 50) |
| 113 | M.diff_buf_map[vim.api.nvim_get_current_buf()] = diff_buf |
| 114 | end) |
| 115 | |
| 116 | return true |
| 117 | end, |
| 118 | }) |
| 119 | end |
| 120 | |
| 121 | --- Registers a key with the leader from settings. |
| 122 | M.register_keymap = function(key, command, desc) |
| 123 | local leader = M.settings and M.settings.leader or M.defaults.leader |
| 124 | vim.keymap.set("n", leader .. key, command, { desc = desc }) |
| 125 | end |
| 126 | |
| 127 | --- Sets up the extension. Make sure to set up `lewis6991/gitsigns.nvim` |
| 128 | --- and `tpope/vim-fugitive` first. |
| 129 | --- @param opts git-extensions.settings? |
| 130 | M.setup = function(opts) |
| 131 | M.settings = opts |
| 132 | -- Leader maps |
| 133 | M.register_keymap("h", ":Gitsigns preview_hunk<cr>", "Preview Hunk") |
| 134 | M.register_keymap("b", M.extended_bcommit_picker, "Buffer Commits") |
| 135 | M.register_keymap("g", ":Git<cr>", "Fugitive") |
| 136 | M.register_keymap("s", ":Telescope git_status<cr>", "Git Status") |
| 137 | M.register_keymap("l", M.toggle_last_gvdiff, "Toggle Diff") |
| 138 | M.register_keymap("P", M.open_previous_commits, "Previous Commit Files") |
| 139 | M.register_keymap("g", ":G<cr>", "Fugitive") |
| 140 | M.register_keymap("q", ":Gitsigns setqflist all<cr>", "Quickfix All Hunks") |
| 141 | M.register_keymap("c", ":Gitsigns setqflist<cr>", "Quickfix Current Buffer Hunks") |
| 142 | M.register_keymap("a", ":Git add -A<cr>", "Add All Files") |
| 143 | M.register_keymap("p", ":Git push<cr>", "Push") |
| 144 | M.register_keymap("x", function() |
| 145 | vim.fn.system("git add -A") |
| 146 | require("claude.commit").commit() |
| 147 | end, "Add All + Claude Commit") |
| 148 | |
| 149 | -- Jump maps |
| 150 | vim.keymap.set("n", "]h", ":Gitsigns next_hunk<cr>", { desc = "Next Hunk" }) |
| 151 | vim.keymap.set("n", "[h", ":Gitsigns prev_hunk<cr>", { desc = "Prev Hunk" }) |
| 152 | vim.keymap.set("n", "]H", M.next_global_hunk, { desc = "Global Hunk" }) |
| 153 | vim.keymap.set("n", "[H", M.prev_global_hunk, { desc = "Global Hunk" }) |
| 154 | |
| 155 | -- Autocmds |
| 156 | vim.api.nvim_create_autocmd({ "BufEnter", "WinClosed" }, { |
| 157 | callback = function() |
| 158 | M.clear_diff_bufs() |
| 159 | end, |
| 160 | }) |
| 161 | end |
| 162 | |
| 163 | return M |