mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 03:05:11 -07:00
Merge pull request #9170 from bfredl/lua_cb
lua callbacks for nvim_buf_attach
This commit is contained in:
commit
3adb8a10b1
@ -199,6 +199,21 @@ paste a block of 6 lines, emits: >
|
||||
User reloads the buffer with ":edit", emits: >
|
||||
nvim_buf_detach_event[{buf}]
|
||||
|
||||
*api-buffer-updates-lua*
|
||||
In-process lua plugins can also recieve buffer updates, in the form of lua
|
||||
callbacks. These callbacks are called frequently in various contexts, buffer
|
||||
contents or window layout should not be changed inside these |textlock|.
|
||||
|
||||
|nvim_buf_attach| will take keyword args for the callbacks. "on_lines" will
|
||||
receive parameters ("lines", {buf}, {changedtick}, {firstline}, {lastline}, {new_lastline}).
|
||||
Unlike remote channels the text contents are not passed. The new text can be
|
||||
accessed inside the callback as
|
||||
`vim.api.nvim_buf_get_lines(buf, firstline, new_lastline, true)`
|
||||
"on_changedtick" is invoked when |b:changedtick| was incremented but no text
|
||||
was changed. The parameters recieved are ("changedtick", {buf}, {changedtick}).
|
||||
|
||||
|
||||
|
||||
==============================================================================
|
||||
Buffer highlighting *api-highlights*
|
||||
|
||||
|
@ -2539,6 +2539,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
||||
r'(?<!\bkbtree_t)'
|
||||
r'(?<!\bkbitr_t)'
|
||||
r'(?<!\bPMap)'
|
||||
r'(?<!\bArrayOf)'
|
||||
r'(?<!\bDictionaryOf)'
|
||||
r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)'
|
||||
r' +'
|
||||
r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "nvim/api/buffer.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
@ -98,37 +99,62 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// Activates buffer-update events on the channel.
|
||||
/// Activates buffer-update events on a channel, or as lua callbacks.
|
||||
///
|
||||
/// @param channel_id
|
||||
/// @param buffer Buffer handle, or 0 for current buffer
|
||||
/// @param send_buffer Set to true if the initial notification should contain
|
||||
/// the whole buffer. If so, the first notification will be a
|
||||
/// `nvim_buf_lines_event`. Otherwise, the first notification will be
|
||||
/// a `nvim_buf_changedtick_event`
|
||||
/// @param opts Optional parameters. Reserved for future use.
|
||||
/// a `nvim_buf_changedtick_event`. Not used for lua callbacks.
|
||||
/// @param opts Optional parameters.
|
||||
/// `on_lines`: lua callback received on change.
|
||||
/// `on_changedtick`: lua callback received on changedtick
|
||||
/// increment without text change.
|
||||
/// See |api-buffer-updates-lua| for more information
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return False when updates couldn't be enabled because the buffer isn't
|
||||
/// loaded or `opts` contained an invalid key; otherwise True.
|
||||
/// TODO: LUA_API_NO_EVAL
|
||||
Boolean nvim_buf_attach(uint64_t channel_id,
|
||||
Buffer buffer,
|
||||
Boolean send_buffer,
|
||||
Dictionary opts,
|
||||
DictionaryOf(LuaRef) opts,
|
||||
Error *err)
|
||||
FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
|
||||
FUNC_API_SINCE(4)
|
||||
{
|
||||
if (opts.size > 0) {
|
||||
api_set_error(err, kErrorTypeValidation, "dict isn't empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return buf_updates_register(buf, channel_id, send_buffer);
|
||||
bool is_lua = (channel_id == LUA_INTERNAL_CALL);
|
||||
BufUpdateCallbacks cb = BUF_UPDATE_CALLBACKS_INIT;
|
||||
for (size_t i = 0; i < opts.size; i++) {
|
||||
String k = opts.items[i].key;
|
||||
Object *v = &opts.items[i].value;
|
||||
if (is_lua && strequal("on_lines", k.data)) {
|
||||
if (v->type != kObjectTypeLuaRef) {
|
||||
api_set_error(err, kErrorTypeValidation, "callback is not a function");
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
cb.on_changedtick = v->data.luaref;
|
||||
v->data.integer = LUA_NOREF;
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return buf_updates_register(buf, channel_id, cb, send_buffer);
|
||||
}
|
||||
|
||||
/// Deactivates buffer-update events on the channel.
|
||||
@ -307,7 +333,7 @@ void buffer_set_line_slice(Buffer buffer,
|
||||
Integer end,
|
||||
Boolean include_start,
|
||||
Boolean include_end,
|
||||
ArrayOf(String) replacement, // NOLINT
|
||||
ArrayOf(String) replacement,
|
||||
Error *err)
|
||||
{
|
||||
start = convert_index(start) + !include_start;
|
||||
@ -340,7 +366,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
|
||||
Integer start,
|
||||
Integer end,
|
||||
Boolean strict_indexing,
|
||||
ArrayOf(String) replacement, // NOLINT
|
||||
ArrayOf(String) replacement,
|
||||
Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
|
@ -104,6 +104,7 @@ typedef enum {
|
||||
kObjectTypeString,
|
||||
kObjectTypeArray,
|
||||
kObjectTypeDictionary,
|
||||
kObjectTypeLuaRef,
|
||||
// EXT types, cannot be split or reordered, see #EXT_OBJECT_TYPE_SHIFT
|
||||
kObjectTypeBuffer,
|
||||
kObjectTypeWindow,
|
||||
@ -119,6 +120,7 @@ struct object {
|
||||
String string;
|
||||
Array array;
|
||||
Dictionary dictionary;
|
||||
LuaRef luaref;
|
||||
} data;
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/handle.h"
|
||||
#include "nvim/msgpack_rpc/helpers.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/ascii.h"
|
||||
#include "nvim/assert.h"
|
||||
#include "nvim/vim.h"
|
||||
@ -1147,6 +1148,10 @@ void api_free_object(Object value)
|
||||
api_free_dictionary(value.data.dictionary);
|
||||
break;
|
||||
|
||||
case kObjectTypeLuaRef:
|
||||
executor_free_luaref(value.data.luaref);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -48,6 +48,10 @@
|
||||
.type = kObjectTypeDictionary, \
|
||||
.data.dictionary = d })
|
||||
|
||||
#define LUAREF_OBJ(r) ((Object) { \
|
||||
.type = kObjectTypeLuaRef, \
|
||||
.data.luaref = r })
|
||||
|
||||
#define NIL ((Object) {.type = kObjectTypeNil})
|
||||
|
||||
#define PUT(dict, k, v) \
|
||||
|
@ -1836,6 +1836,8 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
|
||||
buf->b_p_bl = (flags & BLN_LISTED) ? true : false; // init 'buflisted'
|
||||
kv_destroy(buf->update_channels);
|
||||
kv_init(buf->update_channels);
|
||||
kv_destroy(buf->update_callbacks);
|
||||
kv_init(buf->update_callbacks);
|
||||
if (!(flags & BLN_DUMMY)) {
|
||||
// Tricky: these autocommands may change the buffer list. They could also
|
||||
// split the window with re-using the one empty buffer. This may result in
|
||||
|
@ -453,6 +453,12 @@ typedef struct {
|
||||
/// Primary exists so that literals of relevant type can be made.
|
||||
typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem;
|
||||
|
||||
typedef struct {
|
||||
LuaRef on_lines;
|
||||
LuaRef on_changedtick;
|
||||
} BufUpdateCallbacks;
|
||||
#define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF }
|
||||
|
||||
#define BUF_HAS_QF_ENTRY 1
|
||||
#define BUF_HAS_LL_ENTRY 2
|
||||
|
||||
@ -796,6 +802,7 @@ struct file_buffer {
|
||||
// array of channelids which have asked to receive updates for this
|
||||
// buffer.
|
||||
kvec_t(uint64_t) update_channels;
|
||||
kvec_t(BufUpdateCallbacks) update_callbacks;
|
||||
|
||||
int b_diff_failed; // internal diff failed for this buffer
|
||||
};
|
||||
|
@ -5,19 +5,30 @@
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/msgpack_rpc/channel.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/assert.h"
|
||||
#include "nvim/buffer.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "buffer_updates.c.generated.h"
|
||||
#endif
|
||||
|
||||
// Register a channel. Return True if the channel was added, or already added.
|
||||
// Return False if the channel couldn't be added because the buffer is
|
||||
// unloaded.
|
||||
bool buf_updates_register(buf_T *buf, uint64_t channel_id, bool send_buffer)
|
||||
bool buf_updates_register(buf_T *buf, uint64_t channel_id,
|
||||
BufUpdateCallbacks cb, bool send_buffer)
|
||||
{
|
||||
// must fail if the buffer isn't loaded
|
||||
if (buf->b_ml.ml_mfp == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (channel_id == LUA_INTERNAL_CALL) {
|
||||
kv_push(buf->update_callbacks, cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
// count how many channels are currently watching the buffer
|
||||
size_t size = kv_size(buf->update_channels);
|
||||
if (size) {
|
||||
@ -69,6 +80,11 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, bool send_buffer)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool buf_updates_active(buf_T *buf)
|
||||
{
|
||||
return kv_size(buf->update_channels) || kv_size(buf->update_callbacks);
|
||||
}
|
||||
|
||||
void buf_updates_send_end(buf_T *buf, uint64_t channelid)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
@ -125,6 +141,12 @@ void buf_updates_unregister_all(buf_T *buf)
|
||||
kv_destroy(buf->update_channels);
|
||||
kv_init(buf->update_channels);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
|
||||
free_update_callbacks(kv_A(buf->update_callbacks, i));
|
||||
}
|
||||
kv_destroy(buf->update_callbacks);
|
||||
kv_init(buf->update_callbacks);
|
||||
}
|
||||
|
||||
void buf_updates_send_changes(buf_T *buf,
|
||||
@ -133,6 +155,10 @@ void buf_updates_send_changes(buf_T *buf,
|
||||
int64_t num_removed,
|
||||
bool send_tick)
|
||||
{
|
||||
if (!buf_updates_active(buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if one the channels doesn't work, put its ID here so we can remove it later
|
||||
uint64_t badchannelid = 0;
|
||||
|
||||
@ -183,6 +209,47 @@ void buf_updates_send_changes(buf_T *buf,
|
||||
ELOG("Disabling buffer updates for dead channel %"PRIu64, badchannelid);
|
||||
buf_updates_unregister(buf, badchannelid);
|
||||
}
|
||||
|
||||
// notify each of the active channels
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
|
||||
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
|
||||
bool keep = true;
|
||||
if (cb.on_lines != LUA_NOREF) {
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
Object items[5];
|
||||
args.size = 5;
|
||||
args.items = items;
|
||||
|
||||
// the first argument is always the buffer handle
|
||||
args.items[0] = BUFFER_OBJ(buf->handle);
|
||||
|
||||
// next argument is b:changedtick
|
||||
args.items[1] = send_tick ? INTEGER_OBJ(buf_get_changedtick(buf)) : NIL;
|
||||
|
||||
// the first line that changed (zero-indexed)
|
||||
args.items[2] = INTEGER_OBJ(firstline - 1);
|
||||
|
||||
// the last line that was changed
|
||||
args.items[3] = INTEGER_OBJ(firstline - 1 + num_removed);
|
||||
|
||||
// the last line in the updated range
|
||||
args.items[4] = INTEGER_OBJ(firstline - 1 + num_added);
|
||||
|
||||
textlock++;
|
||||
Object res = executor_exec_lua_cb(cb.on_lines, "lines", args);
|
||||
textlock--;
|
||||
|
||||
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
|
||||
free_update_callbacks(cb);
|
||||
keep = false;
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
|
||||
}
|
||||
}
|
||||
kv_size(buf->update_callbacks) = j;
|
||||
}
|
||||
|
||||
void buf_updates_changedtick(buf_T *buf)
|
||||
@ -192,6 +259,36 @@ void buf_updates_changedtick(buf_T *buf)
|
||||
uint64_t channel_id = kv_A(buf->update_channels, i);
|
||||
buf_updates_changedtick_single(buf, channel_id);
|
||||
}
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
|
||||
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
|
||||
bool keep = true;
|
||||
if (cb.on_changedtick != LUA_NOREF) {
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
Object items[2];
|
||||
args.size = 2;
|
||||
args.items = items;
|
||||
|
||||
// the first argument is always the buffer handle
|
||||
args.items[0] = BUFFER_OBJ(buf->handle);
|
||||
|
||||
// next argument is b:changedtick
|
||||
args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf));
|
||||
|
||||
textlock++;
|
||||
Object res = executor_exec_lua_cb(cb.on_changedtick, "changedtick", args);
|
||||
textlock--;
|
||||
|
||||
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
|
||||
free_update_callbacks(cb);
|
||||
keep = false;
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
|
||||
}
|
||||
}
|
||||
kv_size(buf->update_callbacks) = j;
|
||||
}
|
||||
|
||||
void buf_updates_changedtick_single(buf_T *buf, uint64_t channel_id)
|
||||
@ -209,3 +306,9 @@ void buf_updates_changedtick_single(buf_T *buf, uint64_t channel_id)
|
||||
// don't try and clean up dead channels here
|
||||
rpc_send_event(channel_id, "nvim_buf_changedtick_event", args);
|
||||
}
|
||||
|
||||
static void free_update_callbacks(BufUpdateCallbacks cb)
|
||||
{
|
||||
executor_free_luaref(cb.on_lines);
|
||||
executor_free_luaref(cb.on_changedtick);
|
||||
}
|
||||
|
@ -900,9 +900,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
|
||||
changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra, false);
|
||||
|
||||
// send update regarding the new lines that were added
|
||||
if (kv_size(curbuf->update_channels)) {
|
||||
buf_updates_send_changes(curbuf, dest + 1, num_lines, 0, true);
|
||||
}
|
||||
buf_updates_send_changes(curbuf, dest + 1, num_lines, 0, true);
|
||||
|
||||
/*
|
||||
* Now we delete the original text -- webb
|
||||
@ -939,9 +937,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
|
||||
}
|
||||
|
||||
// send nvim_buf_lines_event regarding lines that were deleted
|
||||
if (kv_size(curbuf->update_channels)) {
|
||||
buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines, true);
|
||||
}
|
||||
buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines, true);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@ -4074,12 +4070,10 @@ skip:
|
||||
i = curbuf->b_ml.ml_line_count - old_line_count;
|
||||
changed_lines(first_line, 0, last_line - i, i, false);
|
||||
|
||||
if (kv_size(curbuf->update_channels)) {
|
||||
int64_t num_added = last_line - first_line;
|
||||
int64_t num_removed = num_added - i;
|
||||
buf_updates_send_changes(curbuf, first_line, num_added, num_removed,
|
||||
do_buf_event);
|
||||
}
|
||||
int64_t num_added = last_line - first_line;
|
||||
int64_t num_removed = num_added - i;
|
||||
buf_updates_send_changes(curbuf, first_line, num_added, num_removed,
|
||||
do_buf_event);
|
||||
}
|
||||
|
||||
xfree(sub_firstline); /* may have to free allocated copy of the line */
|
||||
|
@ -737,15 +737,13 @@ void deleteFold(
|
||||
changed_lines(first_lnum, (colnr_T)0, last_lnum, 0L, false);
|
||||
|
||||
// send one nvim_buf_lines_event at the end
|
||||
if (kv_size(curbuf->update_channels)) {
|
||||
// last_lnum is the line *after* the last line of the outermost fold
|
||||
// that was modified. Note also that deleting a fold might only require
|
||||
// the modification of the *first* line of the fold, but we send through a
|
||||
// notification that includes every line that was part of the fold
|
||||
int64_t num_changed = last_lnum - first_lnum;
|
||||
buf_updates_send_changes(curbuf, first_lnum, num_changed,
|
||||
num_changed, true);
|
||||
}
|
||||
// last_lnum is the line *after* the last line of the outermost fold
|
||||
// that was modified. Note also that deleting a fold might only require
|
||||
// the modification of the *first* line of the fold, but we send through a
|
||||
// notification that includes every line that was part of the fold
|
||||
int64_t num_changed = last_lnum - first_lnum;
|
||||
buf_updates_send_changes(curbuf, first_lnum, num_changed,
|
||||
num_changed, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1584,13 +1582,11 @@ static void foldCreateMarkers(linenr_T start, linenr_T end)
|
||||
* changed when the start marker is inserted and the end isn't. */
|
||||
changed_lines(start, (colnr_T)0, end, 0L, false);
|
||||
|
||||
if (kv_size(curbuf->update_channels)) {
|
||||
// Note: foldAddMarker() may not actually change start and/or end if
|
||||
// u_save() is unable to save the buffer line, but we send the
|
||||
// nvim_buf_lines_event anyway since it won't do any harm.
|
||||
int64_t num_changed = 1 + end - start;
|
||||
buf_updates_send_changes(curbuf, start, num_changed, num_changed, true);
|
||||
}
|
||||
// Note: foldAddMarker() may not actually change start and/or end if
|
||||
// u_save() is unable to save the buffer line, but we send the
|
||||
// nvim_buf_lines_event anyway since it won't do any harm.
|
||||
int64_t num_changed = 1 + end - start;
|
||||
buf_updates_send_changes(curbuf, start, num_changed, num_changed, true);
|
||||
}
|
||||
|
||||
/* foldAddMarker() {{{2 */
|
||||
|
@ -135,7 +135,7 @@ for i,f in ipairs(shallowcopy(functions)) do
|
||||
end
|
||||
|
||||
-- don't expose internal attributes like "impl_name" in public metadata
|
||||
exported_attributes = {'name', 'parameters', 'return_type', 'method',
|
||||
exported_attributes = {'name', 'return_type', 'method',
|
||||
'since', 'deprecated_since'}
|
||||
exported_functions = {}
|
||||
for _,f in ipairs(functions) do
|
||||
@ -144,6 +144,13 @@ for _,f in ipairs(functions) do
|
||||
for _,attr in ipairs(exported_attributes) do
|
||||
f_exported[attr] = f[attr]
|
||||
end
|
||||
f_exported.parameters = {}
|
||||
for i,param in ipairs(f.parameters) do
|
||||
if param[1] == "DictionaryOf(LuaRef)" then
|
||||
param = {"Dictionary", param[2]}
|
||||
end
|
||||
f_exported.parameters[i] = param
|
||||
end
|
||||
exported_functions[#exported_functions+1] = f_exported
|
||||
end
|
||||
end
|
||||
@ -371,14 +378,18 @@ local function process_function(fn)
|
||||
param = fn.parameters[j]
|
||||
cparam = string.format('arg%u', j)
|
||||
param_type = real_type(param[1])
|
||||
lc_param_type = param_type:lower()
|
||||
lc_param_type = real_type(param[1]):lower()
|
||||
extra = ((param_type == "Object" or param_type == "Dictionary") and "false, ") or ""
|
||||
if param[1] == "DictionaryOf(LuaRef)" then
|
||||
extra = "true, "
|
||||
end
|
||||
write_shifted_output(output, string.format([[
|
||||
const %s %s = nlua_pop_%s(lstate, &err);
|
||||
const %s %s = nlua_pop_%s(lstate, %s&err);
|
||||
|
||||
if (ERROR_SET(&err)) {
|
||||
goto exit_%u;
|
||||
}
|
||||
]], param[1], cparam, param_type, #fn.parameters - j))
|
||||
]], param[1], cparam, param_type, extra, #fn.parameters - j))
|
||||
free_code[#free_code + 1] = ('api_free_%s(%s);'):format(
|
||||
lc_param_type, cparam)
|
||||
cparams = cparam .. ', ' .. cparams
|
||||
|
@ -706,6 +706,10 @@ void nlua_push_Object(lua_State *lstate, const Object obj)
|
||||
lua_pushnil(lstate);
|
||||
break;
|
||||
}
|
||||
case kObjectTypeLuaRef: {
|
||||
nlua_pushref(lstate, obj.data.luaref);
|
||||
break;
|
||||
}
|
||||
#define ADD_TYPE(type, data_key) \
|
||||
case kObjectType##type: { \
|
||||
nlua_push_##type(lstate, obj.data.data_key); \
|
||||
@ -862,7 +866,7 @@ static Array nlua_pop_Array_unchecked(lua_State *const lstate,
|
||||
|
||||
lua_rawgeti(lstate, -1, (int)i);
|
||||
|
||||
val = nlua_pop_Object(lstate, err);
|
||||
val = nlua_pop_Object(lstate, false, err);
|
||||
if (ERROR_SET(err)) {
|
||||
ret.size = i - 1;
|
||||
lua_pop(lstate, 1);
|
||||
@ -900,6 +904,7 @@ Array nlua_pop_Array(lua_State *lstate, Error *err)
|
||||
/// @param[out] err Location where error will be saved.
|
||||
static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate,
|
||||
const LuaTableProps table_props,
|
||||
bool ref,
|
||||
Error *err)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
@ -923,7 +928,7 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate,
|
||||
// stack: dict, key, value
|
||||
|
||||
if (!ERROR_SET(err)) {
|
||||
ret.items[i].value = nlua_pop_Object(lstate, err);
|
||||
ret.items[i].value = nlua_pop_Object(lstate, ref, err);
|
||||
// stack: dict, key
|
||||
} else {
|
||||
lua_pop(lstate, 1);
|
||||
@ -951,7 +956,7 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate,
|
||||
/// Convert lua table to dictionary
|
||||
///
|
||||
/// Always pops one value from the stack.
|
||||
Dictionary nlua_pop_Dictionary(lua_State *lstate, Error *err)
|
||||
Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Error *err)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
const LuaTableProps table_props = nlua_check_type(lstate, err,
|
||||
@ -961,7 +966,7 @@ Dictionary nlua_pop_Dictionary(lua_State *lstate, Error *err)
|
||||
return (Dictionary) { .size = 0, .items = NULL };
|
||||
}
|
||||
|
||||
return nlua_pop_Dictionary_unchecked(lstate, table_props, err);
|
||||
return nlua_pop_Dictionary_unchecked(lstate, table_props, ref, err);
|
||||
}
|
||||
|
||||
/// Helper structure for nlua_pop_Object
|
||||
@ -973,7 +978,7 @@ typedef struct {
|
||||
/// Convert lua table to object
|
||||
///
|
||||
/// Always pops one value from the stack.
|
||||
Object nlua_pop_Object(lua_State *const lstate, Error *const err)
|
||||
Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
|
||||
{
|
||||
Object ret = NIL;
|
||||
const int initial_size = lua_gettop(lstate);
|
||||
@ -1122,7 +1127,18 @@ Object nlua_pop_Object(lua_State *const lstate, Error *const err)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LUA_TFUNCTION: {
|
||||
if (ref) {
|
||||
*cur.obj = LUAREF_OBJ(nlua_ref(lstate, -1));
|
||||
} else {
|
||||
goto type_error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
type_error:
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Cannot convert given lua type");
|
||||
break;
|
||||
|
@ -363,6 +363,33 @@ static int nlua_getenv(lua_State *lstate)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// add the value to the registry
|
||||
LuaRef nlua_ref(lua_State *lstate, int index)
|
||||
{
|
||||
lua_pushvalue(lstate, index);
|
||||
return luaL_ref(lstate, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
/// remove the value from the registry
|
||||
void nlua_unref(lua_State *lstate, LuaRef ref)
|
||||
{
|
||||
if (ref > 0) {
|
||||
luaL_unref(lstate, LUA_REGISTRYINDEX, ref);
|
||||
}
|
||||
}
|
||||
|
||||
void executor_free_luaref(LuaRef ref)
|
||||
{
|
||||
lua_State *const lstate = nlua_enter();
|
||||
nlua_unref(lstate, ref);
|
||||
}
|
||||
|
||||
/// push a value referenced in the regirstry
|
||||
void nlua_pushref(lua_State *lstate, LuaRef ref)
|
||||
{
|
||||
lua_rawgeti(lstate, LUA_REGISTRYINDEX, ref);
|
||||
}
|
||||
|
||||
/// Evaluate lua string
|
||||
///
|
||||
/// Used for luaeval().
|
||||
@ -451,9 +478,29 @@ Object executor_exec_lua_api(const String str, const Array args, Error *err)
|
||||
return NIL;
|
||||
}
|
||||
|
||||
return nlua_pop_Object(lstate, err);
|
||||
return nlua_pop_Object(lstate, false, err);
|
||||
}
|
||||
|
||||
Object executor_exec_lua_cb(LuaRef ref, const char *name, Array args)
|
||||
{
|
||||
lua_State *const lstate = nlua_enter();
|
||||
nlua_pushref(lstate, ref);
|
||||
lua_pushstring(lstate, name);
|
||||
for (size_t i = 0; i < args.size; i++) {
|
||||
nlua_push_Object(lstate, args.items[i]);
|
||||
}
|
||||
|
||||
if (lua_pcall(lstate, (int)args.size+1, 1, 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.
|
||||
nlua_error(lstate, _("Error executing lua callback: %.*s"));
|
||||
return NIL;
|
||||
}
|
||||
Error err = ERROR_INIT;
|
||||
|
||||
return nlua_pop_Object(lstate, false, &err);
|
||||
}
|
||||
|
||||
/// Run lua string
|
||||
///
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define NVIM_LUA_EXECUTOR_H
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/func_attr.h"
|
||||
|
@ -1847,9 +1847,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
|
||||
changedOneline(curbuf, lnum);
|
||||
changed_common(lnum, col, lnum + 1, 0L);
|
||||
// notify any channels that are watching
|
||||
if (kv_size(curbuf->update_channels)) {
|
||||
buf_updates_send_changes(curbuf, lnum, 1, 1, true);
|
||||
}
|
||||
buf_updates_send_changes(curbuf, lnum, 1, 1, true);
|
||||
|
||||
/* Diff highlighting in other diff windows may need to be updated too. */
|
||||
if (curwin->w_p_diff) {
|
||||
@ -1973,7 +1971,7 @@ changed_lines(
|
||||
|
||||
changed_common(lnum, col, lnume, xtra);
|
||||
|
||||
if (do_buf_event && kv_size(curbuf->update_channels)) {
|
||||
if (do_buf_event) {
|
||||
int64_t num_added = (int64_t)(lnume + xtra - lnum);
|
||||
int64_t num_removed = lnume - lnum;
|
||||
buf_updates_send_changes(curbuf, lnum, num_added, num_removed, true);
|
||||
|
@ -253,7 +253,8 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
|
||||
case kObjectTypeFloat:
|
||||
case kObjectTypeString:
|
||||
case kObjectTypeArray:
|
||||
case kObjectTypeDictionary: {
|
||||
case kObjectTypeDictionary:
|
||||
case kObjectTypeLuaRef: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -387,6 +388,13 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res)
|
||||
msgpack_pack_nil(res);
|
||||
break;
|
||||
}
|
||||
case kObjectTypeLuaRef: {
|
||||
// TODO(bfredl): could also be an error. Though kObjectTypeLuaRef
|
||||
// should only appear when the caller has opted in to handle references,
|
||||
// see nlua_pop_Object.
|
||||
msgpack_pack_nil(res);
|
||||
break;
|
||||
}
|
||||
case kObjectTypeBoolean: {
|
||||
msgpack_rpc_from_boolean(cur.aobj->data.boolean, res);
|
||||
break;
|
||||
|
@ -16,6 +16,11 @@ typedef uint32_t u8char_T;
|
||||
// Opaque handle used by API clients to refer to various objects in vim
|
||||
typedef int handle_T;
|
||||
|
||||
// Opaque handle to a lua value. Must be free with `executor_free_luaref` when
|
||||
// not needed anymore! LUA_NOREF represents missing reference, i e to indicate
|
||||
// absent callback etc.
|
||||
typedef int LuaRef;
|
||||
|
||||
typedef struct expand expand_T;
|
||||
|
||||
#endif // NVIM_TYPES_H
|
||||
|
@ -2299,7 +2299,7 @@ static void u_undoredo(int undo, bool do_buf_event)
|
||||
// because the calls to changed()/unchanged() above will bump changedtick
|
||||
// again, we need to send a nvim_buf_lines_event with just the new value of
|
||||
// b:changedtick
|
||||
if (do_buf_event && kv_size(curbuf->update_channels)) {
|
||||
if (do_buf_event) {
|
||||
buf_updates_changedtick(curbuf);
|
||||
}
|
||||
|
||||
|
@ -760,7 +760,7 @@ describe('API: buffer events:', function()
|
||||
it('returns a proper error on nonempty options dict', function()
|
||||
clear()
|
||||
local b = editoriginal(false)
|
||||
expect_err("dict isn't empty", buffer, 'attach', b, false, {builtin="asfd"})
|
||||
expect_err("unexpected key: builtin", buffer, 'attach', b, false, {builtin="asfd"})
|
||||
end)
|
||||
|
||||
it('nvim_buf_attach returns response after delay #8634', function()
|
||||
|
Loading…
Reference in New Issue
Block a user