Summary
Opening the diff for a second file whose name needs Ex-command-line escaping (contains spaces, single quotes, or braces, e.g. from a name like test_{{ foo }}.txt) throws an error, after the first such file opened without any problem.
Steps to reproduce
- Create two directories, each containing a file with the same name and differing content, where the filename needs escaping for Vim's Ex command line, e.g.
test file.txt or test_{{ foo }}.txt. Include at least two such files (or one such file plus a second, normal file) so there is a "previous" file to navigate away from.
:DirDiff dirA dirB
- Open the first differing entry (Enter, or
:DirDiffNext).
- Navigate to the second differing entry and open it (Enter, or
:DirDiffNext again).
Actual result
The first file opens fine. Opening the second one throws:
Error detected while processing function <SNR>NN_DirDiffOpen:
line 68:
line 73:
E94: No matching buffer for -1
(Line numbers are relative to the start of the function and vary by Vim/plugin version; the underlying error is always E94: No matching buffer for -1.)
Root cause
In DirDiffOpen(), s:FilenameA/s:FilenameB are set via EscapeFileName() (fnameescape()), which is correct for building :execute "edit " . name command strings. The same escaped value is then reused as previousFileA/previousFileB on the next invocation and passed into bufnr(previousFileA) and Drop(previousFileB) (which itself calls bufwinid(a:fname)).
bufnr()/bufwinid() match against the real (unescaped) buffer name, not an Ex-command-escaped string. Vim strips the command-line escaping when it resolves :edit <escaped> to the actual file path, so the buffer's stored name never contains the escaping backslashes. Passing the escaped string into bufnr()/bufwinid() therefore never matches, bufnr() returns -1, and the subsequent :bd -1/bufwinid() fallback throws E94.
This only surfaces on the second (or later) file opened in a session, since there is no "previous" file to look up on the first one, which matches the reported behavior above.
Fix
I have a minimal, tested fix ready (keep the raw filename alongside the escaped one, and use the raw form for bufnr()/bufwinid()/Drop() buffer lookups, escaping only at the point of building the :edit/:split/:diffsplit command strings). Verified under both Vim and Neovim, including repeated back-and-forth navigation and the manual keypress interaction path, with a plain-filename regression check. Will follow up with a PR referencing this issue.
Summary
Opening the diff for a second file whose name needs Ex-command-line escaping (contains spaces, single quotes, or braces, e.g. from a name like
test_{{ foo }}.txt) throws an error, after the first such file opened without any problem.Steps to reproduce
test file.txtortest_{{ foo }}.txt. Include at least two such files (or one such file plus a second, normal file) so there is a "previous" file to navigate away from.:DirDiff dirA dirB:DirDiffNext).:DirDiffNextagain).Actual result
The first file opens fine. Opening the second one throws:
(Line numbers are relative to the start of the function and vary by Vim/plugin version; the underlying error is always
E94: No matching buffer for -1.)Root cause
In
DirDiffOpen(),s:FilenameA/s:FilenameBare set viaEscapeFileName()(fnameescape()), which is correct for building:execute "edit " . namecommand strings. The same escaped value is then reused aspreviousFileA/previousFileBon the next invocation and passed intobufnr(previousFileA)andDrop(previousFileB)(which itself callsbufwinid(a:fname)).bufnr()/bufwinid()match against the real (unescaped) buffer name, not an Ex-command-escaped string. Vim strips the command-line escaping when it resolves:edit <escaped>to the actual file path, so the buffer's stored name never contains the escaping backslashes. Passing the escaped string intobufnr()/bufwinid()therefore never matches,bufnr()returns-1, and the subsequent:bd -1/bufwinid()fallback throwsE94.This only surfaces on the second (or later) file opened in a session, since there is no "previous" file to look up on the first one, which matches the reported behavior above.
Fix
I have a minimal, tested fix ready (keep the raw filename alongside the escaped one, and use the raw form for
bufnr()/bufwinid()/Drop()buffer lookups, escaping only at the point of building the:edit/:split/:diffsplitcommand strings). Verified under both Vim and Neovim, including repeated back-and-forth navigation and the manual keypress interaction path, with a plain-filename regression check. Will follow up with a PR referencing this issue.