From 9a08a42fb5f28cafc4fc73688f8a54a02ae1a5b6 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 5 May 2026 10:10:20 +0200 Subject: [PATCH] refactor: separate smoothing fn and scroll fn --- plugin/smoothie.lua | 111 ++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/plugin/smoothie.lua b/plugin/smoothie.lua index 6e44e89..304a9c6 100644 --- a/plugin/smoothie.lua +++ b/plugin/smoothie.lua @@ -17,54 +17,15 @@ local opts = vim.defaulttable(function(key) return opt(defaults[key], key) end) -local active = false +local M, H = {}, {} ----@param min number ----@param n number ----@param max number ----@return number -local function clamp(min, n, max) - return math.max(min, math.min(n, max)) -end - ----@param n number ----@param t number ----@return number -local function coillerp(n, t) - t = clamp(0, t, 1) - return (math.log(1 + t) * 1 + math.log(1 + t)) * n -end - -local function scroll(vcount) - local win = vim.api.nvim_get_current_win() - -- distance to scroll - local d = vim.api.nvim_get_option_value("scroll", { scope = "local", win = win }) - - local m = vcount / math.abs(vcount) - - local function step(start, dst, t) - local diff = dst.row - start.row - - -- reached dst or overshot - -- - diff == 0: no more distance to travel - -- - diff * m: both need to be oriented in the same direction (positive product) - if diff == 0 or diff * m < 0 then - return true - end - - -- travel t of the current diff (ceiled), at least 1 - local s = m * math.max(1, math.ceil(coillerp(math.abs(diff), t))) - start.row = start.row + s - - vim._with({ win = win }, function() - vim.fn.setpos(".", { start.buf, start.row + 1, start.col, 0 }) - end) - end +local active = false +function M.smooth(win, step, init) -- on resume, returns whether still active - local repeater = coroutine.wrap(function(...) + local repeater = coroutine.wrap(function(delta, ...) while true do - if step(...) then + if step(delta, ...) then break end coroutine.yield(true) @@ -80,9 +41,7 @@ local function scroll(vcount) end active = true - local buf, lnum, col = unpack(vim.fn.getpos(".")) - local start = vim.pos.cursor(buf, { lnum, col }) - local dst = vim.pos.cursor(buf, { math.max(0, lnum + (vcount * d)), col }) + local args = init() local now = vim.uv.now() @@ -90,7 +49,7 @@ local function scroll(vcount) local rel = vim.uv.now() - now local done = -1 vim.defer_fn(function() - done = repeater(start, dst, rel / opts.maxtime) + done = repeater(rel / opts.maxtime, unpack(args)) end, opts.interval) if not vim.wait(1000, function() return done ~= -1 @@ -103,13 +62,65 @@ local function scroll(vcount) end)() end +function M.scroll(vcount) + local win = vim.api.nvim_get_current_win() + -- distance to scroll + local d = vim.api.nvim_get_option_value("scroll", { scope = "local", win = win }) + + local m = vcount / math.abs(vcount) + + local function step(delta, start, dst) + local diff = dst.row - start.row + + -- reached dst or overshot + -- - diff == 0: no more distance to travel + -- - diff * m: both need to be oriented in the same direction (positive product) + if diff == 0 or diff * m < 0 then + return true + end + + -- travel t of the current diff (ceiled), at least 1 + local s = m * math.max(1, math.ceil(H.coillerp(math.abs(diff), delta))) + start.row = start.row + s + + vim._with({ win = win }, function() + vim.fn.setpos(".", { start.buf, start.row + 1, start.col, 0 }) + end) + end + + M.smooth(win, step, function() + local buf, lnum, col = unpack(vim.fn.getpos(".")) + local start = vim.pos.cursor(buf, { lnum, col }) + local dst = vim.pos.cursor(buf, { math.max(0, math.ceil(lnum + (vcount * d))), col }) + return { start, dst } + end) +end + vim.keymap.set("n", "(smoothie-ctrl-d)", function() vim._with({ win = 0 }, function() - scroll(vim.v.count1) + M.scroll(vim.v.count1) end) end) vim.keymap.set("n", "(smoothie-ctrl-u)", function() vim._with({ win = 0 }, function() - scroll(-vim.v.count1) + M.scroll(-vim.v.count1) end) end) + +-- helpers ==================================================================== + +---@param min number +---@param n number +---@param max number +---@return number +function H.clamp(min, n, max) + return math.max(min, math.min(n, max)) +end + +---@param n number +---@param t number +---@return number +function H.coillerp(n, t) + t = H.clamp(0, t, 1) + return (math.log(1 + t) * 1 + math.log(1 + t)) * n +end -- 2.51.2