Open nickolashkraus opened 5 years ago
I'm having this issue too:
OS: Manjaro Linux with Linux 4.19.36
Vim: VIM - Vi IMproved 8.1 (2018 May 18, compiled Mar 29 2019 20:08:59)
NerdTree: 3c06335
NerdTree related configuration:
let NERDTreeHijackNetrw=1
let NERDTreeIgnore=['\.pyc', '\~$', '\.swo$', '\.swp$', '\.git$', '\.hg', '\.svn', '\.bzr']
let NERDTreeChDirMode=0
let NERDTreeMouseMode=2
let NERDTreeShowHidden=1
let NERDTreeKeepTreeInNewTab=1
let g:nerdtree_tabs_open_on_gui_startup=0
let g:NERDShutUp=1
let g:NERDTreeWinSize=41 " original + 10
noremap <silent> <F4> :NERDTreeToggle<Cr>
noremap <leader>n :NERDTreeFind<Cr>
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif
Can either of you give me a clearer set of steps to reproduce this? I see that @NickolasHKraus is using syntastic
and @hbarcelos is using ALE
. I'm wondering if there is some interaction between either of those plugins and NERDTree that's causing this issue. I've tried both, and I don't see the problem that you've described.
I can trigger this bug reliably by opening a file that causes the quickfix dialog to open, opening another file, then writing that file. Here is another example GIF:
To me, this issue occurs when there's any open floating window (I'm using Coc.nvim) as seen here:
After debugging to find which setting was responsible, I found that setting set updatetime=<value>
would change how soon this would happen as it affects CursorHold
but I could not figure out how NERDTree uses this setting.
I also raised an issue in chat with Coc.nvim
's maintainer who advised that I should avoid jump to float window
but unfortunately I couldn't get any pointers on how to do that.
@NickolasHKraus @hbarcelos @codingedward I suspect there is an autocommand that is calling some other function, which causing this behavior. Please add a comment with the contents of these Vim commands:
autocmd CursorHold
autocmd CursorHoldI
autocmd CursorMoved
autocmd CursorMovedI
autocmd BufEnter
autocmd BufLeave
autocmd WinEnter
autocmd WinLeave
I'm sure this is only the first step in tracking this down. Thanks for your help.
Here's what I got:
autocmd CursorHold
:
:autocmd CursorHold
--- Autocommands ---
coc_nvim CursorHold
* call s:Autocmd('CursorHold', +expand('<abuf>'))
gitgutter CursorHold
* call gitgutter#process_buffer(bufnr(''), 0)
nerdtreegitplugin CursorHold
* silent! call s:CursorHoldUpdate()
webdevicons_cursor_hold CursorHold
* silent! call s:CursorHoldUpdate()
airline_whitespace CursorHold
* call <sid>ws_refresh()
autocmd CursorHoldI
:
:autocmd CursorHoldI
--- Autocommands ---
coc_nvim CursorHoldI
* call s:Autocmd('CursorHoldI', +expand('<abuf>'))
gitgutter CursorHoldI
* call gitgutter#process_buffer(bufnr(''), 0)
autocmd CursorMoved
:
:autocmd CursorMoved
--- Autocommands ---
coc_nvim CursorMoved
* call s:Autocmd('CursorMoved', +expand('<abuf>'), [line('.'), col('.')])
airline CursorMoved
* call <sid>on_cursor_moved()
matchparen CursorMoved
* call s:Highlight_Matching_Pair()
jsx_comment CursorMoved
<buffer=5>
call jsx_pretty#comment#update_commentstring(b:original_commentstring)
<buffer=6>
call jsx_pretty#comment#update_commentstring(b:original_commentstring)
<buffer=7>
call jsx_pretty#comment#update_commentstring(b:original_commentstring)
<buffer=15>
call jsx_pretty#comment#update_commentstring(b:original_commentstring)
<buffer=16>
call jsx_pretty#comment#update_commentstring(b:original_commentstring)
<buffer=19>
call jsx_pretty#comment#update_commentstring(b:original_commentstring)
// ... these go on upto <buffer=104>
autocmd CursorMovedI
:
:autocmd CursorMovedI
--- Autocommands ---
coc_nvim CursorMovedI
* call s:Autocmd('CursorMovedI', +expand('<abuf>'), [line('.'), col('.')])
matchparen CursorMovedI
* call s:Highlight_Matching_Pair()
autocmd BufEnter
:
:autocmd BufEnter
--- Autocommands ---
filetypedetect BufEnter
*.xpm if getline(1) =~ "XPM2" | setf xpm2 | else | setf xpm | endif
*.xpm2 setf xpm2
numbertoggle BufEnter
* set relativenumber
coc_nvim BufEnter
* call s:Autocmd('BufEnter', +expand('<abuf>'))
gitgutter BufEnter
* call s:on_bufenter()
BufEnter
* :call AutoPairsTryInit()
NERDTree BufEnter
NERD_tree_*
stopinsert
NERDTreeHijackNetrw BufEnter
* call nerdtree#checkForBrowse(expand('<amatch>'))
Vimball BufEnter
*.vba setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vba.gz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vba.bz2 setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vba.zip setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vba.xz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vmb setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vmb.gz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vmb.bz2 setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vmb.zip setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
*.vmb.xz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
fugitive_status BufEnter
index call s:ReloadWinStatus()
index.lock
call s:ReloadWinStatus()
fugitive_merge BufEnter
* if exists('s:rebase_continue') | if has('timers') | call timer_start(0, function('s:RebaseContinue', [remove(s:, 'rebase_continue')])) | else | call s:RebaseContinue(remove(s:, 'rebase_continue')) | endif | e
ndif
autocmd BufLeave
:
:autocmd BufLeave
--- Autocommands ---
numbertoggle BufLeave
* set norelativenumber
gitgutter BufLeave
term://* call gitgutter#all(1)
NERDTree BufLeave
NERD_tree_*
if g:NERDTree.IsOpen() | call b:NERDTree.ui.saveScreenState() | endif
autocmd WinEnter
:
:autocmd WinEnter
--- Autocommands ---
NERDTree WinEnter
NERD_tree_*
stopinsert
tmux_navigator WinEnter
* let s:tmux_is_last_pane = 0
GoldenView WinEnter
* call GoldenView#Enter({'event' : 'WinEnter'})
fzf_popd WinEnter
* call s:dopopd()
fzf_buffers WinEnter
* let g:fzf#vim#buffers[bufnr('')] = reltimefloat(reltime())
airline WinEnter
* call <sid>on_window_changed('WinEnter')
matchparen WinEnter
* call s:Highlight_Matching_Pair()
autocmd WinLeave
:
:autocmd WinLeave
--- Autocommands ---
coc_nvim WinLeave
* call coc#util#clearmatches(get(w:, 'coc_matchids', []))
NERDTree WinLeave
NERD_tree_*
if g:NERDTree.IsOpen() | call b:NERDTree.ui.saveScreenState() | endif
GoldenView WinLeave
* call GoldenView#Leave()
@hbarcelos @codingedward are you using nerdtree-git-plugin
?
Hi @NickolasHKraus Yes, I am using it.
About the commands you asked for:
Yes @NickolasHKraus I'm using it.
I have tried disabling it (by commenting out the Plug 'Xuyuanp/nerdtree-git-plugin'
entry) but the problem persists.
Alright, I have a down day so I am going to dig into this issue. I have had problems with nerdtree-git-plugin in the past, since it hijacks NERDTree's functions.
@codingedward You may need to run PlugClean
to remove the plugin after commenting it out in your .vimrc
.
I was able to reliably reproduce the issue. After removing nerdtree-git-plugin, NERDTree no longer jumped to the open file on write. Looking into why this is the case. I like nerdtree-git-plugin, however it is buggy as hell and the maintainer is unresponsive.
Hello @NickolasHKraus, I removed nerd-tree-git-plugin
(by running :PlugClean
) but the problem still persists, unfortunately.
I'll also do a bit of debugging on my side and see if I can provide a minimal .vimrc
that reproduces the issue. I am guessing I might have another plugin that could be using the same function (or vim API call?) as nerdtree-git-plugin
.
Hello @NickolasHKraus, @PhilRunninger.
After a bit of debugging, I can confirm that the culprit is nerd-tree-git-plugin
as @NickolasHKraus has pointed out.
Interestingly, since I use another NERDTree plugin vim-devicons, disabling nerd-tree-git-plugin
alone was not enough to stop the jumpy behavior.
It turns out that these two plugins share a chunk of code that is responsible for the malfunction:
In nerdtree-git-plugin
, here,
" FUNCTION: s:CursorHoldUpdate() {{{2
function! s:CursorHoldUpdate()
if g:NERDTreeUpdateOnCursorHold != 1
return
endif
if !g:NERDTree.IsOpen()
return
endif
" Do not update when a special buffer is selected
if !empty(&l:buftype)
return
endif
let l:winnr = winnr()
let l:altwinnr = winnr('#')
call g:NERDTree.CursorToTreeWin() " <-------- the offending call
call b:NERDTree.root.refreshFlags()
call NERDTreeRender()
exec l:altwinnr . 'wincmd w'
exec l:winnr . 'wincmd w'
endfunction
and in vim-devicons
here
" scope: local
" stole solution/idea from nerdtree-git-plugin :)
function! s:CursorHoldUpdate()
if g:NERDTreeUpdateOnCursorHold != 1
return
endif
if !exists('g:NERDTree') || !g:NERDTree.IsOpen()
return
endif
" Do not update when a special buffer is selected
if !empty(&l:buftype)
return
endif
" winnr need to make focus go to opened file
" CursorToTreeWin needed to avoid error on opening file
let l:winnr = winnr()
let l:altwinnr = winnr('#')
call g:NERDTree.CursorToTreeWin() " <-------- the offending call
call b:NERDTree.root.refreshFlags()
call NERDTreeRender()
exec l:altwinnr . 'wincmd w'
exec l:winnr . 'wincmd w'
endfunction
The function causing the issues is the call to g:NERDTree.CursorToTreeWin()
as commenting it out solves the problem.
Instead of this, I am guessing both authors ought to have called g:NERDTreeFocus()
. I would have raised a PR but I'm not entirely conversant with VimScript.
Excellent sleuthing @codingedward. Even if you don't submit PR's to fix them, raising issues on both the other repositories and linking them back here is probably the best first step to getting fixes landed in the right places.
@NickolasHKraus @codingedward Thanks for digging into the code, while I currently cannot spare the time to do so. Regarding your proposed solution, I'm not sure it's that simple, because NERDTreeFocus
calls NERDTree.CursorToTreeWin()
.
function! NERDTreeFocus()
if g:NERDTree.IsOpen()
call g:NERDTree.CursorToTreeWin()
else
call g:NERDTreeCreator.ToggleTabTree('')
endif
endfunction
function! s:NERDTree.CursorToTreeWin()
call g:NERDTree.MustBeOpen()
call nerdtree#exec(g:NERDTree.GetWinNum() . 'wincmd w', 1)
endfunction
function! s:NERDTree.MustBeOpen()
if !s:NERDTree.IsOpen()
throw 'NERDTree.TreeNotOpen'
endif
endfunction
Agreed. I don't think it is NERDTree.CursorToTreeWin()
, but rather the call to NERDTreeRender()
. Here is the call chain:
function! NERDTreeRender()
call nerdtree#renderView()
endfunction
"FUNCTION: nerdtree#renderView {{{2
function! nerdtree#renderView()
call b:NERDTree.render()
endfunction
" FUNCTION: s:UI.render() {{{1
function! s:UI.render()
setlocal noreadonly modifiable
" remember the top line of the buffer and the current line so we can
" restore the view exactly how it was
let curLine = line(".")
let curCol = col(".")
let topLine = line("w0")
" delete all lines in the buffer (being careful not to clobber a register)
silent 1,$delete _
call self._dumpHelp()
" delete the blank line before the help and add one after it
if !self.isMinimal()
call setline(line(".")+1, "")
call cursor(line(".")+1, col("."))
endif
if self.getShowBookmarks()
call self._renderBookmarks()
endif
" add the 'up a dir' line
if !self.isMinimal()
call setline(line(".")+1, s:UI.UpDirLine())
call cursor(line(".")+1, col("."))
endif
" draw the header line
let header = self.nerdtree.root.path.str({'format': 'UI', 'truncateTo': winwidth(0)})
call setline(line(".")+1, header)
call cursor(line(".")+1, col("."))
" draw the tree
silent put =self.nerdtree.root.renderToString()
" delete the blank line at the top of the buffer
silent 1,1delete _
" restore the view
let old_scrolloff=&scrolloff
let &scrolloff=0
call cursor(topLine, 1)
normal! zt
call cursor(curLine, curCol)
let &scrolloff = old_scrolloff
setlocal readonly nomodifiable
endfunction
Of interest are the last few lines:
" restore the view
let old_scrolloff=&scrolloff
let &scrolloff=0
call cursor(topLine, 1)
normal! zt
call cursor(curLine, curCol)
let &scrolloff = old_scrolloff
This is responsible for returning the view by repositioning the edit window.
This only occurs however when the window height is sufficiently small (execute :echo winheight('%')
to get the window height) and/or the buffer is sufficiently large (execute :echo line('$')
to get the last line of the current buffer). AND there needs to be a Location List buffer (execute :lopen
).
Steps to repro for me are:
:lopen
).:w
). Location list closes.:w
). NERDTree jumps to open file.I can confirm that disabling the call to NERDTreeRender()
removes the problem entirely.
Interestingly, also calling g:NERDTree.NERDTreeFocus()
instead of g:NERDTree.CursorToTreeWin()
also solves it.
g:NERDTree.NERDTreeFocus()
is not a function. Did you mean NERDTreeFocus()
?
NERDTreeFocus()
simply calls g:NERDTree.CursorToTreeWin()
then exits.
My bad, I was using g:NERDTree.NERDTreeFocus()
and it must have been failing at that point, leading me to believe it was the solution.
Disabling NERDTreeRender()
is indeed the solution 👍
I could raise issues on the two plugins (nerdtree-git-plugin
and vim-devicons
) but I'm afraid I might not have enough context (Vim API knowledge) to know how to phrase this in the best way.
Disabling NERDTreeRender()
renders these plugins rather inert.. I think we are getting close.
I'd be more inclined to find a root cause and a solution in NERDTreeRender. These and other plugins need to know that's a safe function to call after their Listener functions do their thing.
@hbarcelos @codingedward can you provide the output of :autocmd BufWritePost
?
Hello @NickolasHKraus,
Here's what I got:
:autocmd BufWritePost
--- Autocommands ---
vimrc BufWritePost
.vimrc so $MYVIMRC | if has('gui_running') | so $MYGVIMRC | endif
_vimrc so $MYVIMRC | if has('gui_running') | so $MYGVIMRC | endif
vimrc so $MYVIMRC | if has('gui_running') | so $MYGVIMRC | endif
.gvimrc so $MYVIMRC | if has('gui_running') | so $MYGVIMRC | endif
_gvimrc so $MYVIMRC | if has('gui_running') | so $MYGVIMRC | endif
gvimrc so $MYVIMRC | if has('gui_running') | so $MYGVIMRC | endif
coc_nvim BufWritePost
* call s:Autocmd('BufWritePost', +expand('<abuf>'))
airline BufWritePost
*/autoload/airline/themes/*.vim
exec 'source '.split(globpath(&rtp, 'autoload/airline/themes/'.g:airline_theme.'.vim', 1), "\n")[0] | call airline#load_theme()
gzip BufWritePost
*.gz call gzip#write("gzip")
*.bz2 call gzip#write("bzip2")
*.Z call gzip#write("compress -f")
*.lzma call gzip#write("lzma -z")
*.xz call gzip#write("xz -z")
*.lz call gzip#write("lzip")
*.zst call gzip#write("zstd --rm")
BufWritePost
* call s:reset_untracked_cache(0)
airline_whitespace BufWritePost
* call <sid>ws_refresh()
fugitive_status BufWritePost
* call fugitive#ReloadStatus(-1, 0)
There you go:
ALEEvents BufWritePost
* call ale#events#SaveEvent(str2nr(expand('<abuf>')))
CtrlPMRUF BufWritePost
* cal s:record(expand('<abuf>', 1))
signify BufWritePost
* call sy#start()
nerdtreegitplugin BufWritePost
* call s:FileUpdate(expand('%:p'))
gzip BufWritePost
*.gz call gzip#write("gzip")
*.bz2 call gzip#write("bzip2")
*.Z call gzip#write("compress -f")
*.lzma call gzip#write("lzma -z")
*.xz call gzip#write("xz -z")
*.lz call gzip#write("lzip")
*.zst call gzip#write("zstd --rm")
Sorry not being able to help with the debugging process, it's been some pretty busy days for me :disappointed:
I just merged a pull request that addresses three other issues. Given it also was related to autocommands, it may fix this one too. No guarantees though. Give it a shot with the latest master, and let us know.
@PhilRunninger, I have updated my NERDTree plugin and I can confirm that the issue persists.
For what it's worth, I am (still) having the same issue, with up-to-date NerdTree
, vim-devicons
, and nerd-tree-git-plugin
as of today.
My nerdtree is "scrolling" each time when I invoke my ctrlP plugin. After ctrlP dialog is closed nerdtree content is not coming back down and I need to adjust it manuallly. Let me know if I can help with debugging somehow.
@codingedward have you found a solution to this problem? I also use NERDTree
and vim-devicons
and as of today, I observe the same issue - whenever float window appears, NERDTree jumps.
Yep, sad problem :(
No workaround?
Environment
:version
: VIM - Vi IMproved 8.1 macOS versiongit rev-parse --short HEAD
:f4d2b30
" show hidden files by default let NERDTreeShowHidden=1
" ignore specifc files let NERDTreeIgnore=['.pyc$', '\~$', '.swp$']