From 378c3622a8b1b1931df43bd94ff8bfa639d84b2e Mon Sep 17 00:00:00 2001 From: Oscar Wallberg Date: Sun, 10 May 2026 01:58:37 +0200 Subject: [PATCH] refactor(git): adopt lazy-require plugin/git.lua pattern --- init.lua | 2 - lua/core/keymap.lua | 32 ++--- lua/git/init.lua | 187 ------------------------- lua/git/statusline.lua | 43 +++--- plugin/git.lua | 251 +++++++++++++++++++++++++++++++++- test/git/cmd_test.lua | 2 - test/git/object_test.lua | 2 - test/git/repo_test.lua | 2 - test/git/status_view_test.lua | 2 - test/runner.lua | 4 + 10 files changed, 279 insertions(+), 248 deletions(-) delete mode 100644 lua/git/init.lua diff --git a/init.lua b/init.lua index d0fe2a4..1912fb4 100644 --- a/init.lua +++ b/init.lua @@ -95,5 +95,3 @@ require("ts").setup({ "https://github.com/tree-sitter-grammars/tree-sitter-yaml", "https://github.com/georgeharker/tree-sitter-zsh", }) - -require("git.statusline").enable() diff --git a/lua/core/keymap.lua b/lua/core/keymap.lua index 1d60ad0..f0b40c7 100644 --- a/lua/core/keymap.lua +++ b/lua/core/keymap.lua @@ -227,27 +227,11 @@ vim.keymap.set("n", "fD", vim.diagnostic.setqflist) vim.keymap.set("n", "grt", vim.lsp.buf.type_definition) vim.keymap.set("n", "gd", vim.lsp.buf.definition) -vim.keymap.set("n", "gd", function() - require("git.diff").split({ vertical = true }) -end) -vim.keymap.set("n", "gD", function() - require("git.diff").split({ rev = "HEAD", vertical = true }) -end) -vim.keymap.set("n", "gh", function() - require("git.diff").split({ vertical = false }) -end) -vim.keymap.set("n", "gH", function() - require("git.diff").split({ rev = "HEAD", vertical = false }) -end) -vim.keymap.set("n", "gg", function() - require("git.status_view").toggle() -end) -vim.keymap.set("n", "gc", function() - require("git.commit").commit() -end) -vim.keymap.set("n", "ga", function() - require("git.commit").commit({ args = { "--amend" } }) -end) -vim.keymap.set("n", "gl", function() - require("git.log_view").open({ max_count = 1000 }) -end) +vim.keymap.set("n", "gd", "(git-diff-vertical)") +vim.keymap.set("n", "gD", "(git-diff-vertical-head)") +vim.keymap.set("n", "gh", "(git-diff-horizontal)") +vim.keymap.set("n", "gH", "(git-diff-horizontal-head)") +vim.keymap.set("n", "gg", "(git-status-toggle)") +vim.keymap.set("n", "gc", "(git-commit)") +vim.keymap.set("n", "ga", "(git-commit-amend)") +vim.keymap.set("n", "gl", "(git-log)") diff --git a/lua/git/init.lua b/lua/git/init.lua deleted file mode 100644 index 0cba633..0000000 --- a/lua/git/init.lua +++ /dev/null @@ -1,187 +0,0 @@ -local DEFAULT_HIGHLIGHTS = { - GitIgnored = "Comment", - GitSha = "Identifier", - GitStaged = "Constant", - GitUnmerged = "Todo", - GitUnpulled = "Removed", - GitUnpushed = "Added", - GitUnstaged = "Changed", - GitUntracked = "Added", - - GitStagedAdded = "GitStaged", - GitStagedCopied = "GitStaged", - GitStagedDeleted = "GitStaged", - GitStagedModified = "GitStaged", - GitStagedRenamed = "GitStaged", - GitStagedTypeChanged = "GitStaged", - - GitUnstagedAdded = "GitUnstaged", - GitUnstagedCopied = "GitUnstaged", - GitUnstagedDeleted = "Removed", - GitUnstagedModified = "GitUnstaged", - GitUnstagedRenamed = "GitStaged", - GitUnstagedTypeChanged = "GitUnstaged", - - GitUnmergedAddedByThem = "GitUnmerged", - GitUnmergedAddedByUs = "GitUnmerged", - GitUnmergedBothAdded = "GitUnmerged", - GitUnmergedBothDeleted = "GitUnmerged", - GitUnmergedBothModified = "GitUnmerged", - GitUnmergedDeletedByThem = "GitUnmerged", - GitUnmergedDeletedByUs = "GitUnmerged", -} - -local M = {} - -function M.init() - for name, link in pairs(DEFAULT_HIGHLIGHTS) do - vim.api.nvim_set_hl(0, name, { link = link, default = true }) - end - - local group = vim.api.nvim_create_augroup("ow.git", { clear = true }) - - vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, { - group = group, - callback = function(args) - require("git.repo").track(args.buf) - end, - }) - vim.api.nvim_create_autocmd({ "BufWritePost", "FileChangedShellPost" }, { - group = group, - callback = function(args) - require("git.repo").refresh(args.buf) - end, - }) - vim.api.nvim_create_autocmd({ "BufDelete", "BufWipeout" }, { - group = group, - callback = function(args) - require("git.repo").unbind(args.buf) - end, - }) - vim.api.nvim_create_autocmd("FocusGained", { - group = group, - callback = function() - require("git.repo").refresh_all() - end, - }) - vim.api.nvim_create_autocmd("VimLeavePre", { - group = group, - callback = function() - require("git.repo").stop_all() - end, - }) - - vim.api.nvim_create_autocmd({ "VimEnter", "DirChanged", "TabEnter" }, { - group = group, - callback = function() - require("git.repo").update_cwd_repo() - end, - }) - vim.api.nvim_create_autocmd("TabClosed", { - group = group, - callback = function(args) - local tab = tonumber(args.file) --[[@as integer?]] - if tab then - require("git.repo").release_tab(tab) - end - end, - }) - - vim.api.nvim_create_autocmd("BufReadCmd", { - pattern = "git://*", - group = group, - callback = function(args) - require("git.object").read_uri(args.buf) - end, - }) - - vim.api.nvim_create_autocmd("BufReadCmd", { - pattern = "gitlog://*", - group = group, - callback = function(args) - require("git.log_view").read_uri(args.buf) - end, - }) - vim.api.nvim_create_autocmd("BufReadCmd", { - pattern = "gitstatus://*", - group = group, - callback = function(args) - require("git.status_view").read_uri(args.buf) - end, - }) - vim.api.nvim_create_user_command("Glog", function(opts) - require("git.log_view").run_glog(opts) - end, { - nargs = "*", - complete = function(...) - return require("git.log_view").complete_glog(...) - end, - desc = "Show git log", - }) - - local function diff_split_cmd(vertical) - return function(opts) - require("git.diff").split({ - rev = opts.args ~= "" and opts.args or nil, - vertical = vertical, - }) - end - end - local function complete_rev(...) - return require("git.cmd").complete_rev(...) - end - vim.api.nvim_create_user_command("Gdiffsplit", diff_split_cmd(true), { - nargs = "?", - complete = complete_rev, - desc = "Diff against ", - }) - vim.api.nvim_create_user_command("Gvdiffsplit", diff_split_cmd(true), { - nargs = "?", - complete = complete_rev, - desc = "Diff against (vertical split)", - }) - vim.api.nvim_create_user_command("Ghdiffsplit", diff_split_cmd(false), { - nargs = "?", - complete = complete_rev, - desc = "Diff against (horizontal split)", - }) - vim.api.nvim_create_user_command("Gedit", function(opts) - vim.cmd.edit({ - args = { require("git.object").URI_PREFIX .. opts.args }, - magic = { file = false }, - }) - end, { - nargs = 1, - complete = complete_rev, - desc = "Edit a git object ()", - }) - - vim.api.nvim_create_user_command("G", function(opts) - local cmd = require("git.cmd") - cmd.run(cmd.parse_args(opts.args), { bang = opts.bang }) - end, { - nargs = "*", - bang = true, - complete = function(...) - return require("git.cmd").complete(...) - end, - }) - - vim.api.nvim_create_user_command("Grefresh", function() - require("git.repo").refresh_all() - end, { desc = "Refresh git status for all repos" }) - - vim.api.nvim_create_user_command("Gstatus", function(opts) - require("git.status_view").open({ - placement = opts.fargs[1] --[[@as ow.Git.StatusView.Placement]] or "split", - }) - end, { - nargs = "?", - complete = function() - return require("git.status_view").PLACEMENTS - end, - desc = "Open git status view", - }) -end - -return M diff --git a/lua/git/statusline.lua b/lua/git/statusline.lua index 0c14567..d7265e6 100644 --- a/lua/git/statusline.lua +++ b/lua/git/statusline.lua @@ -57,35 +57,26 @@ local function update_buf(buf, r) vim.b[buf].git_status_string = render(entry) end -local enabled = false - -function M.enable() - if enabled then - return - end - enabled = true - repo.on("refresh", function(r) - for buf in pairs(r.buffers) do - if vim.api.nvim_buf_is_loaded(buf) then - update_buf(buf, r) - end - end - vim.cmd.redrawstatus({ bang = true }) - end) - vim.api.nvim_create_autocmd("BufWinEnter", { - group = vim.api.nvim_create_augroup( - "ow.git.statusline", - { clear = true } - ), - callback = function(args) - update_buf(args.buf, nil) - end, - }) - for _, buf in ipairs(vim.api.nvim_list_bufs()) do +repo.on("refresh", function(r) + for buf in pairs(r.buffers) do if vim.api.nvim_buf_is_loaded(buf) then - update_buf(buf, nil) + update_buf(buf, r) end end + vim.cmd.redrawstatus({ bang = true }) +end) + +vim.api.nvim_create_autocmd("BufWinEnter", { + group = vim.api.nvim_create_augroup("ow.git.statusline", { clear = true }), + callback = function(args) + update_buf(args.buf, nil) + end, +}) + +for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_loaded(buf) then + update_buf(buf, nil) + end end return M diff --git a/plugin/git.lua b/plugin/git.lua index 8d38679..866dc37 100644 --- a/plugin/git.lua +++ b/plugin/git.lua @@ -3,4 +3,253 @@ if vim.g.loaded_git then end vim.g.loaded_git = 1 -require("git").init() +local DEFAULT_HIGHLIGHTS = { + GitIgnored = "Comment", + GitSha = "Identifier", + GitStaged = "Constant", + GitUnmerged = "Todo", + GitUnpulled = "Removed", + GitUnpushed = "Added", + GitUnstaged = "Changed", + GitUntracked = "Added", + + GitStagedAdded = "GitStaged", + GitStagedCopied = "GitStaged", + GitStagedDeleted = "GitStaged", + GitStagedModified = "GitStaged", + GitStagedRenamed = "GitStaged", + GitStagedTypeChanged = "GitStaged", + + GitUnstagedAdded = "GitUnstaged", + GitUnstagedCopied = "GitUnstaged", + GitUnstagedDeleted = "Removed", + GitUnstagedModified = "GitUnstaged", + GitUnstagedRenamed = "GitStaged", + GitUnstagedTypeChanged = "GitUnstaged", + + GitUnmergedAddedByThem = "GitUnmerged", + GitUnmergedAddedByUs = "GitUnmerged", + GitUnmergedBothAdded = "GitUnmerged", + GitUnmergedBothDeleted = "GitUnmerged", + GitUnmergedBothModified = "GitUnmerged", + GitUnmergedDeletedByThem = "GitUnmerged", + GitUnmergedDeletedByUs = "GitUnmerged", +} +for name, link in pairs(DEFAULT_HIGHLIGHTS) do + vim.api.nvim_set_hl(0, name, { link = link, default = true }) +end + +local group = vim.api.nvim_create_augroup("ow.git", { clear = true }) + +vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, { + group = group, + callback = function(args) + require("git.repo").track(args.buf) + end, +}) +vim.api.nvim_create_autocmd({ "BufWritePost", "FileChangedShellPost" }, { + group = group, + callback = function(args) + require("git.repo").refresh(args.buf) + end, +}) +vim.api.nvim_create_autocmd({ "BufDelete", "BufWipeout" }, { + group = group, + callback = function(args) + require("git.repo").unbind(args.buf) + end, +}) +vim.api.nvim_create_autocmd("FocusGained", { + group = group, + callback = function() + require("git.repo").refresh_all() + end, +}) +vim.api.nvim_create_autocmd("VimLeavePre", { + group = group, + callback = function() + require("git.repo").stop_all() + end, +}) +vim.api.nvim_create_autocmd({ "VimEnter", "DirChanged", "TabEnter" }, { + group = group, + callback = function() + require("git.repo").update_cwd_repo() + end, +}) +vim.api.nvim_create_autocmd("TabClosed", { + group = group, + callback = function(args) + local tab = tonumber(args.file) --[[@as integer?]] + if tab then + require("git.repo").release_tab(tab) + end + end, +}) + +vim.api.nvim_create_autocmd("BufReadCmd", { + pattern = "git://*", + group = group, + callback = function(args) + require("git.object").read_uri(args.buf) + end, +}) +vim.api.nvim_create_autocmd("BufReadCmd", { + pattern = "gitlog://*", + group = group, + callback = function(args) + require("git.log_view").read_uri(args.buf) + end, +}) +vim.api.nvim_create_autocmd("BufReadCmd", { + pattern = "gitstatus://*", + group = group, + callback = function(args) + require("git.status_view").read_uri(args.buf) + end, +}) + +vim.api.nvim_create_user_command("G", function(opts) + local cmd = require("git.cmd") + cmd.run(cmd.parse_args(opts.args), { bang = opts.bang }) +end, { + nargs = "*", + bang = true, + complete = function(...) + return require("git.cmd").complete(...) + end, +}) + +vim.api.nvim_create_user_command("Grefresh", function() + require("git.repo").refresh_all() +end, { desc = "Refresh git status for all repos" }) + +vim.api.nvim_create_user_command("Glog", function(opts) + require("git.log_view").run_glog(opts) +end, { + nargs = "*", + complete = function(...) + return require("git.log_view").complete_glog(...) + end, + desc = "Show git log", +}) + +local function complete_rev(...) + return require("git.cmd").complete_rev(...) +end + +local DIFF_DIRECTIONS = { "vertical", "horizontal" } + +local function default_vertical() + return vim.tbl_contains(vim.opt.diffopt:get(), "vertical") +end + +vim.api.nvim_create_user_command("Gdiffsplit", function(opts) + local fargs = opts.fargs + local vertical = default_vertical() + local rev_idx = 1 + if fargs[1] == "vertical" then + vertical = true + rev_idx = 2 + elseif fargs[1] == "horizontal" then + vertical = false + rev_idx = 2 + end + require("git.diff").split({ rev = fargs[rev_idx], vertical = vertical }) +end, { + nargs = "*", + complete = function(arg_lead, cmd_line, _) + local rest = cmd_line:gsub("^%s*%S+%s*", "", 1) + local trailing_space = rest == "" or rest:sub(-1):match("%s") ~= nil + local tokens = vim.split(vim.trim(rest), "%s+", { trimempty = true }) + local prior = trailing_space and tokens + or vim.list_slice(tokens, 1, #tokens - 1) + local results = {} + if #prior == 0 then + for _, d in ipairs(DIFF_DIRECTIONS) do + if vim.startswith(d, arg_lead) then + table.insert(results, d) + end + end + end + local first_is_direction = prior[1] == "vertical" + or prior[1] == "horizontal" + if #prior == 0 or (#prior == 1 and first_is_direction) then + vim.list_extend(results, complete_rev(arg_lead)) + end + return results + end, + desc = "Open a diff split", +}) +vim.api.nvim_create_user_command("Gedit", function(opts) + vim.cmd.edit({ + args = { require("git.object").URI_PREFIX .. opts.args }, + magic = { file = false }, + }) +end, { + nargs = 1, + complete = complete_rev, + desc = "Edit a git object ()", +}) + +vim.api.nvim_create_user_command("Gstatus", function(opts) + require("git.status_view").open({ + placement = opts.fargs[1] --[[@as ow.Git.StatusView.Placement]] + or "split", + }) +end, { + nargs = "?", + complete = function() + return require("git.status_view").PLACEMENTS + end, + desc = "Open git status view", +}) + +vim.keymap.set("n", "(git-edit)", function() + local rev = vim.fn.input("Edit git object: ") + if rev == "" then + return + end + vim.cmd.edit({ + args = { require("git.object").URI_PREFIX .. rev }, + magic = { file = false }, + }) +end, { silent = true, desc = "Edit a git object" }) + +vim.keymap.set("n", "(git-diff-vertical)", function() + require("git.diff").split({ vertical = true }) +end, { silent = true, desc = "Diff against index (vertical)" }) +vim.keymap.set("n", "(git-diff-horizontal)", function() + require("git.diff").split({ vertical = false }) +end, { silent = true, desc = "Diff against index (horizontal)" }) +vim.keymap.set("n", "(git-diff-vertical-head)", function() + require("git.diff").split({ rev = "HEAD", vertical = true }) +end, { silent = true, desc = "Diff against HEAD (vertical)" }) +vim.keymap.set("n", "(git-diff-horizontal-head)", function() + require("git.diff").split({ rev = "HEAD", vertical = false }) +end, { silent = true, desc = "Diff against HEAD (horizontal)" }) + +vim.keymap.set("n", "(git-status-toggle)", function() + require("git.status_view").toggle() +end, { silent = true, desc = "Toggle git status sidebar" }) + +vim.keymap.set("n", "(git-log)", function() + require("git.log_view").open({ max_count = 1000 }) +end, { silent = true, desc = "Open git log" }) + +vim.keymap.set("n", "(git-commit)", function() + require("git.commit").commit() +end, { silent = true, desc = "Start a git commit" }) +vim.keymap.set("n", "(git-commit-amend)", function() + require("git.commit").commit({ args = { "--amend" } }) +end, { silent = true, desc = "Amend the last git commit" }) + +if vim.g.git_statusline ~= false then + vim.api.nvim_create_autocmd("BufWinEnter", { + group = group, + once = true, + callback = function() + require("git.statusline") + end, + }) +end diff --git a/test/git/cmd_test.lua b/test/git/cmd_test.lua index 797bff1..d5f63ff 100644 --- a/test/git/cmd_test.lua +++ b/test/git/cmd_test.lua @@ -2,8 +2,6 @@ local cmd = require("git.cmd") local h = require("test.git.helpers") local t = require("test") -require("git").init() - ---@param files table? ---@return string dir local function make_repo(files) diff --git a/test/git/object_test.lua b/test/git/object_test.lua index 785620e..3d504ab 100644 --- a/test/git/object_test.lua +++ b/test/git/object_test.lua @@ -3,8 +3,6 @@ local h = require("test.git.helpers") local object = require("git.object") local t = require("test") -require("git").init() - ---@return integer? buf local function find_git_buf() for _, b in ipairs(vim.api.nvim_list_bufs()) do diff --git a/test/git/repo_test.lua b/test/git/repo_test.lua index 191c4f4..841c9fe 100644 --- a/test/git/repo_test.lua +++ b/test/git/repo_test.lua @@ -2,8 +2,6 @@ local h = require("test.git.helpers") local t = require("test") -require("git").init() - ---@param r ow.Git.Repo ---@param key string ---@param timeout integer? diff --git a/test/git/status_view_test.lua b/test/git/status_view_test.lua index f517fe3..303b920 100644 --- a/test/git/status_view_test.lua +++ b/test/git/status_view_test.lua @@ -1,8 +1,6 @@ local h = require("test.git.helpers") local t = require("test") -require("git").init() - ---Replicate the user's global cursor-restore autocmd. Scoped to a ---named augroup + cleanup so it doesn't leak between tests. local function install_cursor_restore_autocmd() diff --git a/test/runner.lua b/test/runner.lua index 16b2b20..1d584d7 100644 --- a/test/runner.lua +++ b/test/runner.lua @@ -15,6 +15,10 @@ for _, lua_dir in ipairs(vim.fn.glob(opt .. "/*/lua", false, true)) do .. (";" .. lua_dir .. "/?/init.lua") end +for _, f in ipairs(vim.fn.glob(cfg .. "/plugin/*.lua", false, true)) do + dofile(f) +end + local t = require("test") ---@param target string