Closed eyalz800 closed 1 year ago
I'm hesitant to make this change because it's going to affect all buffers, not just symbols-outline, and it's difficult to tell what all the side effects would be.
Does symbols-outline need the buffer to have a particular name? Could you solve this instead by just renaming the buffer with an autocmd to something random when it's created?
I went for this because this was the simplest solution, I have not explored autocmd but it sounded like it could leave an opening for an error there.
Since the plugin pretends to delete the buffer, I thought that renaming it is going to provide a more complete illusion that the buffer is indeed deleted - any attempt to access the original buffer name is anyway erroneous, and the expected result is that it does not exist. Many plugins have unique buffer names and simply hitting "q" or a scripted open/close of those plugins is throwing an error in nvim_buf_set_name.
I re-read the code and realized I'm changing the name too early, it's now better reflecting my explanation from earlier.
edit: but it does not always work, I'm checking why
I probably understand less about the edge cases than you, I have multiple issues with multiple plugins just when I moved from the original solution that was changing names to the new solution that I pushed now.
When I was changing the name outside of the BufHidden, then everything worked, and then I added a change that changes the name only when BufHidden is fired but it is not fired in some cases, and I'm not sure why. The folke/edgy.nvim plugin sets the bufhidden = 'wipe' then closes the window then sets name immediately line after line, and then set name..
I'm still debugging it and not sure why I am not getting BufHidden for it.
I found why the bufhidden was not being called on some edgy windows - and the reason is they use noautocmd in certain places, when removed it is indeed called
Yeah both stickybuf and edgy do some fairly invasive magic with buffer and window handling. I wouldn't expect them to play nice together.
I think we need to drop this change, this is getting too technical too quickly, so I'm closing the pull request, if you have some ideas to share please do so here
@stevearc what do you think about the following way to pin? It's probably filled with edge cases, I just added in an edit the WinClosed command which I forgot initially
local m = {}
local v = vim
local augroup = v.api.nvim_create_augroup
local autocmd = v.api.nvim_create_autocmd
local clear_autocmds = v.api.nvim_clear_autocmds
m.filetyeps = {
'qf',
'fugitiveblame',
'NvimTree',
'Outline',
}
local make_win_ref = function(buf)
local win = v.api.nvim_open_win(buf, false, {
relative = 'editor',
width = 1,
height = 1,
col = 0,
row = 0,
style = 'minimal',
noautocmd = true,
})
v.w[win].pin_data = { buf = buf }
return win
end
local open_in_best_win = function(buf)
for winnr = 1, v.fn.winnr '$' do
local winid = v.fn.win_getid(winnr)
if not v.w[winid].pin_data and v.api.nvim_win_get_config(winid or 0).relative == '' then
v.cmd.wincmd({ count = winnr, args = { 'w' } })
v.cmd.buffer({ args = { buf } })
return
end
end
v.fn.win_execute(v.fn.win_getid(1), string.format('vertical rightbelow sbuffer %d', buf))
v.cmd.wincmd({ count = 2, args = { 'w' } })
end
m.pin = function(opts)
opts = opts or {}
local buf = opts.buf or v.api.nvim_get_current_buf()
local win = opts.win or v.api.nvim_get_current_win()
local buf_pin_data = {
win = win,
name = v.api.nvim_buf_get_name(buf),
}
local win_pin_data = { buf = buf }
v.b[buf].pin_data = buf_pin_data
v.w[win].pin_data = win_pin_data
local group = augroup('init.lua.pin.bufwinleave', { clear = false })
clear_autocmds({ group = group, buffer = buf })
autocmd('bufwinleave', {
group = group,
buffer = buf,
callback = function(args)
local pin_data = v.b[args.buf].pin_data
if not pin_data.closing then
pin_data.win_ref = make_win_ref(args.buf)
v.b[args.buf].pin_data = pin_data
end
end,
})
group = augroup('init.lua.pin.winclosed', { clear = false })
clear_autocmds({ group = group, buffer = buf })
autocmd('winclosed', {
group = group,
buffer = buf,
callback = function(args)
local pin_data = v.b[args.buf].pin_data
pin_data.closing = true
v.b[args.buf].pin_data = pin_data
end,
})
end
m.setup = function()
autocmd('bufenter', {
group = augroup('init.lua.pin.bufenter', {}),
callback = function(args)
local win_pin_data = v.w.pin_data
local buf = args.buf
if win_pin_data then
if buf == win_pin_data.buf then
return
end
local buf_pin_data = v.b[win_pin_data.buf].pin_data
if not buf_pin_data.win_ref then
return
end
local win_ref = make_win_ref(buf)
v.api.nvim_win_set_buf(buf_pin_data.win, win_pin_data.buf)
v.api.nvim_win_close(buf_pin_data.win_ref, true)
buf_pin_data.win_ref = nil
open_in_best_win(buf)
v.api.nvim_win_close(win_ref, true)
return
end
local buf_pin_data = v.b[buf].pin_data
if buf_pin_data then
win_pin_data = { buf = buf }
v.w.pin_data = win_pin_data
buf_pin_data.win = v.api.nvim_get_current_win()
v.b[buf].pin_data = buf_pin_data
else
local buf_ft = v.bo[buf].filetype
for _, ft in ipairs(m.filetyeps) do
if buf_ft == ft then
m.pin({buf=buf, win=v.api.nvim_get_current_win()})
win_pin_data = { buf = buf }
buf_pin_data = v.b[buf].pin_data
break
end
end
end
end,
})
end
return m
After some stress tests I arrived at the following
local m = {}
local v = require 'vim'
local augroup = v.api.nvim_create_augroup
local autocmd = v.api.nvim_create_autocmd
local clear_autocmds = v.api.nvim_clear_autocmds
m.filetyeps = {
'qf',
'fugitiveblame',
'NvimTree',
'Outline',
}
local make_win_ref = function(buf)
local success, win = pcall(v.api.nvim_open_win, buf, false, {
relative = 'editor',
width = 1,
height = 2,
col = 0,
row = 0,
style = 'minimal',
noautocmd = true,
})
if success then
v.w[win].pin_data = { buf = buf }
return win
end
end
local open_in_best_win = function(buf)
for winnr = 1, v.fn.winnr '$' do
local winid = v.fn.win_getid(winnr)
if not v.w[winid].pin_data and v.api.nvim_win_get_config(winid or 0).relative == '' then
v.cmd.wincmd({ count = winnr, args = { 'w' } })
v.cmd.buffer({ args = { buf } })
return
end
end
v.fn.win_execute(v.fn.win_getid(1), string.format('vertical rightbelow sbuffer %d', buf))
v.cmd.wincmd({ count = 2, args = { 'w' } })
end
local is_auto_deleted = function(buf)
local bufhidden = v.bo[buf].bufhidden
return bufhidden == 'unload' or bufhidden == 'delete' or bufhidden == 'wipe'
end
m.pin = function(opts)
opts = opts or {}
local buf = opts.buf or v.api.nvim_get_current_buf()
local win = opts.win or v.api.nvim_get_current_win()
local buf_pin_data = {
win = win,
name = v.api.nvim_buf_get_name(buf),
}
local win_pin_data = { buf = buf }
v.b[buf].pin_data = buf_pin_data
v.w[win].pin_data = win_pin_data
local group = augroup('init.lua.pin.bufwinleave', { clear = false })
clear_autocmds({ group = group, buffer = buf })
autocmd('bufwinleave', {
group = group,
buffer = buf,
callback = function(args)
local pin_data = v.b[args.buf].pin_data
if not pin_data.closing then
if is_auto_deleted(args.buf) then
pin_data.win_ref = make_win_ref(args.buf)
end
v.b[args.buf].pin_data = pin_data
end
end,
})
group = augroup('init.lua.pin.winclosed', { clear = false })
clear_autocmds({ group = group, buffer = buf })
autocmd('winclosed', {
group = group,
buffer = buf,
callback = function(args)
local pin_data = v.b[args.buf].pin_data
pin_data.closing = true
v.b[args.buf].pin_data = pin_data
end,
})
end
m.setup = function()
autocmd('bufenter', {
group = augroup('init.lua.pin.bufenter', {}),
callback = function(args)
local win_pin_data = v.w.pin_data
local buf = args.buf
if win_pin_data then
if buf == win_pin_data.buf then
return
end
local buf_pin_data = nil
if not v.api.nvim_buf_is_valid(win_pin_data.buf) then
win_pin_data = nil
else
buf_pin_data = v.b[win_pin_data.buf].pin_data
if not buf_pin_data then
win_pin_data = nil
end
end
if not win_pin_data then
v.w.pin_data = nil
else
local win_ref = nil
if is_auto_deleted(buf) then
win_ref = make_win_ref(buf)
if not win_ref then
if buf_pin_data.win_ref then
pcall(v.api.nvim_win_close, buf_pin_data.win_ref, true)
v.b[win_pin_data.buf].pin_data.win_ref = nil
end
return
end
end
local success, _ = pcall(v.api.nvim_win_set_buf, buf_pin_data.win, win_pin_data.buf)
if buf_pin_data.win_ref then
pcall(v.api.nvim_win_close, buf_pin_data.win_ref, true)
v.b[win_pin_data.buf].pin_data.win_ref = nil
end
if success then
pcall(open_in_best_win, buf)
end
if win_ref then
pcall(v.api.nvim_win_close, win_ref, true)
end
return
end
end
local buf_pin_data = v.b[buf].pin_data
if buf_pin_data then
if not v.api.nvim_win_is_valid(buf_pin_data.win) then
win_pin_data = { buf = buf }
v.w.pin_data = win_pin_data
buf_pin_data.win = v.api.nvim_get_current_win()
v.b[buf].pin_data = buf_pin_data
end
else
local buf_ft = v.bo[buf].filetype
for _, ft in ipairs(m.filetyeps) do
if buf_ft == ft then
m.pin({buf=buf, win=v.api.nvim_get_current_win()})
break
end
end
end
end,
})
end
return m
@stevearc I would really appreciate if you have any feedback on the approach taken in the post above, only if you have time to review reply
It looks like a reasonable approach. Is there anything in particular you wanted feedback on?
Mostly missed edge cases or issues it could have, so far I noticed that I miss a case if I’m on nvim-tree and I open a directory via :n directory then nvim tree gets the bufenter before there is anything there and I don’t pin it, I decided to leave as is because it’s not part of my workflow so much
I had an issue with symbols-outline where it expected to be able to immediately create new buffer with the same name - and so I ended up renaming the buffer in the bufhidden override