Files
dotfiles/vis/.config/vis/fast-jump.lua

186 lines
3.6 KiB
Lua

require('vis')
local lpeg = vis.lpeg
local P = lpeg.P
local Cp = lpeg.Cp
local Ct = lpeg.Ct
local function search(p)
return Ct(((1 - p)^0 * Cp() * p)^0)
end
local up_keys = {
'a', 's', 'd', 'f' ,
'q', 'w', 'e', 'r', 't', 'g',
'z', 'x', 'c', 'v', 'b',
'1', '2', '3', '4', '5',
'A', 'S', 'D', 'F' ,
'Q', 'W', 'E', 'R', 'T', 'G',
'Z', 'X', 'C', 'V', 'B',
'!', '@', '#', '$', '%',
'`', '~',
}
local down_keys = {
'j', 'k', 'l', ';',
'y', 'u', 'i', 'o', 'p',
'n', 'm', ',', '.', '/',
'6', '7', '8', '9', '0',
'J', 'K', 'L', ':',
'Y', 'U', 'I', 'O', 'P',
'N', 'M', '<', '>', '?',
'^', '&', '*', '(', ')',
"'", '"', '\\', '|',
}
local function merge(a, b)
local c = {}
for i = 1, #a do
c[i] = a[i]
end
for i = 1, #b do
c[#a + i] = b[i]
end
return c
end
local function layout()
local l, r, t, n = 0, 0, 0, 0
local opt = vis.win.options
if opt.numbers or opt.relativenumbers then
n = #tostring(vis.win.viewport.lines.finish)
end
if vis.ui.layout == vis.ui.layouts.HORIZONTAL then
for win in vis:windows() do
if win == vis.win then
break
end
t = t + win.height
end
elseif vis.ui.layout == vis.ui.layouts.VERTICAL then
local left = true
for win in vis:windows() do
if win == vis.win then
left = false
if opt.wrapcolumn ~= 0 then
r = r + win.width - n - opt.wrapcolumn
end
elseif left then
l = l + win.width + 1
else
r = r + win.width
end
end
end
return '--wrap --padding=' .. t .. ',' .. r .. ',0,' .. (l + n) .. ' --tabstop=' .. opt.tabwidth
end
local mu = #up_keys
local md = #down_keys
local up = merge(up_keys, down_keys)
local down = merge(down_keys, up_keys)
local jumps = {}
local function jump(keys)
local _, esc = keys:find('<Escape>')
if esc then
return esc
end
local ret = #keys
keys = keys:gsub('<Enter>', '\n')
if #keys < 2 then
return -1
end
if #keys > 2 then
vis:info('not found')
return ret
end
local v = vis.win.viewport.bytes
local data = vis.win.file:content(v)
local p = search(P(keys))
local lst = p:match(data)
if not lst or #lst == 0 then
vis:info('not found')
return ret
end
if #lst == 1 then
vis.win.selection.pos = v.start + lst[1] - 1
return ret
end
local pos = vis.win.selection.pos - v.start
local prev = 0
local next = #lst + 1
local t = {}
jumps = {}
for i = 1, #lst do
local a = lst[i]
if a < pos then
prev = i
end
if a > pos then
next = i
break
end
end
local nu = prev
local nd = #lst - next
local j = 1
for i = prev, 1, -1 do
if j == mu + 1 then
j = j + nd
end
if not up[j] then
break
end
t[i] = up[j]
jumps[t[i]] = v.start + lst[i] - 1
j = j + 1
end
j = 1
for i = next, #lst do
if j == md + 1 then
j = j + nu
end
if not down[j] then
break
end
t[i] = down[j]
jumps[t[i]] = v.start + lst[i] - 1
j = j + 1
end
local input = ''
local k = 1
for i = 1, #lst do
input = input .. data:sub(k, lst[i] - 1)
if t[i] then
input = input .. '\x1b[1;37;44m' .. keys .. '\x1b[42m' .. t[i] .. '\x1b[0m'
k = lst[i] + 3
if data:sub(k - 1, k - 1) == '\n' then
k = k - 1
end
else
input = input .. '\x1b[1;37;44m' .. keys .. '\x1b[0m'
k = lst[i] + 2
end
end
input = input .. data:sub(k)
local code, out, err = vis:pipe(input, 'fzf --ansi --layout=reverse-list --no-info --no-separator --color gutter:-1,bg+:-1 --marker="" ' .. layout() .. ' --print-query --bind change:accept')
if code ~= 0 then
vis:info(err or ('fzf exit code ' .. code))
else
local p = jumps[out:sub(1, 1)]
if p then
vis.win.selection.pos = p
else
vis:info('not found')
end
end
return ret
end
vis:map(vis.modes.NORMAL, '\\', jump)