mirror of
https://github.com/folke/snacks.nvim
synced 2025-12-23 08:47:57 +00:00
fix(win): better implementation of window styles (previously views)
This commit is contained in:
parent
39a63ce2a3
commit
66810971b9
6 changed files with 119 additions and 105 deletions
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue