···1717 return opt(defaults[key], key)
1818end)
19192020-local active = false
2121-2222----@param min number
2323----@param n number
2424----@param max number
2525----@return number
2626-local function clamp(min, n, max)
2727- return math.max(min, math.min(n, max))
2828-end
2929-3030----@param n number
3131----@param t number
3232----@return number
3333-local function coillerp(n, t)
3434- t = clamp(0, t, 1)
3535- return (math.log(1 + t) * 1 + math.log(1 + t)) * n
3636-end
3737-3838-local function scroll(vcount)
3939- local win = vim.api.nvim_get_current_win()
4040- -- distance to scroll
4141- local d = vim.api.nvim_get_option_value("scroll", { scope = "local", win = win })
4242-4343- local m = vcount / math.abs(vcount)
4444-4545- local function step(start, dst, t)
4646- local diff = dst.row - start.row
4747-4848- -- reached dst or overshot
4949- -- - diff == 0: no more distance to travel
5050- -- - diff * m: both need to be oriented in the same direction (positive product)
5151- if diff == 0 or diff * m < 0 then
5252- return true
5353- end
5454-5555- -- travel t of the current diff (ceiled), at least 1
5656- local s = m * math.max(1, math.ceil(coillerp(math.abs(diff), t)))
5757- start.row = start.row + s
2020+local M, H = {}, {}
58215959- vim._with({ win = win }, function()
6060- vim.fn.setpos(".", { start.buf, start.row + 1, start.col, 0 })
6161- end)
6262- end
2222+local active = false
63232424+function M.smooth(win, step, init)
6425 -- on resume, returns whether still active
6565- local repeater = coroutine.wrap(function(...)
2626+ local repeater = coroutine.wrap(function(delta, ...)
6627 while true do
6767- if step(...) then
2828+ if step(delta, ...) then
6829 break
6930 end
7031 coroutine.yield(true)
···8041 end
81428243 active = true
8383- local buf, lnum, col = unpack(vim.fn.getpos("."))
8484- local start = vim.pos.cursor(buf, { lnum, col })
8585- local dst = vim.pos.cursor(buf, { math.max(0, lnum + (vcount * d)), col })
4444+ local args = init()
86458746 local now = vim.uv.now()
8847···9049 local rel = vim.uv.now() - now
9150 local done = -1
9251 vim.defer_fn(function()
9393- done = repeater(start, dst, rel / opts.maxtime)
5252+ done = repeater(rel / opts.maxtime, unpack(args))
9453 end, opts.interval)
9554 if not vim.wait(1000, function()
9655 return done ~= -1
···10362 end)()
10463end
105646565+function M.scroll(vcount)
6666+ local win = vim.api.nvim_get_current_win()
6767+ -- distance to scroll
6868+ local d = vim.api.nvim_get_option_value("scroll", { scope = "local", win = win })
6969+7070+ local m = vcount / math.abs(vcount)
7171+7272+ local function step(delta, start, dst)
7373+ local diff = dst.row - start.row
7474+7575+ -- reached dst or overshot
7676+ -- - diff == 0: no more distance to travel
7777+ -- - diff * m: both need to be oriented in the same direction (positive product)
7878+ if diff == 0 or diff * m < 0 then
7979+ return true
8080+ end
8181+8282+ -- travel t of the current diff (ceiled), at least 1
8383+ local s = m * math.max(1, math.ceil(H.coillerp(math.abs(diff), delta)))
8484+ start.row = start.row + s
8585+8686+ vim._with({ win = win }, function()
8787+ vim.fn.setpos(".", { start.buf, start.row + 1, start.col, 0 })
8888+ end)
8989+ end
9090+9191+ M.smooth(win, step, function()
9292+ local buf, lnum, col = unpack(vim.fn.getpos("."))
9393+ local start = vim.pos.cursor(buf, { lnum, col })
9494+ local dst = vim.pos.cursor(buf, { math.max(0, math.ceil(lnum + (vcount * d))), col })
9595+ return { start, dst }
9696+ end)
9797+end
9898+10699vim.keymap.set("n", "<plug>(smoothie-ctrl-d)", function()
107100 vim._with({ win = 0 }, function()
108108- scroll(vim.v.count1)
101101+ M.scroll(vim.v.count1)
109102 end)
110103end)
111104vim.keymap.set("n", "<plug>(smoothie-ctrl-u)", function()
112105 vim._with({ win = 0 }, function()
113113- scroll(-vim.v.count1)
106106+ M.scroll(-vim.v.count1)
114107 end)
115108end)
109109+110110+-- helpers ====================================================================
111111+112112+---@param min number
113113+---@param n number
114114+---@param max number
115115+---@return number
116116+function H.clamp(min, n, max)
117117+ return math.max(min, math.min(n, max))
118118+end
119119+120120+---@param n number
121121+---@param t number
122122+---@return number
123123+function H.coillerp(n, t)
124124+ t = H.clamp(0, t, 1)
125125+ return (math.log(1 + t) * 1 + math.log(1 + t)) * n
126126+end