api/buffer: create new buffers in the "opened" state

Otherwise vim will think that ml_append() needs to "enter" the buffer,
which emits unexpected autocommands.

ref https://github.com/vim-airline/vim-airline/issues/1930
This commit is contained in:
Björn Linse 2019-06-03 13:08:05 +02:00
parent 3273e39db6
commit 7ac3c311ee
2 changed files with 33 additions and 7 deletions

View File

@ -27,6 +27,7 @@
#include "nvim/types.h" #include "nvim/types.h"
#include "nvim/ex_docmd.h" #include "nvim/ex_docmd.h"
#include "nvim/screen.h" #include "nvim/screen.h"
#include "nvim/memline.h"
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/edit.h" #include "nvim/edit.h"
@ -977,11 +978,20 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0)); BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
try_end(err); try_end(err);
if (buf == NULL) { if (buf == NULL) {
if (!ERROR_SET(err)) { goto fail;
api_set_error(err, kErrorTypeException, "Failed to create buffer");
}
return 0;
} }
// Open the memline for the buffer. This will avoid spurious autocmds when
// a later nvim_buf_set_lines call would have needed to "open" the buffer.
try_start();
block_autocmds();
int status = ml_open(buf);
unblock_autocmds();
try_end(err);
if (status == FAIL) {
goto fail;
}
if (scratch) { if (scratch) {
aco_save_T aco; aco_save_T aco;
aucmd_prepbuf(&aco, buf); aucmd_prepbuf(&aco, buf);
@ -991,6 +1001,12 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
aucmd_restbuf(&aco); aucmd_restbuf(&aco);
} }
return buf->b_fnum; return buf->b_fnum;
fail:
if (!ERROR_SET(err)) {
api_set_error(err, kErrorTypeException, "Failed to create buffer");
}
return 0;
} }
/// Open a new window. /// Open a new window.

View File

@ -1336,7 +1336,7 @@ describe('API', function()
eq({id=2}, meths.create_buf(true, false)) eq({id=2}, meths.create_buf(true, false))
eq({id=3}, meths.create_buf(false, false)) eq({id=3}, meths.create_buf(false, false))
eq(' 1 %a "[No Name]" line 1\n'.. eq(' 1 %a "[No Name]" line 1\n'..
' 2 "[No Name]" line 0', ' 2 h "[No Name]" line 0',
meths.command_output("ls")) meths.command_output("ls"))
-- current buffer didn't change -- current buffer didn't change
eq({id=1}, meths.get_current_buf()) eq({id=1}, meths.get_current_buf())
@ -1367,14 +1367,24 @@ describe('API', function()
eq({id=1}, meths.get_current_buf()) eq({id=1}, meths.get_current_buf())
end) end)
it("doesn't cause BufEnter or BufWinEnter autocmds", function()
command("let g:fired = v:false")
command("au BufEnter,BufWinEnter * let g:fired = v:true")
eq({id=2}, meths.create_buf(true, false))
meths.buf_set_lines(2, 0, -1, true, {"test", "text"})
eq(false, eval('g:fired'))
end)
it('|scratch-buffer|', function() it('|scratch-buffer|', function()
eq({id=2}, meths.create_buf(false, true)) eq({id=2}, meths.create_buf(false, true))
eq({id=3}, meths.create_buf(true, true)) eq({id=3}, meths.create_buf(true, true))
eq({id=4}, meths.create_buf(true, true)) eq({id=4}, meths.create_buf(true, true))
local scratch_bufs = { 2, 3, 4 } local scratch_bufs = { 2, 3, 4 }
eq(' 1 %a "[No Name]" line 1\n'.. eq(' 1 %a "[No Name]" line 1\n'..
' 3 "[Scratch]" line 0\n'.. ' 3 h "[Scratch]" line 0\n'..
' 4 "[Scratch]" line 0', ' 4 h "[Scratch]" line 0',
meths.command_output("ls")) meths.command_output("ls"))
-- current buffer didn't change -- current buffer didn't change
eq({id=1}, meths.get_current_buf()) eq({id=1}, meths.get_current_buf())