mirror of
https://github.com/folke/snacks.nvim
synced 2025-12-23 08:47:57 +00:00
Some checks are pending
CI / ci (push) Waiting to run
🤖 I have created a release *beep* *boop* --- ## [2.29.0](https://github.com/folke/snacks.nvim/compare/v2.28.0...v2.29.0) (2025-11-04) ### Features * **gh.diff:** show git status in PR diff ([c671d06](c671d062d1)) * **gh:** added reviews and nice diffs to gh buffer views. See [#2411](https://github.com/folke/snacks.nvim/issues/2411) ([1335ca1](1335ca1956)) * **gh:** allow to update pr branch ([#2419](https://github.com/folke/snacks.nvim/issues/2419)) ([f75f307](f75f307af3)) * **gh:** use new diff renderer for gh pr reviews ([714edec](714edec900)) * **gh:** when on a review comment, the `gh_comment` action will now do a reply instead of a top-level comment. Fixes [#2410](https://github.com/folke/snacks.nvim/issues/2410) ([a4f2b9d](a4f2b9da2d)) * **gh:** you can now use `Snacks.picker.gh_actions()` directly to see actions for the checked out PR ([d0d10f6](d0d10f6d13)) * **picker.diff:** new fancy diff renderer ([22eea90](22eea90a95)) * **picker.git_diff:** show proper git status for git diff files ([ab48eeb](ab48eebeb3)) * **picker.git_diff:** show renames ([77609a0](77609a0013)) * **picker.lsp_config:** added server/dynamic capabilities to preview ([da14fac](da14fac1e5)) * **picker:** consolidate all diff options under `opts.previewers.diff`. Default style is `fancy` ([b65b06c](b65b06ca0e)) * **zen:** added `center` option that defaults to `true` for zen mode and `false` for zoom mode. Closes [#2422](https://github.com/folke/snacks.nvim/issues/2422) ([3c2d791](3c2d79162f)) ### Bug Fixes * **dashboard:** start job after the terminal window is shown to make sure it has the correct size. Closes [#2421](https://github.com/folke/snacks.nvim/issues/2421) ([e440df3](e440df387d)) * **diff:** fallback if `Normal` has no fg color. Closes [#2436](https://github.com/folke/snacks.nvim/issues/2436) ([7f453c4](7f453c4f32)) * **diff:** improved diff parsing. Closes [#2424](https://github.com/folke/snacks.nvim/issues/2424). Closes [#2420](https://github.com/folke/snacks.nvim/issues/2420) ([b6e4eb7](b6e4eb7e60)) * **diff:** remove diff injections. Closes [#2406](https://github.com/folke/snacks.nvim/issues/2406) ([ecc21bb](ecc21bbb9b)) * **gh.api:** get repo from upstream remote if availble. fallback to origin ([5043637](50436373c2)) * **gh.api:** pass repo to cmd. Closes [#2415](https://github.com/folke/snacks.nvim/issues/2415) ([78046eb](78046eb481)) * **gh.diff:** fixed rendering of diff header when wrap=true ([07c569d](07c569dfd5)) * **gh.item:** better method to extract repo from gh url. Closes [#2418](https://github.com/folke/snacks.nvim/issues/2418) ([52d544c](52d544cc64)) * **gh.render:** added support for older `StatusContext` checks. Closes [#2407](https://github.com/folke/snacks.nvim/issues/2407) ([74864a7](74864a7bb8)) * **gh.render:** use check name. See [#2407](https://github.com/folke/snacks.nvim/issues/2407) ([6f60105](6f60105302)) * **gh:** better way of determining current PR ([bd3c1a0](bd3c1a0714)) * **gh:** input for api should be a table, not a string. Closes [#2427](https://github.com/folke/snacks.nvim/issues/2427) ([1b3e409](1b3e4090a0)) * **image:** allow to fully disable all image rendering with `opts.image.enabled = false`. Closes [#2404](https://github.com/folke/snacks.nvim/issues/2404) ([34a6591](34a6591a61)) * **image:** disable image conversion error notifications by default ([cfcf525](cfcf525207)) * **lsp:** check lsp handlers after LspAttach, since attached_buffers won't have been set ([1861b0a](1861b0a8ea)) * **picker.actions:** only allow stage/unstage/restore for some diffs ([9cde35b](9cde35b7b1)) * **picker.diff:** move git status calc based on diff to format ([b553c18](b553c18c26)) * **picker.diff:** parse full diff including diff and hunk headers. Closes [#2429](https://github.com/folke/snacks.nvim/issues/2429) ([53d8012](53d8012e5e)) * **picker.git_diff:** don't show git status, disable stage/unstage/restore when merge-base is set. Closes [#2397](https://github.com/folke/snacks.nvim/issues/2397) ([6ff82ca](6ff82cab7b)) * **picker.highlight:** resolve all text chunks when needed. Not just the first. ([962aadd](962aadd310)) * **picker.undo:** cleanup tmp files in `async:on("done")`. Closes [#2434](https://github.com/folke/snacks.nvim/issues/2434) ([3038dac](3038dac460)) * **picker.undo:** set initial target to the current undo entry. See [#2434](https://github.com/folke/snacks.nvim/issues/2434) ([dc245ef](dc245ef04e)) * **picker:** don't focus a picker window when toggling a window and picker wasn't current. closes [#2417](https://github.com/folke/snacks.nvim/issues/2417) ([b80b330](b80b330091)) * revert rename of actions.lua to tomdar87@outlook.com ([#2423](https://github.com/folke/snacks.nvim/issues/2423)) ([8bb3ad6](8bb3ad6c53)) * **win:** fixed fixbuf. Closes [#2409](https://github.com/folke/snacks.nvim/issues/2409) ([2099572](2099572fe8)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
227 lines
5.8 KiB
Lua
227 lines
5.8 KiB
Lua
---@class Snacks: snacks.plugins
|
|
local M = {}
|
|
|
|
setmetatable(M, {
|
|
__index = function(t, k)
|
|
---@diagnostic disable-next-line: no-unknown
|
|
t[k] = require("snacks." .. k)
|
|
return rawget(t, k)
|
|
end,
|
|
})
|
|
|
|
_G.Snacks = M
|
|
_G.svim = vim.fn.has("nvim-0.11") == 1 and vim or require("snacks.compat")
|
|
|
|
M.version = "2.29.0" -- x-release-please-version
|
|
|
|
---@class snacks.Config.base
|
|
---@field example? string
|
|
---@field config? fun(opts: table, defaults: table)
|
|
|
|
---@class snacks.Config: snacks.plugins.Config
|
|
---@field styles? table<string, snacks.win.Config>
|
|
---@field image? snacks.image.Config|{}
|
|
local config = {
|
|
image = {
|
|
-- define these here, so that we don't need to load the image module
|
|
formats = {
|
|
"png",
|
|
"jpg",
|
|
"jpeg",
|
|
"gif",
|
|
"bmp",
|
|
"webp",
|
|
"tiff",
|
|
"heic",
|
|
"avif",
|
|
"mp4",
|
|
"mov",
|
|
"avi",
|
|
"mkv",
|
|
"webm",
|
|
"pdf",
|
|
"icns",
|
|
},
|
|
},
|
|
}
|
|
config.styles = {}
|
|
|
|
---@class snacks.config: snacks.Config
|
|
M.config = setmetatable({}, {
|
|
__index = function(_, k)
|
|
config[k] = config[k] or {}
|
|
return config[k]
|
|
end,
|
|
__newindex = function(_, k, v)
|
|
config[k] = v
|
|
end,
|
|
})
|
|
|
|
local is_dict_like = function(v) -- has string and number keys
|
|
return type(v) == "table" and (vim.tbl_isempty(v) or not svim.islist(v))
|
|
end
|
|
local is_dict = function(v) -- has only string keys
|
|
return type(v) == "table" and (vim.tbl_isempty(v) or not v[1])
|
|
end
|
|
|
|
--- Merges the values similar to vim.tbl_deep_extend with the **force** behavior,
|
|
--- but the values can be any type
|
|
---@generic T
|
|
---@param ... T
|
|
---@return T
|
|
function M.config.merge(...)
|
|
local ret = select(1, ...)
|
|
for i = 2, select("#", ...) do
|
|
local value = select(i, ...)
|
|
if is_dict_like(ret) and is_dict(value) then
|
|
for k, v in pairs(value) do
|
|
ret[k] = M.config.merge(ret[k], v)
|
|
end
|
|
elseif value ~= nil then
|
|
ret = value
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
--- Get an example config from the docs/examples directory.
|
|
---@param snack string
|
|
---@param name string
|
|
---@param opts? table
|
|
function M.config.example(snack, name, opts)
|
|
local path = vim.fn.fnamemodify(debug.getinfo(1, "S").source:sub(2), ":h:h:h") .. "/docs/examples/" .. snack .. ".lua"
|
|
local ok, ret = pcall(function()
|
|
return loadfile(path)().examples[name] or error(("`%s` not found"):format(name))
|
|
end)
|
|
if not ok then
|
|
M.notify.error(("Failed to load `%s.%s`:\n%s"):format(snack, name, ret))
|
|
end
|
|
return ok and vim.tbl_deep_extend("force", {}, vim.deepcopy(ret), opts or {}) or {}
|
|
end
|
|
|
|
---@generic T: table
|
|
---@param snack string
|
|
---@param defaults T
|
|
---@param ... T[]
|
|
---@return T
|
|
function M.config.get(snack, defaults, ...)
|
|
local merge, todo = {}, { defaults, config[snack] or {}, ... }
|
|
for i = 1, select("#", ...) + 2 do
|
|
local v = todo[i] --[[@as snacks.Config.base]]
|
|
if type(v) == "table" then
|
|
if v.example then
|
|
table.insert(merge, vim.deepcopy(M.config.example(snack, v.example)))
|
|
v.example = nil
|
|
end
|
|
table.insert(merge, vim.deepcopy(v))
|
|
end
|
|
end
|
|
local ret = M.config.merge(unpack(merge))
|
|
if type(ret.config) == "function" then
|
|
ret.config(ret, defaults)
|
|
end
|
|
return ret
|
|
end
|
|
|
|
--- Register a new window style config.
|
|
---@param name string
|
|
---@param defaults snacks.win.Config|{}
|
|
---@return string
|
|
function M.config.style(name, defaults)
|
|
config.styles[name] = vim.tbl_deep_extend("force", vim.deepcopy(defaults), config.styles[name] or {})
|
|
return name
|
|
end
|
|
|
|
M.did_setup = false
|
|
M.did_setup_after_vim_enter = false
|
|
|
|
---@param opts snacks.Config?
|
|
function M.setup(opts)
|
|
if M.did_setup then
|
|
return vim.notify("snacks.nvim is already setup", vim.log.levels.ERROR, { title = "snacks.nvim" })
|
|
end
|
|
M.did_setup = true
|
|
|
|
if vim.fn.has("nvim-0.9.4") ~= 1 then
|
|
return vim.notify("snacks.nvim requires Neovim >= 0.9.4", vim.log.levels.ERROR, { title = "snacks.nvim" })
|
|
end
|
|
|
|
-- enable all by default when config is passed
|
|
opts = opts or {}
|
|
for k in pairs(opts) do
|
|
opts[k].enabled = opts[k].enabled == nil or opts[k].enabled
|
|
end
|
|
config = vim.tbl_deep_extend("force", config, opts or {})
|
|
|
|
local events = {
|
|
BufReadPre = { "bigfile", "image" },
|
|
BufReadPost = { "quickfile", "indent" },
|
|
BufEnter = { "explorer" },
|
|
LspAttach = { "words" },
|
|
UIEnter = { "dashboard", "scroll", "input", "scope", "picker" },
|
|
}
|
|
|
|
---@param event string
|
|
---@param ev? vim.api.keyset.create_autocmd.callback_args
|
|
local function load(event, ev)
|
|
local todo = events[event] or {}
|
|
events[event] = nil
|
|
for _, snack in ipairs(todo) do
|
|
if M.config[snack] and M.config[snack].enabled then
|
|
if M[snack].setup then
|
|
M[snack].setup(ev)
|
|
elseif M[snack].enable then
|
|
M[snack].enable()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if vim.v.vim_did_enter == 1 then
|
|
M.did_setup_after_vim_enter = true
|
|
load("UIEnter")
|
|
end
|
|
|
|
local group = vim.api.nvim_create_augroup("snacks", { clear = true })
|
|
vim.api.nvim_create_autocmd(vim.tbl_keys(events), {
|
|
group = group,
|
|
once = true,
|
|
nested = true,
|
|
callback = function(ev)
|
|
load(ev.event, ev)
|
|
end,
|
|
})
|
|
|
|
if M.config.image.enabled and #M.config.image.formats > 0 then
|
|
vim.api.nvim_create_autocmd("BufReadCmd", {
|
|
once = true,
|
|
pattern = "*." .. table.concat(M.config.image.formats, ",*."),
|
|
group = group,
|
|
callback = function(e)
|
|
require("snacks.image").setup(e)
|
|
end,
|
|
})
|
|
end
|
|
|
|
vim.api.nvim_create_autocmd("BufReadCmd", {
|
|
once = true,
|
|
pattern = "gh://*",
|
|
group = group,
|
|
callback = function(e)
|
|
require("snacks.gh").setup(e)
|
|
end,
|
|
})
|
|
|
|
if M.config.statuscolumn.enabled then
|
|
vim.o.statuscolumn = [[%!v:lua.require'snacks.statuscolumn'.get()]]
|
|
end
|
|
|
|
if M.config.notifier.enabled then
|
|
vim.notify = function(msg, level, o)
|
|
vim.notify = Snacks.notifier.notify
|
|
return Snacks.notifier.notify(msg, level, o)
|
|
end
|
|
end
|
|
end
|
|
|
|
return M
|