fix(win): better implementation of window styles (previously views)

This commit is contained in:
Folke Lemaitre 2024-11-06 10:56:08 +01:00
parent 39a63ce2a3
commit 66810971b9
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
6 changed files with 119 additions and 105 deletions

View file

@ -1,6 +1,15 @@
---@class snacks.git
local M = {}
Snacks.config.style("blame_line", {
width = 0.6,
height = 0.6,
border = "rounded",
title = " Git Blame ",
title_pos = "center",
bo = { filetype = "git" },
})
--- Gets the git root for a buffer or path.
--- Defaults to the current buffer.
---@param path? number|string buffer or path
@ -15,20 +24,10 @@ end
--- Show git log for the current line.
---@param opts? snacks.terminal.Config | {count?: number}
function M.blame_line(opts)
Snacks.config.view("blame_line", {
win = {
width = 0.6,
height = 0.6,
border = "rounded",
title = " Git Blame ",
title_pos = "center",
},
bo = { filetype = "git" },
})
opts = vim.tbl_deep_extend("force", {
count = 5,
interactive = false,
win = { view = "blame_line" },
win = { style = "blame_line" },
}, opts or {})
local cursor = vim.api.nvim_win_get_cursor(0)
local line = cursor[1]

View file

@ -36,11 +36,11 @@ _G.Snacks = M
---@field statuscolumn? snacks.statuscolumn.Config | { enabled: boolean }
---@field terminal? snacks.terminal.Config
---@field toggle? snacks.toggle.Config
---@field views? table<string, snacks.win.Config>
---@field styles? table<string, snacks.win.Config>
---@field win? snacks.win.Config
---@field words? snacks.words.Config
local config = {
views = {},
styles = {},
bigfile = { enabled = true },
notifier = { enabled = true },
quickfile = { enabled = true },
@ -67,11 +67,11 @@ function M.config.get(snack, defaults, ...)
return vim.tbl_deep_extend("force", unpack(merge))
end
--- Register a new window view config.
--- Register a new window style config.
---@param name string
---@param defaults snacks.win.Config
function M.config.view(name, defaults)
config.views[name] = vim.tbl_deep_extend("force", vim.deepcopy(defaults), config.views[name] or {})
function M.config.style(name, defaults)
config.styles[name] = vim.tbl_deep_extend("force", vim.deepcopy(defaults), config.styles[name] or {})
end
---@param opts snacks.Config?

View file

@ -38,10 +38,12 @@ local defaults = {
unstagedChangesColor = { fg = "DiagnosticError" },
},
win = {
view = "lazygit",
style = "lazygit",
},
}
Snacks.config.style("lazygit", {})
-- re-create config file on startup
local dirty = true
local config_dir ---@type string?

View file

@ -4,7 +4,7 @@
---@field dirty boolean
local M = {}
Snacks.config.view("notification", {
Snacks.config.style("notification", {
border = "rounded",
zindex = 100,
wo = {
@ -173,14 +173,16 @@ function M:start()
if #self.queue == 0 then
return
end
local ok, err = pcall(function()
xpcall(function()
self:update()
self:layout()
end)
if not ok then
vim.api.nvim_err_writeln("Snacks notifier failed. Dropping queue. Error:\n " .. err)
end, function(err)
local trace = debug.traceback(err, 2)
vim.api.nvim_err_writeln(
("Snacks notifier failed. Dropping queue. Error:\n%s\n\nTrace:\n%s"):format(error, trace)
)
self.queue = {}
end
end)
end)
)
end
@ -280,7 +282,7 @@ function M:render(notif)
local win = notif.win
or Snacks.win({
show = false,
view = "notification",
style = "notification",
enter = false,
backdrop = false,
bo = { filetype = notif.ft or "markdown", modifiable = false },

View file

@ -15,38 +15,42 @@ local M = setmetatable({}, {
---@field interactive? boolean
---@field override? fun(cmd?: string|string[], opts?: snacks.terminal.Config) Use this to use a different terminal implementation
local defaults = {
win = {
bo = {
filetype = "snacks_terminal",
},
wo = {},
keys = {
gf = function(self)
local f = vim.fn.findfile(vim.fn.expand("<cfile>"))
if f ~= "" then
vim.cmd("close")
vim.cmd("e " .. f)
win = { style = "terminal" },
}
Snacks.config.style("terminal", {
bo = {
filetype = "snacks_terminal",
},
wo = {},
keys = {
gf = function(self)
local f = vim.fn.findfile(vim.fn.expand("<cfile>"))
if f == "" then
Snacks.notify.warn("No file under cursor")
else
self:close()
vim.cmd("e " .. f)
end
end,
term_normal = {
"<esc>",
function(self)
self.esc_timer = self.esc_timer or (vim.uv or vim.loop).new_timer()
if self.esc_timer:is_active() then
self.esc_timer:stop()
vim.cmd("stopinsert")
else
self.esc_timer:start(200, 0, function() end)
return "<esc>"
end
end,
term_normal = {
"<esc>",
function(self)
self.esc_timer = self.esc_timer or (vim.uv or vim.loop).new_timer()
if self.esc_timer:is_active() then
self.esc_timer:stop()
vim.cmd("stopinsert")
else
self.esc_timer:start(200, 0, function() end)
return "<esc>"
end
end,
mode = "t",
expr = true,
desc = "Double escape to normal mode",
},
mode = "t",
expr = true,
desc = "Double escape to normal mode",
},
},
}
})
---@type table<string, snacks.win>
local terminals = {}
@ -56,8 +60,8 @@ local terminals = {}
function M.open(cmd, opts)
local id = vim.v.count1
---@type snacks.terminal.Config
opts = Snacks.config.get("terminal", defaults, { win = Snacks.win.resolve(opts and opts.win) }, opts)
opts.win.position = opts.win.position or (cmd and "float" or "bottom")
opts = Snacks.config.get("terminal", defaults, { win = { position = cmd and "float" or "bottom" } }, opts)
opts.win = Snacks.win.resolve("terminal", opts.win)
opts.win.wo.winbar = opts.win.wo.winbar or (opts.win.position == "float" and "" or (id .. ": %{b:term_title}"))
if opts.override then

View file

@ -18,7 +18,9 @@ local M = setmetatable({}, {
---@field mode? string|string[]
---@class snacks.win.Config: vim.api.keyset.win_config
---@field view? string merges with config from `Snacks.config.views[view]`
---@field style? string merges with config from `Snacks.config.views[view]`
---@field show? boolean Show the window immediately (default: true)
---@field minimal? boolean
---@field position? "float"|"bottom"|"top"|"left"|"right"
---@field buf? number
---@field file? string
@ -31,9 +33,10 @@ local M = setmetatable({}, {
---@field on_buf? fun(self: snacks.win)
---@field on_win? fun(self: snacks.win)
local defaults = {
position = "float",
show = true,
relative = "editor",
style = "minimal",
position = "float",
minimal = true,
wo = {
winhighlight = "Normal:NormalFloat,NormalNC:NormalFloat",
},
@ -43,19 +46,39 @@ local defaults = {
},
}
---@type snacks.win.Config
local defaults_float = {
Snacks.config.style("float", {
position = "float",
backdrop = 60,
height = 0.9,
width = 0.9,
zindex = 50,
}
})
---@type snacks.win.Config
local defaults_split = {
Snacks.config.style("split", {
position = "bottom",
height = 0.4,
width = 0.4,
}
})
Snacks.config.style("minimal", {
wo = {
cursorcolumn = false,
cursorline = false,
cursorlineopt = "both",
fillchars = "eob: ,lastline:…",
list = false,
listchars = "extends:…,tab: ",
number = false,
relativenumber = false,
signcolumn = "no",
spell = false,
winbar = "",
statuscolumn = "",
winfixheight = true,
winfixwidth = true,
wrap = false,
},
})
local split_commands = {
editor = {
@ -72,27 +95,6 @@ local split_commands = {
},
}
---@type snacks.win.Config
local minimal = {
wo = {
cursorcolumn = false,
cursorline = false,
cursorlineopt = "both",
fillchars = "eob: ,lastline:…",
list = false,
listchars = "extends:…",
number = false,
relativenumber = false,
signcolumn = "no",
spell = false,
winbar = "",
statuscolumn = "",
winfixheight = true,
winfixwidth = true,
wrap = false,
},
}
local win_opts = {
"anchor",
"border",
@ -120,40 +122,45 @@ vim.api.nvim_set_hl(0, "SnackFloatBackdrop", { bg = "#000000", default = true })
local id = 0
---@param opts? snacks.win.Config
---@param ... snacks.win.Config|string
---@return snacks.win.Config
function M.resolve(opts)
opts = opts or {}
function M.resolve(...)
local done = {} ---@type string[]
local views = { opts } ---@type snacks.win.Config[]
local view = opts.view
while view and not vim.tbl_contains(done, view) do
table.insert(done, view)
if not Snacks.config.views[view] then
break
local merge = {} ---@type snacks.win.Config[]
local stack = { ... }
while #stack > 0 do
local next = table.remove(stack)
next = type(next) == "table" and next or Snacks.config.styles[next]
---@cast next snacks.win.Config?
if next then
table.insert(merge, 1, next)
if next.style and not vim.tbl_contains(done, next.style) then
table.insert(done, next.style)
table.insert(stack, next.style)
end
end
table.insert(views, 1, Snacks.config.views[view])
view = Snacks.config.views[view].view
end
local ret = #views == 0 and {} or #views == 1 and views[1] or vim.tbl_deep_extend("force", {}, unpack(views))
ret.view = nil
local ret = #merge == 0 and {} or #merge == 1 and merge[1] or vim.tbl_deep_extend("force", {}, unpack(merge))
ret.style = nil
return ret
end
---@param opts? snacks.win.Config | { show?: boolean }
---@param opts? snacks.win.Config
---@return snacks.win
function M.new(opts)
local self = setmetatable({}, { __index = M })
id = id + 1
self.id = id
opts = Snacks.config.get("win", defaults, M.resolve(opts))
opts =
vim.tbl_deep_extend("force", {}, vim.deepcopy(opts.position == "float" and defaults_float or defaults_split), opts)
---@cast opts snacks.win.Config
if opts.style == "minimal" then
opts = vim.tbl_deep_extend("force", {}, vim.deepcopy(minimal), opts) --[[@as snacks.win.Config]]
opts.style = nil
opts = M.resolve(Snacks.config.get("win", defaults, opts))
if opts.minimal then
opts = M.resolve("minimal", opts)
end
if opts.position == "float" then
opts = M.resolve("float", opts)
else
opts = M.resolve("split", opts)
end
---@cast opts snacks.win.Config
self.opts = opts
if opts.show ~= false then
self:show()