From 0571145c40b0a2ae106acc43891c2680c76b8383 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 25 Sep 2019 09:15:33 +0200 Subject: paste: fix handling of "<" in cmdline (#11094) Fixes https://github.com/neovim/neovim/issues/11088. --- src/nvim/lua/vim.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index a03e97490d..b1a684b977 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -189,7 +189,7 @@ paste = (function() if mode == 'c' and not got_line1 then -- cmdline-mode: paste only 1 line. got_line1 = (#lines > 1) vim.api.nvim_set_option('paste', true) -- For nvim_input(). - local line1, _ = string.gsub(lines[1], '[\r\n\012\027]', ' ') -- Scrub. + local line1 = lines[1]:gsub('<', ''):gsub('[\r\n\012\027]', ' ') -- Scrub. vim.api.nvim_input(line1) vim.api.nvim_set_option('paste', false) elseif mode ~= 'c' then -- Else: discard remaining cmdline-mode chunks. -- cgit From cd100963866b2c33a286cbf6aac8e42cd16fd248 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 29 Oct 2018 19:11:41 +0100 Subject: tree-sitter: initial tree-sitter support --- src/nvim/lua/executor.c | 42 ++++ src/nvim/lua/tree_sitter.c | 472 +++++++++++++++++++++++++++++++++++++++++++++ src/nvim/lua/tree_sitter.h | 10 + 3 files changed, 524 insertions(+) create mode 100644 src/nvim/lua/tree_sitter.c create mode 100644 src/nvim/lua/tree_sitter.h (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index f51aa3c6d4..8b0140f794 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -31,6 +31,7 @@ #include "nvim/lua/executor.h" #include "nvim/lua/converter.h" +#include "nvim/lua/tree_sitter.h" #include "luv/luv.h" @@ -310,7 +311,11 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "luv"); lua_pop(lstate, 3); + // internal vim._treesitter... API + nlua_add_treesitter(lstate); + lua_setglobal(lstate, "vim"); + return 0; } @@ -816,3 +821,40 @@ void ex_luafile(exarg_T *const eap) return; } } + +static int unsafe_ptr_to_ts_tree(lua_State *L) +{ + if (!lua_gettop(L)) { + return 0; + } + TSTree *const *ptr = lua_topointer(L,1); + tslua_push_tree(L, *ptr); + return 1; +} + +static int create_tslua_parser(lua_State *L) +{ + TSLanguage *tree_sitter_c(void), *tree_sitter_javascript(void); + + if (!lua_gettop(L)) { + return 0; + } + char *str = lua_tostring(L,1); + + TSLanguage *lang = tree_sitter_c(); + if (str && striequal(str, "javascript")) { + lang = tree_sitter_javascript(); + } + tslua_push_parser(L, lang); + return 1; +} + +static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + tslua_init(lstate); + lua_pushcfunction(lstate, unsafe_ptr_to_ts_tree); + lua_setfield(lstate, -2, "unsafe_ts_tree"); + + lua_pushcfunction(lstate, create_tslua_parser); + lua_setfield(lstate, -2, "ts_parser"); +} diff --git a/src/nvim/lua/tree_sitter.c b/src/nvim/lua/tree_sitter.c new file mode 100644 index 0000000000..1ecb2bcac4 --- /dev/null +++ b/src/nvim/lua/tree_sitter.c @@ -0,0 +1,472 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// lua bindings for tree-siter. +// NB: this file should contain a generic lua interface for +// tree-sitter trees and nodes, and could be broken out as a reusable library + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tree_sitter/api.h" + +// NOT state-safe, delete when GC is confimed working: +static int debug_n_trees = 0, debug_n_cursors = 0; + +#define REG_KEY "tree_sitter-private" + +#include "nvim/lua/tree_sitter.h" +#include "nvim/api/private/handle.h" +#include "nvim/memline.h" + +typedef struct { + TSParser *parser; + TSTree *tree; +} Tslua_parser; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/tree_sitter.c.generated.h" +#endif + +static struct luaL_Reg parser_meta[] = { + {"__gc", parser_gc}, + {"__tostring", parser_tostring}, + {"parse_buf", parser_parse_buf}, + {"edit", parser_edit}, + {"tree", parser_tree}, + {NULL, NULL} +}; + +static struct luaL_Reg tree_meta[] = { + {"__gc", tree_gc}, + {"__tostring", tree_tostring}, + {"root", tree_root}, + {NULL, NULL} +}; + +static struct luaL_Reg node_meta[] = { + {"__tostring", node_tostring}, + {"__len", node_child_count}, + {"range", node_range}, + {"start", node_start}, + {"type", node_type}, + {"symbol", node_symbol}, + {"child_count", node_child_count}, + {"child", node_child}, + {"descendant_for_point_range", node_descendant_for_point_range}, + {"parent", node_parent}, + {NULL, NULL} +}; + +void build_meta(lua_State *L, const luaL_Reg *meta) +{ + // [env, target] + for (size_t i = 0; meta[i].name != NULL; i++) { + lua_pushcfunction(L, meta[i].func); // [env, target, func] + lua_pushvalue(L, -3); // [env, target, func, env] + lua_setfenv(L, -2); // [env, target, func] + lua_setfield(L, -2, meta[i].name); // [env, target] + } + + lua_pushvalue(L, -1); // [env, target, target] + lua_setfield(L, -2, "__index"); // [env, target] +} + + + +/// init the tslua library +/// +/// all global state is stored in the regirstry of the lua_State +void tslua_init(lua_State *L) +{ + lua_createtable(L, 0, 0); + + // type metatables + lua_createtable(L, 0, 0); + build_meta(L, parser_meta); + lua_setfield(L, -2, "parser-meta"); + + lua_createtable(L, 0, 0); + build_meta(L, tree_meta); + lua_setfield(L, -2, "tree-meta"); + + lua_createtable(L, 0, 0); + build_meta(L, node_meta); + lua_setfield(L, -2, "node-meta"); + + lua_setfield(L, LUA_REGISTRYINDEX, REG_KEY); + + lua_pushcfunction(L, tslua_debug); + lua_setglobal(L, "_tslua_debug"); +} + +static int tslua_debug(lua_State *L) +{ + lua_pushinteger(L, debug_n_trees); + lua_pushinteger(L, debug_n_cursors); + return 2; +} + +void tslua_push_parser(lua_State *L, TSLanguage *lang) +{ + TSParser *parser = ts_parser_new(); + ts_parser_set_language(parser, lang); + Tslua_parser *p = lua_newuserdata(L, sizeof(Tslua_parser)); // [udata] + p->parser = parser; + p->tree = NULL; + + lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] + lua_getfield(L, -1, "parser-meta"); // [udata, env, meta] + lua_setmetatable(L, -3); // [udata, env] + lua_pop(L, 1); // [udata] +} + +static Tslua_parser *parser_check(lua_State *L) +{ + if (!lua_gettop(L)) { + return 0; + } + if (!lua_isuserdata(L, 1)) { + return 0; + } + // TODO: typecheck! + return lua_touserdata(L, 1); +} + +static int parser_gc(lua_State *L) +{ + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + ts_parser_delete(p->parser); + if (p->tree) { + ts_tree_delete(p->tree); + } + + return 0; +} + +static int parser_tostring(lua_State *L) +{ + lua_pushstring(L, ""); + return 1; +} + +static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position, uint32_t *bytes_read) +{ + buf_T *bp = payload; + static char buf[200]; + if ((linenr_T)position.row >= bp->b_ml.ml_line_count) { + *bytes_read = 0; + return ""; + } + char_u *line = ml_get_buf(bp, position.row+1, false); + size_t len = STRLEN(line); + size_t tocopy = MIN(len-position.column,200); + + // TODO: translate embedded \n to \000 + memcpy(buf, line+position.column, tocopy); + *bytes_read = (uint32_t)tocopy; + if (tocopy < 200) { + buf[tocopy] = '\n'; + (*bytes_read)++; + } + return buf; +} + +static int parser_parse_buf(lua_State *L) +{ + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + long bufnr = lua_tointeger(L, 2); + void *payload = handle_get_buffer(bufnr); + TSInput input = {payload, input_cb, TSInputEncodingUTF8}; + TSTree *new_tree = ts_parser_parse(p->parser, p->tree, input); + if (p->tree) { + ts_tree_delete(p->tree); + } + p->tree = new_tree; + + tslua_push_tree(L, ts_tree_copy(p->tree)); + return 1; +} + +static int parser_tree(lua_State *L) +{ + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + if (p->tree) { + tslua_push_tree(L, ts_tree_copy(p->tree)); + } else { + lua_pushnil(L); + } + return 1; +} + +static int parser_edit(lua_State *L) +{ + if(lua_gettop(L) < 10) { + lua_pushstring(L, "not enough args to parser:edit()"); + lua_error(L); + return 0; // unreachable + } + + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + if (!p->tree) { + return 0; + } + + long start_byte = lua_tointeger(L, 2); + long old_end_byte = lua_tointeger(L, 3); + long new_end_byte = lua_tointeger(L, 4); + TSPoint start_point = { lua_tointeger(L, 5), lua_tointeger(L, 6) }; + TSPoint old_end_point = { lua_tointeger(L, 7), lua_tointeger(L, 8) }; + TSPoint new_end_point = { lua_tointeger(L, 9), lua_tointeger(L, 10) }; + + TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, + start_point, old_end_point, new_end_point }; + + ts_tree_edit(p->tree, &edit); + + return 0; +} + + +// Tree methods + +/// push tree interface on lua stack. +/// +/// This takes "ownership" of the tree and will free it +/// when the wrapper object is garbage collected +void tslua_push_tree(lua_State *L, TSTree *tree) +{ + TSTree **ud = lua_newuserdata(L, sizeof(TSTree *)); // [udata] + *ud = tree; + lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] + lua_getfield(L, -1, "tree-meta"); // [udata, env, meta] + lua_setmetatable(L, -3); // [udata, env] + lua_pop(L, 1); // [udata] + + // table used for node wrappers to keep a reference to tree wrapper + // NB: in lua 5.3 the uservalue for the node could just be the tree, but + // in lua 5.1 the uservalue (fenv) must be a table. + lua_createtable(L, 1, 0); // [udata, reftable] + lua_pushvalue(L, -2); // [udata, reftable, udata] + lua_rawseti(L, -2, 1); // [udata, reftable] + lua_setfenv(L, -2); // [udata] + debug_n_trees++; +} + +static TSTree *tree_check(lua_State *L) +{ + if (!lua_gettop(L)) { + return 0; + } + if (!lua_isuserdata(L, 1)) { + return 0; + } + // TODO: typecheck! + TSTree **ud = lua_touserdata(L, 1); + return *ud; +} + +static int tree_gc(lua_State *L) +{ + TSTree *tree = tree_check(L); + if (!tree) { + return 0; + } + + ts_tree_delete(tree); + debug_n_trees--; + return 0; +} + +static int tree_tostring(lua_State *L) +{ + lua_pushstring(L, ""); + return 1; +} + +static int tree_root(lua_State *L) +{ + TSTree *tree = tree_check(L); + if (!tree) { + return 0; + } + TSNode root = ts_tree_root_node(tree); + push_node(L, root); + return 1; +} + +// Node methods + +/// push node interface on lua stack +/// +/// top of stack must either be the tree this node belongs to or another node +/// of the same tree! This value is not popped. Can only be called inside a +/// cfunction with the tslua environment. +static void push_node(lua_State *L, TSNode node) +{ + if (ts_node_is_null(node)) { + lua_pushnil(L); // [src, nil] + return; + } + TSNode *ud = lua_newuserdata(L, sizeof(TSNode)); // [src, udata] + *ud = node; + lua_getfield(L, LUA_ENVIRONINDEX, "node-meta"); // [src, udata, meta] + lua_setmetatable(L, -2); // [src, udata] + lua_getfenv(L, -2); // [src, udata, reftable] + lua_setfenv(L, -2); // [src, udata] +} + +static bool node_check(lua_State *L, TSNode *res) +{ + if (!lua_gettop(L)) { + return 0; + } + if (!lua_isuserdata(L, 1)) { + return 0; + } + // TODO: typecheck! + TSNode *ud = lua_touserdata(L, 1); + *res = *ud; + return true; +} + + +static int node_tostring(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + lua_pushstring(L, ""); + lua_concat(L, 3); + return 1; +} + +static int node_range(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint start = ts_node_start_point(node); + TSPoint end = ts_node_end_point(node); + lua_pushnumber(L, start.row); + lua_pushnumber(L, start.column); + lua_pushnumber(L, end.row); + lua_pushnumber(L, end.column); + return 4; +} + +static int node_start(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint start = ts_node_start_point(node); + uint32_t start_byte = ts_node_start_byte(node); + lua_pushnumber(L, start.row); + lua_pushnumber(L, start.column); + lua_pushnumber(L, start_byte); + return 3; +} + +static int node_child_count(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + uint32_t count = ts_node_child_count(node); + lua_pushnumber(L, count); + return 1; +} + +static int node_type(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + lua_pushstring(L, ts_node_type(node)); + return 1; +} + +static int node_symbol(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSSymbol symbol = ts_node_symbol(node); + lua_pushnumber(L, symbol); + return 1; +} + +static int node_child(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + long num = lua_tointeger(L, 2); + TSNode child = ts_node_child(node, (uint32_t)num); + + lua_pushvalue(L, 1); + push_node(L, child); + return 1; +} + +static int node_descendant_for_point_range(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint start = {(uint32_t)lua_tointeger(L, 2), + (uint32_t)lua_tointeger(L, 3)}; + TSPoint end = {(uint32_t)lua_tointeger(L, 4), + (uint32_t)lua_tointeger(L, 5)}; + TSNode child = ts_node_descendant_for_point_range(node, start, end); + + lua_pushvalue(L, 1); + push_node(L, child); + return 1; +} + +static int node_parent(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSNode parent = ts_node_parent(node); + push_node(L, parent); + return 1; +} + diff --git a/src/nvim/lua/tree_sitter.h b/src/nvim/lua/tree_sitter.h new file mode 100644 index 0000000000..2ae0ec8371 --- /dev/null +++ b/src/nvim/lua/tree_sitter.h @@ -0,0 +1,10 @@ +#ifndef NVIM_LUA_TREE_SITTER_H +#define NVIM_LUA_TREE_SITTER_H + +#include "tree_sitter/api.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/tree_sitter.h.generated.h" +#endif + +#endif // NVIM_LUA_TREE_SITTER_H -- cgit From 0e0beef85e4d3932e0d49528d8474794f7b69b01 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 6 Jun 2019 12:20:07 +0200 Subject: tree-sitter: load parsers as .so files --- src/nvim/lua/executor.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 8b0140f794..1794cee8d8 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -834,16 +834,31 @@ static int unsafe_ptr_to_ts_tree(lua_State *L) static int create_tslua_parser(lua_State *L) { - TSLanguage *tree_sitter_c(void), *tree_sitter_javascript(void); - - if (!lua_gettop(L)) { + if (lua_gettop(L) < 2) { return 0; } - char *str = lua_tostring(L,1); + const char *path = lua_tostring(L,1); + const char *lang_name = lua_tostring(L,2); + + // TODO: unsafe! + char symbol_buf[128] = "tree_sitter_"; + STRCAT(symbol_buf, lang_name); + + // TODO: we should maybe keep the uv_lib_t around, and close them + // at exit, to keep LeakSanitizer happy. + uv_lib_t lib; + if (uv_dlopen(path, &lib)) { + return luaL_error(L, "uv_dlopen: %s", uv_dlerror(&lib)); + } + + TSLanguage *(*lang_parser)(void); + if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { + return luaL_error(L, "uv_dlsym: %s", uv_dlerror(&lib)); + } - TSLanguage *lang = tree_sitter_c(); - if (str && striequal(str, "javascript")) { - lang = tree_sitter_javascript(); + TSLanguage *lang = lang_parser(); + if (lang == NULL) { + return luaL_error(L, "failed to load parser"); } tslua_push_parser(L, lang); return 1; -- cgit From 1e9e2451bef21ff705e677802d1b0980356f1f86 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 7 Jun 2019 18:19:59 +0200 Subject: tree-sitter: objectify API --- src/nvim/lua/executor.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 1794cee8d8..a6447ebb2b 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -822,16 +822,6 @@ void ex_luafile(exarg_T *const eap) } } -static int unsafe_ptr_to_ts_tree(lua_State *L) -{ - if (!lua_gettop(L)) { - return 0; - } - TSTree *const *ptr = lua_topointer(L,1); - tslua_push_tree(L, *ptr); - return 1; -} - static int create_tslua_parser(lua_State *L) { if (lua_gettop(L) < 2) { @@ -867,9 +857,7 @@ static int create_tslua_parser(lua_State *L) static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { tslua_init(lstate); - lua_pushcfunction(lstate, unsafe_ptr_to_ts_tree); - lua_setfield(lstate, -2, "unsafe_ts_tree"); lua_pushcfunction(lstate, create_tslua_parser); - lua_setfield(lstate, -2, "ts_parser"); + lua_setfield(lstate, -2, "_create_ts_parser"); } -- cgit From afba23099fccc929fd0319a9a965a7b727407c7a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 8 Jun 2019 15:51:38 +0200 Subject: tree-sitter: support pre-registration of languages --- src/nvim/lua/executor.c | 32 ++++++--------------------- src/nvim/lua/tree_sitter.c | 54 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 26 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index a6447ebb2b..ae53bfce6a 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -824,34 +824,13 @@ void ex_luafile(exarg_T *const eap) static int create_tslua_parser(lua_State *L) { - if (lua_gettop(L) < 2) { - return 0; + if (lua_gettop(L) < 1 || !lua_isstring(L, 1)) { + return luaL_error(L, "string expected"); } - const char *path = lua_tostring(L,1); - const char *lang_name = lua_tostring(L,2); - // TODO: unsafe! - char symbol_buf[128] = "tree_sitter_"; - STRCAT(symbol_buf, lang_name); + const char *lang_name = lua_tostring(L,1); - // TODO: we should maybe keep the uv_lib_t around, and close them - // at exit, to keep LeakSanitizer happy. - uv_lib_t lib; - if (uv_dlopen(path, &lib)) { - return luaL_error(L, "uv_dlopen: %s", uv_dlerror(&lib)); - } - - TSLanguage *(*lang_parser)(void); - if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { - return luaL_error(L, "uv_dlsym: %s", uv_dlerror(&lib)); - } - - TSLanguage *lang = lang_parser(); - if (lang == NULL) { - return luaL_error(L, "failed to load parser"); - } - tslua_push_parser(L, lang); - return 1; + return tslua_push_parser(L, lang_name); } static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL @@ -860,4 +839,7 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, create_tslua_parser); lua_setfield(lstate, -2, "_create_ts_parser"); + + lua_pushcfunction(lstate, ts_lua_register_lang); + lua_setfield(lstate, -2, "ts_add_language"); } diff --git a/src/nvim/lua/tree_sitter.c b/src/nvim/lua/tree_sitter.c index 1ecb2bcac4..f992639955 100644 --- a/src/nvim/lua/tree_sitter.c +++ b/src/nvim/lua/tree_sitter.c @@ -65,6 +65,8 @@ static struct luaL_Reg node_meta[] = { {NULL, NULL} }; +PMap(cstr_t) *langs; + void build_meta(lua_State *L, const luaL_Reg *meta) { // [env, target] @@ -86,6 +88,9 @@ void build_meta(lua_State *L, const luaL_Reg *meta) /// all global state is stored in the regirstry of the lua_State void tslua_init(lua_State *L) { + + langs = pmap_new(cstr_t)(); + lua_createtable(L, 0, 0); // type metatables @@ -114,9 +119,55 @@ static int tslua_debug(lua_State *L) return 2; } -void tslua_push_parser(lua_State *L, TSLanguage *lang) + +int ts_lua_register_lang(lua_State *L) +{ + if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) { + return luaL_error(L, "string expected"); + } + + const char *path = lua_tostring(L,1); + const char *lang_name = lua_tostring(L,2); + + if (pmap_has(cstr_t)(langs, lang_name)) { + return 0; + } + + // TODO: unsafe! + char symbol_buf[128] = "tree_sitter_"; + STRCAT(symbol_buf, lang_name); + + // TODO: we should maybe keep the uv_lib_t around, and close them + // at exit, to keep LeakSanitizer happy. + uv_lib_t lib; + if (uv_dlopen(path, &lib)) { + return luaL_error(L, "Failed to load parser: uv_dlopen: %s", uv_dlerror(&lib)); + } + + TSLanguage *(*lang_parser)(void); + if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { + return luaL_error(L, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib)); + } + + TSLanguage *lang = lang_parser(); + if (lang == NULL) { + return luaL_error(L, "Failed to load parser: internal error"); + } + + pmap_put(cstr_t)(langs, xstrdup(lang_name), lang); + + lua_pushboolean(L, true); + return 1; +} + +int tslua_push_parser(lua_State *L, const char *lang_name) { TSParser *parser = ts_parser_new(); + TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + if (!lang) { + return luaL_error(L, "no such language: %s", lang_name); + } + ts_parser_set_language(parser, lang); Tslua_parser *p = lua_newuserdata(L, sizeof(Tslua_parser)); // [udata] p->parser = parser; @@ -126,6 +177,7 @@ void tslua_push_parser(lua_State *L, TSLanguage *lang) lua_getfield(L, -1, "parser-meta"); // [udata, env, meta] lua_setmetatable(L, -3); // [udata, env] lua_pop(L, 1); // [udata] + return 1; } static Tslua_parser *parser_check(lua_State *L) -- cgit From 4ea5e63aa8c866b4fcc9d10f1a26078d2517f96a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 9 Jun 2019 13:26:48 +0200 Subject: tree-sitter: add basic testing on ci build tree-sitter c parser on ci for testing purposes --- src/nvim/lua/executor.c | 2 +- src/nvim/lua/vim.lua | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index ae53bfce6a..23c4fcabbc 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -841,5 +841,5 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "_create_ts_parser"); lua_pushcfunction(lstate, ts_lua_register_lang); - lua_setfield(lstate, -2, "ts_add_language"); + lua_setfield(lstate, -2, "_ts_add_language"); } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index b1a684b977..c38926fe24 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -232,6 +232,9 @@ local function __index(t, key) if key == 'inspect' then t.inspect = require('vim.inspect') return t.inspect + elseif key == 'tree_sitter' then + t.tree_sitter = require('vim.tree_sitter') + return t.tree_sitter elseif require('vim.shared')[key] ~= nil then -- Expose all `vim.shared` functions on the `vim` module. t[key] = require('vim.shared')[key] -- cgit From c8f861b739b4703b1198dc1f88b09edbeb0d9f2e Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 15 Jun 2019 12:10:12 +0200 Subject: tree-sitter: rename tree_sitter => treesitter for consistency --- src/nvim/lua/executor.c | 2 +- src/nvim/lua/tree_sitter.c | 524 --------------------------------------------- src/nvim/lua/tree_sitter.h | 10 - src/nvim/lua/treesitter.c | 524 +++++++++++++++++++++++++++++++++++++++++++++ src/nvim/lua/treesitter.h | 10 + src/nvim/lua/vim.lua | 6 +- 6 files changed, 538 insertions(+), 538 deletions(-) delete mode 100644 src/nvim/lua/tree_sitter.c delete mode 100644 src/nvim/lua/tree_sitter.h create mode 100644 src/nvim/lua/treesitter.c create mode 100644 src/nvim/lua/treesitter.h (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 23c4fcabbc..8a35f11c71 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -31,7 +31,7 @@ #include "nvim/lua/executor.h" #include "nvim/lua/converter.h" -#include "nvim/lua/tree_sitter.h" +#include "nvim/lua/treesitter.h" #include "luv/luv.h" diff --git a/src/nvim/lua/tree_sitter.c b/src/nvim/lua/tree_sitter.c deleted file mode 100644 index f992639955..0000000000 --- a/src/nvim/lua/tree_sitter.c +++ /dev/null @@ -1,524 +0,0 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - -// lua bindings for tree-siter. -// NB: this file should contain a generic lua interface for -// tree-sitter trees and nodes, and could be broken out as a reusable library - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "tree_sitter/api.h" - -// NOT state-safe, delete when GC is confimed working: -static int debug_n_trees = 0, debug_n_cursors = 0; - -#define REG_KEY "tree_sitter-private" - -#include "nvim/lua/tree_sitter.h" -#include "nvim/api/private/handle.h" -#include "nvim/memline.h" - -typedef struct { - TSParser *parser; - TSTree *tree; -} Tslua_parser; - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "lua/tree_sitter.c.generated.h" -#endif - -static struct luaL_Reg parser_meta[] = { - {"__gc", parser_gc}, - {"__tostring", parser_tostring}, - {"parse_buf", parser_parse_buf}, - {"edit", parser_edit}, - {"tree", parser_tree}, - {NULL, NULL} -}; - -static struct luaL_Reg tree_meta[] = { - {"__gc", tree_gc}, - {"__tostring", tree_tostring}, - {"root", tree_root}, - {NULL, NULL} -}; - -static struct luaL_Reg node_meta[] = { - {"__tostring", node_tostring}, - {"__len", node_child_count}, - {"range", node_range}, - {"start", node_start}, - {"type", node_type}, - {"symbol", node_symbol}, - {"child_count", node_child_count}, - {"child", node_child}, - {"descendant_for_point_range", node_descendant_for_point_range}, - {"parent", node_parent}, - {NULL, NULL} -}; - -PMap(cstr_t) *langs; - -void build_meta(lua_State *L, const luaL_Reg *meta) -{ - // [env, target] - for (size_t i = 0; meta[i].name != NULL; i++) { - lua_pushcfunction(L, meta[i].func); // [env, target, func] - lua_pushvalue(L, -3); // [env, target, func, env] - lua_setfenv(L, -2); // [env, target, func] - lua_setfield(L, -2, meta[i].name); // [env, target] - } - - lua_pushvalue(L, -1); // [env, target, target] - lua_setfield(L, -2, "__index"); // [env, target] -} - - - -/// init the tslua library -/// -/// all global state is stored in the regirstry of the lua_State -void tslua_init(lua_State *L) -{ - - langs = pmap_new(cstr_t)(); - - lua_createtable(L, 0, 0); - - // type metatables - lua_createtable(L, 0, 0); - build_meta(L, parser_meta); - lua_setfield(L, -2, "parser-meta"); - - lua_createtable(L, 0, 0); - build_meta(L, tree_meta); - lua_setfield(L, -2, "tree-meta"); - - lua_createtable(L, 0, 0); - build_meta(L, node_meta); - lua_setfield(L, -2, "node-meta"); - - lua_setfield(L, LUA_REGISTRYINDEX, REG_KEY); - - lua_pushcfunction(L, tslua_debug); - lua_setglobal(L, "_tslua_debug"); -} - -static int tslua_debug(lua_State *L) -{ - lua_pushinteger(L, debug_n_trees); - lua_pushinteger(L, debug_n_cursors); - return 2; -} - - -int ts_lua_register_lang(lua_State *L) -{ - if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) { - return luaL_error(L, "string expected"); - } - - const char *path = lua_tostring(L,1); - const char *lang_name = lua_tostring(L,2); - - if (pmap_has(cstr_t)(langs, lang_name)) { - return 0; - } - - // TODO: unsafe! - char symbol_buf[128] = "tree_sitter_"; - STRCAT(symbol_buf, lang_name); - - // TODO: we should maybe keep the uv_lib_t around, and close them - // at exit, to keep LeakSanitizer happy. - uv_lib_t lib; - if (uv_dlopen(path, &lib)) { - return luaL_error(L, "Failed to load parser: uv_dlopen: %s", uv_dlerror(&lib)); - } - - TSLanguage *(*lang_parser)(void); - if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { - return luaL_error(L, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib)); - } - - TSLanguage *lang = lang_parser(); - if (lang == NULL) { - return luaL_error(L, "Failed to load parser: internal error"); - } - - pmap_put(cstr_t)(langs, xstrdup(lang_name), lang); - - lua_pushboolean(L, true); - return 1; -} - -int tslua_push_parser(lua_State *L, const char *lang_name) -{ - TSParser *parser = ts_parser_new(); - TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); - if (!lang) { - return luaL_error(L, "no such language: %s", lang_name); - } - - ts_parser_set_language(parser, lang); - Tslua_parser *p = lua_newuserdata(L, sizeof(Tslua_parser)); // [udata] - p->parser = parser; - p->tree = NULL; - - lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] - lua_getfield(L, -1, "parser-meta"); // [udata, env, meta] - lua_setmetatable(L, -3); // [udata, env] - lua_pop(L, 1); // [udata] - return 1; -} - -static Tslua_parser *parser_check(lua_State *L) -{ - if (!lua_gettop(L)) { - return 0; - } - if (!lua_isuserdata(L, 1)) { - return 0; - } - // TODO: typecheck! - return lua_touserdata(L, 1); -} - -static int parser_gc(lua_State *L) -{ - Tslua_parser *p = parser_check(L); - if (!p) { - return 0; - } - - ts_parser_delete(p->parser); - if (p->tree) { - ts_tree_delete(p->tree); - } - - return 0; -} - -static int parser_tostring(lua_State *L) -{ - lua_pushstring(L, ""); - return 1; -} - -static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position, uint32_t *bytes_read) -{ - buf_T *bp = payload; - static char buf[200]; - if ((linenr_T)position.row >= bp->b_ml.ml_line_count) { - *bytes_read = 0; - return ""; - } - char_u *line = ml_get_buf(bp, position.row+1, false); - size_t len = STRLEN(line); - size_t tocopy = MIN(len-position.column,200); - - // TODO: translate embedded \n to \000 - memcpy(buf, line+position.column, tocopy); - *bytes_read = (uint32_t)tocopy; - if (tocopy < 200) { - buf[tocopy] = '\n'; - (*bytes_read)++; - } - return buf; -} - -static int parser_parse_buf(lua_State *L) -{ - Tslua_parser *p = parser_check(L); - if (!p) { - return 0; - } - - long bufnr = lua_tointeger(L, 2); - void *payload = handle_get_buffer(bufnr); - TSInput input = {payload, input_cb, TSInputEncodingUTF8}; - TSTree *new_tree = ts_parser_parse(p->parser, p->tree, input); - if (p->tree) { - ts_tree_delete(p->tree); - } - p->tree = new_tree; - - tslua_push_tree(L, ts_tree_copy(p->tree)); - return 1; -} - -static int parser_tree(lua_State *L) -{ - Tslua_parser *p = parser_check(L); - if (!p) { - return 0; - } - - if (p->tree) { - tslua_push_tree(L, ts_tree_copy(p->tree)); - } else { - lua_pushnil(L); - } - return 1; -} - -static int parser_edit(lua_State *L) -{ - if(lua_gettop(L) < 10) { - lua_pushstring(L, "not enough args to parser:edit()"); - lua_error(L); - return 0; // unreachable - } - - Tslua_parser *p = parser_check(L); - if (!p) { - return 0; - } - - if (!p->tree) { - return 0; - } - - long start_byte = lua_tointeger(L, 2); - long old_end_byte = lua_tointeger(L, 3); - long new_end_byte = lua_tointeger(L, 4); - TSPoint start_point = { lua_tointeger(L, 5), lua_tointeger(L, 6) }; - TSPoint old_end_point = { lua_tointeger(L, 7), lua_tointeger(L, 8) }; - TSPoint new_end_point = { lua_tointeger(L, 9), lua_tointeger(L, 10) }; - - TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, - start_point, old_end_point, new_end_point }; - - ts_tree_edit(p->tree, &edit); - - return 0; -} - - -// Tree methods - -/// push tree interface on lua stack. -/// -/// This takes "ownership" of the tree and will free it -/// when the wrapper object is garbage collected -void tslua_push_tree(lua_State *L, TSTree *tree) -{ - TSTree **ud = lua_newuserdata(L, sizeof(TSTree *)); // [udata] - *ud = tree; - lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] - lua_getfield(L, -1, "tree-meta"); // [udata, env, meta] - lua_setmetatable(L, -3); // [udata, env] - lua_pop(L, 1); // [udata] - - // table used for node wrappers to keep a reference to tree wrapper - // NB: in lua 5.3 the uservalue for the node could just be the tree, but - // in lua 5.1 the uservalue (fenv) must be a table. - lua_createtable(L, 1, 0); // [udata, reftable] - lua_pushvalue(L, -2); // [udata, reftable, udata] - lua_rawseti(L, -2, 1); // [udata, reftable] - lua_setfenv(L, -2); // [udata] - debug_n_trees++; -} - -static TSTree *tree_check(lua_State *L) -{ - if (!lua_gettop(L)) { - return 0; - } - if (!lua_isuserdata(L, 1)) { - return 0; - } - // TODO: typecheck! - TSTree **ud = lua_touserdata(L, 1); - return *ud; -} - -static int tree_gc(lua_State *L) -{ - TSTree *tree = tree_check(L); - if (!tree) { - return 0; - } - - ts_tree_delete(tree); - debug_n_trees--; - return 0; -} - -static int tree_tostring(lua_State *L) -{ - lua_pushstring(L, ""); - return 1; -} - -static int tree_root(lua_State *L) -{ - TSTree *tree = tree_check(L); - if (!tree) { - return 0; - } - TSNode root = ts_tree_root_node(tree); - push_node(L, root); - return 1; -} - -// Node methods - -/// push node interface on lua stack -/// -/// top of stack must either be the tree this node belongs to or another node -/// of the same tree! This value is not popped. Can only be called inside a -/// cfunction with the tslua environment. -static void push_node(lua_State *L, TSNode node) -{ - if (ts_node_is_null(node)) { - lua_pushnil(L); // [src, nil] - return; - } - TSNode *ud = lua_newuserdata(L, sizeof(TSNode)); // [src, udata] - *ud = node; - lua_getfield(L, LUA_ENVIRONINDEX, "node-meta"); // [src, udata, meta] - lua_setmetatable(L, -2); // [src, udata] - lua_getfenv(L, -2); // [src, udata, reftable] - lua_setfenv(L, -2); // [src, udata] -} - -static bool node_check(lua_State *L, TSNode *res) -{ - if (!lua_gettop(L)) { - return 0; - } - if (!lua_isuserdata(L, 1)) { - return 0; - } - // TODO: typecheck! - TSNode *ud = lua_touserdata(L, 1); - *res = *ud; - return true; -} - - -static int node_tostring(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - lua_pushstring(L, ""); - lua_concat(L, 3); - return 1; -} - -static int node_range(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - TSPoint start = ts_node_start_point(node); - TSPoint end = ts_node_end_point(node); - lua_pushnumber(L, start.row); - lua_pushnumber(L, start.column); - lua_pushnumber(L, end.row); - lua_pushnumber(L, end.column); - return 4; -} - -static int node_start(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - TSPoint start = ts_node_start_point(node); - uint32_t start_byte = ts_node_start_byte(node); - lua_pushnumber(L, start.row); - lua_pushnumber(L, start.column); - lua_pushnumber(L, start_byte); - return 3; -} - -static int node_child_count(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - uint32_t count = ts_node_child_count(node); - lua_pushnumber(L, count); - return 1; -} - -static int node_type(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - lua_pushstring(L, ts_node_type(node)); - return 1; -} - -static int node_symbol(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - TSSymbol symbol = ts_node_symbol(node); - lua_pushnumber(L, symbol); - return 1; -} - -static int node_child(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - long num = lua_tointeger(L, 2); - TSNode child = ts_node_child(node, (uint32_t)num); - - lua_pushvalue(L, 1); - push_node(L, child); - return 1; -} - -static int node_descendant_for_point_range(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - TSPoint start = {(uint32_t)lua_tointeger(L, 2), - (uint32_t)lua_tointeger(L, 3)}; - TSPoint end = {(uint32_t)lua_tointeger(L, 4), - (uint32_t)lua_tointeger(L, 5)}; - TSNode child = ts_node_descendant_for_point_range(node, start, end); - - lua_pushvalue(L, 1); - push_node(L, child); - return 1; -} - -static int node_parent(lua_State *L) -{ - TSNode node; - if (!node_check(L, &node)) { - return 0; - } - TSNode parent = ts_node_parent(node); - push_node(L, parent); - return 1; -} - diff --git a/src/nvim/lua/tree_sitter.h b/src/nvim/lua/tree_sitter.h deleted file mode 100644 index 2ae0ec8371..0000000000 --- a/src/nvim/lua/tree_sitter.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef NVIM_LUA_TREE_SITTER_H -#define NVIM_LUA_TREE_SITTER_H - -#include "tree_sitter/api.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "lua/tree_sitter.h.generated.h" -#endif - -#endif // NVIM_LUA_TREE_SITTER_H diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c new file mode 100644 index 0000000000..794bdc6749 --- /dev/null +++ b/src/nvim/lua/treesitter.c @@ -0,0 +1,524 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// lua bindings for tree-siter. +// NB: this file should contain a generic lua interface for +// tree-sitter trees and nodes, and could be broken out as a reusable library + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tree_sitter/api.h" + +// NOT state-safe, delete when GC is confimed working: +static int debug_n_trees = 0, debug_n_cursors = 0; + +#define REG_KEY "treesitter-private" + +#include "nvim/lua/treesitter.h" +#include "nvim/api/private/handle.h" +#include "nvim/memline.h" + +typedef struct { + TSParser *parser; + TSTree *tree; +} Tslua_parser; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/treesitter.c.generated.h" +#endif + +static struct luaL_Reg parser_meta[] = { + {"__gc", parser_gc}, + {"__tostring", parser_tostring}, + {"parse_buf", parser_parse_buf}, + {"edit", parser_edit}, + {"tree", parser_tree}, + {NULL, NULL} +}; + +static struct luaL_Reg tree_meta[] = { + {"__gc", tree_gc}, + {"__tostring", tree_tostring}, + {"root", tree_root}, + {NULL, NULL} +}; + +static struct luaL_Reg node_meta[] = { + {"__tostring", node_tostring}, + {"__len", node_child_count}, + {"range", node_range}, + {"start", node_start}, + {"type", node_type}, + {"symbol", node_symbol}, + {"child_count", node_child_count}, + {"child", node_child}, + {"descendant_for_point_range", node_descendant_for_point_range}, + {"parent", node_parent}, + {NULL, NULL} +}; + +PMap(cstr_t) *langs; + +void build_meta(lua_State *L, const luaL_Reg *meta) +{ + // [env, target] + for (size_t i = 0; meta[i].name != NULL; i++) { + lua_pushcfunction(L, meta[i].func); // [env, target, func] + lua_pushvalue(L, -3); // [env, target, func, env] + lua_setfenv(L, -2); // [env, target, func] + lua_setfield(L, -2, meta[i].name); // [env, target] + } + + lua_pushvalue(L, -1); // [env, target, target] + lua_setfield(L, -2, "__index"); // [env, target] +} + + + +/// init the tslua library +/// +/// all global state is stored in the regirstry of the lua_State +void tslua_init(lua_State *L) +{ + + langs = pmap_new(cstr_t)(); + + lua_createtable(L, 0, 0); + + // type metatables + lua_createtable(L, 0, 0); + build_meta(L, parser_meta); + lua_setfield(L, -2, "parser-meta"); + + lua_createtable(L, 0, 0); + build_meta(L, tree_meta); + lua_setfield(L, -2, "tree-meta"); + + lua_createtable(L, 0, 0); + build_meta(L, node_meta); + lua_setfield(L, -2, "node-meta"); + + lua_setfield(L, LUA_REGISTRYINDEX, REG_KEY); + + lua_pushcfunction(L, tslua_debug); + lua_setglobal(L, "_tslua_debug"); +} + +static int tslua_debug(lua_State *L) +{ + lua_pushinteger(L, debug_n_trees); + lua_pushinteger(L, debug_n_cursors); + return 2; +} + + +int ts_lua_register_lang(lua_State *L) +{ + if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) { + return luaL_error(L, "string expected"); + } + + const char *path = lua_tostring(L,1); + const char *lang_name = lua_tostring(L,2); + + if (pmap_has(cstr_t)(langs, lang_name)) { + return 0; + } + + // TODO: unsafe! + char symbol_buf[128] = "tree_sitter_"; + STRCAT(symbol_buf, lang_name); + + // TODO: we should maybe keep the uv_lib_t around, and close them + // at exit, to keep LeakSanitizer happy. + uv_lib_t lib; + if (uv_dlopen(path, &lib)) { + return luaL_error(L, "Failed to load parser: uv_dlopen: %s", uv_dlerror(&lib)); + } + + TSLanguage *(*lang_parser)(void); + if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { + return luaL_error(L, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib)); + } + + TSLanguage *lang = lang_parser(); + if (lang == NULL) { + return luaL_error(L, "Failed to load parser: internal error"); + } + + pmap_put(cstr_t)(langs, xstrdup(lang_name), lang); + + lua_pushboolean(L, true); + return 1; +} + +int tslua_push_parser(lua_State *L, const char *lang_name) +{ + TSParser *parser = ts_parser_new(); + TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + if (!lang) { + return luaL_error(L, "no such language: %s", lang_name); + } + + ts_parser_set_language(parser, lang); + Tslua_parser *p = lua_newuserdata(L, sizeof(Tslua_parser)); // [udata] + p->parser = parser; + p->tree = NULL; + + lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] + lua_getfield(L, -1, "parser-meta"); // [udata, env, meta] + lua_setmetatable(L, -3); // [udata, env] + lua_pop(L, 1); // [udata] + return 1; +} + +static Tslua_parser *parser_check(lua_State *L) +{ + if (!lua_gettop(L)) { + return 0; + } + if (!lua_isuserdata(L, 1)) { + return 0; + } + // TODO: typecheck! + return lua_touserdata(L, 1); +} + +static int parser_gc(lua_State *L) +{ + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + ts_parser_delete(p->parser); + if (p->tree) { + ts_tree_delete(p->tree); + } + + return 0; +} + +static int parser_tostring(lua_State *L) +{ + lua_pushstring(L, ""); + return 1; +} + +static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position, uint32_t *bytes_read) +{ + buf_T *bp = payload; + static char buf[200]; + if ((linenr_T)position.row >= bp->b_ml.ml_line_count) { + *bytes_read = 0; + return ""; + } + char_u *line = ml_get_buf(bp, position.row+1, false); + size_t len = STRLEN(line); + size_t tocopy = MIN(len-position.column,200); + + // TODO: translate embedded \n to \000 + memcpy(buf, line+position.column, tocopy); + *bytes_read = (uint32_t)tocopy; + if (tocopy < 200) { + buf[tocopy] = '\n'; + (*bytes_read)++; + } + return buf; +} + +static int parser_parse_buf(lua_State *L) +{ + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + long bufnr = lua_tointeger(L, 2); + void *payload = handle_get_buffer(bufnr); + TSInput input = {payload, input_cb, TSInputEncodingUTF8}; + TSTree *new_tree = ts_parser_parse(p->parser, p->tree, input); + if (p->tree) { + ts_tree_delete(p->tree); + } + p->tree = new_tree; + + tslua_push_tree(L, ts_tree_copy(p->tree)); + return 1; +} + +static int parser_tree(lua_State *L) +{ + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + if (p->tree) { + tslua_push_tree(L, ts_tree_copy(p->tree)); + } else { + lua_pushnil(L); + } + return 1; +} + +static int parser_edit(lua_State *L) +{ + if(lua_gettop(L) < 10) { + lua_pushstring(L, "not enough args to parser:edit()"); + lua_error(L); + return 0; // unreachable + } + + Tslua_parser *p = parser_check(L); + if (!p) { + return 0; + } + + if (!p->tree) { + return 0; + } + + long start_byte = lua_tointeger(L, 2); + long old_end_byte = lua_tointeger(L, 3); + long new_end_byte = lua_tointeger(L, 4); + TSPoint start_point = { lua_tointeger(L, 5), lua_tointeger(L, 6) }; + TSPoint old_end_point = { lua_tointeger(L, 7), lua_tointeger(L, 8) }; + TSPoint new_end_point = { lua_tointeger(L, 9), lua_tointeger(L, 10) }; + + TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, + start_point, old_end_point, new_end_point }; + + ts_tree_edit(p->tree, &edit); + + return 0; +} + + +// Tree methods + +/// push tree interface on lua stack. +/// +/// This takes "ownership" of the tree and will free it +/// when the wrapper object is garbage collected +void tslua_push_tree(lua_State *L, TSTree *tree) +{ + TSTree **ud = lua_newuserdata(L, sizeof(TSTree *)); // [udata] + *ud = tree; + lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] + lua_getfield(L, -1, "tree-meta"); // [udata, env, meta] + lua_setmetatable(L, -3); // [udata, env] + lua_pop(L, 1); // [udata] + + // table used for node wrappers to keep a reference to tree wrapper + // NB: in lua 5.3 the uservalue for the node could just be the tree, but + // in lua 5.1 the uservalue (fenv) must be a table. + lua_createtable(L, 1, 0); // [udata, reftable] + lua_pushvalue(L, -2); // [udata, reftable, udata] + lua_rawseti(L, -2, 1); // [udata, reftable] + lua_setfenv(L, -2); // [udata] + debug_n_trees++; +} + +static TSTree *tree_check(lua_State *L) +{ + if (!lua_gettop(L)) { + return 0; + } + if (!lua_isuserdata(L, 1)) { + return 0; + } + // TODO: typecheck! + TSTree **ud = lua_touserdata(L, 1); + return *ud; +} + +static int tree_gc(lua_State *L) +{ + TSTree *tree = tree_check(L); + if (!tree) { + return 0; + } + + ts_tree_delete(tree); + debug_n_trees--; + return 0; +} + +static int tree_tostring(lua_State *L) +{ + lua_pushstring(L, ""); + return 1; +} + +static int tree_root(lua_State *L) +{ + TSTree *tree = tree_check(L); + if (!tree) { + return 0; + } + TSNode root = ts_tree_root_node(tree); + push_node(L, root); + return 1; +} + +// Node methods + +/// push node interface on lua stack +/// +/// top of stack must either be the tree this node belongs to or another node +/// of the same tree! This value is not popped. Can only be called inside a +/// cfunction with the tslua environment. +static void push_node(lua_State *L, TSNode node) +{ + if (ts_node_is_null(node)) { + lua_pushnil(L); // [src, nil] + return; + } + TSNode *ud = lua_newuserdata(L, sizeof(TSNode)); // [src, udata] + *ud = node; + lua_getfield(L, LUA_ENVIRONINDEX, "node-meta"); // [src, udata, meta] + lua_setmetatable(L, -2); // [src, udata] + lua_getfenv(L, -2); // [src, udata, reftable] + lua_setfenv(L, -2); // [src, udata] +} + +static bool node_check(lua_State *L, TSNode *res) +{ + if (!lua_gettop(L)) { + return 0; + } + if (!lua_isuserdata(L, 1)) { + return 0; + } + // TODO: typecheck! + TSNode *ud = lua_touserdata(L, 1); + *res = *ud; + return true; +} + + +static int node_tostring(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + lua_pushstring(L, ""); + lua_concat(L, 3); + return 1; +} + +static int node_range(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint start = ts_node_start_point(node); + TSPoint end = ts_node_end_point(node); + lua_pushnumber(L, start.row); + lua_pushnumber(L, start.column); + lua_pushnumber(L, end.row); + lua_pushnumber(L, end.column); + return 4; +} + +static int node_start(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint start = ts_node_start_point(node); + uint32_t start_byte = ts_node_start_byte(node); + lua_pushnumber(L, start.row); + lua_pushnumber(L, start.column); + lua_pushnumber(L, start_byte); + return 3; +} + +static int node_child_count(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + uint32_t count = ts_node_child_count(node); + lua_pushnumber(L, count); + return 1; +} + +static int node_type(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + lua_pushstring(L, ts_node_type(node)); + return 1; +} + +static int node_symbol(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSSymbol symbol = ts_node_symbol(node); + lua_pushnumber(L, symbol); + return 1; +} + +static int node_child(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + long num = lua_tointeger(L, 2); + TSNode child = ts_node_child(node, (uint32_t)num); + + lua_pushvalue(L, 1); + push_node(L, child); + return 1; +} + +static int node_descendant_for_point_range(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint start = {(uint32_t)lua_tointeger(L, 2), + (uint32_t)lua_tointeger(L, 3)}; + TSPoint end = {(uint32_t)lua_tointeger(L, 4), + (uint32_t)lua_tointeger(L, 5)}; + TSNode child = ts_node_descendant_for_point_range(node, start, end); + + lua_pushvalue(L, 1); + push_node(L, child); + return 1; +} + +static int node_parent(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSNode parent = ts_node_parent(node); + push_node(L, parent); + return 1; +} + diff --git a/src/nvim/lua/treesitter.h b/src/nvim/lua/treesitter.h new file mode 100644 index 0000000000..d59b5e47a0 --- /dev/null +++ b/src/nvim/lua/treesitter.h @@ -0,0 +1,10 @@ +#ifndef NVIM_LUA_TREESITTER_H +#define NVIM_LUA_TREESITTER_H + +#include "tree_sitter/api.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/treesitter.h.generated.h" +#endif + +#endif // NVIM_LUA_TREESITTER_H diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index c38926fe24..b67762e48e 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -232,9 +232,9 @@ local function __index(t, key) if key == 'inspect' then t.inspect = require('vim.inspect') return t.inspect - elseif key == 'tree_sitter' then - t.tree_sitter = require('vim.tree_sitter') - return t.tree_sitter + elseif key == 'treesitter' then + t.treesitter = require('vim.treesitter') + return t.treesitter elseif require('vim.shared')[key] ~= nil then -- Expose all `vim.shared` functions on the `vim` module. t[key] = require('vim.shared')[key] -- cgit From a361e09cc531caf9dfb41bf860ca2d540ac2789d Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 15 Jun 2019 12:50:41 +0200 Subject: tree-sitter: use standard luaL_newmetatable and luaL_checkudata pattern --- src/nvim/lua/treesitter.c | 94 +++++++++++++++-------------------------------- 1 file changed, 30 insertions(+), 64 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 794bdc6749..8d24d15ccd 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -20,15 +20,13 @@ // NOT state-safe, delete when GC is confimed working: static int debug_n_trees = 0, debug_n_cursors = 0; -#define REG_KEY "treesitter-private" - #include "nvim/lua/treesitter.h" #include "nvim/api/private/handle.h" #include "nvim/memline.h" typedef struct { TSParser *parser; - TSTree *tree; + TSTree *tree; // internal tree, used for editing/reparsing } Tslua_parser; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -67,18 +65,18 @@ static struct luaL_Reg node_meta[] = { PMap(cstr_t) *langs; -void build_meta(lua_State *L, const luaL_Reg *meta) +void build_meta(lua_State *L, const char* tname, const luaL_Reg *meta) { - // [env, target] - for (size_t i = 0; meta[i].name != NULL; i++) { - lua_pushcfunction(L, meta[i].func); // [env, target, func] - lua_pushvalue(L, -3); // [env, target, func, env] - lua_setfenv(L, -2); // [env, target, func] - lua_setfield(L, -2, meta[i].name); // [env, target] - } + if (luaL_newmetatable(L, tname)) { // [meta] + for (size_t i = 0; meta[i].name != NULL; i++) { + lua_pushcfunction(L, meta[i].func); // [meta, func] + lua_setfield(L, -2, meta[i].name); // [meta] + } - lua_pushvalue(L, -1); // [env, target, target] - lua_setfield(L, -2, "__index"); // [env, target] + lua_pushvalue(L, -1); // [meta, meta] + lua_setfield(L, -2, "__index"); // [meta] + } + lua_pop(L, 1); // [] (don't use it now) } @@ -91,22 +89,12 @@ void tslua_init(lua_State *L) langs = pmap_new(cstr_t)(); - lua_createtable(L, 0, 0); - // type metatables - lua_createtable(L, 0, 0); - build_meta(L, parser_meta); - lua_setfield(L, -2, "parser-meta"); + build_meta(L, "treesitter_parser", parser_meta); - lua_createtable(L, 0, 0); - build_meta(L, tree_meta); - lua_setfield(L, -2, "tree-meta"); + build_meta(L, "treesitter_tree", tree_meta); - lua_createtable(L, 0, 0); - build_meta(L, node_meta); - lua_setfield(L, -2, "node-meta"); - - lua_setfield(L, LUA_REGISTRYINDEX, REG_KEY); + build_meta(L, "treesitter_node", node_meta); lua_pushcfunction(L, tslua_debug); lua_setglobal(L, "_tslua_debug"); @@ -173,23 +161,14 @@ int tslua_push_parser(lua_State *L, const char *lang_name) p->parser = parser; p->tree = NULL; - lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] - lua_getfield(L, -1, "parser-meta"); // [udata, env, meta] - lua_setmetatable(L, -3); // [udata, env] - lua_pop(L, 1); // [udata] + lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_parser"); // [udata, meta] + lua_setmetatable(L, -2); // [udata] return 1; } static Tslua_parser *parser_check(lua_State *L) { - if (!lua_gettop(L)) { - return 0; - } - if (!lua_isuserdata(L, 1)) { - return 0; - } - // TODO: typecheck! - return lua_touserdata(L, 1); + return luaL_checkudata(L, 1, "treesitter_parser"); } static int parser_gc(lua_State *L) @@ -313,31 +292,22 @@ void tslua_push_tree(lua_State *L, TSTree *tree) { TSTree **ud = lua_newuserdata(L, sizeof(TSTree *)); // [udata] *ud = tree; - lua_getfield(L, LUA_REGISTRYINDEX, REG_KEY); // [udata, env] - lua_getfield(L, -1, "tree-meta"); // [udata, env, meta] - lua_setmetatable(L, -3); // [udata, env] - lua_pop(L, 1); // [udata] + lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_tree"); // [udata, meta] + lua_setmetatable(L, -2); // [udata] // table used for node wrappers to keep a reference to tree wrapper // NB: in lua 5.3 the uservalue for the node could just be the tree, but // in lua 5.1 the uservalue (fenv) must be a table. - lua_createtable(L, 1, 0); // [udata, reftable] - lua_pushvalue(L, -2); // [udata, reftable, udata] - lua_rawseti(L, -2, 1); // [udata, reftable] - lua_setfenv(L, -2); // [udata] + lua_createtable(L, 1, 0); // [udata, reftable] + lua_pushvalue(L, -2); // [udata, reftable, udata] + lua_rawseti(L, -2, 1); // [udata, reftable] + lua_setfenv(L, -2); // [udata] debug_n_trees++; } static TSTree *tree_check(lua_State *L) { - if (!lua_gettop(L)) { - return 0; - } - if (!lua_isuserdata(L, 1)) { - return 0; - } - // TODO: typecheck! - TSTree **ud = lua_touserdata(L, 1); + TSTree **ud = luaL_checkudata(L, 1, "treesitter_tree"); return *ud; } @@ -385,7 +355,7 @@ static void push_node(lua_State *L, TSNode node) } TSNode *ud = lua_newuserdata(L, sizeof(TSNode)); // [src, udata] *ud = node; - lua_getfield(L, LUA_ENVIRONINDEX, "node-meta"); // [src, udata, meta] + lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_node"); // [src, udata, meta] lua_setmetatable(L, -2); // [src, udata] lua_getfenv(L, -2); // [src, udata, reftable] lua_setfenv(L, -2); // [src, udata] @@ -393,16 +363,12 @@ static void push_node(lua_State *L, TSNode node) static bool node_check(lua_State *L, TSNode *res) { - if (!lua_gettop(L)) { - return 0; - } - if (!lua_isuserdata(L, 1)) { - return 0; + TSNode *ud = luaL_checkudata(L, 1, "treesitter_node"); + if (ud) { + *res = *ud; + return true; } - // TODO: typecheck! - TSNode *ud = lua_touserdata(L, 1); - *res = *ud; - return true; + return false; } -- cgit From c1dc1bedba4e0e3db2cd2e52d9241991567f8218 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 15 Jun 2019 13:12:59 +0200 Subject: tree-sitter: style --- src/nvim/lua/executor.c | 3 +- src/nvim/lua/treesitter.c | 113 ++++++++++++++++++++++++---------------------- 2 files changed, 59 insertions(+), 57 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 8a35f11c71..c208711985 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -828,8 +828,7 @@ static int create_tslua_parser(lua_State *L) return luaL_error(L, "string expected"); } - const char *lang_name = lua_tostring(L,1); - + const char *lang_name = lua_tostring(L, 1); return tslua_push_parser(L, lang_name); } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 8d24d15ccd..2337d598b3 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -34,38 +34,38 @@ typedef struct { #endif static struct luaL_Reg parser_meta[] = { - {"__gc", parser_gc}, - {"__tostring", parser_tostring}, - {"parse_buf", parser_parse_buf}, - {"edit", parser_edit}, - {"tree", parser_tree}, - {NULL, NULL} + { "__gc", parser_gc }, + { "__tostring", parser_tostring }, + { "parse_buf", parser_parse_buf }, + { "edit", parser_edit }, + { "tree", parser_tree }, + { NULL, NULL } }; static struct luaL_Reg tree_meta[] = { - {"__gc", tree_gc}, - {"__tostring", tree_tostring}, - {"root", tree_root}, - {NULL, NULL} + { "__gc", tree_gc }, + { "__tostring", tree_tostring }, + { "root", tree_root }, + { NULL, NULL } }; static struct luaL_Reg node_meta[] = { - {"__tostring", node_tostring}, - {"__len", node_child_count}, - {"range", node_range}, - {"start", node_start}, - {"type", node_type}, - {"symbol", node_symbol}, - {"child_count", node_child_count}, - {"child", node_child}, - {"descendant_for_point_range", node_descendant_for_point_range}, - {"parent", node_parent}, - {NULL, NULL} + { "__tostring", node_tostring }, + { "__len", node_child_count }, + { "range", node_range }, + { "start", node_start }, + { "type", node_type }, + { "symbol", node_symbol }, + { "child_count", node_child_count }, + { "child", node_child }, + { "descendant_for_point_range", node_descendant_for_point_range }, + { "parent", node_parent }, + { NULL, NULL } }; PMap(cstr_t) *langs; -void build_meta(lua_State *L, const char* tname, const luaL_Reg *meta) +void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) { if (luaL_newmetatable(L, tname)) { // [meta] for (size_t i = 0; meta[i].name != NULL; i++) { @@ -86,7 +86,6 @@ void build_meta(lua_State *L, const char* tname, const luaL_Reg *meta) /// all global state is stored in the regirstry of the lua_State void tslua_init(lua_State *L) { - langs = pmap_new(cstr_t)(); // type metatables @@ -114,8 +113,8 @@ int ts_lua_register_lang(lua_State *L) return luaL_error(L, "string expected"); } - const char *path = lua_tostring(L,1); - const char *lang_name = lua_tostring(L,2); + const char *path = lua_tostring(L, 1); + const char *lang_name = lua_tostring(L, 2); if (pmap_has(cstr_t)(langs, lang_name)) { return 0; @@ -129,12 +128,14 @@ int ts_lua_register_lang(lua_State *L) // at exit, to keep LeakSanitizer happy. uv_lib_t lib; if (uv_dlopen(path, &lib)) { - return luaL_error(L, "Failed to load parser: uv_dlopen: %s", uv_dlerror(&lib)); + return luaL_error(L, "Failed to load parser: uv_dlopen: %s", + uv_dlerror(&lib)); } TSLanguage *(*lang_parser)(void); if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { - return luaL_error(L, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib)); + return luaL_error(L, "Failed to load parser: uv_dlsym: %s", + uv_dlerror(&lib)); } TSLanguage *lang = lang_parser(); @@ -192,26 +193,29 @@ static int parser_tostring(lua_State *L) return 1; } -static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position, uint32_t *bytes_read) +static const char *input_cb(void *payload, uint32_t byte_index, + TSPoint position, uint32_t *bytes_read) { - buf_T *bp = payload; - static char buf[200]; - if ((linenr_T)position.row >= bp->b_ml.ml_line_count) { - *bytes_read = 0; - return ""; - } - char_u *line = ml_get_buf(bp, position.row+1, false); - size_t len = STRLEN(line); - size_t tocopy = MIN(len-position.column,200); - - // TODO: translate embedded \n to \000 - memcpy(buf, line+position.column, tocopy); - *bytes_read = (uint32_t)tocopy; - if (tocopy < 200) { - buf[tocopy] = '\n'; - (*bytes_read)++; - } - return buf; + buf_T *bp = payload; +#define BUFSIZE 256 + static char buf[BUFSIZE]; + if ((linenr_T)position.row >= bp->b_ml.ml_line_count) { + *bytes_read = 0; + return ""; + } + char_u *line = ml_get_buf(bp, position.row+1, false); + size_t len = STRLEN(line); + size_t tocopy = MIN(len-position.column, BUFSIZE); + + // TODO: translate embedded \n to \000 + memcpy(buf, line+position.column, tocopy); + *bytes_read = (uint32_t)tocopy; + if (tocopy < 200) { + buf[tocopy] = '\n'; + (*bytes_read)++; + } + return buf; +#undef BUFSIZE } static int parser_parse_buf(lua_State *L) @@ -223,7 +227,7 @@ static int parser_parse_buf(lua_State *L) long bufnr = lua_tointeger(L, 2); void *payload = handle_get_buffer(bufnr); - TSInput input = {payload, input_cb, TSInputEncodingUTF8}; + TSInput input = { payload, input_cb, TSInputEncodingUTF8 }; TSTree *new_tree = ts_parser_parse(p->parser, p->tree, input); if (p->tree) { ts_tree_delete(p->tree); @@ -251,10 +255,9 @@ static int parser_tree(lua_State *L) static int parser_edit(lua_State *L) { - if(lua_gettop(L) < 10) { + if (lua_gettop(L) < 10) { lua_pushstring(L, "not enough args to parser:edit()"); - lua_error(L); - return 0; // unreachable + return lua_error(L); } Tslua_parser *p = parser_check(L); @@ -350,7 +353,7 @@ static int tree_root(lua_State *L) static void push_node(lua_State *L, TSNode node) { if (ts_node_is_null(node)) { - lua_pushnil(L); // [src, nil] + lua_pushnil(L); // [src, nil] return; } TSNode *ud = lua_newuserdata(L, sizeof(TSNode)); // [src, udata] @@ -466,10 +469,10 @@ static int node_descendant_for_point_range(lua_State *L) if (!node_check(L, &node)) { return 0; } - TSPoint start = {(uint32_t)lua_tointeger(L, 2), - (uint32_t)lua_tointeger(L, 3)}; - TSPoint end = {(uint32_t)lua_tointeger(L, 4), - (uint32_t)lua_tointeger(L, 5)}; + TSPoint start = { (uint32_t)lua_tointeger(L, 2), + (uint32_t)lua_tointeger(L, 3) }; + TSPoint end = { (uint32_t)lua_tointeger(L, 4), + (uint32_t)lua_tointeger(L, 5) }; TSNode child = ts_node_descendant_for_point_range(node, start, end); lua_pushvalue(L, 1); -- cgit From a88a9f128e29b27315a87d0119fbc649196999bc Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 15 Jun 2019 13:43:30 +0200 Subject: tree-sitter: add some more API --- src/nvim/lua/treesitter.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 2337d598b3..9d599da85f 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -54,11 +54,19 @@ static struct luaL_Reg node_meta[] = { { "__len", node_child_count }, { "range", node_range }, { "start", node_start }, + { "end_", node_end }, { "type", node_type }, { "symbol", node_symbol }, + { "named", node_named }, + { "missing", node_missing }, + { "has_error", node_has_error }, + { "sexpr", node_sexpr }, { "child_count", node_child_count }, + { "named_child_count", node_named_child_count }, { "child", node_child }, + { "named_child", node_named_child }, { "descendant_for_point_range", node_descendant_for_point_range }, + { "named_descendant_for_point_range", node_named_descendant_for_point_range }, { "parent", node_parent }, { NULL, NULL } }; @@ -417,6 +425,20 @@ static int node_start(lua_State *L) return 3; } +static int node_end(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint end = ts_node_end_point(node); + uint32_t end_byte = ts_node_end_byte(node); + lua_pushnumber(L, end.row); + lua_pushnumber(L, end.column); + lua_pushnumber(L, end_byte); + return 3; +} + static int node_child_count(lua_State *L) { TSNode node; @@ -428,6 +450,17 @@ static int node_child_count(lua_State *L) return 1; } +static int node_named_child_count(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + uint32_t count = ts_node_named_child_count(node); + lua_pushnumber(L, count); + return 1; +} + static int node_type(lua_State *L) { TSNode node; @@ -449,6 +482,48 @@ static int node_symbol(lua_State *L) return 1; } +static int node_named(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + lua_pushboolean(L, ts_node_is_named(node)); + return 1; +} + +static int node_sexpr(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + char *allocated = ts_node_string(node); + lua_pushstring(L, allocated); + xfree(allocated); + return 1; +} + +static int node_missing(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + lua_pushboolean(L, ts_node_is_missing(node)); + return 1; +} + +static int node_has_error(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + lua_pushboolean(L, ts_node_has_error(node)); + return 1; +} + static int node_child(lua_State *L) { TSNode node; @@ -463,6 +538,20 @@ static int node_child(lua_State *L) return 1; } +static int node_named_child(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + long num = lua_tointeger(L, 2); + TSNode child = ts_node_named_child(node, (uint32_t)num); + + lua_pushvalue(L, 1); + push_node(L, child); + return 1; +} + static int node_descendant_for_point_range(lua_State *L) { TSNode node; @@ -480,6 +569,23 @@ static int node_descendant_for_point_range(lua_State *L) return 1; } +static int node_named_descendant_for_point_range(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + TSPoint start = { (uint32_t)lua_tointeger(L, 2), + (uint32_t)lua_tointeger(L, 3) }; + TSPoint end = { (uint32_t)lua_tointeger(L, 4), + (uint32_t)lua_tointeger(L, 5) }; + TSNode child = ts_node_named_descendant_for_point_range(node, start, end); + + lua_pushvalue(L, 1); + push_node(L, child); + return 1; +} + static int node_parent(lua_State *L) { TSNode node; -- cgit From d24dec596c25690aba0aca658546ffdfcc6a952c Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 15 Jun 2019 14:05:35 +0200 Subject: tree-sitter: inspect language --- src/nvim/lua/executor.c | 3 +++ src/nvim/lua/treesitter.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index c208711985..aa83e3c1ba 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -841,4 +841,7 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, ts_lua_register_lang); lua_setfield(lstate, -2, "_ts_add_language"); + + lua_pushcfunction(lstate, ts_lua_inspect_lang); + lua_setfield(lstate, -2, "_ts_inspect_language"); } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 9d599da85f..db337db533 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -157,6 +157,51 @@ int ts_lua_register_lang(lua_State *L) return 1; } +int ts_lua_inspect_lang(lua_State *L) +{ + if (lua_gettop(L) < 1 || !lua_isstring(L, 1)) { + return luaL_error(L, "string expected"); + } + const char *lang_name = lua_tostring(L, 1); + + TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + if (!lang) { + return luaL_error(L, "no such language: %s", lang_name); + } + + lua_createtable(L, 0, 2); // [retval] + + size_t nsymbols = (size_t)ts_language_symbol_count(lang); + + lua_createtable(L, nsymbols-1, 1); // [retval, symbols] + for (size_t i = 0; i < nsymbols; i++) { + TSSymbolType t = ts_language_symbol_type(lang, i); + if (t == TSSymbolTypeAuxiliary) { + // not used by the API + continue; + } + lua_createtable(L, 2, 0); // [retval, symbols, elem] + lua_pushstring(L, ts_language_symbol_name(lang, i)); + lua_rawseti(L, -2, 1); + lua_pushboolean(L, t == TSSymbolTypeRegular); + lua_rawseti(L, -2, 2); // [retval, symbols, elem] + lua_rawseti(L, -2, i); // [retval, symbols] + } + + lua_setfield(L, -2, "symbols"); // [retval] + + // TODO: this seems to be empty, what langs have fields? + size_t nfields = (size_t)ts_language_field_count(lang); + lua_createtable(L, nfields-1, 1); // [retval, fields] + for (size_t i = 0; i < nfields; i++) { + lua_pushstring(L, ts_language_field_name_for_id(lang, i)); + lua_rawseti(L, -2, i); // [retval, fields] + } + + lua_setfield(L, -2, "fields"); // [retval] + return 1; +} + int tslua_push_parser(lua_State *L, const char *lang_name) { TSParser *parser = ts_parser_new(); -- cgit From d697841a9d3030efaf10dbddaee9f3c0a8fe1b78 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 16 Jun 2019 14:09:18 +0200 Subject: tree-sitter: cleanup tree refcounting --- src/nvim/lua/treesitter.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index db337db533..016c175b59 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -17,9 +17,6 @@ #include "tree_sitter/api.h" -// NOT state-safe, delete when GC is confimed working: -static int debug_n_trees = 0, debug_n_cursors = 0; - #include "nvim/lua/treesitter.h" #include "nvim/api/private/handle.h" #include "nvim/memline.h" @@ -102,19 +99,8 @@ void tslua_init(lua_State *L) build_meta(L, "treesitter_tree", tree_meta); build_meta(L, "treesitter_node", node_meta); - - lua_pushcfunction(L, tslua_debug); - lua_setglobal(L, "_tslua_debug"); } -static int tslua_debug(lua_State *L) -{ - lua_pushinteger(L, debug_n_trees); - lua_pushinteger(L, debug_n_cursors); - return 2; -} - - int ts_lua_register_lang(lua_State *L) { if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) { @@ -280,6 +266,9 @@ static int parser_parse_buf(lua_State *L) long bufnr = lua_tointeger(L, 2); void *payload = handle_get_buffer(bufnr); + if (!payload) { + return luaL_error(L, "invalid buffer handle: %d", bufnr); + } TSInput input = { payload, input_cb, TSInputEncodingUTF8 }; TSTree *new_tree = ts_parser_parse(p->parser, p->tree, input); if (p->tree) { @@ -287,7 +276,7 @@ static int parser_parse_buf(lua_State *L) } p->tree = new_tree; - tslua_push_tree(L, ts_tree_copy(p->tree)); + tslua_push_tree(L, p->tree); return 1; } @@ -298,11 +287,7 @@ static int parser_tree(lua_State *L) return 0; } - if (p->tree) { - tslua_push_tree(L, ts_tree_copy(p->tree)); - } else { - lua_pushnil(L); - } + tslua_push_tree(L, p->tree); return 1; } @@ -342,12 +327,15 @@ static int parser_edit(lua_State *L) /// push tree interface on lua stack. /// -/// This takes "ownership" of the tree and will free it -/// when the wrapper object is garbage collected +/// This makes a copy of the tree, so ownership of the argument is unaffected. void tslua_push_tree(lua_State *L, TSTree *tree) { + if (tree == NULL) { + lua_pushnil(L); + return; + } TSTree **ud = lua_newuserdata(L, sizeof(TSTree *)); // [udata] - *ud = tree; + *ud = ts_tree_copy(tree); lua_getfield(L, LUA_REGISTRYINDEX, "treesitter_tree"); // [udata, meta] lua_setmetatable(L, -2); // [udata] @@ -358,7 +346,6 @@ void tslua_push_tree(lua_State *L, TSTree *tree) lua_pushvalue(L, -2); // [udata, reftable, udata] lua_rawseti(L, -2, 1); // [udata, reftable] lua_setfenv(L, -2); // [udata] - debug_n_trees++; } static TSTree *tree_check(lua_State *L) @@ -375,7 +362,6 @@ static int tree_gc(lua_State *L) } ts_tree_delete(tree); - debug_n_trees--; return 0; } -- cgit From 06ee45b9b1c14c7ce6cb23403cdbe2852d495cad Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 21 Jun 2019 14:14:51 +0200 Subject: tree-sitter: fix lint, delete "demo" plugin (replaced by functional tests) --- src/nvim/lua/executor.c | 4 +-- src/nvim/lua/treesitter.c | 70 +++++++++++++++++++++++++---------------------- src/nvim/lua/treesitter.h | 4 +++ 3 files changed, 43 insertions(+), 35 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index aa83e3c1ba..127458fe39 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -839,9 +839,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, create_tslua_parser); lua_setfield(lstate, -2, "_create_ts_parser"); - lua_pushcfunction(lstate, ts_lua_register_lang); + lua_pushcfunction(lstate, tslua_register_lang); lua_setfield(lstate, -2, "_ts_add_language"); - lua_pushcfunction(lstate, ts_lua_inspect_lang); + lua_pushcfunction(lstate, tslua_inspect_lang); lua_setfield(lstate, -2, "_ts_inspect_language"); } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 016c175b59..c27ae8c877 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -1,9 +1,9 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// lua bindings for tree-siter. -// NB: this file should contain a generic lua interface for -// tree-sitter trees and nodes, and could be broken out as a reusable library +// lua bindings for tree-sitter. +// NB: this file mostly contains a generic lua interface for tree-sitter +// trees and nodes, and could be broken out as a reusable lua package #include #include @@ -22,9 +22,9 @@ #include "nvim/memline.h" typedef struct { - TSParser *parser; - TSTree *tree; // internal tree, used for editing/reparsing -} Tslua_parser; + TSParser *parser; + TSTree *tree; // internal tree, used for editing/reparsing +} TSLua_parser; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/treesitter.c.generated.h" @@ -68,9 +68,9 @@ static struct luaL_Reg node_meta[] = { { NULL, NULL } }; -PMap(cstr_t) *langs; +static PMap(cstr_t) *langs; -void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) +static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) { if (luaL_newmetatable(L, tname)) { // [meta] for (size_t i = 0; meta[i].name != NULL; i++) { @@ -84,8 +84,6 @@ void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) lua_pop(L, 1); // [] (don't use it now) } - - /// init the tslua library /// /// all global state is stored in the regirstry of the lua_State @@ -95,13 +93,11 @@ void tslua_init(lua_State *L) // type metatables build_meta(L, "treesitter_parser", parser_meta); - build_meta(L, "treesitter_tree", tree_meta); - build_meta(L, "treesitter_node", node_meta); } -int ts_lua_register_lang(lua_State *L) +int tslua_register_lang(lua_State *L) { if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) { return luaL_error(L, "string expected"); @@ -114,22 +110,27 @@ int ts_lua_register_lang(lua_State *L) return 0; } - // TODO: unsafe! - char symbol_buf[128] = "tree_sitter_"; - STRCAT(symbol_buf, lang_name); +#define BUFSIZE 128 + char symbol_buf[BUFSIZE]; + snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", lang_name); +#undef BUFSIZE - // TODO: we should maybe keep the uv_lib_t around, and close them - // at exit, to keep LeakSanitizer happy. uv_lib_t lib; if (uv_dlopen(path, &lib)) { - return luaL_error(L, "Failed to load parser: uv_dlopen: %s", - uv_dlerror(&lib)); + snprintf((char *)IObuff, IOSIZE, "Failed to load parser: uv_dlopen: %s", + uv_dlerror(&lib)); + uv_dlclose(&lib); + lua_pushstring(L, (char *)IObuff); + return lua_error(L); } TSLanguage *(*lang_parser)(void); if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { - return luaL_error(L, "Failed to load parser: uv_dlsym: %s", - uv_dlerror(&lib)); + snprintf((char *)IObuff, IOSIZE, "Failed to load parser: uv_dlsym: %s", + uv_dlerror(&lib)); + uv_dlclose(&lib); + lua_pushstring(L, (char *)IObuff); + return lua_error(L); } TSLanguage *lang = lang_parser(); @@ -143,7 +144,7 @@ int ts_lua_register_lang(lua_State *L) return 1; } -int ts_lua_inspect_lang(lua_State *L) +int tslua_inspect_lang(lua_State *L) { if (lua_gettop(L) < 1 || !lua_isstring(L, 1)) { return luaL_error(L, "string expected"); @@ -176,7 +177,6 @@ int ts_lua_inspect_lang(lua_State *L) lua_setfield(L, -2, "symbols"); // [retval] - // TODO: this seems to be empty, what langs have fields? size_t nfields = (size_t)ts_language_field_count(lang); lua_createtable(L, nfields-1, 1); // [retval, fields] for (size_t i = 0; i < nfields; i++) { @@ -190,14 +190,14 @@ int ts_lua_inspect_lang(lua_State *L) int tslua_push_parser(lua_State *L, const char *lang_name) { - TSParser *parser = ts_parser_new(); TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); } + TSParser *parser = ts_parser_new(); ts_parser_set_language(parser, lang); - Tslua_parser *p = lua_newuserdata(L, sizeof(Tslua_parser)); // [udata] + TSLua_parser *p = lua_newuserdata(L, sizeof(TSLua_parser)); // [udata] p->parser = parser; p->tree = NULL; @@ -206,14 +206,14 @@ int tslua_push_parser(lua_State *L, const char *lang_name) return 1; } -static Tslua_parser *parser_check(lua_State *L) +static TSLua_parser *parser_check(lua_State *L) { return luaL_checkudata(L, 1, "treesitter_parser"); } static int parser_gc(lua_State *L) { - Tslua_parser *p = parser_check(L); + TSLua_parser *p = parser_check(L); if (!p) { return 0; } @@ -238,6 +238,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, buf_T *bp = payload; #define BUFSIZE 256 static char buf[BUFSIZE]; + if ((linenr_T)position.row >= bp->b_ml.ml_line_count) { *bytes_read = 0; return ""; @@ -246,10 +247,13 @@ static const char *input_cb(void *payload, uint32_t byte_index, size_t len = STRLEN(line); size_t tocopy = MIN(len-position.column, BUFSIZE); - // TODO: translate embedded \n to \000 memcpy(buf, line+position.column, tocopy); + // Translate embedded \n to NUL + memchrsub(buf, '\n', '\0', tocopy); *bytes_read = (uint32_t)tocopy; - if (tocopy < 200) { + if (tocopy < BUFSIZE) { + // now add the final \n. If it didn't fit, input_cb will be called again + // on the same line with advanced column. buf[tocopy] = '\n'; (*bytes_read)++; } @@ -259,7 +263,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, static int parser_parse_buf(lua_State *L) { - Tslua_parser *p = parser_check(L); + TSLua_parser *p = parser_check(L); if (!p) { return 0; } @@ -282,7 +286,7 @@ static int parser_parse_buf(lua_State *L) static int parser_tree(lua_State *L) { - Tslua_parser *p = parser_check(L); + TSLua_parser *p = parser_check(L); if (!p) { return 0; } @@ -298,7 +302,7 @@ static int parser_edit(lua_State *L) return lua_error(L); } - Tslua_parser *p = parser_check(L); + TSLua_parser *p = parser_check(L); if (!p) { return 0; } diff --git a/src/nvim/lua/treesitter.h b/src/nvim/lua/treesitter.h index d59b5e47a0..812166f67b 100644 --- a/src/nvim/lua/treesitter.h +++ b/src/nvim/lua/treesitter.h @@ -1,6 +1,10 @@ #ifndef NVIM_LUA_TREESITTER_H #define NVIM_LUA_TREESITTER_H +#include +#include +#include + #include "tree_sitter/api.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From e0d6228978dd1389f75a3e0351f6e6d5625643ae Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 21 Sep 2019 10:10:47 +0200 Subject: tree-sitter: use "range" instead of "point_range" consistently in lua API --- src/nvim/lua/treesitter.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index c27ae8c877..a71234d2c4 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -62,8 +62,8 @@ static struct luaL_Reg node_meta[] = { { "named_child_count", node_named_child_count }, { "child", node_child }, { "named_child", node_named_child }, - { "descendant_for_point_range", node_descendant_for_point_range }, - { "named_descendant_for_point_range", node_named_descendant_for_point_range }, + { "descendant_for_range", node_descendant_for_range }, + { "named_descendant_for_range", node_named_descendant_for_range }, { "parent", node_parent }, { NULL, NULL } }; @@ -587,7 +587,7 @@ static int node_named_child(lua_State *L) return 1; } -static int node_descendant_for_point_range(lua_State *L) +static int node_descendant_for_range(lua_State *L) { TSNode node; if (!node_check(L, &node)) { @@ -604,7 +604,7 @@ static int node_descendant_for_point_range(lua_State *L) return 1; } -static int node_named_descendant_for_point_range(lua_State *L) +static int node_named_descendant_for_range(lua_State *L) { TSNode node; if (!node_check(L, &node)) { -- cgit From d5a69eb07648a515d03aa5c9e268aef852015ea9 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 22 Sep 2019 11:33:55 +0200 Subject: tree-sitter: handle node equality --- src/nvim/lua/treesitter.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index a71234d2c4..d2072402bb 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -48,6 +48,7 @@ static struct luaL_Reg tree_meta[] = { static struct luaL_Reg node_meta[] = { { "__tostring", node_tostring }, + { "__eq", node_eq }, { "__len", node_child_count }, { "range", node_range }, { "start", node_start }, @@ -431,6 +432,23 @@ static int node_tostring(lua_State *L) return 1; } +static int node_eq(lua_State *L) +{ + TSNode node; + if (!node_check(L, &node)) { + return 0; + } + // This should only be called if both x and y in "x == y" has the + // treesitter_node metatable. So it is ok to error out otherwise. + TSNode *ud = luaL_checkudata(L, 2, "treesitter_node"); + if (!ud) { + return 0; + } + TSNode node2 = *ud; + lua_pushboolean(L, ts_node_eq(node, node2)); + return 1; +} + static int node_range(lua_State *L) { TSNode node; -- cgit From 8ee7c94a92598d46b488b7fe3b1a5cff6b1bf94a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 25 Aug 2019 22:01:35 +0200 Subject: lua: add vim.fn.{func} for direct access to vimL function compared to vim.api.|nvim_call_function|, this fixes some typing issues due to the indirect conversion via the API. float values are preserved as such (fixes #9389) as well as empty dicts/arrays. Ref https://github.com/norcalli/nvim.lua for the call syntax --- src/nvim/lua/converter.c | 14 +++++++++-- src/nvim/lua/executor.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++- src/nvim/lua/vim.lua | 12 +++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 9665655e74..844232c64a 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -401,6 +401,8 @@ nlua_pop_typval_table_processing_end: return ret; } +static bool typval_conv_special = false; + #define TYPVAL_ENCODE_ALLOW_SPECIALS true #define TYPVAL_ENCODE_CONV_NIL(tv) \ @@ -439,7 +441,13 @@ nlua_pop_typval_table_processing_end: lua_createtable(lstate, 0, 0) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary) + do { \ + if (typval_conv_special) { \ + nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \ + } else { \ + lua_createtable(lstate, 0, 0); \ + } \ + } while (0) #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ do { \ @@ -548,9 +556,11 @@ nlua_pop_typval_table_processing_end: /// @param[in] tv typval_T to convert. /// /// @return true in case of success, false otherwise. -bool nlua_push_typval(lua_State *lstate, typval_T *const tv) +bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) { + typval_conv_special = special; const int initial_size = lua_gettop(lstate); + if (!lua_checkstack(lstate, initial_size + 2)) { emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); return false; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 127458fe39..b7c1d57964 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -295,6 +295,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL // in_fast_event lua_pushcfunction(lstate, &nlua_in_fast_event); lua_setfield(lstate, -2, "in_fast_event"); + // call + lua_pushcfunction(lstate, &nlua_call); + lua_setfield(lstate, -2, "call"); // vim.loop luv_set_loop(lstate, &main_loop.uv); @@ -539,6 +542,64 @@ int nlua_in_fast_event(lua_State *lstate) return 1; } +int nlua_call(lua_State *lstate) +{ + Error err = ERROR_INIT; + size_t name_len; + const char_u *name = (const char_u *)luaL_checklstring(lstate, 1, &name_len); + int nargs = lua_gettop(lstate)-1; + if (nargs > MAX_FUNC_ARGS) { + return luaL_error(lstate, "Function called with too many arguments"); + } + + typval_T vim_args[MAX_FUNC_ARGS + 1]; + int i = 0; // also used for freeing the variables + for (; i < nargs; i++) { + lua_pushvalue(lstate, (int)i+2); + if (!nlua_pop_typval(lstate, &vim_args[i])) { + api_set_error(&err, kErrorTypeException, + "error converting argument %d", i+1); + goto free_vim_args; + } + } + + // TODO(bfredl): this should be simplified in error handling refactor + struct msglist **saved_msg_list = msg_list; + struct msglist *private_msg_list = NULL; + msg_list = &private_msg_list; + + force_abort = false; + suppress_errthrow = false; + current_exception = NULL; + did_emsg = false; + + try_start(); + typval_T rettv; + int dummy; + // call_func() retval is deceptive, ignore it. Instead we set `msg_list` + // (see above) to capture abort-causing non-exception errors. + (void)call_func(name, (int)name_len, &rettv, nargs, + vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &dummy, true, NULL, NULL); + if (!try_end(&err)) { + nlua_push_typval(lstate, &rettv, false); + } + tv_clear(&rettv); + + msg_list = saved_msg_list; + +free_vim_args: + while (i > 0) { + tv_clear(&vim_args[--i]); + } + if (ERROR_SET(&err)) { + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + return lua_error(lstate); + } + return 1; +} + #ifdef WIN32 /// os.getenv: override os.getenv to maintain coherency. #9681 /// @@ -623,7 +684,7 @@ void executor_eval_lua(const String str, typval_T *const arg, if (arg->v_type == VAR_UNKNOWN) { lua_pushnil(lstate); } else { - nlua_push_typval(lstate, arg); + nlua_push_typval(lstate, arg, true); } if (lua_pcall(lstate, 1, 1, 0)) { nlua_error(lstate, diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index b67762e48e..5514819a02 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -242,6 +242,17 @@ local function __index(t, key) end end + +-- vim.fn.{func}(...) +local function fn_index(t, key) + local function func(...) + return vim.call(key, ...) + end + t[key] = func + return func +end +local fn = setmetatable({}, {__index=fn_index}) + local module = { _update_package_paths = _update_package_paths, _os_proc_children = _os_proc_children, @@ -249,6 +260,7 @@ local module = { _system = _system, paste = paste, schedule_wrap = schedule_wrap, + fn=fn, } setmetatable(module, { -- cgit From efaf4732e26e2f0fbfab947296141376223b30d7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 27 Oct 2019 15:26:32 -0700 Subject: lua/executor.c: use TRY_WRAP --- src/nvim/lua/executor.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index b7c1d57964..c7ff163f83 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -563,11 +563,8 @@ int nlua_call(lua_State *lstate) } } + TRY_WRAP({ // TODO(bfredl): this should be simplified in error handling refactor - struct msglist **saved_msg_list = msg_list; - struct msglist *private_msg_list = NULL; - msg_list = &private_msg_list; - force_abort = false; suppress_errthrow = false; current_exception = NULL; @@ -577,7 +574,7 @@ int nlua_call(lua_State *lstate) typval_T rettv; int dummy; // call_func() retval is deceptive, ignore it. Instead we set `msg_list` - // (see above) to capture abort-causing non-exception errors. + // (TRY_WRAP) to capture abort-causing non-exception errors. (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, true, NULL, NULL); @@ -585,8 +582,7 @@ int nlua_call(lua_State *lstate) nlua_push_typval(lstate, &rettv, false); } tv_clear(&rettv); - - msg_list = saved_msg_list; + }); free_vim_args: while (i > 0) { -- cgit From 9ef16a1628722958b6e14fe9274006e50ed6682d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 27 Oct 2019 15:05:59 -0700 Subject: doc: vim.fn, vim.call(), vim.api [ci skip] --- src/nvim/lua/vim.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 5514819a02..adb90084db 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -165,6 +165,20 @@ end --- Paste handler, invoked by |nvim_paste()| when a conforming UI --- (such as the |TUI|) pastes text into the editor. --- +--- Example: To remove ANSI color codes when pasting: +---
+--- vim.paste = (function()
+---   local overridden = vim.paste
+---   return function(lines, phase)
+---     for i,line in ipairs(lines) do
+---       -- Scrub ANSI color codes from paste input.
+---       lines[i] = line:gsub('\27%[[0-9;mK]+', '')
+---     end
+---     overridden(lines, phase)
+---   end
+--- end)()
+--- 
+--- --@see |paste| --- --@param lines |readfile()|-style list of lines to paste. |channel-lines| -- cgit From 7a23b67d3594ffb8b6d8629fd9ca1ef8147596db Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 9 Nov 2019 21:18:51 -0800 Subject: paste: Select-mode, Visual-mode #11360 fix #11344 --- src/nvim/lua/vim.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index adb90084db..1ebdde99d5 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -206,8 +206,11 @@ paste = (function() local line1 = lines[1]:gsub('<', ''):gsub('[\r\n\012\027]', ' ') -- Scrub. vim.api.nvim_input(line1) vim.api.nvim_set_option('paste', false) - elseif mode ~= 'c' then -- Else: discard remaining cmdline-mode chunks. - if phase < 2 and mode ~= 'i' and mode ~= 'R' and mode ~= 't' then + elseif mode ~= 'c' then + if phase < 2 and mode:find('^[vV\22sS\19]') then + vim.api.nvim_command([[exe "normal! \"]]) + vim.api.nvim_put(lines, 'c', false, true) + elseif phase < 2 and not mode:find('^[iRt]') then vim.api.nvim_put(lines, 'c', true, true) -- XXX: Normal-mode: workaround bad cursor-placement after first chunk. vim.api.nvim_command('normal! a') -- cgit From 474d0bcbf724c7eed740f60391a0ed35d651e1d3 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 27 Oct 2019 20:49:03 +0100 Subject: lua: vim.rpcrequest, vim.rpcnotify, vim.NIL --- src/nvim/lua/converter.c | 40 +++++++++++++++++++++-- src/nvim/lua/executor.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/lua/executor.h | 2 ++ 3 files changed, 123 insertions(+), 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 844232c64a..44fe60e9c8 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -377,6 +377,19 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) nlua_pop_typval_table_processing_end: break; } + case LUA_TUSERDATA: { + nlua_pushref(lstate, nlua_nil_ref); + bool is_nil = lua_rawequal(lstate, -2, -1); + lua_pop(lstate, 1); + if (is_nil) { + cur.tv->v_type = VAR_SPECIAL; + cur.tv->vval.v_special = kSpecialVarNull; + } else { + EMSG(_("E5101: Cannot convert given lua type")); + ret = false; + } + break; + } default: { EMSG(_("E5101: Cannot convert given lua type")); ret = false; @@ -406,7 +419,13 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_ALLOW_SPECIALS true #define TYPVAL_ENCODE_CONV_NIL(tv) \ - lua_pushnil(lstate) + do { \ + if (typval_conv_special) { \ + lua_pushnil(lstate); \ + } else { \ + nlua_pushref(lstate, nlua_nil_ref); \ + } \ + } while (0) #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ lua_pushboolean(lstate, (bool)(num)) @@ -718,7 +737,11 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) { switch (obj.type) { case kObjectTypeNil: { - lua_pushnil(lstate); + if (special) { + lua_pushnil(lstate); + } else { + nlua_pushref(lstate, nlua_nil_ref); + } break; } case kObjectTypeLuaRef: { @@ -1152,6 +1175,19 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) break; } + case LUA_TUSERDATA: { + nlua_pushref(lstate, nlua_nil_ref); + bool is_nil = lua_rawequal(lstate, -2, -1); + lua_pop(lstate, 1); + if (is_nil) { + *cur.obj = NIL; + } else { + api_set_error(err, kErrorTypeValidation, + "Cannot convert userdata"); + } + break; + } + default: { type_error: api_set_error(err, kErrorTypeValidation, diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index c7ff163f83..5e5cb0cea5 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -12,6 +12,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" +#include "nvim/msgpack_rpc/channel.h" #include "nvim/vim.h" #include "nvim/ex_getln.h" #include "nvim/ex_cmds2.h" @@ -299,6 +300,14 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_call); lua_setfield(lstate, -2, "call"); + // rpcrequest + lua_pushcfunction(lstate, &nlua_rpcrequest); + lua_setfield(lstate, -2, "rpcrequest"); + + // rpcnotify + lua_pushcfunction(lstate, &nlua_rpcnotify); + lua_setfield(lstate, -2, "rpcnotify"); + // vim.loop luv_set_loop(lstate, &main_loop.uv); luv_set_callback(lstate, nlua_luv_cfpcall); @@ -314,6 +323,15 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "luv"); lua_pop(lstate, 3); + // vim.NIL + lua_newuserdata(lstate, 0); + lua_createtable(lstate, 0, 0); + lua_pushcfunction(lstate, &nlua_nil_tostring); + lua_setfield(lstate, -2, "__tostring"); + lua_setmetatable(lstate, -2); + nlua_nil_ref = nlua_ref(lstate, -1); + lua_setfield(lstate, -2, "NIL"); + // internal vim._treesitter... API nlua_add_treesitter(lstate); @@ -547,6 +565,10 @@ int nlua_call(lua_State *lstate) Error err = ERROR_INIT; size_t name_len; const char_u *name = (const char_u *)luaL_checklstring(lstate, 1, &name_len); + if (!nlua_is_deferred_safe(lstate)) { + return luaL_error(lstate, e_luv_api_disabled, "vimL function"); + } + int nargs = lua_gettop(lstate)-1; if (nargs > MAX_FUNC_ARGS) { return luaL_error(lstate, "Function called with too many arguments"); @@ -596,6 +618,67 @@ free_vim_args: return 1; } +static int nlua_rpcrequest(lua_State *lstate) +{ + if (!nlua_is_deferred_safe(lstate)) { + return luaL_error(lstate, e_luv_api_disabled, "rpcrequest"); + } + return nlua_rpc(lstate, true); +} + +static int nlua_rpcnotify(lua_State *lstate) +{ + return nlua_rpc(lstate, false); +} + +static int nlua_rpc(lua_State *lstate, bool request) +{ + size_t name_len; + uint64_t chan_id = (uint64_t)luaL_checkinteger(lstate, 1); + const char *name = luaL_checklstring(lstate, 2, &name_len); + int nargs = lua_gettop(lstate)-2; + Error err = ERROR_INIT; + Array args = ARRAY_DICT_INIT; + + for (int i = 0; i < nargs; i++) { + lua_pushvalue(lstate, (int)i+3); + ADD(args, nlua_pop_Object(lstate, false, &err)); + if (ERROR_SET(&err)) { + api_free_array(args); + goto check_err; + } + } + + if (request) { + Object result = rpc_send_call(chan_id, name, args, &err); + if (!ERROR_SET(&err)) { + nlua_push_Object(lstate, result, false); + api_free_object(result); + } + } else { + if (!rpc_send_event(chan_id, name, args)) { + api_set_error(&err, kErrorTypeValidation, + "Invalid channel: %"PRIu64, chan_id); + } + } + +check_err: + if (ERROR_SET(&err)) { + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + return lua_error(lstate); + } + + return request ? 1 : 0; +} + +static int nlua_nil_tostring(lua_State *lstate) +{ + lua_pushstring(lstate, "vim.NIL"); + return 1; +} + + #ifdef WIN32 /// os.getenv: override os.getenv to maintain coherency. #9681 /// diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 8d356a5600..32f66b629c 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -12,6 +12,8 @@ // Generated by msgpack-gen.lua void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; +EXTERN LuaRef nlua_nil_ref INIT(= LUA_NOREF); + #define set_api_error(s, err) \ do { \ Error *err_ = (err); \ -- cgit From f59e1f58a2ed24198fb1653edeffb6ab5ec2bbdb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 10 Nov 2019 23:07:36 -0800 Subject: Lua: mark some functions as "private" Problem: scripts/gen_vimdoc.py gets confused and tries to generate docs for `fn_index` and `func`. Solution: Rename them to be private. --- src/nvim/lua/vim.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 1ebdde99d5..2b4c5486c7 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -261,14 +261,14 @@ end -- vim.fn.{func}(...) -local function fn_index(t, key) - local function func(...) +local function _fn_index(t, key) + local function _fn(...) return vim.call(key, ...) end - t[key] = func - return func + t[key] = _fn + return _fn end -local fn = setmetatable({}, {__index=fn_index}) +local fn = setmetatable({}, {__index=_fn_index}) local module = { _update_package_paths = _update_package_paths, -- cgit From 00dc12c5d8454a2d3c6806710f63bbb446076e96 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 13 Nov 2019 12:55:26 -0800 Subject: lua LSP client: initial implementation (#11336) Mainly configuration and RPC infrastructure can be considered "done". Specific requests and their callbacks will be improved later (and also served by plugins). There are also some TODO:s for the client itself, like incremental updates. Co-authored by at-tjdevries and at-h-michael, with many review/suggestion contributions. --- src/nvim/lua/vim.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 2b4c5486c7..ce24d1716d 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -256,6 +256,13 @@ local function __index(t, key) -- Expose all `vim.shared` functions on the `vim` module. t[key] = require('vim.shared')[key] return t[key] + elseif require('vim.uri')[key] ~= nil then + -- Expose all `vim.uri` functions on the `vim` module. + t[key] = require('vim.uri')[key] + return t[key] + elseif key == 'lsp' then + t.lsp = require('vim.lsp') + return t.lsp end end -- cgit From dab40f43b18d35b283804ecb033310198cbf7548 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 30 Oct 2019 20:53:09 +0100 Subject: Add v:lua.func() vimL syntax for calling lua Also simplify error messages when calling lua from vimL. --- src/nvim/lua/executor.c | 114 ++++++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 52 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5e5cb0cea5..093c130c5f 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -48,9 +48,6 @@ typedef struct { # include "lua/executor.c.generated.h" #endif -/// Name of the run code for use in messages -#define NLUA_EVAL_NAME "" - /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. @@ -397,29 +394,6 @@ static lua_State *nlua_enter(void) return lstate; } -/// Execute lua string -/// -/// @param[in] str String to execute. -/// @param[out] ret_tv Location where result will be saved. -/// -/// @return Result of the execution. -void executor_exec_lua(const String str, typval_T *const ret_tv) - FUNC_ATTR_NONNULL_ALL -{ - lua_State *const lstate = nlua_enter(); - - if (luaL_loadbuffer(lstate, str.data, str.size, NLUA_EVAL_NAME)) { - nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s")); - return; - } - if (lua_pcall(lstate, 0, 1, 0)) { - nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s")); - return; - } - - nlua_pop_typval(lstate, ret_tv); -} - static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -732,10 +706,6 @@ void executor_eval_lua(const String str, typval_T *const arg, typval_T *const ret_tv) FUNC_ATTR_NONNULL_ALL { - lua_State *const lstate = nlua_enter(); - - garray_T str_ga; - ga_init(&str_ga, 1, 80); #define EVALHEADER "local _A=select(1,...) return (" const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str.size + 1; char *lcmd; @@ -748,30 +718,71 @@ void executor_eval_lua(const String str, typval_T *const arg, memcpy(lcmd + sizeof(EVALHEADER) - 1, str.data, str.size); lcmd[lcmd_len - 1] = ')'; #undef EVALHEADER - if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { - nlua_error(lstate, - _("E5107: Error while creating lua chunk for luaeval(): %.*s")); - if (lcmd != (char *)IObuff) { - xfree(lcmd); - } - return; - } + typval_exec_lua(lcmd, lcmd_len, "luaeval()", arg, 1, true, ret_tv); + if (lcmd != (char *)IObuff) { xfree(lcmd); } +} - if (arg->v_type == VAR_UNKNOWN) { - lua_pushnil(lstate); +void executor_call_lua(const char *str, size_t len, typval_T *const args, + int argcount, typval_T *ret_tv) + FUNC_ATTR_NONNULL_ALL +{ +#define CALLHEADER "return " +#define CALLSUFFIX "(...)" + const size_t lcmd_len = sizeof(CALLHEADER) - 1 + len + sizeof(CALLSUFFIX) - 1; + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; } else { - nlua_push_typval(lstate, arg, true); + lcmd = xmalloc(lcmd_len); + } + memcpy(lcmd, CALLHEADER, sizeof(CALLHEADER) - 1); + memcpy(lcmd + sizeof(CALLHEADER) - 1, str, len); + memcpy(lcmd + sizeof(CALLHEADER) - 1 + len, CALLSUFFIX, + sizeof(CALLSUFFIX) - 1); +#undef CALLHEADER +#undef CALLSUFFIX + + typval_exec_lua(lcmd, lcmd_len, "v:lua", args, argcount, false, ret_tv); + + if (lcmd != (char *)IObuff) { + xfree(lcmd); + } +} + +static void typval_exec_lua(const char *lcmd, size_t lcmd_len, const char *name, + typval_T *const args, int argcount, bool special, + typval_T *ret_tv) +{ + if (check_restricted() || check_secure()) { + ret_tv->v_type = VAR_NUMBER; + ret_tv->vval.v_number = 0; + return; } - if (lua_pcall(lstate, 1, 1, 0)) { - nlua_error(lstate, - _("E5108: Error while calling lua chunk for luaeval(): %.*s")); + + lua_State *const lstate = nlua_enter(); + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, name)) { + nlua_error(lstate, _("E5107: Error loading lua %.*s")); return; } - nlua_pop_typval(lstate, ret_tv); + for (int i = 0; i < argcount; i++) { + if (args[i].v_type == VAR_UNKNOWN) { + lua_pushnil(lstate); + } else { + nlua_push_typval(lstate, &args[i], special); + } + } + if (lua_pcall(lstate, argcount, ret_tv ? 1 : 0, 0)) { + nlua_error(lstate, _("E5108: Error executing lua %.*s")); + return; + } + + if (ret_tv) { + nlua_pop_typval(lstate, ret_tv); + } } /// Execute lua string @@ -857,9 +868,8 @@ void ex_lua(exarg_T *const eap) xfree(code); return; } - typval_T tv = { .v_type = VAR_UNKNOWN }; - executor_exec_lua((String) { .data = code, .size = len }, &tv); - tv_clear(&tv); + typval_exec_lua(code, len, ":lua", NULL, 0, false, NULL); + xfree(code); } @@ -897,8 +907,8 @@ void ex_luado(exarg_T *const eap) #undef DOSTART #undef DOEND - if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { - nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, ":luado")) { + nlua_error(lstate, _("E5109: Error loading lua: %.*s")); if (lcmd_len >= IOSIZE) { xfree(lcmd); } @@ -908,7 +918,7 @@ void ex_luado(exarg_T *const eap) xfree(lcmd); } if (lua_pcall(lstate, 0, 1, 0)) { - nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); + nlua_error(lstate, _("E5110: Error executing lua: %.*s")); return; } for (linenr_T l = eap->line1; l <= eap->line2; l++) { @@ -919,7 +929,7 @@ void ex_luado(exarg_T *const eap) lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); lua_pushnumber(lstate, (lua_Number)l); if (lua_pcall(lstate, 2, 1, 0)) { - nlua_error(lstate, _("E5111: Error while calling lua function: %.*s")); + nlua_error(lstate, _("E5111: Error calling lua: %.*s")); break; } if (lua_isstring(lstate, -1)) { -- cgit From af53a0c0123338575dd59934449d7fe836835d1c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 17 Nov 2019 19:06:59 -0800 Subject: doc: Lua [ci skip] #11378 - Rework :help lua-commands - Rename if_lua.txt => lua.txt --- src/nvim/lua/vim.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index ce24d1716d..1665a55aff 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -167,8 +167,7 @@ end --- --- Example: To remove ANSI color codes when pasting: ---
---- vim.paste = (function()
----   local overridden = vim.paste
+--- vim.paste = (function(overridden)
 ---   return function(lines, phase)
 ---     for i,line in ipairs(lines) do
 ---       -- Scrub ANSI color codes from paste input.
@@ -176,7 +175,7 @@ end
 ---     end
 ---     overridden(lines, phase)
 ---   end
---- end)()
+--- end)(vim.paste)
 --- 
--- --@see |paste| -- cgit From d0d38fc36e0c1602186aa540417070fa6c1e2746 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 24 Nov 2019 02:28:48 -0800 Subject: Lua: vim.env, vim.{g,v,w,bo,wo} #11442 - Add vim variable meta accessors: vim.env, vim.{g,v,w,bo,wo} - Redo gen_char_blob to generate multiple blobs instead of just one so that multiple Lua modules can be inlined. - Reorder vim.lua inclusion so that it can use previously defined C functions and utility functions like vim.shared and vim.inspect things. - Inline shared.lua into nvim, but also keep it available in runtime. --- src/nvim/lua/executor.c | 25 ++++-- src/nvim/lua/vim.lua | 227 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 172 insertions(+), 80 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 093c130c5f..5450f62f54 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -268,12 +268,7 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL #endif // vim - const char *code = (char *)&vim_module[0]; - if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua") - || lua_pcall(lstate, 0, LUA_MULTRET, 0)) { - nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); - return 1; - } + lua_newtable(lstate); // vim.api nlua_add_api_functions(lstate); // vim.types, vim.type_idx, vim.val_idx @@ -334,6 +329,24 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setglobal(lstate, "vim"); + { + const char *code = (char *)&shared_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@shared.lua") + || lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5106: Error while creating shared module: %.*s")); + return 1; + } + } + + { + const char *code = (char *)&vim_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua") + || lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); + return 1; + } + } + return 0; } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 1665a55aff..8019511317 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -33,35 +33,35 @@ -- - https://github.com/bakpakin/Fennel (pretty print, repl) -- - https://github.com/howl-editor/howl/tree/master/lib/howl/util +local vim = vim +assert(vim) -- Internal-only until comments in #8107 are addressed. -- Returns: -- {errcode}, {output} -local function _system(cmd) - local out = vim.api.nvim_call_function('system', { cmd }) - local err = vim.api.nvim_get_vvar('shell_error') +function vim._system(cmd) + local out = vim.fn.system(cmd) + local err = vim.v.shell_error return err, out end -- Gets process info from the `ps` command. -- Used by nvim_get_proc() as a fallback. -local function _os_proc_info(pid) +function vim._os_proc_info(pid) if pid == nil or pid <= 0 or type(pid) ~= 'number' then error('invalid pid') end local cmd = { 'ps', '-p', pid, '-o', 'comm=', } - local err, name = _system(cmd) - if 1 == err and string.gsub(name, '%s*', '') == '' then + local err, name = vim._system(cmd) + if 1 == err and vim.trim(name) == '' then return {} -- Process not found. elseif 0 ~= err then - local args_str = vim.api.nvim_call_function('string', { cmd }) - error('command failed: '..args_str) + error('command failed: '..vim.fn.string(cmd)) end - local _, ppid = _system({ 'ps', '-p', pid, '-o', 'ppid=', }) + local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=', }) -- Remove trailing whitespace. - name = string.gsub(string.gsub(name, '%s+$', ''), '^.*/', '') - ppid = string.gsub(ppid, '%s+$', '') - ppid = tonumber(ppid) == nil and -1 or tonumber(ppid) + name = vim.trim(name):gsub('^.*/', '') + ppid = tonumber(ppid) or -1 return { name = name, pid = pid, @@ -71,20 +71,19 @@ end -- Gets process children from the `pgrep` command. -- Used by nvim_get_proc_children() as a fallback. -local function _os_proc_children(ppid) +function vim._os_proc_children(ppid) if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then error('invalid ppid') end local cmd = { 'pgrep', '-P', ppid, } - local err, rv = _system(cmd) - if 1 == err and string.gsub(rv, '%s*', '') == '' then + local err, rv = vim._system(cmd) + if 1 == err and vim.trim(rv) == '' then return {} -- Process not found. elseif 0 ~= err then - local args_str = vim.api.nvim_call_function('string', { cmd }) - error('command failed: '..args_str) + error('command failed: '..vim.fn.string(cmd)) end local children = {} - for s in string.gmatch(rv, '%S+') do + for s in rv:gmatch('%S+') do local i = tonumber(s) if i ~= nil then table.insert(children, i) @@ -98,7 +97,7 @@ end -- Last inserted paths. Used to clear out items from package.[c]path when they -- are no longer in &runtimepath. local last_nvim_paths = {} -local function _update_package_paths() +function vim._update_package_paths() local cur_nvim_paths = {} local rtps = vim.api.nvim_list_runtime_paths() local sep = package.config:sub(1, 1) @@ -162,35 +161,35 @@ local function inspect(object, options) -- luacheck: no unused error(object, options) -- Stub for gen_vimdoc.py end ---- Paste handler, invoked by |nvim_paste()| when a conforming UI ---- (such as the |TUI|) pastes text into the editor. ---- ---- Example: To remove ANSI color codes when pasting: ----
---- vim.paste = (function(overridden)
----   return function(lines, phase)
----     for i,line in ipairs(lines) do
----       -- Scrub ANSI color codes from paste input.
----       lines[i] = line:gsub('\27%[[0-9;mK]+', '')
----     end
----     overridden(lines, phase)
----   end
---- end)(vim.paste)
---- 
---- ---@see |paste| ---- ---@param lines |readfile()|-style list of lines to paste. |channel-lines| ---@param phase -1: "non-streaming" paste: the call contains all lines. ---- If paste is "streamed", `phase` indicates the stream state: ---- - 1: starts the paste (exactly once) ---- - 2: continues the paste (zero or more times) ---- - 3: ends the paste (exactly once) ---@returns false if client should cancel the paste. -local function paste(lines, phase) end -- luacheck: no unused -paste = (function() +do local tdots, tick, got_line1 = 0, 0, false - return function(lines, phase) + + --- Paste handler, invoked by |nvim_paste()| when a conforming UI + --- (such as the |TUI|) pastes text into the editor. + --- + --- Example: To remove ANSI color codes when pasting: + ---
+  --- vim.paste = (function(overridden)
+  ---   return function(lines, phase)
+  ---     for i,line in ipairs(lines) do
+  ---       -- Scrub ANSI color codes from paste input.
+  ---       lines[i] = line:gsub('\27%[[0-9;mK]+', '')
+  ---     end
+  ---     overridden(lines, phase)
+  ---   end
+  --- end)(vim.paste)
+  --- 
+ --- + --@see |paste| + --- + --@param lines |readfile()|-style list of lines to paste. |channel-lines| + --@param phase -1: "non-streaming" paste: the call contains all lines. + --- If paste is "streamed", `phase` indicates the stream state: + --- - 1: starts the paste (exactly once) + --- - 2: continues the paste (zero or more times) + --- - 3: ends the paste (exactly once) + --@returns false if client should cancel the paste. + function vim.paste(lines, phase) local call = vim.api.nvim_call_function local now = vim.loop.now() local mode = call('mode', {}):sub(1,1) @@ -230,20 +229,33 @@ paste = (function() end return true -- Paste will not continue if not returning `true`. end -end)() +end --- Defers callback `cb` until the Nvim API is safe to call. --- ---@see |lua-loop-callbacks| ---@see |vim.schedule()| ---@see |vim.in_fast_event()| -local function schedule_wrap(cb) +function vim.schedule_wrap(cb) return (function (...) local args = {...} vim.schedule(function() cb(unpack(args)) end) end) end +-- vim.fn.{func}(...) +vim.fn = setmetatable({}, { + __index = function(t, key) + local function _fn(...) + return vim.call(key, ...) + end + t[key] = _fn + return _fn + end +}) + +-- These are for loading runtime modules lazily since they aren't available in +-- the nvim binary as specified in executor.c local function __index(t, key) if key == 'inspect' then t.inspect = require('vim.inspect') @@ -251,10 +263,6 @@ local function __index(t, key) elseif key == 'treesitter' then t.treesitter = require('vim.treesitter') return t.treesitter - elseif require('vim.shared')[key] ~= nil then - -- Expose all `vim.shared` functions on the `vim` module. - t[key] = require('vim.shared')[key] - return t[key] elseif require('vim.uri')[key] ~= nil then -- Expose all `vim.uri` functions on the `vim` module. t[key] = require('vim.uri')[key] @@ -265,29 +273,100 @@ local function __index(t, key) end end +setmetatable(vim, { + __index = __index +}) --- vim.fn.{func}(...) -local function _fn_index(t, key) - local function _fn(...) - return vim.call(key, ...) +do + local a = vim.api + local validate = vim.validate + local function make_meta_accessor(get, set, del) + validate { + get = {get, 'f'}; + set = {set, 'f'}; + del = {del, 'f', true}; + } + local mt = {} + if del then + function mt:__newindex(k, v) + if v == nil then + return del(k) + end + return set(k, v) + end + else + function mt:__newindex(k, v) + return set(k, v) + end + end + function mt:__index(k) + return get(k) + end + return setmetatable({}, mt) + end + local function pcall_ret(status, ...) + if status then return ... end + end + local function nil_wrap(fn) + return function(...) + return pcall_ret(pcall(fn, ...)) + end + end + vim.g = make_meta_accessor(nil_wrap(a.nvim_get_var), a.nvim_set_var, a.nvim_del_var) + vim.v = make_meta_accessor(nil_wrap(a.nvim_get_vvar), a.nvim_set_vvar) + vim.o = make_meta_accessor(nil_wrap(a.nvim_get_option), a.nvim_set_option) + vim.env = make_meta_accessor(vim.fn.getenv, vim.fn.setenv) + -- TODO(ashkan) if/when these are available from an API, generate them + -- instead of hardcoding. + local window_options = { + arab = true; arabic = true; breakindent = true; breakindentopt = true; + bri = true; briopt = true; cc = true; cocu = true; + cole = true; colorcolumn = true; concealcursor = true; conceallevel = true; + crb = true; cuc = true; cul = true; cursorbind = true; + cursorcolumn = true; cursorline = true; diff = true; fcs = true; + fdc = true; fde = true; fdi = true; fdl = true; + fdm = true; fdn = true; fdt = true; fen = true; + fillchars = true; fml = true; fmr = true; foldcolumn = true; + foldenable = true; foldexpr = true; foldignore = true; foldlevel = true; + foldmarker = true; foldmethod = true; foldminlines = true; foldnestmax = true; + foldtext = true; lbr = true; lcs = true; linebreak = true; + list = true; listchars = true; nu = true; number = true; + numberwidth = true; nuw = true; previewwindow = true; pvw = true; + relativenumber = true; rightleft = true; rightleftcmd = true; rl = true; + rlc = true; rnu = true; scb = true; scl = true; + scr = true; scroll = true; scrollbind = true; signcolumn = true; + spell = true; statusline = true; stl = true; wfh = true; + wfw = true; winbl = true; winblend = true; winfixheight = true; + winfixwidth = true; winhighlight = true; winhl = true; wrap = true; + } + local function new_buf_opt_accessor(bufnr) + local function get(k) + if window_options[k] then + return a.nvim_err_writeln(k.." is a window option, not a buffer option") + end + return a.nvim_buf_get_option(bufnr, k) + end + local function set(k, v) + if window_options[k] then + return a.nvim_err_writeln(k.." is a window option, not a buffer option") + end + return a.nvim_buf_set_option(bufnr, k, v) + end + return make_meta_accessor(nil_wrap(get), set) + end + vim.bo = new_buf_opt_accessor(0) + getmetatable(vim.bo).__call = function(_, bufnr) + return new_buf_opt_accessor(bufnr) + end + local function new_win_opt_accessor(winnr) + local function get(k) return a.nvim_win_get_option(winnr, k) end + local function set(k, v) return a.nvim_win_set_option(winnr, k, v) end + return make_meta_accessor(nil_wrap(get), set) + end + vim.wo = new_win_opt_accessor(0) + getmetatable(vim.wo).__call = function(_, winnr) + return new_win_opt_accessor(winnr) end - t[key] = _fn - return _fn end -local fn = setmetatable({}, {__index=_fn_index}) - -local module = { - _update_package_paths = _update_package_paths, - _os_proc_children = _os_proc_children, - _os_proc_info = _os_proc_info, - _system = _system, - paste = paste, - schedule_wrap = schedule_wrap, - fn=fn, -} - -setmetatable(module, { - __index = __index -}) return module -- cgit From a76a669ac24ec91144153b65e0a0dc5598802653 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 26 Nov 2019 17:57:53 +0100 Subject: lua: make vim.wo and vim.bo used nested indexing for specified handle Also missing option should be an error. Options are functionality, not arbitrary variable names (as for vim.g) --- src/nvim/lua/vim.lua | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 8019511317..e13b44a8ed 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -314,7 +314,7 @@ do end vim.g = make_meta_accessor(nil_wrap(a.nvim_get_var), a.nvim_set_var, a.nvim_del_var) vim.v = make_meta_accessor(nil_wrap(a.nvim_get_vvar), a.nvim_set_vvar) - vim.o = make_meta_accessor(nil_wrap(a.nvim_get_option), a.nvim_set_option) + vim.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option) vim.env = make_meta_accessor(vim.fn.getenv, vim.fn.setenv) -- TODO(ashkan) if/when these are available from an API, generate them -- instead of hardcoding. @@ -344,29 +344,31 @@ do if window_options[k] then return a.nvim_err_writeln(k.." is a window option, not a buffer option") end - return a.nvim_buf_get_option(bufnr, k) + if bufnr == nil and type(k) == "number" then + return new_buf_opt_accessor(k) + end + return a.nvim_buf_get_option(bufnr or 0, k) end local function set(k, v) if window_options[k] then return a.nvim_err_writeln(k.." is a window option, not a buffer option") end - return a.nvim_buf_set_option(bufnr, k, v) + return a.nvim_buf_set_option(bufnr or 0, k, v) end - return make_meta_accessor(nil_wrap(get), set) - end - vim.bo = new_buf_opt_accessor(0) - getmetatable(vim.bo).__call = function(_, bufnr) - return new_buf_opt_accessor(bufnr) + return make_meta_accessor(get, set) end + vim.bo = new_buf_opt_accessor(nil) local function new_win_opt_accessor(winnr) - local function get(k) return a.nvim_win_get_option(winnr, k) end - local function set(k, v) return a.nvim_win_set_option(winnr, k, v) end - return make_meta_accessor(nil_wrap(get), set) - end - vim.wo = new_win_opt_accessor(0) - getmetatable(vim.wo).__call = function(_, winnr) - return new_win_opt_accessor(winnr) + local function get(k) + if winnr == nil and type(k) == "number" then + return new_win_opt_accessor(k) + end + return a.nvim_win_get_option(winnr or nil, k) + end + local function set(k, v) return a.nvim_win_set_option(winnr or nil, k, v) end + return make_meta_accessor(get, set) end + vim.wo = new_win_opt_accessor(nil) end return module -- cgit From 001e69cd4602e84219fd7cfd8ade62f0cb24097c Mon Sep 17 00:00:00 2001 From: Brian Wignall Date: Tue, 26 Nov 2019 07:15:14 -0500 Subject: doc: fix typos close #11459 --- src/nvim/lua/converter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 44fe60e9c8..09d1a68898 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1225,7 +1225,7 @@ GENERATE_INDEX_FUNCTION(Tabpage) #undef GENERATE_INDEX_FUNCTION -/// Record some auxilary values in vim module +/// Record some auxiliary values in vim module /// /// Assumes that module table is on top of the stack. /// -- cgit From edca84cfc91e637e97fcadd484a57911b20cfe75 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 1 Dec 2019 05:04:57 -0800 Subject: Return nil instead of NIL for vim.env (#11486) --- src/nvim/lua/vim.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index e13b44a8ed..00f7c8a48b 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -315,7 +315,14 @@ do vim.g = make_meta_accessor(nil_wrap(a.nvim_get_var), a.nvim_set_var, a.nvim_del_var) vim.v = make_meta_accessor(nil_wrap(a.nvim_get_vvar), a.nvim_set_vvar) vim.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option) - vim.env = make_meta_accessor(vim.fn.getenv, vim.fn.setenv) + local function getenv(k) + local v = vim.fn.getenv(k) + if v == vim.NIL then + return nil + end + return v + end + vim.env = make_meta_accessor(getenv, vim.fn.setenv) -- TODO(ashkan) if/when these are available from an API, generate them -- instead of hardcoding. local window_options = { -- cgit From e6da21d12895e2f34c6ce41bb16400d5eef3ea12 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 1 Dec 2019 05:28:53 -0800 Subject: Add vim.cmd as an alias for nvim_command (#11446) --- src/nvim/lua/vim.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 00f7c8a48b..090869c68e 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -277,6 +277,10 @@ setmetatable(vim, { __index = __index }) +-- An easier alias for commands. +vim.cmd = vim.api.nvim_command + +-- These are the vim.env/v/g/o/bo/wo variable magic accessors. do local a = vim.api local validate = vim.validate -- cgit From a3b6c2a3dc5576db45fe4e893cfb8482af591c92 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 2 Dec 2019 00:46:46 -0800 Subject: API: rename nvim_execute_lua => nvim_exec_lua - We already find ourselves renaming nvim_execute_lua in tests and scripts, which suggests "exec" is the verb we actually want. - Add "exec" verb to `:help dev-api`. --- src/nvim/lua/executor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5450f62f54..25f4be1c4d 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -798,9 +798,9 @@ static void typval_exec_lua(const char *lcmd, size_t lcmd_len, const char *name, } } -/// Execute lua string +/// Execute Lua string /// -/// Used for nvim_execute_lua(). +/// Used for nvim_exec_lua(). /// /// @param[in] str String to execute. /// @param[in] args array of ... args -- cgit From 0e6c6261e1b5a518995f53ca3898a68b9bb02012 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sat, 7 Dec 2019 03:34:02 -0800 Subject: Fix access on vim.wo (#11517) * Add more tests for vim.wo --- src/nvim/lua/vim.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 090869c68e..e7c5458102 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -374,9 +374,9 @@ do if winnr == nil and type(k) == "number" then return new_win_opt_accessor(k) end - return a.nvim_win_get_option(winnr or nil, k) + return a.nvim_win_get_option(winnr or 0, k) end - local function set(k, v) return a.nvim_win_set_option(winnr or nil, k, v) end + local function set(k, v) return a.nvim_win_set_option(winnr or 0, k, v) end return make_meta_accessor(get, set) end vim.wo = new_win_opt_accessor(nil) -- cgit