aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2019-06-15 15:59:45 +0200
committerGitHub <noreply@github.com>2019-06-15 15:59:45 +0200
commit2f1a653a845680efab1a27f6411e36e8cd2f3696 (patch)
treefa562d5f83dbeb611d91cdf780bc1ce1e75a4408
parent93f8c2793cf5020d6fca00b6f10e4a158578034f (diff)
parent64cdf9f78a9bfd4d08a9d9bf2df48a4b2f296190 (diff)
downloadrneovim-2f1a653a845680efab1a27f6411e36e8cd2f3696.tar.gz
rneovim-2f1a653a845680efab1a27f6411e36e8cd2f3696.tar.bz2
rneovim-2f1a653a845680efab1a27f6411e36e8cd2f3696.zip
Merge pull request #10231 from bfredl/bufcb_end
api/lua: add on_detach to nvim_buf_attach
-rw-r--r--src/nvim/api/buffer.c21
-rw-r--r--src/nvim/buffer_defs.h3
-rw-r--r--src/nvim/buffer_updates.c23
-rw-r--r--src/nvim/lua/executor.c11
-rw-r--r--test/functional/lua/buffer_updates_spec.lua92
5 files changed, 140 insertions, 10 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 81b3851c53..b0b65545ab 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -12,6 +12,7 @@
#include "nvim/api/buffer.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/defs.h"
+#include "nvim/lua/executor.h"
#include "nvim/vim.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
@@ -137,24 +138,38 @@ Boolean nvim_buf_attach(uint64_t channel_id,
if (is_lua && strequal("on_lines", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
- return false;
+ goto error;
}
cb.on_lines = v->data.luaref;
v->data.integer = LUA_NOREF;
} else if (is_lua && strequal("on_changedtick", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
- return false;
+ goto error;
}
cb.on_changedtick = v->data.luaref;
v->data.integer = LUA_NOREF;
+ } else if (is_lua && strequal("on_detach", k.data)) {
+ if (v->type != kObjectTypeLuaRef) {
+ api_set_error(err, kErrorTypeValidation, "callback is not a function");
+ goto error;
+ }
+ cb.on_detach = v->data.luaref;
+ v->data.integer = LUA_NOREF;
} else {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
- return false;
+ goto error;
}
}
return buf_updates_register(buf, channel_id, cb, send_buffer);
+
+error:
+ // TODO(bfredl): ASAN build should check that the ref table is empty?
+ executor_free_luaref(cb.on_lines);
+ executor_free_luaref(cb.on_changedtick);
+ executor_free_luaref(cb.on_detach);
+ return false;
}
/// Deactivates buffer-update events on the channel.
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 117a9183a4..69cefe2247 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -456,8 +456,9 @@ typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem;
typedef struct {
LuaRef on_lines;
LuaRef on_changedtick;
+ LuaRef on_detach;
} BufUpdateCallbacks;
-#define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF }
+#define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF }
#define BUF_HAS_QF_ENTRY 1
#define BUF_HAS_LL_ENTRY 2
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index 2515e3f8aa..21efda9fd9 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -143,7 +143,21 @@ void buf_updates_unregister_all(buf_T *buf)
}
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
- free_update_callbacks(kv_A(buf->update_callbacks, i));
+ BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
+ if (cb.on_detach != LUA_NOREF) {
+ Array args = ARRAY_DICT_INIT;
+ Object items[1];
+ args.size = 1;
+ args.items = items;
+
+ // the first argument is always the buffer handle
+ args.items[0] = BUFFER_OBJ(buf->handle);
+
+ textlock++;
+ executor_exec_lua_cb(cb.on_detach, "detach", args, false);
+ textlock--;
+ }
+ free_update_callbacks(cb);
}
kv_destroy(buf->update_callbacks);
kv_init(buf->update_callbacks);
@@ -237,13 +251,14 @@ void buf_updates_send_changes(buf_T *buf,
args.items[4] = INTEGER_OBJ(firstline - 1 + num_added);
textlock++;
- Object res = executor_exec_lua_cb(cb.on_lines, "lines", args);
+ Object res = executor_exec_lua_cb(cb.on_lines, "lines", args, true);
textlock--;
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
free_update_callbacks(cb);
keep = false;
}
+ api_free_object(res);
}
if (keep) {
kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
@@ -276,13 +291,15 @@ void buf_updates_changedtick(buf_T *buf)
args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf));
textlock++;
- Object res = executor_exec_lua_cb(cb.on_changedtick, "changedtick", args);
+ Object res = executor_exec_lua_cb(cb.on_changedtick, "changedtick",
+ args, true);
textlock--;
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
free_update_callbacks(cb);
keep = false;
}
+ api_free_object(res);
}
if (keep) {
kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 8cb282356a..ae872c1540 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -520,7 +520,8 @@ Object executor_exec_lua_api(const String str, const Array args, Error *err)
return nlua_pop_Object(lstate, false, err);
}
-Object executor_exec_lua_cb(LuaRef ref, const char *name, Array args)
+Object executor_exec_lua_cb(LuaRef ref, const char *name, Array args,
+ bool retval)
{
lua_State *const lstate = nlua_enter();
nlua_pushref(lstate, ref);
@@ -529,7 +530,7 @@ Object executor_exec_lua_cb(LuaRef ref, const char *name, Array args)
nlua_push_Object(lstate, args.items[i]);
}
- if (lua_pcall(lstate, (int)args.size+1, 1, 0)) {
+ if (lua_pcall(lstate, (int)args.size+1, retval ? 1 : 0, 0)) {
// TODO(bfredl): callbacks:s might not always be msg-safe, for instance
// lua callbacks for redraw events. Later on let the caller deal with the
// error instead.
@@ -538,7 +539,11 @@ Object executor_exec_lua_cb(LuaRef ref, const char *name, Array args)
}
Error err = ERROR_INIT;
- return nlua_pop_Object(lstate, false, &err);
+ if (retval) {
+ return nlua_pop_Object(lstate, false, &err);
+ } else {
+ return NIL;
+ }
}
/// Run lua string
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
new file mode 100644
index 0000000000..c419d89be3
--- /dev/null
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -0,0 +1,92 @@
+-- Test suite for testing interactions with API bindings
+local helpers = require('test.functional.helpers')(after_each)
+
+local command = helpers.command
+local meths = helpers.meths
+local clear = helpers.clear
+local eq = helpers.eq
+
+local origlines = {"original line 1",
+ "original line 2",
+ "original line 3",
+ "original line 4",
+ "original line 5",
+ "original line 6"}
+
+describe('lua: buffer event callbacks', function()
+ before_each(function()
+ clear()
+ meths.execute_lua([[
+ local events = {}
+
+ function test_register(bufnr, id, changedtick)
+ local function callback(...)
+ table.insert(events, {id, ...})
+ if test_unreg == id then
+ return true
+ end
+ end
+ local opts = {on_lines=callback, on_detach=callback}
+ if changedtick then
+ opts.on_changedtick = callback
+ end
+ vim.api.nvim_buf_attach(bufnr, false, opts)
+ end
+
+ function get_events()
+ local ret_events = events
+ events = {}
+ return ret_events
+ end
+ ]], {})
+ end)
+
+ it('works', function()
+ meths.buf_set_lines(0, 0, -1, true, origlines)
+ meths.execute_lua("return test_register(...)", {0, "test1"})
+ local tick = meths.buf_get_changedtick(0)
+
+ command('normal! GyyggP')
+ tick = tick + 1
+ eq({{ "test1", "lines", 1, tick, 0, 0, 1 }},
+ meths.execute_lua("return get_events(...)", {}))
+
+ meths.buf_set_lines(0, 3, 5, true, {"changed line"})
+ tick = tick + 1
+ eq({{ "test1", "lines", 1, tick, 3, 5, 4 }},
+ meths.execute_lua("return get_events(...)", {}))
+
+ meths.execute_lua("return test_register(...)", {0, "test2", true})
+ tick = tick + 1
+ command('undo')
+
+ -- plugins can opt in to receive changedtick events, or choose
+ -- to only recieve actual changes.
+ eq({{ "test1", "lines", 1, tick, 3, 4, 5 },
+ { "test2", "lines", 1, tick, 3, 4, 5 },
+ { "test2", "changedtick", 1, tick+1 } },
+ meths.execute_lua("return get_events(...)", {}))
+ tick = tick + 1
+
+ -- simulate next callback returning true
+ meths.execute_lua("test_unreg = 'test1'", {})
+
+ meths.buf_set_lines(0, 6, 7, true, {"x1","x2","x3"})
+ tick = tick + 1
+
+ -- plugins can opt in to receive changedtick events, or choose
+ -- to only recieve actual changes.
+ eq({{ "test1", "lines", 1, tick, 6, 7, 9 },
+ { "test2", "lines", 1, tick, 6, 7, 9 }},
+ meths.execute_lua("return get_events(...)", {}))
+
+ meths.buf_set_lines(0, 1, 1, true, {"added"})
+ tick = tick + 1
+ eq({{ "test2", "lines", 1, tick, 1, 1, 2 }},
+ meths.execute_lua("return get_events(...)", {}))
+
+ command('bwipe!')
+ eq({{ "test2", "detach", 1 }},
+ meths.execute_lua("return get_events(...)", {}))
+ end)
+end)