From 24712351a1206a04ff95f15cb626783fb6f82ead Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 1 Jun 2024 22:51:47 +0800 Subject: [PATCH] perf(guide): improve key binding guide --- lua/spacevim/api/vim/compatible.lua | 165 ++++++++++++++++------------ lua/spacevim/plugin/guide.lua | 108 +++++++++++------- 2 files changed, 161 insertions(+), 112 deletions(-) diff --git a/lua/spacevim/api/vim/compatible.lua b/lua/spacevim/api/vim/compatible.lua index 71184fcd6..8a72064c3 100644 --- a/lua/spacevim/api/vim/compatible.lua +++ b/lua/spacevim/api/vim/compatible.lua @@ -7,125 +7,144 @@ --============================================================================= local M = {} -local system = require('spacevim.api').import('system') - function M.eval(l) - if vim.api ~= nil then - return vim.api.nvim_eval(l) + if vim.api ~= nil then + return vim.api.nvim_eval(l) + else + return vim.eval(l) + end +end + +function M.islist(val) + if vim.islist then + return vim.islist(val) + ---@diagnostic disable-next-line + elseif vim.tbl_islist then + ---@diagnostic disable-next-line + return vim.tbl_islist(val) + else + if type(val) ~= 'table' then + return false else - return vim.eval(l) + for k, _ in pairs(val) do + if type(k) ~= 'number' then + return false + end + end + return true end + end end if vim.command ~= nil then - function M.cmd(command) - return vim.command(command) - end + function M.cmd(command) + return vim.command(command) + end else - function M.cmd(command) - return vim.api.nvim_command(command) - end + function M.cmd(command) + return vim.api.nvim_command(command) + end end -- there is no want to call viml function in old vim and neovim local function build_argv(...) - local str = '' - for index, value in ipairs(...) do - if str ~= '' then - str = str .. ',' - end - if type(value) == 'string' then - str = str .. '"' .. value .. '"' - elseif type(value) == 'number' then - str = str .. value - end + local str = '' + for _, value in ipairs(...) do + if str ~= '' then + str = str .. ',' end - return str + if type(value) == 'string' then + str = str .. '"' .. value .. '"' + elseif type(value) == 'number' then + str = str .. value + end + end + return str end function M.call(funcname, ...) - if vim.call ~= nil then - return vim.call(funcname, ...) + if vim.call ~= nil then + return vim.call(funcname, ...) + else + if vim.api ~= nil then + return vim.api.nvim_call_function(funcname, { ... }) else - if vim.api ~= nil then - return vim.api.nvim_call_function(funcname, {...}) - else - -- call not call vim script function in lua - vim.command('let g:lua_rst = ' .. funcname .. '(' .. build_argv({...}) .. ')') - return M.eval('g:lua_rst') - end + -- call not call vim script function in lua + vim.command('let g:lua_rst = ' .. funcname .. '(' .. build_argv({ ... }) .. ')') + return M.eval('g:lua_rst') end + end end -- this is for Vim and old neovim M.fn = setmetatable({}, { - __index = function(t, key) - local _fn - if vim.api ~= nil and vim.api[key] ~= nil then - _fn = function() - error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key)) - end - else - _fn = function(...) - return M.call(key, ...) - end - end - t[key] = _fn - return _fn + __index = function(t, key) + local _fn + if vim.api ~= nil and vim.api[key] ~= nil then + _fn = function() + error(string.format('Tried to call API function with vim.fn: use vim.api.%s instead', key)) + end + else + _fn = function(...) + return M.call(key, ...) + end end + t[key] = _fn + return _fn + end, }) -- This is for vim and old neovim to use vim.o M.vim_options = setmetatable({}, { - __index = function(t, key) - local _fn - if vim.api ~= nil then - -- for neovim - return vim.api.nvim_get_option(key) - else - -- for vim - _fn = M.eval('&' .. key) - end - t[key] = _fn - return _fn + __index = function(t, key) + local _fn + if vim.api ~= nil then + -- for neovim + return vim.api.nvim_get_option(key) + else + -- for vim + _fn = M.eval('&' .. key) end + t[key] = _fn + return _fn + end, }) function M.echo(msg) - if vim.api ~= nil then - vim.api.nvim_echo({{msg}}, false, {}) - else - vim.command('echo ' .. build_argv({msg})) - end + if vim.api ~= nil then + vim.api.nvim_echo({ { msg } }, false, {}) + else + vim.command('echo ' .. build_argv({ msg })) + end end local has_cache = {} function M.has(feature) - if has_cache[feature] ~= nil then - return has_cache[feature] - else - return M.eval('float2nr(has("' .. feature .. '"))') - end + if has_cache[feature] ~= nil then + return has_cache[feature] + else + return M.eval('float2nr(has("' .. feature .. '"))') + end end if M.has('patch-7.4.279') then - function M.globpath(dir, expr) - return M.fn.globpath(dir, expr, 1, 1) - end + function M.globpath(dir, expr) + return M.fn.globpath(dir, expr, 1, 1) + end else - function M.globpath(dir, expr) - return M.fn.split(fn.globpath(dir, expr), "\n") - end + function M.globpath(dir, expr) + return M.fn.split(vim.fn.globpath(dir, expr), '\n') + end end function M.execute(cmd, silent) - return M.fn.execute(cmd, silent) + return M.fn.execute(cmd, silent) end function M.win_screenpos(nr) - return vim.fn.win_screenpos(nr) + return vim.fn.win_screenpos(nr) end return M diff --git a/lua/spacevim/plugin/guide.lua b/lua/spacevim/plugin/guide.lua index 146d24bd5..3f2b1e4da 100644 --- a/lua/spacevim/plugin/guide.lua +++ b/lua/spacevim/plugin/guide.lua @@ -15,6 +15,7 @@ local cmp = require('spacevim.api').import('vim.compatible') local buffer = require('spacevim.api').import('vim.buffer') local VIM = require('spacevim.api').import('vim') local SL = require('spacevim.api').import('vim.statusline') +local hl = require('spacevim.api.vim.highlight') -- all local values should be listed here: @@ -164,27 +165,27 @@ local function add_map_to_dict(map, level, dict) local nlevel = level + 1 if not dict[curkey] then dict[curkey] = { name = vim.g.leaderGuide_default_group_name } - elseif vim.tbl_islist(dict[curkey]) and vim.g.leaderGuide_flatten == 1 then + elseif cmp.islist(dict[curkey]) and vim.g.leaderGuide_flatten == 1 then local cmd = escape_mappings(map) curkey = table.concat(map.lhs, '', level) nlevel = level if not dict[curkey] then dict[curkey] = { cmd, map.display } end - elseif vim.tbl_islist(dict[curkey]) and vim.g.leaderGuide_flatten == 0 then + elseif cmp.islist(dict[curkey]) and vim.g.leaderGuide_flatten == 0 then curkey = curkey .. 'm' if not dict[curkey] then dict[curkey] = { name = vim.g.leaderGuide_default_group_name } end end - if not vim.tbl_islist(dict[curkey]) then + if not cmp.islist(dict[curkey]) then add_map_to_dict(map, nlevel, dict[curkey]) end else local cmd = escape_mappings(map) if not dict[map.lhs[level]] then dict[map.lhs[level]] = { cmd, map.display } - elseif not vim.tbl_islist(dict[map.lhs[level]]) and vim.g.leaderGuide_flatten == 1 then + elseif not cmp.islist(dict[map.lhs[level]]) and vim.g.leaderGuide_flatten == 1 then local childmap = flattenmap(dict[map.lhs[level]], map.lhs[level]) for it, _ in pairs(childmap) do dict[it] = childmap[it] @@ -282,22 +283,44 @@ local function start_parser(key, dict) end end +---@param v string key board string +---@return string # displayed string of each item +local function get_displaystring(v) + local desc = '' + if lmap[v].name then + desc = lmap[v].name + else + desc = lmap[v][2] or '' + end + local offset = string.rep(' ', 8 - #v) + local displaystring + if vim.g.spacevim_leader_guide_theme == 'whichkey' then + displaystring = offset .. v .. ' -> ' .. desc + else + displaystring = offset .. '[' .. v .. '] ' .. desc + end + return displaystring +end + local function calc_layout() local ret = {} + local smap = {} - local smap = vim.fn.filter(vim.fn.copy(lmap), 'v:key !=# "name"') - ret.n_items = vim.fn.len(smap) - local length = {} - for k, v in pairs(smap) do - if v.name then - table.insert(length, vim.fn.strdisplaywidth('[' .. k .. ']' .. v.name)) - else - table.insert(length, vim.fn.strdisplaywidth('[' .. k .. ']' .. v[2])) + for k, v in pairs(lmap) do + if v ~= 'name' then + smap[k] = v end end - local maxlength = vim.fn.max(length) + vim.g.leaderGuide_hspace + ret.n_items = vim.fn.len(smap) + local length = {} + log.debug('smap is:' .. vim.inspect(smap)) + for k, _ in pairs(smap) do + table.insert(length, vim.fn.strdisplaywidth(get_displaystring(k))) + end + local maxlength = vim.fn.max(length) + vim.g.leaderGuide_hspace + log.debug('maxlength is:' .. maxlength) if vim.g.leaderGuide_vertical == 1 then ret.n_rows = vim.fn.winheight(0) - 2 ret.n_cols = math.floor(ret.n_items / ret.n_rows) @@ -319,6 +342,7 @@ local function calc_layout() end ret.win_dim = ret.n_rows end + log.debug('layout is:' .. vim.inspect(ret)) return ret end @@ -396,19 +420,7 @@ local function create_string(layout) table.sort(smap, compare_key) for _, k in ipairs(smap) do - local desc = '' - if lmap[k].name then - desc = lmap[k].name - else - desc = lmap[k][2] or '' - end - local offset = string.rep(' ', 8 - #k) - local displaystring - if vim.g.spacevim_leader_guide_theme == 'whichkey' then - displaystring = offset .. k .. ' -> ' .. desc - else - displaystring = offset .. '[' .. k .. '] ' .. desc - end + local displaystring = get_displaystring(k) crow = rows[row] or {} if #crow == 0 then @@ -416,10 +428,10 @@ local function create_string(layout) end -- if the displaystring is too long if #displaystring > l.col_width then - table.insert(crow, string.sub(displaystring, 1, l.col_width -5) .. '... ') + table.insert(crow, string.sub(displaystring, 1, l.col_width - 5) .. '... ') else table.insert(crow, displaystring) - table.insert(crow, vim.fn['repeat'](' ', l.col_width - vim.fn.strdisplaywidth(displaystring))) + table.insert(crow, string.rep(' ', l.col_width - vim.fn.strdisplaywidth(displaystring))) end if vim.g.leaderGuide_sort_horizontal == 0 then if row > n_rows then @@ -455,8 +467,16 @@ local function create_string(layout) return r end +local cursor_highlight_info local function highlight_cursor() - vim.cmd('hi! def link SpaceVimGuideCursor Cursor') +cursor_highlight_info = hl.group2dict('Cursor') + local hlinfo = { + name = 'SpaceVimGuideCursor', + guibg = cursor_highlight_info.guibg, + ctermbg = cursor_highlight_info.ctermbg, + } + hl.hi(hlinfo) + hl.hide_in_normal('Cursor') if vis == 'gv' then local _begin = vim.fn.getpos("'<") local _end = vim.fn.getpos("'>") @@ -480,6 +500,7 @@ local function highlight_cursor() end local function remove_cursor_highlight() + hl.hi(cursor_highlight_info) pcall(vim.fn.matchdelete, cursor_hilight_id) end @@ -505,14 +526,23 @@ local function updateStatusline() end local keys = prefix_key_inp + local separators = { + arrow = '', + curve = '', + slant = '', + brace = '', + fire = '', + ['nil'] = '', + } + local sep = separators[vim.g.spacevim_statusline_separator] or separators.arrow SL.open_float({ { 'Guide: ', 'LeaderGuiderPrompt' }, - { ' ', 'LeaderGuiderSep1' }, + { sep .. ' ', 'LeaderGuiderSep1' }, { vim.fn['SpaceVim#mapping#leader#getName'](prefix_key) .. table.concat(keys, '') .. gname, 'LeaderGuiderName', }, - { ' ', 'LeaderGuiderSep2' }, + { sep .. ' ', 'LeaderGuiderSep2' }, { guide_help_msg(false), 'LeaderGuiderFill' }, { string.rep(' ', 999), 'LeaderGuiderFill' }, }) @@ -563,11 +593,12 @@ local function winopen() winfixwidth = true, winfixheight = true, }) - updateStatusline() return winid, bufnr end local function start_buffer() - winid, bufnr = winopen() + if not vim.api.nvim_win_is_valid(winid) or not vim.api.nvim_buf_is_valid(bufnr) then + winid, bufnr = winopen() + end local layout = calc_layout() local text = create_string(layout) if vim.g.leaderGuide_max_size then @@ -583,12 +614,13 @@ local function start_buffer() col = 0, }) - vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, text) + updateStatusline() + cmp.fn.setbufvar(bufnr, '&modifiable', 0) - vim.cmd('redraw!') + vim.cmd('redraw') wait_for_input() end @@ -603,11 +635,11 @@ local function winclose() end local function handle_input(input) - winclose() - if not vim.tbl_islist(input) then + if not cmp.islist(input) then lmap = input start_buffer() else + winclose() prefix_key_inp = {} cmp.fn.feedkeys(vis .. reg .. count, 'ti') @@ -628,7 +660,6 @@ local function page_down() end local function page_undo() - winclose() if #prefix_key_inp then table.remove(prefix_key_inp) end @@ -639,7 +670,6 @@ local function page_undo() end local function page_up() - -- vim.api.nvim_feedkeys(Key.t(''), 'n', false) vim.api.nvim_feedkeys(Key.t(''), 'x', false) vim.cmd('redraw!')