I am writing a vimscript function which should keep in a consistent state a "home made buffer manager". The idea is that I want to keep a trace of my buffers in the tabs they were open in.
This "buffer manager" simply is a dictionary which has the numbers of the tabs as keys and lists of buffers numbers as values.
To add the different buffers to this dictionary I created the following autocommand:
autocmd! BufEnter * call AddBufferToTab()
And then I have my function:
function! AddBufferToTab()
let newBufNr = bufnr("%")
" create an entry for the current tab if necessary
if !has_key(g:BuffersManager, tabpagenr())
let g:BuffersManager[tabpagenr()] = []
endif
" Add the buffer to the tab
if buflisted(newBufNr) && index(g:BuffersManager[tabpagenr()], newBufNr) == -1
call add (g:BuffersManager[tabpagenr()],newBufNr)
endif
endfunction
When vim enters a buffer the function is called, an empty list is created if the tab wasn't already managed.
Then I test if the buffer is listed by :ls
with buflisted(newBufNr)
and if the buffer is already in the list of the tab.
It is important to note that the first test seems to work: I don't want to add buffers which are not listed by :ls
and when I open an help buffer for example, the buffer is not added
The function seems to work properly excepted for one use case:
When I open NERDTree with :NERDTree
or :NERDTreeToggle
the function doesn't seems to be executed (at least the debug echom
that I used never appeared) and the buffer still end added to the manager.
Note that when I add
echom "ADD " . bufnr("%") . " " . bufname("%") . " " . buflisted(bufnr("%"))
To the function the informations seems to be the good ones (the ones of the currently entered buffer).
I also checked that NERDTree buffer have their property buflisted
to 0.
I have been working on this problem for several days now and I really can't figure out what's happening.
TL;DR Why NERDTree buffers are added to the dictionary when the function is meant to ignore nobuflisted
buffers?
NOTE If ones wants a big picture of the problem the function is a part of a plug-in I'm developing and which is available on github
NOTE 2 Of course if someone needs any additional information I'll gladly add them to this question, ask in the comments.
Additional information
I followed the comment of @VanLaser and splitted the if
condition and based my test also on the name of the buffer. I also added some debugging message, here is the function in "debugging state":
function! AddBufferToTab()
let newBufNr = bufnr("%")
" create an entry for the current tab if necessary
if !has_key(g:BuffersManager, tabpagenr())
let g:BuffersManager[tabpagenr()] = []
endif
" Get conditions to add the tab
let isListed = buflisted(newBufNr)
let isAlreadyInManager = 1
if (index(g:BuffersManager[tabpagenr()], newBufNr) == -1)
let isAlreadyInManager = 0
endif
let isNERDTreeBuffer = 0
if (bufname("%") =~ "NERD_Tree_")
let isNERDTreeBuffer = 1
endif
" Debugging messages
echom bufname("%")
if (isListed)
echom "is Listed"
else
echom "is not Listed"
endif
if (isAlreadyInManager)
echom "is AlreadyInManager"
else
echom "is not AlreadyInManager"
endif
if (isNERDTreeBuffer)
echom "is NERDTreeBuffer"
else
echom "is not NERDTreeBuffer"
endif
echom "========="
" Add the buffer to the tab
if isListed && !isAlreadyInManager && !isNERDTreeBuffer
call add (g:BuffersManager[tabpagenr()],newBufNr)
endif
endfunction
I am sure that I don't have a call to the add
function anywhere else than in this function.
I then followed this workflow:
- sourcing the file, the buffer manager is empty
:e %
(add the current file to the manager):e $MYVIMRC
:NERDTree
- select the first file
betterTabs.vim
in NERDTree.
And here is the log I had:
As you can see the opening of NerdTree doesn't seem to trigger the function (since I don't have any message about it in the log) BUT when I output the content of my dictionnary I get:
As I said I don't have any other call to add
in my whole file (I gave sooner in the post the link to the github repo if anyone wants to check).
I don't understand how the buffer can be added when it doesn't even trigger the function.
More infos to follow the investigations of @VanLaser in the comments (many thanks to him):
First, to quote :h ls
:
Each buffer has a unique number. That number will not change, so you can always go to a specific buffer with
:buffer N
orN CTRL-^
, where N is the buffer number.
So in theory if I get the number of the buffer I'm using it shouldn't change in the future.
Also I added the following lines at the very beginning of my function:
echom bufnr('%')
echom expand('<abuf>')
if bufnr('%') != expand('<abuf>')
echom "RETURN"
return
endif
and with the same workflow I get the same results, also I checked that the buffer numbers was the good ones:
Here is the output of messages
:
The output of ls!
:
And finally the content of my dictionary:
As you can see it seems that the numbers are correctly returned, but once again NERDTree is launched but doesn't trigger the function.
More infos to follow the comments of @lcd047
I changed the autocommand to
autocmd! BufEnter * call AddBufferToTab(expand('<abuf>'))
The function prototype to
function! AddBufferToTab(bufNumber)
and I replaced let newBufNr = bufnr("%")
with let newBufNr = a:bufNumber
and the behavior gets even weirder:
I added after the assignation of newBufNr
the following debugging echos:
echom newBufNr
echom bufnr('%')
echom expand('<abuf>')
echom a:bufNumber
For every buffers they show the same number (so using <abuf>
in the autocommand doesn't make a difference in the cases I tried).
But it get even weirder because then every buffers appears as not listed: for a reason that I don't understand buflisted(expand('<abuf>'))
returns 0 for every buffers.
PS I'm sorry for the length of this post I'm just trying to give as much informations as possible because I really absolutely don't understand what's happening there.
:ls!
. Do you get0
when do:echo buflisted(bufnr('%'))
inside the NerdTree buffer? You may also want to usehas_key(g:MyDic, newBufNr)
. See:h has_key
. Maybe it would be best to useexpand('<abuf>')
in your autocmd calls. See:h <abuf>
buflisted(bufnr('%'))
for nerdtree buffers is 0. Also I didn't know:ls!
but I get au
in front of those buffers, so know I'm really sure they are unlisted :-) Thanks for your other advices I'll definitely check them!if
in 2 parts, and assign those parts, each, to a variable, then you can check their values before the condition (if nonlisted && unmanaged ...
).buflisted
parameter: for now I've had problem only with NerdTree, but I'm not sure I will not have the same issue with another plugin and adding a condition for each case might be not very convenient. But if I don't find another solution that's probably what I'll do. Also yes when I'm debugging I split theif
I put it back together for the question.