mirror of
https://github.com/folke/snacks.nvim
synced 2025-08-06 19:58:22 +00:00
feat(indent): animation styles out
, up_down
, up
, down
This commit is contained in:
parent
5d1ae36d81
commit
0a9b013ff1
1 changed files with 62 additions and 20 deletions
|
@ -32,9 +32,16 @@ local defaults = {
|
||||||
},
|
},
|
||||||
-- animate scopes. Enabled by default for Neovim >= 0.10
|
-- animate scopes. Enabled by default for Neovim >= 0.10
|
||||||
-- Works on older versions but has to trigger redraws during animation.
|
-- Works on older versions but has to trigger redraws during animation.
|
||||||
---@type snacks.animate.Config|{enabled?: boolean}
|
---@class snacks.indent.animate: snacks.animate.Config
|
||||||
|
---@field enabled? boolean
|
||||||
|
--- * out: animate outwards from the cursor
|
||||||
|
--- * up: animate upwards from the cursor
|
||||||
|
--- * down: animate downwards from the cursor
|
||||||
|
--- * up_down: animate up or down based on the cursor position
|
||||||
|
---@field style? "out"|"up_down"|"down"|"up"
|
||||||
animate = {
|
animate = {
|
||||||
enabled = vim.fn.has("nvim-0.10") == 1,
|
enabled = vim.fn.has("nvim-0.10") == 1,
|
||||||
|
style = "out",
|
||||||
easing = "linear",
|
easing = "linear",
|
||||||
duration = {
|
duration = {
|
||||||
step = 20, -- ms per step
|
step = 20, -- ms per step
|
||||||
|
@ -82,6 +89,7 @@ local defaults = {
|
||||||
---@class snacks.indent.Scope: snacks.scope.Scope
|
---@class snacks.indent.Scope: snacks.scope.Scope
|
||||||
---@field win number
|
---@field win number
|
||||||
---@field step? number
|
---@field step? number
|
||||||
|
---@field animate? {from: number, to: number}
|
||||||
|
|
||||||
local config = Snacks.config.get("scope", defaults)
|
local config = Snacks.config.get("scope", defaults)
|
||||||
local ns = vim.api.nvim_create_namespace("snacks_indent")
|
local ns = vim.api.nvim_create_namespace("snacks_indent")
|
||||||
|
@ -172,10 +180,6 @@ local function get_extmark(indent, state)
|
||||||
return cache_extmarks[key]
|
return cache_extmarks[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function animating(buf)
|
|
||||||
return Snacks.animate.enabled({ buf = buf, name = "indent" })
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param win number
|
---@param win number
|
||||||
---@param buf number
|
---@param buf number
|
||||||
---@param top number
|
---@param top number
|
||||||
|
@ -271,6 +275,20 @@ function M.on_win(win, buf, top, bottom)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param scope snacks.indent.Scope
|
||||||
|
---@param state snacks.indent.State
|
||||||
|
---@return number from, number to
|
||||||
|
local function bounds(scope, state)
|
||||||
|
local from, to = scope.from, scope.to
|
||||||
|
if scope.animate then
|
||||||
|
from = math.max(scope.animate.from, scope.from)
|
||||||
|
to = math.min(scope.animate.to, scope.to)
|
||||||
|
end
|
||||||
|
from = math.max(from, state.top)
|
||||||
|
to = math.min(to, state.bottom)
|
||||||
|
return from, to
|
||||||
|
end
|
||||||
|
|
||||||
--- Render the scope overlappping the given range
|
--- Render the scope overlappping the given range
|
||||||
---@param scope snacks.indent.Scope
|
---@param scope snacks.indent.Scope
|
||||||
---@param state snacks.indent.State
|
---@param state snacks.indent.State
|
||||||
|
@ -278,10 +296,10 @@ end
|
||||||
function M.render_scope(scope, state)
|
function M.render_scope(scope, state)
|
||||||
local indent = (scope.indent or 2)
|
local indent = (scope.indent or 2)
|
||||||
local hl = get_hl(scope.indent + 1, config.scope.hl)
|
local hl = get_hl(scope.indent + 1, config.scope.hl)
|
||||||
local to = animating(scope.buf) and scope.step or scope.to
|
local from, to = bounds(scope, state)
|
||||||
local col = indent - state.leftcol
|
local col = indent - state.leftcol
|
||||||
|
|
||||||
if config.scope.underline and scope.from >= state.top and scope.from <= state.bottom then
|
if config.scope.underline and scope.from == from then
|
||||||
vim.api.nvim_buf_set_extmark(scope.buf, ns, scope.from - 1, math.max(col, 0), {
|
vim.api.nvim_buf_set_extmark(scope.buf, ns, scope.from - 1, math.max(col, 0), {
|
||||||
end_col = #vim.api.nvim_buf_get_lines(scope.buf, scope.from - 1, scope.from, false)[1],
|
end_col = #vim.api.nvim_buf_get_lines(scope.buf, scope.from - 1, scope.from, false)[1],
|
||||||
hl_group = get_underline_hl(hl),
|
hl_group = get_underline_hl(hl),
|
||||||
|
@ -296,7 +314,7 @@ function M.render_scope(scope, state)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for l = math.max(scope.from, state.top), math.min(to, state.bottom) do
|
for l = from, to do
|
||||||
local i = state.indents[l]
|
local i = state.indents[l]
|
||||||
if i and i > indent then
|
if i and i > indent then
|
||||||
vim.api.nvim_buf_set_extmark(scope.buf, ns, l - 1, 0, {
|
vim.api.nvim_buf_set_extmark(scope.buf, ns, l - 1, 0, {
|
||||||
|
@ -322,7 +340,7 @@ function M.render_chunk(scope, state)
|
||||||
if col < 0 then -- scope is hidden
|
if col < 0 then -- scope is hidden
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local to = animating(scope.buf) and scope.step or scope.to
|
local from, to = bounds(scope, state)
|
||||||
local hl = get_hl(scope.indent + 1, config.chunk.hl)
|
local hl = get_hl(scope.indent + 1, config.chunk.hl)
|
||||||
local char = config.chunk.char
|
local char = config.chunk.char
|
||||||
|
|
||||||
|
@ -340,7 +358,7 @@ function M.render_chunk(scope, state)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
for l = math.max(scope.from, state.top), math.min(to, state.bottom) do
|
for l = from, to do
|
||||||
local i = state.indents[l] - state.leftcol
|
local i = state.indents[l] - state.leftcol
|
||||||
if l == scope.from then -- top line
|
if l == scope.from then -- top line
|
||||||
add(l, char.corner_top .. (char.horizontal):rep(i - col - 1))
|
add(l, char.corner_top .. (char.horizontal):rep(i - col - 1))
|
||||||
|
@ -352,6 +370,30 @@ function M.render_chunk(scope, state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param scope snacks.indent.Scope
|
||||||
|
---@param value number
|
||||||
|
---@param prev? number
|
||||||
|
local function step(scope, value, prev)
|
||||||
|
prev = prev or 0
|
||||||
|
local cursor = vim.api.nvim_win_get_cursor(scope.win)
|
||||||
|
local dt = math.abs(scope.from - cursor[1])
|
||||||
|
local db = math.abs(scope.to - cursor[1])
|
||||||
|
local style = config.animate.style == "up_down" and (dt < db and "down" or "up") or config.animate.style
|
||||||
|
if style == "down" then
|
||||||
|
scope.animate = { from = scope.from, to = scope.from + value }
|
||||||
|
elseif style == "up" then
|
||||||
|
scope.animate = { from = scope.to - value, to = scope.to }
|
||||||
|
elseif style == "out" then
|
||||||
|
scope.animate = {
|
||||||
|
from = math.max(scope.from, cursor[1] - value),
|
||||||
|
to = math.min(scope.to, cursor[1] + value),
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Snacks.notify.error("Invalid animate style: " .. style, { title = "Snacks Indent", once = true })
|
||||||
|
end
|
||||||
|
Snacks.util.redraw_range(scope.win, scope.animate.from, scope.animate.to)
|
||||||
|
end
|
||||||
|
|
||||||
-- Called when the scope changes
|
-- Called when the scope changes
|
||||||
---@param win number
|
---@param win number
|
||||||
---@param buf number
|
---@param buf number
|
||||||
|
@ -360,22 +402,18 @@ end
|
||||||
---@private
|
---@private
|
||||||
function M.on_scope(win, buf, scope, prev)
|
function M.on_scope(win, buf, scope, prev)
|
||||||
stats.scope = stats.scope + 1
|
stats.scope = stats.scope + 1
|
||||||
if prev then -- clear previous scope
|
|
||||||
Snacks.util.redraw_range(win, prev.from, prev.to)
|
|
||||||
end
|
|
||||||
if scope then
|
if scope then
|
||||||
scope.win = win
|
scope.win = win
|
||||||
scope.step = scope.from
|
if Snacks.animate.enabled({ buf = buf, name = "indent" }) then
|
||||||
if animating(scope.buf) then
|
step(scope, 0)
|
||||||
Snacks.animate(
|
Snacks.animate(
|
||||||
scope.from,
|
0,
|
||||||
scope.to,
|
scope.to - scope.from,
|
||||||
function(value, ctx)
|
function(value, ctx)
|
||||||
if scopes and scopes:get(win) ~= scope then
|
if scopes and scopes:get(win) ~= scope then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
scope.step = value
|
step(scope, value, ctx.prev)
|
||||||
Snacks.util.redraw_range(win, math.min(ctx.prev, value), math.max(ctx.prev, value))
|
|
||||||
end,
|
end,
|
||||||
vim.tbl_extend("keep", {
|
vim.tbl_extend("keep", {
|
||||||
int = true,
|
int = true,
|
||||||
|
@ -383,8 +421,12 @@ function M.on_scope(win, buf, scope, prev)
|
||||||
buf = buf,
|
buf = buf,
|
||||||
}, config.animate)
|
}, config.animate)
|
||||||
)
|
)
|
||||||
|
else
|
||||||
|
Snacks.util.redraw_range(win, scope.from, scope.to)
|
||||||
end
|
end
|
||||||
Snacks.util.redraw_range(win, scope.from, animating(scope.buf) and scope.from + 1 or scope.to)
|
end
|
||||||
|
if prev then -- clear previous scope
|
||||||
|
Snacks.util.redraw_range(win, prev.from, prev.to)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue