mirror of
https://github.com/folke/snacks.nvim
synced 2025-07-07 21:25:11 +00:00
230 lines
6.8 KiB
Lua
230 lines
6.8 KiB
Lua
---@class snacks.lazygit
|
|
---@overload fun(opts?: snacks.lazygit.Config): snacks.win
|
|
local M = setmetatable({}, {
|
|
__call = function(t, ...)
|
|
return t.open(...)
|
|
end,
|
|
})
|
|
|
|
M.meta = {
|
|
desc = "Open LazyGit in a float, auto-configure colorscheme and integration with Neovim",
|
|
}
|
|
|
|
---@alias snacks.lazygit.Color {fg?:string, bg?:string, bold?:boolean}
|
|
|
|
---@class snacks.lazygit.Theme: table<number, snacks.lazygit.Color>
|
|
---@field activeBorderColor snacks.lazygit.Color
|
|
---@field cherryPickedCommitBgColor snacks.lazygit.Color
|
|
---@field cherryPickedCommitFgColor snacks.lazygit.Color
|
|
---@field defaultFgColor snacks.lazygit.Color
|
|
---@field inactiveBorderColor snacks.lazygit.Color
|
|
---@field optionsTextColor snacks.lazygit.Color
|
|
---@field searchingActiveBorderColor snacks.lazygit.Color
|
|
---@field selectedLineBgColor snacks.lazygit.Color
|
|
---@field unstagedChangesColor snacks.lazygit.Color
|
|
|
|
---@class snacks.lazygit.Config: snacks.terminal.Opts
|
|
---@field args? string[]
|
|
---@field theme? snacks.lazygit.Theme
|
|
local defaults = {
|
|
-- automatically configure lazygit to use the current colorscheme
|
|
-- and integrate edit with the current neovim instance
|
|
configure = true,
|
|
-- extra configuration for lazygit that will be merged with the default
|
|
-- snacks does NOT have a full yaml parser, so if you need `"test"` to appear with the quotes
|
|
-- you need to double quote it: `"\"test\""`
|
|
config = {
|
|
os = { editPreset = "nvim-remote" },
|
|
gui = {
|
|
-- set to an empty string "" to disable icons
|
|
nerdFontsVersion = "3",
|
|
},
|
|
},
|
|
theme_path = svim.fs.normalize(vim.fn.stdpath("cache") .. "/lazygit-theme.yml"),
|
|
-- Theme for lazygit
|
|
-- stylua: ignore
|
|
theme = {
|
|
[241] = { fg = "Special" },
|
|
activeBorderColor = { fg = "MatchParen", bold = true },
|
|
cherryPickedCommitBgColor = { fg = "Identifier" },
|
|
cherryPickedCommitFgColor = { fg = "Function" },
|
|
defaultFgColor = { fg = "Normal" },
|
|
inactiveBorderColor = { fg = "FloatBorder" },
|
|
optionsTextColor = { fg = "Function" },
|
|
searchingActiveBorderColor = { fg = "MatchParen", bold = true },
|
|
selectedLineBgColor = { bg = "Visual" }, -- set to `default` to have no background colour
|
|
unstagedChangesColor = { fg = "DiagnosticError" },
|
|
},
|
|
win = {
|
|
style = "lazygit",
|
|
},
|
|
}
|
|
|
|
Snacks.config.style("lazygit", {})
|
|
|
|
-- re-create config file on startup
|
|
local dirty = true
|
|
local config_dir ---@type string?
|
|
|
|
-- re-create theme file on ColorScheme change
|
|
vim.api.nvim_create_autocmd("ColorScheme", {
|
|
callback = function()
|
|
dirty = true
|
|
end,
|
|
})
|
|
|
|
---@param opts snacks.lazygit.Config
|
|
local function env(opts)
|
|
if not config_dir then
|
|
local out = vim.fn.system({ "lazygit", "-cd" })
|
|
local lines = vim.split(out, "\n", { plain = true })
|
|
|
|
if vim.v.shell_error == 0 and #lines > 1 then
|
|
config_dir = vim.split(lines[1], "\n", { plain = true })[1]
|
|
|
|
---@type string[]
|
|
local config_files = vim.tbl_filter(function(v)
|
|
return v:match("%S")
|
|
end, vim.split(vim.env.LG_CONFIG_FILE or "", ",", { plain = true }))
|
|
|
|
-- add the default config file if it's not already there
|
|
if #config_files == 0 then
|
|
config_files[1] = svim.fs.normalize(config_dir .. "/config.yml")
|
|
end
|
|
|
|
-- add the theme file if it's not already there
|
|
if not vim.tbl_contains(config_files, opts.theme_path) then
|
|
table.insert(config_files, opts.theme_path)
|
|
end
|
|
|
|
vim.env.LG_CONFIG_FILE = table.concat(config_files, ",")
|
|
else
|
|
local msg = {
|
|
"Failed to get **lazygit** config directory.",
|
|
"Will not apply **lazygit** config.",
|
|
"",
|
|
"# Error:",
|
|
vim.trim(out),
|
|
}
|
|
Snacks.notify.error(msg, { title = "lazygit" })
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param v snacks.lazygit.Color
|
|
---@return string[]
|
|
local function get_color(v)
|
|
---@type string[]
|
|
local color = {}
|
|
for _, c in ipairs({ "fg", "bg" }) do
|
|
if v[c] then
|
|
local name = v[c]
|
|
local hl = vim.api.nvim_get_hl(0, { name = name, link = false })
|
|
local hl_color ---@type number?
|
|
if c == "fg" then
|
|
hl_color = hl and hl.fg or hl.foreground
|
|
else
|
|
hl_color = hl and hl.bg or hl.background
|
|
end
|
|
if hl_color then
|
|
table.insert(color, string.format("#%06x", hl_color))
|
|
end
|
|
end
|
|
end
|
|
if v.bold then
|
|
table.insert(color, "bold")
|
|
end
|
|
return color
|
|
end
|
|
|
|
---@param opts snacks.lazygit.Config
|
|
local function update_config(opts)
|
|
---@type table<string, string[]>
|
|
local theme = {}
|
|
|
|
for k, v in pairs(opts.theme) do
|
|
if type(k) == "number" then
|
|
local color = get_color(v)
|
|
-- LazyGit uses color 241 a lot, so also set it to a nice color
|
|
-- pcall, since some terminals don't like this
|
|
pcall(io.write, ("\27]4;%d;%s\7"):format(k, color[1]))
|
|
else
|
|
theme[k] = get_color(v)
|
|
end
|
|
end
|
|
|
|
local config = vim.tbl_deep_extend("force", { gui = { theme = theme } }, opts.config or {})
|
|
|
|
local function yaml_val(val)
|
|
if type(val) == "boolean" then
|
|
return tostring(val)
|
|
end
|
|
return type(val) == "string" and not val:find("^\"'`") and ("%q"):format(val) or val
|
|
end
|
|
|
|
local function to_yaml(tbl, indent)
|
|
indent = indent or 0
|
|
local lines = {}
|
|
for k, v in pairs(tbl) do
|
|
table.insert(lines, string.rep(" ", indent) .. k .. (type(v) == "table" and ":" or ": " .. yaml_val(v)))
|
|
if type(v) == "table" then
|
|
if (vim.islist or vim.tbl_islist)(v) then
|
|
for _, item in ipairs(v) do
|
|
table.insert(lines, string.rep(" ", indent + 2) .. "- " .. yaml_val(item))
|
|
end
|
|
else
|
|
vim.list_extend(lines, to_yaml(v, indent + 2))
|
|
end
|
|
end
|
|
end
|
|
return lines
|
|
end
|
|
vim.fn.writefile(to_yaml(config), opts.theme_path)
|
|
dirty = false
|
|
end
|
|
|
|
-- Opens lazygit, properly configured to use the current colorscheme
|
|
-- and integrate with the current neovim instance
|
|
---@param opts? snacks.lazygit.Config
|
|
function M.open(opts)
|
|
---@type snacks.lazygit.Config
|
|
opts = Snacks.config.get("lazygit", defaults, opts)
|
|
|
|
local cmd = { "lazygit" }
|
|
vim.list_extend(cmd, opts.args or {})
|
|
|
|
if opts.configure then
|
|
if dirty then
|
|
update_config(opts)
|
|
end
|
|
env(opts)
|
|
end
|
|
|
|
return Snacks.terminal(cmd, opts)
|
|
end
|
|
|
|
-- Opens lazygit with the log view
|
|
---@param opts? snacks.lazygit.Config
|
|
function M.log(opts)
|
|
opts = opts or {}
|
|
opts.args = opts.args or { "log" }
|
|
return M.open(opts)
|
|
end
|
|
|
|
-- Opens lazygit with the log of the current file
|
|
---@param opts? snacks.lazygit.Config
|
|
function M.log_file(opts)
|
|
local file = vim.trim(vim.api.nvim_buf_get_name(0))
|
|
opts = opts or {}
|
|
opts.args = { "-f", file }
|
|
opts.cwd = vim.fn.fnamemodify(file, ":h")
|
|
return M.open(opts)
|
|
end
|
|
|
|
---@private
|
|
function M.health()
|
|
local ok = vim.fn.executable("lazygit") == 1
|
|
Snacks.health[ok and "ok" or "error"](("{lazygit} %sinstalled"):format(ok and "" or "not "))
|
|
end
|
|
|
|
return M
|