From edfaf03512597cff24b5f06ee73becf85de7c5f0 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Fri, 5 Apr 2024 22:07:45 +0900 Subject: [PATCH] perf: avoid copying buffer/window variables to lua Problem: Calling vim.fn.{getbufinfo,getwininfo} in lua can be slow because it has to convert potentially big and cyclic data structures stored in vim buffer/window variables into lua values. Solution: Remove buffer/window variables from the result before passing it to lua. --- autoload/fzf_lua.vim | 22 ++++++++++++++++++++++ lua/fzf-lua/previewer/builtin.lua | 10 +++++----- lua/fzf-lua/providers/buffers.lua | 2 +- lua/fzf-lua/utils.lua | 12 ++++++------ lua/fzf-lua/win.lua | 4 ++-- 5 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 autoload/fzf_lua.vim diff --git a/autoload/fzf_lua.vim b/autoload/fzf_lua.vim new file mode 100644 index 00000000..fb62c77a --- /dev/null +++ b/autoload/fzf_lua.vim @@ -0,0 +1,22 @@ +" Calling vim.fn.getbufinfo in lua can be expensive because it has to convert +" all the buffer variables into lua values. Since fzf-lua does not access +" buffer variables, this cost can be avoided by clearing the entry before +" passing the info to lua. +function! fzf_lua#getbufinfo(bufnr) abort + let info = getbufinfo(a:bufnr) + if empty(info) + return v:false " there is no way to return `nil` from vimscript + endif + unlet! info[0].variables + return info[0] +endfunction + +" Similar to fzf_lua#getbufinfo, but for getwininfo. +function! fzf_lua#getwininfo(winid) abort + let info = getwininfo(a:winid) + if empty(info) + return v:false + endif + unlet! info[0].variables + return info[0] +endfunction diff --git a/lua/fzf-lua/previewer/builtin.lua b/lua/fzf-lua/previewer/builtin.lua index 112708bf..732fb842 100644 --- a/lua/fzf-lua/previewer/builtin.lua +++ b/lua/fzf-lua/previewer/builtin.lua @@ -108,7 +108,7 @@ end function Previewer.base:preview_is_terminal() if not self.win or not self.win:validate_preview() then return end - return vim.fn.getwininfo(self.win.preview_winid)[1].terminal == 1 + return vim.fn["fzf_lua#getwininfo"](self.win.preview_winid).terminal == 1 end function Previewer.base:get_tmp_buffer() @@ -368,10 +368,10 @@ function Previewer.base:scroll(direction) -- user scrolls, the highlight is no longer relevant (#462). -- Conditionally toggle 'cursorline' based on cursor position if self.orig_pos and self.winopts.cursorline then - local wininfo = vim.fn.getwininfo(preview_winid) - if wininfo and wininfo[1] and - self.orig_pos[1] >= wininfo[1].topline and - self.orig_pos[1] <= wininfo[1].botline then + local wininfo = vim.fn["fzf_lua#getwininfo"](preview_winid) + if wininfo and + self.orig_pos[1] >= wininfo.topline and + self.orig_pos[1] <= wininfo.botline then -- reset cursor pos even when it's already there, no bigggie -- local curpos = vim.api.nvim_win_get_cursor(preview_winid) vim.api.nvim_win_set_cursor(preview_winid, self.orig_pos) diff --git a/lua/fzf-lua/providers/buffers.lua b/lua/fzf-lua/providers/buffers.lua index 20141713..57944999 100644 --- a/lua/fzf-lua/providers/buffers.lua +++ b/lua/fzf-lua/providers/buffers.lua @@ -67,7 +67,7 @@ local populate_buffer_entries = function(opts, bufnrs, tabh) local element = { bufnr = bufnr, flag = flag, - info = vim.fn.getbufinfo(bufnr)[1], + info = vim.fn["fzf_lua#getbufinfo"](bufnr), readonly = vim.bo[bufnr].readonly } diff --git a/lua/fzf-lua/utils.lua b/lua/fzf-lua/utils.lua index b502f6de..f81041ba 100644 --- a/lua/fzf-lua/utils.lua +++ b/lua/fzf-lua/utils.lua @@ -805,7 +805,7 @@ function M.is_term_buffer(bufnr) bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr local winid = vim.fn.bufwinid(bufnr) if tonumber(winid) > 0 and vim.api.nvim_win_is_valid(winid) then - return vim.fn.getwininfo(winid)[1].terminal == 1 + return vim.fn["fzf_lua#getwininfo"](winid).terminal == 1 end local bufname = vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_get_name(bufnr) return M.is_term_bufname(bufname) @@ -813,9 +813,9 @@ end function M.buffer_is_dirty(bufnr, warn, only_if_last_buffer) bufnr = tonumber(bufnr) or vim.api.nvim_get_current_buf() - local info = bufnr and vim.fn.getbufinfo(bufnr)[1] + local info = bufnr and vim.fn["fzf_lua#getbufinfo"](bufnr) if info and info.changed ~= 0 then - if only_if_last_buffer and 1 < M.tbl_length(vim.fn.win_findbuf(bufnr)) then + if only_if_last_buffer and 1 < #vim.fn.win_findbuf(bufnr) then return false end if warn then @@ -829,7 +829,7 @@ end function M.save_dialog(bufnr) bufnr = tonumber(bufnr) or vim.api.nvim_get_current_buf() - local info = bufnr and vim.fn.getbufinfo(bufnr)[1] + local info = bufnr and vim.fn["fzf_lua#getbufinfo"](bufnr) if not info.name or #info.name == 0 then -- unnamed buffers can't be saved M.warn(string.format("buffer %d has unsaved changes", bufnr)) @@ -854,7 +854,7 @@ end -- 2 for loc list function M.win_is_qf(winid, wininfo) wininfo = wininfo or - (vim.api.nvim_win_is_valid(winid) and vim.fn.getwininfo(winid)[1]) + (vim.api.nvim_win_is_valid(winid) and vim.fn["fzf_lua#getwininfo"](winid)) if wininfo and wininfo.quickfix == 1 then return wininfo.loclist == 1 and 2 or 1 end @@ -863,7 +863,7 @@ end function M.buf_is_qf(bufnr, bufinfo) bufinfo = bufinfo or - (vim.api.nvim_buf_is_valid(bufnr) and vim.fn.getbufinfo(bufnr)[1]) + (vim.api.nvim_buf_is_valid(bufnr) and vim.fn["fzf_lua#getbufinfo"](bufnr)) if bufinfo and bufinfo.variables and bufinfo.variables.current_syntax == "qf" and not vim.tbl_isempty(bufinfo.windows) then diff --git a/lua/fzf-lua/win.lua b/lua/fzf-lua/win.lua index 6f747e32..ee87e48f 100644 --- a/lua/fzf-lua/win.lua +++ b/lua/fzf-lua/win.lua @@ -466,7 +466,7 @@ end function FzfWin:preview_layout() if self.winopts.split and self.previewer_is_builtin then - local wininfo = fn.getwininfo(self.fzf_winid)[1] + local wininfo = fn["fzf_lua#getwininfo"](self.fzf_winid) -- unlike floating win popups, split windows inherit the global -- 'signcolumn' setting which affects the available width for fzf -- 'generate_layout' will then use the sign column available width @@ -1144,7 +1144,7 @@ function FzfWin:update_scrollbar(hide) local buf = api.nvim_win_get_buf(self.preview_winid) local o = {} - o.wininfo = fn.getwininfo(self.preview_winid)[1] + o.wininfo = fn["fzf_lua#getwininfo"](self.preview_winid) o.line_count = api.nvim_buf_line_count(buf) local topline, height = o.wininfo.topline, o.wininfo.height