diff options
-rw-r--r-- | runtime/doc/api.txt | 1 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 14 | ||||
-rw-r--r-- | src/nvim/buffer_updates.c | 29 | ||||
-rw-r--r-- | test/functional/lua/buffer_updates_spec.lua | 11 |
4 files changed, 40 insertions, 15 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 3ca50dda15..2930f2314b 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -356,6 +356,7 @@ In-process Lua plugins can receive buffer updates in the form of Lua callbacks. These callbacks are called frequently in various contexts; |textlock| prevents changing buffer contents and window layout (use |vim.schedule()| to defer such operations to the main loop instead). +Moving the cursor is allowed, but it is restored afterwards. |nvim_buf_attach()| will take keyword args for the callbacks. "on_lines" will receive parameters ("lines", {buf}, {changedtick}, {firstline}, {lastline}, diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index b70452d7cb..1b82aeac34 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -155,8 +155,18 @@ typedef struct { msglist_T *private_msg_list; \ msg_list = &private_msg_list; \ private_msg_list = NULL; \ - code \ - msg_list = saved_msg_list; /* Restore the exception context. */ \ + code; \ + msg_list = saved_msg_list; /* Restore the exception context. */ \ + } while (0) + +// Execute code with cursor position saved and restored and textlock active. +#define TEXTLOCK_WRAP(code) \ + do { \ + const pos_T save_cursor = curwin->w_cursor; \ + textlock++; \ + code; \ + textlock--; \ + curwin->w_cursor = save_cursor; \ } while (0) // Useful macro for executing some `code` for each item in an array. diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index 075ac2adbf..9543731c9b 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -188,9 +188,9 @@ void buf_updates_unload(buf_T *buf, bool can_reload) // the first argument is always the buffer handle args.items[0] = BUFFER_OBJ(buf->handle); - textlock++; - nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL); - textlock--; + TEXTLOCK_WRAP({ + nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL); + }); } if (keep) { @@ -305,9 +305,11 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added, args.items[6] = INTEGER_OBJ((Integer)deleted_codepoints); args.items[7] = INTEGER_OBJ((Integer)deleted_codeunits); } - textlock++; - Object res = nlua_call_ref(cb.on_lines, "lines", args, false, NULL); - textlock--; + + Object res; + TEXTLOCK_WRAP({ + res = nlua_call_ref(cb.on_lines, "lines", args, false, NULL); + }); if (res.type == kObjectTypeBoolean && res.data.boolean == true) { buffer_update_callbacks_free(cb); @@ -354,9 +356,10 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun ADD_C(args, INTEGER_OBJ(new_col)); ADD_C(args, INTEGER_OBJ(new_byte)); - textlock++; - Object res = nlua_call_ref(cb.on_bytes, "bytes", args, false, NULL); - textlock--; + Object res; + TEXTLOCK_WRAP({ + res = nlua_call_ref(cb.on_bytes, "bytes", args, false, NULL); + }); if (res.type == kObjectTypeBoolean && res.data.boolean == true) { buffer_update_callbacks_free(cb); @@ -389,10 +392,10 @@ void buf_updates_changedtick(buf_T *buf) // next argument is b:changedtick ADD_C(args, INTEGER_OBJ(buf_get_changedtick(buf))); - textlock++; - Object res = nlua_call_ref(cb.on_changedtick, "changedtick", - args, false, NULL); - textlock--; + Object res; + TEXTLOCK_WRAP({ + res = nlua_call_ref(cb.on_changedtick, "changedtick", args, false, NULL); + }); if (res.type == kObjectTypeBoolean && res.data.boolean == true) { buffer_update_callbacks_free(cb); diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 2fd44b8b5f..b1b39501f7 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -317,7 +317,18 @@ describe('lua buffer event callbacks: on_lines', function() feed('1G0') feed('P') eq(meths.get_var('linesev'), { "lines", 1, 6, 0, 3, 3, 9 }) + end) + it('calling nvim_buf_call() from callback does not cause Normal mode CTRL-A to misbehave #16729', function() + exec_lua([[ + vim.api.nvim_buf_attach(0, false, { + on_lines = function(...) + vim.api.nvim_buf_call(0, function() end) + end, + }) + ]]) + feed('itest123<Esc><C-A>') + eq('test124', meths.get_current_line()) end) end) |