irongit

My nvim configuration

hygo-nvim/lua/extensions/git-extensions.lua
163 lines5.3 KBLua
1local 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.
22M.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
31end
32
33--- Toggle gvdiff from current status to last time the file was changed.
34--- Example: The file was last changed 5 commits ago.
35M.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
49end
50
51--- Set quickfix list with global hunks and go the next one.
52M.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)
61end
62
63--- Set quickfix list with global hunks and go to the previous one.
64M.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)
72end
73
74local function is_buf_visible(buf)
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
81end
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.
86M.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
94end
95
96--- Telescope git_bcommit with Gvdiff with settings.git_bcommit_diff_map
97M.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 })
119end
120
121--- Registers a key with the leader from settings.
122M.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 })
125end
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?
130M.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 })
161end
162
163return M