🧋 a tiny plugin for smoother movement keybinds
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: separate smoothing fn and scroll fn

robin 9a08a42f 8c710b7b

+61 -50
+61 -50
plugin/smoothie.lua
··· 17 17 return opt(defaults[key], key) 18 18 end) 19 19 20 - local active = false 21 - 22 - ---@param min number 23 - ---@param n number 24 - ---@param max number 25 - ---@return number 26 - local function clamp(min, n, max) 27 - return math.max(min, math.min(n, max)) 28 - end 29 - 30 - ---@param n number 31 - ---@param t number 32 - ---@return number 33 - local function coillerp(n, t) 34 - t = clamp(0, t, 1) 35 - return (math.log(1 + t) * 1 + math.log(1 + t)) * n 36 - end 37 - 38 - local function scroll(vcount) 39 - local win = vim.api.nvim_get_current_win() 40 - -- distance to scroll 41 - local d = vim.api.nvim_get_option_value("scroll", { scope = "local", win = win }) 42 - 43 - local m = vcount / math.abs(vcount) 44 - 45 - local function step(start, dst, t) 46 - local diff = dst.row - start.row 47 - 48 - -- reached dst or overshot 49 - -- - diff == 0: no more distance to travel 50 - -- - diff * m: both need to be oriented in the same direction (positive product) 51 - if diff == 0 or diff * m < 0 then 52 - return true 53 - end 54 - 55 - -- travel t of the current diff (ceiled), at least 1 56 - local s = m * math.max(1, math.ceil(coillerp(math.abs(diff), t))) 57 - start.row = start.row + s 20 + local M, H = {}, {} 58 21 59 - vim._with({ win = win }, function() 60 - vim.fn.setpos(".", { start.buf, start.row + 1, start.col, 0 }) 61 - end) 62 - end 22 + local active = false 63 23 24 + function M.smooth(win, step, init) 64 25 -- on resume, returns whether still active 65 - local repeater = coroutine.wrap(function(...) 26 + local repeater = coroutine.wrap(function(delta, ...) 66 27 while true do 67 - if step(...) then 28 + if step(delta, ...) then 68 29 break 69 30 end 70 31 coroutine.yield(true) ··· 80 41 end 81 42 82 43 active = true 83 - local buf, lnum, col = unpack(vim.fn.getpos(".")) 84 - local start = vim.pos.cursor(buf, { lnum, col }) 85 - local dst = vim.pos.cursor(buf, { math.max(0, lnum + (vcount * d)), col }) 44 + local args = init() 86 45 87 46 local now = vim.uv.now() 88 47 ··· 90 49 local rel = vim.uv.now() - now 91 50 local done = -1 92 51 vim.defer_fn(function() 93 - done = repeater(start, dst, rel / opts.maxtime) 52 + done = repeater(rel / opts.maxtime, unpack(args)) 94 53 end, opts.interval) 95 54 if not vim.wait(1000, function() 96 55 return done ~= -1 ··· 103 62 end)() 104 63 end 105 64 65 + function M.scroll(vcount) 66 + local win = vim.api.nvim_get_current_win() 67 + -- distance to scroll 68 + local d = vim.api.nvim_get_option_value("scroll", { scope = "local", win = win }) 69 + 70 + local m = vcount / math.abs(vcount) 71 + 72 + local function step(delta, start, dst) 73 + local diff = dst.row - start.row 74 + 75 + -- reached dst or overshot 76 + -- - diff == 0: no more distance to travel 77 + -- - diff * m: both need to be oriented in the same direction (positive product) 78 + if diff == 0 or diff * m < 0 then 79 + return true 80 + end 81 + 82 + -- travel t of the current diff (ceiled), at least 1 83 + local s = m * math.max(1, math.ceil(H.coillerp(math.abs(diff), delta))) 84 + start.row = start.row + s 85 + 86 + vim._with({ win = win }, function() 87 + vim.fn.setpos(".", { start.buf, start.row + 1, start.col, 0 }) 88 + end) 89 + end 90 + 91 + M.smooth(win, step, function() 92 + local buf, lnum, col = unpack(vim.fn.getpos(".")) 93 + local start = vim.pos.cursor(buf, { lnum, col }) 94 + local dst = vim.pos.cursor(buf, { math.max(0, math.ceil(lnum + (vcount * d))), col }) 95 + return { start, dst } 96 + end) 97 + end 98 + 106 99 vim.keymap.set("n", "<plug>(smoothie-ctrl-d)", function() 107 100 vim._with({ win = 0 }, function() 108 - scroll(vim.v.count1) 101 + M.scroll(vim.v.count1) 109 102 end) 110 103 end) 111 104 vim.keymap.set("n", "<plug>(smoothie-ctrl-u)", function() 112 105 vim._with({ win = 0 }, function() 113 - scroll(-vim.v.count1) 106 + M.scroll(-vim.v.count1) 114 107 end) 115 108 end) 109 + 110 + -- helpers ==================================================================== 111 + 112 + ---@param min number 113 + ---@param n number 114 + ---@param max number 115 + ---@return number 116 + function H.clamp(min, n, max) 117 + return math.max(min, math.min(n, max)) 118 + end 119 + 120 + ---@param n number 121 + ---@param t number 122 + ---@return number 123 + function H.coillerp(n, t) 124 + t = H.clamp(0, t, 1) 125 + return (math.log(1 + t) * 1 + math.log(1 + t)) * n 126 + end