minimize preview buffer flicker between reloads (#298)

main
bhagwan 2 years ago
parent f3857a98a7
commit 0fa4f3d7d7

@ -408,6 +408,8 @@ M.make_entry_lcol = function(opts, entry)
if not entry then return nil end
local filename = entry.filename or vim.api.nvim_buf_get_name(entry.bufnr)
return string.format("%s:%s:%s:%s%s",
-- uncomment to test URIs
-- "file://" .. filename,
filename, --utils.ansi_codes.magenta(filename),
utils.ansi_codes.green(tostring(entry.lnum)),
utils.ansi_codes.blue(tostring(entry.col)),

@ -152,17 +152,17 @@ end
function M.entry_to_location(entry)
local uri, line, col = entry:match("^(.*://.*):(%d+):(%d+):")
line = line and tonumber(line-1) or 0
col = col and tonumber(col-1) or 0
line = line and tonumber(line) or 1
col = col and tonumber(col) or 1
return {
stripped = entry,
line = line+1,
line = line,
col = col,
uri = uri,
range = {
start = {
line = line,
character = col,
line = line-1,
character = col-1,
}
}
}

@ -62,34 +62,49 @@ function Previewer.base:set_winopts(win)
end
end
function Previewer.base:set_tmp_buffer()
if not self.win or not self.win:validate_preview() then return end
function Previewer.base:get_tmp_buffer()
local tmp_buf = api.nvim_create_buf(false, true)
api.nvim_buf_set_option(tmp_buf, 'bufhidden', 'wipe')
api.nvim_win_set_buf(self.win.preview_winid, tmp_buf)
return tmp_buf
end
function Previewer.base:set_preview_buf(newbuf)
if not self.win or not self.win:validate_preview() then return end
api.nvim_win_set_buf(self.win.preview_winid, newbuf)
self.preview_bufnr = newbuf
-- set preview window options
if not self.preview_isterm then
self:set_winopts(self.win.preview_winid)
end
end
function Previewer.base:clear_preview_buf()
local retbuf = nil
if self.win and self.win:validate_preview() then
if self.win and api.nvim_win_is_valid(self.win.preview_winid) then
-- attach a temp buffer to the window
-- so we can safely delete the buffer
-- ('nvim_buf_delete' removes the attached win)
retbuf = self:set_tmp_buffer()
retbuf = self:get_tmp_buffer()
api.nvim_win_set_buf(self.win.preview_winid, retbuf)
end
if self.preview_bufloaded then
local bufnr = self.preview_bufnr
if vim.api.nvim_buf_is_valid(bufnr) then
api.nvim_buf_call(bufnr, function()
vim.cmd('delm \\"')
end)
vim.api.nvim_buf_delete(bufnr, {force=true})
end
-- since our temp buffers have 'bufhidden=wipe' the tmp
-- buffer will be automatically wiped and 'nvim_buf_is_valid'
-- will return false
-- one case where the buffer may remain valid after detaching
-- from the preview window is with URI type entries after calling
-- 'vim.lsp.util.jump_to_location' which can reuse existing buffers
-- so techinically this should never be executed unless we're the
-- user wrote an fzf-lua extension and set the preview buffer to
-- a random buffer without the 'bufhidden' property
if not self.preview_isuri
and self.preview_bufnr
and vim.api.nvim_buf_is_valid(self.preview_bufnr) then
api.nvim_buf_call(self.preview_bufnr, function()
vim.cmd('delm \\"')
end)
vim.api.nvim_buf_delete(self.preview_bufnr, {force=true})
end
self.preview_bufnr = nil
self.preview_isterm = nil
self.preview_bufloaded = nil
self.loaded_entry = nil
return retbuf
end
@ -110,11 +125,13 @@ function Previewer.base:display_entry(entry_str)
end
local previous_bufnr = api.nvim_win_get_buf(self.win.preview_winid)
assert(not self.preview_bufnr or previous_bufnr == self.preview_bufnr)
-- clear the current preview buffer
-- store the new preview buffer
-- clears the current preview buffer and set to a new temp buffer
-- recommended to return false from 'should_clear_preview' and use
-- 'self:set_preview_buf()' instead for flicker-free exeperience
local should_clear = self.should_clear_preview and
self:should_clear_preview(entry_str)
if should_clear == nil or should_clear == true then
if should_clear ~= false then
self.preview_bufnr = self:clear_preview_buf()
end
@ -133,10 +150,11 @@ function Previewer.base:display_entry(entry_str)
self.win:reset_win_highlights(self.win.preview_winid)
end
if not self._entry_count then self._entry_count=1
else self._entry_count = self._entry_count+1 end
local entry_count = self._entry_count
if self.delay>0 then
-- debounce preview entries
if tonumber(self.delay)>0 then
if not self._entry_count then self._entry_count=1
else self._entry_count = self._entry_count+1 end
local entry_count = self._entry_count
vim.defer_fn(function()
-- only display if entry hasn't changed
if self._entry_count == entry_count then
@ -224,7 +242,11 @@ function Previewer.buffer_or_file:parse_entry(entry_str)
return entry
end
function Previewer.buffer_or_file:should_clear_preview(entry)
function Previewer.buffer_or_file:should_clear_preview(_)
return false
end
function Previewer.buffer_or_file:should_load_buffer(entry)
-- we don't have a previous entry to compare to
-- return 'true' so the buffer will be loaded in
-- ::populate_preview_buf
@ -243,28 +265,24 @@ function Previewer.buffer_or_file:populate_preview_buf(entry_str)
if not self.win or not self.win:validate_preview() then return end
local entry = self:parse_entry(entry_str)
if vim.tbl_isempty(entry) then return end
if not self:should_clear_preview(entry) then
-- mark terminal buffers so we don't call 'set_winopts'
-- mark uri entries so we do not delete the preview buffer
self.preview_isuri = (entry.uri ~= nil)
self.preview_isterm = entry.terminal
if not self:should_load_buffer(entry) then
-- same file/buffer as previous entry
-- no need to reload content
-- call post to set cusror location
self:preview_buf_post(entry)
elseif entry.bufnr and api.nvim_buf_is_loaded(entry.bufnr) then
-- WE NO LONGER REUSE THE CURRENT BUFFER (except for term)
-- WE NO LONGER REUSE THE CURRENT BUFFER
-- this changes the buffer's 'getbufinfo[1].lastused'
-- which messes up our `buffers()` sort
self.preview_isterm = entry.terminal
--[[ if self.preview_isterm then
-- display the buffer in the preview
api.nvim_win_set_buf(self.win.preview_winid, entry.bufnr)
-- store current preview buffer
self.preview_bufnr = entry.bufnr
else --]]
-- mark the buffer for unloading the next call
self.preview_bufloaded = true
entry.filetype = vim.api.nvim_buf_get_option(entry.bufnr, 'filetype')
local lines = vim.api.nvim_buf_get_lines(entry.bufnr, 0, -1, false)
vim.api.nvim_buf_set_lines(self.preview_bufnr, 0, -1, false, lines)
-- end
entry.filetype = vim.api.nvim_buf_get_option(entry.bufnr, 'filetype')
local lines = vim.api.nvim_buf_get_lines(entry.bufnr, 0, -1, false)
local tmpbuf = self:get_tmp_buffer()
vim.api.nvim_buf_set_lines(tmpbuf, 0, -1, false, lines)
self:set_preview_buf(tmpbuf)
self:preview_buf_post(entry)
elseif entry.uri then
-- LSP 'jdt://' entries, see issue #195
@ -281,24 +299,20 @@ function Previewer.buffer_or_file:populate_preview_buf(entry_str)
-- filename only
entry.path = path.relative(vim.api.nvim_buf_get_name(entry.bufnr), vim.loop.cwd())
end
-- mark the buffer for unloading the next call
self.preview_bufloaded = true
-- make sure the file is readable (or bad entry.path)
if not entry.path or not vim.loop.fs_stat(entry.path) then return end
local tmpbuf = self:get_tmp_buffer()
if utils.perl_file_is_binary(entry.path) then
vim.api.nvim_buf_set_lines(self.preview_bufnr, 0, -1, false, {
vim.api.nvim_buf_set_lines(tmpbuf, 0, -1, false, {
"Preview is not supported for binary files."
})
-- swap preview buffer with new one
self:set_preview_buf(tmpbuf)
self:preview_buf_post(entry)
return
end
-- read the file into the buffer
utils.read_file_async(entry.path, vim.schedule_wrap(function(data)
if not self.preview_bufnr or
not vim.api.nvim_buf_is_valid(self.preview_bufnr) then
return
end
local lines = vim.split(data, "[\r]?\n")
-- if file ends in new line, don't write an empty string as the last
@ -306,11 +320,9 @@ function Previewer.buffer_or_file:populate_preview_buf(entry_str)
if data:sub(#data, #data) == "\n" or data:sub(#data-1,#data) == "\r\n" then
table.remove(lines)
end
local ok = pcall(vim.api.nvim_buf_set_lines, self.preview_bufnr, 0, -1, false, lines)
if not ok then
return
end
vim.api.nvim_buf_set_lines(tmpbuf, 0, -1, false, lines)
-- swap preview buffer with new one
self:set_preview_buf(tmpbuf)
self:preview_buf_post(entry)
end))
end
@ -321,7 +333,7 @@ function Previewer.buffer_or_file:do_syntax(entry)
if not entry or not entry.path then return end
local bufnr = self.preview_bufnr
local preview_winid = self.win.preview_winid
if self.preview_bufloaded and vim.bo[bufnr].filetype == '' then
if api.nvim_buf_is_valid(bufnr) and vim.bo[bufnr].filetype == '' then
if fn.bufwinid(bufnr) == preview_winid then
-- do not enable for large files, treesitter still has perf issues:
-- https://github.com/nvim-treesitter/nvim-treesitter/issues/556
@ -414,6 +426,7 @@ function Previewer.buffer_or_file:update_border(entry)
end
function Previewer.buffer_or_file:preview_buf_post(entry)
if not self.win or not self.win:validate_preview() then return end
-- set preview win options or load the file
-- if not already loaded from buffer
self:set_cursor_hl(entry)
@ -440,6 +453,10 @@ end
Previewer.help_tags = Previewer.base:extend()
function Previewer.help_tags:should_clear_preview(_)
return false
end
function Previewer.help_tags:new(o, opts, fzf_win)
Previewer.help_tags.super.new(self, o, opts, fzf_win)
self.split = o.split
@ -552,14 +569,14 @@ end
function Previewer.man_pages:populate_preview_buf(entry_str)
local entry = self:parse_entry(entry_str)
-- mark the buffer for unloading the next call
self.preview_bufloaded = true
local cmd = self.cmd:format(entry)
if type(cmd) == 'string' then cmd = {"sh", "-c", cmd} end
local output, _ = utils.io_systemlist(cmd)
-- vim.api.nvim_buf_set_option(self.preview_bufnr, 'modifiable', true)
vim.api.nvim_buf_set_lines(self.preview_bufnr, 0, -1, false, output)
vim.api.nvim_buf_set_option(self.preview_bufnr, 'filetype', self.filetype)
local tmpbuf = self:get_tmp_buffer()
-- vim.api.nvim_buf_set_option(tmpbuf, 'modifiable', true)
vim.api.nvim_buf_set_lines(tmpbuf, 0, -1, false, output)
vim.api.nvim_buf_set_option(tmpbuf, 'filetype', self.filetype)
self:set_preview_buf(tmpbuf)
self.win:update_scrollbar()
end

Loading…
Cancel
Save