feat(git): add git module and replace vim-flog with :Glog
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
syntax match gitlogGraph contained /^[*|\\\/_ ]*/
|
||||||
|
\ nextgroup=gitlogHash
|
||||||
|
syntax match gitlogHash contained /\<\x\{7,40\}\>/
|
||||||
|
\ nextgroup=gitlogDate skipwhite
|
||||||
|
syntax match gitlogDate contained /\<\d\{4}-\d\{2}-\d\{2}\>/
|
||||||
|
\ nextgroup=gitlogAuthor skipwhite
|
||||||
|
syntax match gitlogAuthor contained /{[^}]\+}/
|
||||||
|
\ nextgroup=gitlogRef skipwhite
|
||||||
|
syntax match gitlogRef contained /([^)]\+)/
|
||||||
|
syntax match gitlogLine
|
||||||
|
\ /^[*|\\\/_ ]*\x\{7,40}\s\+\d\{4}-\d\{2}-\d\{2}\s\+{[^}]\+}.*/
|
||||||
|
\ contains=gitlogGraph
|
||||||
|
syntax match gitlogGraphLine /^[*|\\\/_ ]\+$/
|
||||||
|
\ contains=gitlogGraph
|
||||||
|
|
||||||
|
highlight default link gitlogGraph Comment
|
||||||
|
highlight default link gitlogHash Identifier
|
||||||
|
highlight default link gitlogDate Number
|
||||||
|
highlight default link gitlogAuthor String
|
||||||
|
highlight default link gitlogRef Constant
|
||||||
@@ -41,7 +41,6 @@ require("pack").setup({
|
|||||||
"https://github.com/owallb/mason-auto-install.nvim",
|
"https://github.com/owallb/mason-auto-install.nvim",
|
||||||
"https://github.com/mfussenegger/nvim-dap",
|
"https://github.com/mfussenegger/nvim-dap",
|
||||||
"https://github.com/numToStr/Comment.nvim",
|
"https://github.com/numToStr/Comment.nvim",
|
||||||
"https://github.com/rbong/vim-flog",
|
|
||||||
"https://github.com/tpope/vim-fugitive",
|
"https://github.com/tpope/vim-fugitive",
|
||||||
"https://github.com/lewis6991/gitsigns.nvim",
|
"https://github.com/lewis6991/gitsigns.nvim",
|
||||||
"https://github.com/MagicDuck/grug-far.nvim",
|
"https://github.com/MagicDuck/grug-far.nvim",
|
||||||
|
|||||||
+2
-17
@@ -78,23 +78,8 @@ vim.opt.guicursor:append("a:Cursor")
|
|||||||
vim.opt.inccommand = "split"
|
vim.opt.inccommand = "split"
|
||||||
vim.opt.winborder = "rounded"
|
vim.opt.winborder = "rounded"
|
||||||
|
|
||||||
function _G._status_line_git()
|
require("git").setup()
|
||||||
local status = vim.b.gitsigns_status_dict
|
vim.opt.statusline = "%{expand('%:.')} %{%v:lua.require('git').status()%} %3(%m%)"
|
||||||
|
|
||||||
if not status then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local added = status.added or 0
|
|
||||||
local changed = status.changed or 0
|
|
||||||
local removed = status.removed or 0
|
|
||||||
|
|
||||||
return (added + changed + removed) > 0
|
|
||||||
and "%#NvimTreeGitDirty#M%*"
|
|
||||||
or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.opt.statusline = " %{expand('%:.')} %{%v:lua._status_line_git()%} %3(%m%)"
|
|
||||||
.. " %="
|
.. " %="
|
||||||
.. " %{%v:lua.vim.diagnostic.status()%}"
|
.. " %{%v:lua.vim.diagnostic.status()%}"
|
||||||
.. " %{&filetype} %{&fileencoding} %{&fileformat}"
|
.. " %{&filetype} %{&fileencoding} %{&fileformat}"
|
||||||
|
|||||||
+298
@@ -0,0 +1,298 @@
|
|||||||
|
local log = require("log")
|
||||||
|
local util = require("util")
|
||||||
|
|
||||||
|
local HIGHLIGHTS = {
|
||||||
|
GitDeleted = "Statement",
|
||||||
|
GitDirty = "Statement",
|
||||||
|
GitIgnored = "Comment",
|
||||||
|
GitMerge = "Constant",
|
||||||
|
GitNew = "PreProc",
|
||||||
|
GitRenamed = "PreProc",
|
||||||
|
GitStaged = "Constant",
|
||||||
|
}
|
||||||
|
|
||||||
|
local UNMERGED = {
|
||||||
|
DD = true,
|
||||||
|
AU = true,
|
||||||
|
UD = true,
|
||||||
|
UA = true,
|
||||||
|
DU = true,
|
||||||
|
AA = true,
|
||||||
|
UU = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
---@param code string
|
||||||
|
---@return string?
|
||||||
|
local function format(code)
|
||||||
|
if code == "" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local char, hl
|
||||||
|
if code == "??" then
|
||||||
|
char, hl = "?", "GitNew"
|
||||||
|
elseif code == "!!" then
|
||||||
|
char, hl = "!", "GitIgnored"
|
||||||
|
elseif UNMERGED[code] then
|
||||||
|
char, hl = "U", "GitMerge"
|
||||||
|
else
|
||||||
|
local x, y = code:sub(1, 1), code:sub(2, 2)
|
||||||
|
if x == "R" or y == "R" then
|
||||||
|
char, hl = "R", "GitRenamed"
|
||||||
|
elseif y == "M" or y == "T" then
|
||||||
|
char, hl = "M", "GitDirty"
|
||||||
|
elseif y == "D" then
|
||||||
|
char, hl = "D", "GitDeleted"
|
||||||
|
elseif y == " " and x == "D" then
|
||||||
|
char, hl = "D", "GitStaged"
|
||||||
|
elseif y == " " and x == "A" then
|
||||||
|
char, hl = "A", "GitStaged"
|
||||||
|
elseif y == " " and x ~= " " then
|
||||||
|
char, hl = "M", "GitStaged"
|
||||||
|
else
|
||||||
|
char, hl = "M", "GitDirty"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return string.format("%%#%s#%s%%*", hl, char)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param path string
|
||||||
|
---@return string? gitdir
|
||||||
|
---@return string? worktree
|
||||||
|
local function resolve(path)
|
||||||
|
local found = vim.fs.find(".git", { upward = true, path = path })[1]
|
||||||
|
if not found then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local worktree = vim.fs.dirname(found)
|
||||||
|
local stat = vim.uv.fs_stat(found)
|
||||||
|
if not stat then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if stat.type == "directory" then
|
||||||
|
return found, worktree
|
||||||
|
end
|
||||||
|
local f = io.open(found, "r")
|
||||||
|
if not f then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local content = f:read("*a")
|
||||||
|
f:close()
|
||||||
|
local gitdir = content:match("gitdir:%s*(%S+)")
|
||||||
|
if not gitdir then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if not gitdir:match("^/") then
|
||||||
|
gitdir = vim.fs.joinpath(worktree, gitdir)
|
||||||
|
end
|
||||||
|
return vim.fs.normalize(gitdir), worktree
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class ow.Git.Repo
|
||||||
|
---@field gitdir string
|
||||||
|
---@field worktree string
|
||||||
|
---@field buffers integer[]
|
||||||
|
---@field watcher? uv.uv_fs_event_t
|
||||||
|
---@field refresh ow.Util.Debouncer
|
||||||
|
local Repo = {}
|
||||||
|
Repo.__index = Repo
|
||||||
|
|
||||||
|
function Repo:start_watcher()
|
||||||
|
local watcher, err_msg, err_name = vim.uv.new_fs_event()
|
||||||
|
if not watcher then
|
||||||
|
log.error(
|
||||||
|
"Failed to create fs event watcher: %s (%s)",
|
||||||
|
err_msg,
|
||||||
|
err_name
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
watcher:start(self.gitdir, {}, function(err, filename)
|
||||||
|
if err or (filename ~= "index" and filename ~= "HEAD") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.refresh:call()
|
||||||
|
end)
|
||||||
|
self.watcher = watcher
|
||||||
|
end
|
||||||
|
|
||||||
|
function Repo:stop_watcher()
|
||||||
|
self.refresh:cancel()
|
||||||
|
if self.watcher then
|
||||||
|
self.watcher:stop()
|
||||||
|
self.watcher:close()
|
||||||
|
self.watcher = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param buf integer
|
||||||
|
function Repo:add_buffer(buf)
|
||||||
|
table.insert(self.buffers, buf)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param buf integer
|
||||||
|
function Repo:remove_buffer(buf)
|
||||||
|
for i, b in ipairs(self.buffers) do
|
||||||
|
if b == buf then
|
||||||
|
table.remove(self.buffers, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param repo ow.Git.Repo
|
||||||
|
local function do_refresh(repo)
|
||||||
|
vim.system({
|
||||||
|
"git",
|
||||||
|
"-c",
|
||||||
|
"core.quotePath=false",
|
||||||
|
"status",
|
||||||
|
"--porcelain=v1",
|
||||||
|
}, { cwd = repo.worktree, text = true }, function(obj)
|
||||||
|
vim.schedule(function()
|
||||||
|
local statuses = {}
|
||||||
|
if obj.code == 0 then
|
||||||
|
for line in (obj.stdout or ""):gmatch("[^\r\n]+") do
|
||||||
|
local code = line:sub(1, 2)
|
||||||
|
local path_part = line:sub(4)
|
||||||
|
local arrow = path_part:find(" -> ", 1, true)
|
||||||
|
if arrow then
|
||||||
|
path_part = path_part:sub(arrow + 4)
|
||||||
|
end
|
||||||
|
statuses[vim.fs.joinpath(repo.worktree, path_part)] =
|
||||||
|
format(code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, buf in ipairs(repo.buffers) do
|
||||||
|
if vim.api.nvim_buf_is_valid(buf) then
|
||||||
|
local status = statuses[vim.api.nvim_buf_get_name(buf)]
|
||||||
|
if vim.b[buf].git_status ~= status then
|
||||||
|
vim.b[buf].git_status = status
|
||||||
|
vim.cmd.redrawstatus({ bang = true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
vim.api.nvim_exec_autocmds("User", {
|
||||||
|
pattern = "GitRefresh",
|
||||||
|
data = { gitdir = repo.gitdir, worktree = repo.worktree },
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param gitdir string
|
||||||
|
---@param worktree string
|
||||||
|
---@return ow.Git.Repo
|
||||||
|
function Repo.new(gitdir, worktree)
|
||||||
|
local self = setmetatable({
|
||||||
|
gitdir = gitdir,
|
||||||
|
worktree = worktree,
|
||||||
|
buffers = {},
|
||||||
|
}, Repo)
|
||||||
|
self.refresh = util.debounce(function()
|
||||||
|
do_refresh(self)
|
||||||
|
end, 50)
|
||||||
|
self:start_watcher()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type table<string, ow.Git.Repo>
|
||||||
|
local repo_by_gitdir = {}
|
||||||
|
|
||||||
|
---@type table<integer, ow.Git.Repo>
|
||||||
|
local repo_by_buf = {}
|
||||||
|
|
||||||
|
---@param buf integer
|
||||||
|
---@return ow.Git.Repo?
|
||||||
|
local function register(buf)
|
||||||
|
local existing = repo_by_buf[buf]
|
||||||
|
if existing then
|
||||||
|
return existing
|
||||||
|
end
|
||||||
|
local path = vim.api.nvim_buf_get_name(buf)
|
||||||
|
if path == "" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local gitdir, worktree = resolve(path)
|
||||||
|
if not gitdir or not worktree then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local repo = repo_by_gitdir[gitdir]
|
||||||
|
if not repo then
|
||||||
|
repo = Repo.new(gitdir, worktree)
|
||||||
|
repo_by_gitdir[gitdir] = repo
|
||||||
|
end
|
||||||
|
repo:add_buffer(buf)
|
||||||
|
repo_by_buf[buf] = repo
|
||||||
|
return repo
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param buf integer
|
||||||
|
local function unregister(buf)
|
||||||
|
local repo = repo_by_buf[buf]
|
||||||
|
if not repo then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
repo_by_buf[buf] = nil
|
||||||
|
repo:remove_buffer(buf)
|
||||||
|
if #repo.buffers == 0 then
|
||||||
|
repo:stop_watcher()
|
||||||
|
repo_by_gitdir[repo.gitdir] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param buf integer
|
||||||
|
local function refresh(buf)
|
||||||
|
if not vim.api.nvim_buf_is_valid(buf) or vim.bo[buf].buftype ~= "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local repo = register(buf)
|
||||||
|
if not repo then
|
||||||
|
vim.b[buf].git_status = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
repo.refresh:call()
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.status()
|
||||||
|
return vim.b.git_status or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.setup()
|
||||||
|
for name, link in pairs(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", "BufWritePost", "FileChangedShellPost" },
|
||||||
|
{
|
||||||
|
group = group,
|
||||||
|
callback = function(args)
|
||||||
|
refresh(args.buf)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
vim.api.nvim_create_autocmd({ "BufDelete", "BufWipeout" }, {
|
||||||
|
group = group,
|
||||||
|
callback = function(args)
|
||||||
|
unregister(args.buf)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.api.nvim_create_autocmd("FocusGained", {
|
||||||
|
group = group,
|
||||||
|
callback = function()
|
||||||
|
refresh(vim.api.nvim_get_current_buf())
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.api.nvim_create_autocmd("VimLeavePre", {
|
||||||
|
group = group,
|
||||||
|
callback = function()
|
||||||
|
for _, repo in pairs(repo_by_gitdir) do
|
||||||
|
repo:stop_watcher()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -21,6 +21,9 @@ local highlights = {
|
|||||||
DiffAdd = { bg = "#1a2f22" },
|
DiffAdd = { bg = "#1a2f22" },
|
||||||
DiffChange = { bg = "#15304a" },
|
DiffChange = { bg = "#15304a" },
|
||||||
DiffDelete = { bg = "#311c1e" },
|
DiffDelete = { bg = "#311c1e" },
|
||||||
|
GitDeleted = { fg = c.red },
|
||||||
|
GitDirty = { fg = c.yellow },
|
||||||
|
GitNew = { fg = c.green },
|
||||||
}
|
}
|
||||||
require("onedark").set_options("highlights", highlights)
|
require("onedark").set_options("highlights", highlights)
|
||||||
require("onedark").load()
|
require("onedark").load()
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
vim.keymap.set("n", "<leader>gl", vim.cmd.Flog)
|
|
||||||
@@ -30,6 +30,17 @@ local function toggle_git_status()
|
|||||||
open_git_status()
|
open_git_status()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("Glog", function(opts)
|
||||||
|
local mods = opts.mods ~= "" and (opts.mods .. " ") or ""
|
||||||
|
vim.cmd(
|
||||||
|
mods
|
||||||
|
.. "Git log --graph --all --decorate --date=short "
|
||||||
|
.. "--format=format:'%h %ad {%an}%d %s' "
|
||||||
|
.. opts.args
|
||||||
|
)
|
||||||
|
end, { nargs = "*", desc = "Pretty git log via fugitive" })
|
||||||
|
|
||||||
|
vim.keymap.set("n", "<leader>gl", vim.cmd.Glog)
|
||||||
vim.keymap.set("n", "<leader>gd", vim.cmd.Gvdiffsplit)
|
vim.keymap.set("n", "<leader>gd", vim.cmd.Gvdiffsplit)
|
||||||
vim.keymap.set("n", "<leader>gD", function() vim.cmd.Gvdiffsplit("HEAD") end)
|
vim.keymap.set("n", "<leader>gD", function() vim.cmd.Gvdiffsplit("HEAD") end)
|
||||||
vim.keymap.set("n", "<leader>gh", vim.cmd.Ghdiffsplit)
|
vim.keymap.set("n", "<leader>gh", vim.cmd.Ghdiffsplit)
|
||||||
@@ -39,7 +50,9 @@ vim.keymap.set("n", "<leader>ga", function() vim.cmd.G("commit --amend") end)
|
|||||||
vim.keymap.set("n", "<leader>gp", function() vim.cmd.G("push") end)
|
vim.keymap.set("n", "<leader>gp", function() vim.cmd.G("push") end)
|
||||||
vim.keymap.set("n", "<leader>gg", toggle_git_status)
|
vim.keymap.set("n", "<leader>gg", toggle_git_status)
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("BufWritePost", {
|
vim.api.nvim_create_autocmd("User", {
|
||||||
|
pattern = "GitRefresh",
|
||||||
|
group = vim.api.nvim_create_augroup("ow.fugitive", { clear = true }),
|
||||||
callback = function()
|
callback = function()
|
||||||
vim.fn["fugitive#ReloadStatus"]()
|
vim.fn["fugitive#ReloadStatus"]()
|
||||||
end,
|
end,
|
||||||
|
|||||||
@@ -154,10 +154,6 @@
|
|||||||
"rev": "484e2889f3619b9da90c9b73a6f216a71947c09f",
|
"rev": "484e2889f3619b9da90c9b73a6f216a71947c09f",
|
||||||
"src": "https://github.com/georgeharker/tree-sitter-zsh"
|
"src": "https://github.com/georgeharker/tree-sitter-zsh"
|
||||||
},
|
},
|
||||||
"vim-flog": {
|
|
||||||
"rev": "665b16ac8915f746bc43c9572b4581a5e9047216",
|
|
||||||
"src": "https://github.com/rbong/vim-flog"
|
|
||||||
},
|
|
||||||
"vim-fugitive": {
|
"vim-fugitive": {
|
||||||
"rev": "3b753cf8c6a4dcde6edee8827d464ba9b8c4a6f0",
|
"rev": "3b753cf8c6a4dcde6edee8827d464ba9b8c4a6f0",
|
||||||
"src": "https://github.com/tpope/vim-fugitive"
|
"src": "https://github.com/tpope/vim-fugitive"
|
||||||
|
|||||||
Reference in New Issue
Block a user