justinmk / vim-dirvish

Directory viewer for Vim :zap:
Other
1.18k stars 64 forks source link

hidden terminal buffer causes E95 when viewing directory matching the current working directory #168

Closed lacygoill closed 4 years ago

lacygoill commented 4 years ago

Describe the bug

I have a bug which is due to the interaction of 3 plugins. vim-dirvish, a plugin which automatically resets the current working directory on BufEnter, and a plugin which toggles a terminal buffer in a popup window. The first error is E95:

Error detected while processing function dirvish#open[39]..<SNR>165_open_dir:
line   25:
E95: Buffer with this name already exists

After that, it causes all sorts of unexpected behaviors for the third plugin.

To Reproduce

Run this shell command:

vim -Nu NORC --cmd 'set hidden rtp^=~/.vim/plugged/vim-dirvish|au BufEnter * cd /tmp' --cmd 'au VimEnter * call term_start(&shell, #{hidden: 1})'
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
                                    may need to be replaced to match local installation

Then, execute this Ex command:

:e /tmp

E95 is raised.

Expected behavior

No error is raised.

Environment

Additional context

I think the issue comes from this line: https://github.com/justinmk/vim-dirvish/blob/5d84cb9ce7fdf7d3fd729a9d3f59060ca5ab3a6e/autoload/dirvish.vim#L420

In the second iteration of the surrounding :for loop, d._dir is /tmp/ and pat is :~:., which gives:

let bnr = bufnr('^'.fnamemodify(d._dir, pat).'$')
⇔
let bnr = bufnr('^'.fnamemodify('/tmp/', ':~:.').'$')
⇔
let bnr = bufnr('^'.''.'$')
⇔
let bnr = bufnr('^$')
⇔
let bnr = 2

I think this is wrong; bnr should be -1.


This buffer number 2 results from two evaluations which I don't understand:

:cd /tmp
:echo fnamemodify('/tmp', ':.')
/tmp

:cd /tmp
:echo fnamemodify('/tmp/', ':.') is# ''
1

The output of the first command is expected; but not the one of the second command. In the latter, fnamemodify() is empty because of the trailing slash.

Do you think it's a Vim bug?

Here is the other unexpected evaluation:

vim -Nu NONE
:echo bufnr('^$')
-1

vim -Nu NONE
:call term_start(&shell, #{hidden: 1})
:echo bufnr('^$')
2

Again, the output of the first command is expected; but not the one of the second command. In the latter, bufnr() finds the number of the terminal buffer while I think it should not find any matching buffer.

Btw, it really finds the number of the terminal buffer, not the number of the last buffer.

 vim -Nu NONE
 :call term_start(&shell, #{hidden: 1})
 :e a | e b
 :echo bufnr('^$')
 2

It still outputs 2, and not 3 which is the last buffer number.

Again, do you think it's a Vim bug?

Solutions

I move my second plugin after vim-dirvish, to reverse the order of their BufEnter autocmds.

Issue: it works, but it's brittle, and will probably break or cause issues in the future.


I report one or two of the previous unexpected outputs on Vim's bug tracker.

Issue: people using old Vim versions will still be affected.


I send you a PR to replace this line:

let bnr = bufnr('^'.fnamemodify(d._dir, pat).'$')

With:

let dir = fnamemodify(d._dir, pat)
if dir == '' | continue | endif
let bnr = bufnr('^'.dir.'$')

Issue: it fixes my bug, but I'm not sure it's the right fix in general. An alternative would be to remove trailing slashes (on Windows backslashes?), or to check the buftype of bufnr, and if it's a terminal, reset bufnr to -1.

justinmk commented 4 years ago

a plugin which automatically resets the current working directory on BufEnter

Technically 'autochdir' (or similar behavior faked by a plugin) isn't supported, but I very much would like to avoid gross cascading problems like this.

:cd /tmp
:echo fnamemodify('/tmp', ':.')
/tmp

:cd /tmp
:echo fnamemodify('/tmp/', ':.') is# ''
1

The output of the first command is expected; but not the one of the second command.

In the latter, fnamemodify() is empty because of the trailing slash.

Logically if CWD is /tmp it should return empty string in both cases. But fnamemodify has always been fragile with slashes... :. is behaving similar to :h here, and I doubt Vim can change this now.

An alternative would be to remove trailing slashes (on Windows backslashes?)

I think that's the best fix. PR would be appreciated, and thanks for your great analysis as usual :)