aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/lua/treesitter.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
commit1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch)
treecd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/lua/treesitter.c
parenteaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.gz
rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.bz2
rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.zip
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/lua/treesitter.c')
-rw-r--r--src/nvim/lua/treesitter.c517
1 files changed, 416 insertions, 101 deletions
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 56f4daed1a..008b3f2e95 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -1,11 +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-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 <assert.h>
+#include <ctype.h>
#include <lauxlib.h>
#include <limits.h>
#include <lua.h>
@@ -14,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <tree_sitter/api.h>
#include <uv.h>
#include "klib/kvec.h"
@@ -21,14 +20,13 @@
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
#include "nvim/lua/treesitter.h"
-#include "nvim/macros.h"
-#include "nvim/map.h"
+#include "nvim/macros_defs.h"
+#include "nvim/map_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/pos.h"
+#include "nvim/pos_defs.h"
#include "nvim/strings.h"
-#include "nvim/types.h"
-#include "tree_sitter/api.h"
+#include "nvim/types_defs.h"
#define TS_META_PARSER "treesitter_parser"
#define TS_META_TREE "treesitter_tree"
@@ -43,6 +41,17 @@ typedef struct {
int max_match_id;
} TSLua_cursor;
+typedef struct {
+ LuaRef cb;
+ lua_State *lstate;
+ bool lex;
+ bool parse;
+} TSLuaLoggerOpts;
+
+typedef struct {
+ TSTree *tree;
+} TSLuaTree;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/treesitter.c.generated.h"
#endif
@@ -51,8 +60,13 @@ static struct luaL_Reg parser_meta[] = {
{ "__gc", parser_gc },
{ "__tostring", parser_tostring },
{ "parse", parser_parse },
+ { "reset", parser_reset },
{ "set_included_ranges", parser_set_ranges },
{ "included_ranges", parser_get_ranges },
+ { "set_timeout", parser_set_timeout },
+ { "timeout", parser_get_timeout },
+ { "_set_logger", parser_set_logger },
+ { "_logger", parser_get_logger },
{ NULL, NULL }
};
@@ -61,6 +75,7 @@ static struct luaL_Reg tree_meta[] = {
{ "__tostring", tree_tostring },
{ "root", tree_root },
{ "edit", tree_edit },
+ { "included_ranges", tree_get_ranges },
{ "copy", tree_copy },
{ NULL, NULL }
};
@@ -78,6 +93,8 @@ static struct luaL_Reg node_meta[] = {
{ "field", node_field },
{ "named", node_named },
{ "missing", node_missing },
+ { "extra", node_extra },
+ { "has_changes", node_has_changes },
{ "has_error", node_has_error },
{ "sexpr", node_sexpr },
{ "child_count", node_child_count },
@@ -95,7 +112,9 @@ static struct luaL_Reg node_meta[] = {
{ "prev_named_sibling", node_prev_named_sibling },
{ "named_children", node_named_children },
{ "root", node_root },
+ { "tree", node_tree },
{ "byte_length", node_byte_length },
+ { "equal", node_equal },
{ NULL, NULL }
};
@@ -132,9 +151,9 @@ static 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
+/// Init the tslua library.
///
-/// all global state is stored in the regirstry of the lua_State
+/// All global state is stored in the registry of the lua_State.
void tslua_init(lua_State *L)
{
// type metatables
@@ -145,15 +164,13 @@ void tslua_init(lua_State *L)
build_meta(L, TS_META_QUERYCURSOR, querycursor_meta);
build_meta(L, TS_META_TREECURSOR, treecursor_meta);
-#ifdef NVIM_TS_HAS_SET_ALLOCATOR
ts_set_allocator(xmalloc, xcalloc, xrealloc, xfree);
-#endif
}
int tslua_has_language(lua_State *L)
{
const char *lang_name = luaL_checkstring(L, 1);
- lua_pushboolean(L, pmap_has(cstr_t)(&langs, lang_name));
+ lua_pushboolean(L, map_has(cstr_t, &langs, lang_name));
return 1;
}
@@ -170,7 +187,7 @@ int tslua_add_language(lua_State *L)
symbol_name = luaL_checkstring(L, 3);
}
- if (pmap_has(cstr_t)(&langs, lang_name)) {
+ if (map_has(cstr_t, &langs, lang_name)) {
lua_pushboolean(L, true);
return 1;
}
@@ -223,11 +240,11 @@ int tslua_add_language(lua_State *L)
int tslua_remove_lang(lua_State *L)
{
const char *lang_name = luaL_checkstring(L, 1);
- bool present = pmap_has(cstr_t)(&langs, lang_name);
+ bool present = map_has(cstr_t, &langs, lang_name);
if (present) {
- char *key = (char *)pmap_key(cstr_t)(&langs, lang_name);
- pmap_del(cstr_t)(&langs, lang_name);
- xfree(key);
+ cstr_t key;
+ pmap_del(cstr_t)(&langs, lang_name, &key);
+ xfree((void *)key);
}
lua_pushboolean(L, present);
return 1;
@@ -309,6 +326,17 @@ static TSParser **parser_check(lua_State *L, uint16_t index)
return luaL_checkudata(L, index, TS_META_PARSER);
}
+static void logger_gc(TSLogger logger)
+{
+ if (!logger.log) {
+ return;
+ }
+
+ TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload;
+ luaL_unref(opts->lstate, LUA_REGISTRYINDEX, opts->cb);
+ xfree(opts);
+}
+
static int parser_gc(lua_State *L)
{
TSParser **p = parser_check(L, 1);
@@ -316,6 +344,7 @@ static int parser_gc(lua_State *L)
return 0;
}
+ logger_gc(ts_parser_logger(*p));
ts_parser_delete(*p);
return 0;
}
@@ -329,7 +358,7 @@ static int parser_tostring(lua_State *L)
static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position,
uint32_t *bytes_read)
{
- buf_T *bp = payload;
+ buf_T *bp = payload;
#define BUFSIZE 256
static char buf[BUFSIZE];
@@ -337,7 +366,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position
*bytes_read = 0;
return "";
}
- char *line = ml_get_buf(bp, (linenr_T)position.row + 1, false);
+ char *line = ml_get_buf(bp, (linenr_T)position.row + 1);
size_t len = strlen(line);
if (position.column > len) {
*bytes_read = 0;
@@ -359,19 +388,29 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position
#undef BUFSIZE
}
-static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length)
+static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length,
+ bool include_bytes)
{
lua_createtable(L, (int)length, 0);
for (size_t i = 0; i < length; i++) {
- lua_createtable(L, 4, 0);
+ lua_createtable(L, include_bytes ? 6 : 4, 0);
+ int j = 1;
lua_pushinteger(L, ranges[i].start_point.row);
- lua_rawseti(L, -2, 1);
+ lua_rawseti(L, -2, j++);
lua_pushinteger(L, ranges[i].start_point.column);
- lua_rawseti(L, -2, 2);
+ lua_rawseti(L, -2, j++);
+ if (include_bytes) {
+ lua_pushinteger(L, ranges[i].start_byte);
+ lua_rawseti(L, -2, j++);
+ }
lua_pushinteger(L, ranges[i].end_point.row);
- lua_rawseti(L, -2, 3);
+ lua_rawseti(L, -2, j++);
lua_pushinteger(L, ranges[i].end_point.column);
- lua_rawseti(L, -2, 4);
+ lua_rawseti(L, -2, j++);
+ if (include_bytes) {
+ lua_pushinteger(L, ranges[i].end_byte);
+ lua_rawseti(L, -2, j++);
+ }
lua_rawseti(L, -2, (int)(i + 1));
}
@@ -386,14 +425,14 @@ static int parser_parse(lua_State *L)
TSTree *old_tree = NULL;
if (!lua_isnil(L, 2)) {
- TSTree **tmp = tree_check(L, 2);
- old_tree = tmp ? *tmp : NULL;
+ TSLuaTree *ud = tree_check(L, 2);
+ old_tree = ud ? ud->tree : NULL;
}
TSTree *new_tree = NULL;
size_t len;
const char *str;
- long bufnr;
+ handle_T bufnr;
buf_T *buf;
TSInput input;
@@ -406,13 +445,13 @@ static int parser_parse(lua_State *L)
break;
case LUA_TNUMBER:
- bufnr = lua_tointeger(L, 3);
- buf = handle_get_buffer((handle_T)bufnr);
+ bufnr = (handle_T)lua_tointeger(L, 3);
+ buf = handle_get_buffer(bufnr);
if (!buf) {
#define BUFSIZE 256
char ebuf[BUFSIZE] = { 0 };
- vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %ld", bufnr);
+ vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %d", bufnr);
return luaL_argerror(L, 3, ebuf);
#undef BUFSIZE
}
@@ -426,34 +465,46 @@ static int parser_parse(lua_State *L)
return luaL_argerror(L, 3, "expected either string or buffer handle");
}
+ bool include_bytes = (lua_gettop(L) >= 4) && lua_toboolean(L, 4);
+
// Sometimes parsing fails (timeout, or wrong parser ABI)
// In those case, just return an error.
if (!new_tree) {
return luaL_error(L, "An error occurred when parsing.");
}
- // The new tree will be pushed to the stack, without copy, ownership is now to
- // the lua GC.
- // Old tree is still owned by the lua GC.
+ // The new tree will be pushed to the stack, without copy, ownership is now to the lua GC.
+ // Old tree is owned by lua GC since before
uint32_t n_ranges = 0;
- TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL;
+ TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL;
- push_tree(L, new_tree, false); // [tree]
+ push_tree(L, new_tree); // [tree]
- push_ranges(L, changed, n_ranges); // [tree, ranges]
+ push_ranges(L, changed, n_ranges, include_bytes); // [tree, ranges]
xfree(changed);
return 2;
}
+static int parser_reset(lua_State *L)
+{
+ TSParser **p = parser_check(L, 1);
+ if (p && *p) {
+ ts_parser_reset(*p);
+ }
+
+ return 0;
+}
+
static int tree_copy(lua_State *L)
{
- TSTree **tree = tree_check(L, 1);
- if (!(*tree)) {
+ TSLuaTree *ud = tree_check(L, 1);
+ if (!ud) {
return 0;
}
- push_tree(L, *tree, true); // [tree]
+ TSTree *copy = ts_tree_copy(ud->tree);
+ push_tree(L, copy); // [tree]
return 1;
}
@@ -465,8 +516,8 @@ static int tree_edit(lua_State *L)
return lua_error(L);
}
- TSTree **tree = tree_check(L, 1);
- if (!(*tree)) {
+ TSLuaTree *ud = tree_check(L, 1);
+ if (!ud) {
return 0;
}
@@ -480,11 +531,29 @@ static int tree_edit(lua_State *L)
TSInputEdit edit = { start_byte, old_end_byte, new_end_byte,
start_point, old_end_point, new_end_point };
- ts_tree_edit(*tree, &edit);
+ ts_tree_edit(ud->tree, &edit);
return 0;
}
+static int tree_get_ranges(lua_State *L)
+{
+ TSLuaTree *ud = tree_check(L, 1);
+ if (!ud) {
+ return 0;
+ }
+
+ bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2);
+
+ uint32_t len;
+ TSRange *ranges = ts_tree_included_ranges(ud->tree, &len);
+
+ push_ranges(L, ranges, len, include_bytes);
+
+ xfree(ranges);
+ return 1;
+}
+
// Use the top of the stack (without popping it) to create a TSRange, it can be
// either a lua table or a TSNode
static void range_from_lua(lua_State *L, TSRange *range)
@@ -590,58 +659,159 @@ static int parser_get_ranges(lua_State *L)
return 0;
}
+ bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2);
+
uint32_t len;
const TSRange *ranges = ts_parser_included_ranges(*p, &len);
- push_ranges(L, ranges, len);
+ push_ranges(L, ranges, len, include_bytes);
+ return 1;
+}
+
+static int parser_set_timeout(lua_State *L)
+{
+ TSParser **p = parser_check(L, 1);
+ if (!p) {
+ return 0;
+ }
+
+ if (lua_gettop(L) < 2) {
+ luaL_error(L, "integer expected");
+ }
+
+ uint32_t timeout = (uint32_t)luaL_checkinteger(L, 2);
+ ts_parser_set_timeout_micros(*p, timeout);
+ return 0;
+}
+
+static int parser_get_timeout(lua_State *L)
+{
+ TSParser **p = parser_check(L, 1);
+ if (!p) {
+ return 0;
+ }
+
+ lua_pushinteger(L, (lua_Integer)ts_parser_timeout_micros(*p));
+ return 1;
+}
+
+static void logger_cb(void *payload, TSLogType logtype, const char *s)
+{
+ TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)payload;
+ if ((!opts->lex && logtype == TSLogTypeLex)
+ || (!opts->parse && logtype == TSLogTypeParse)) {
+ return;
+ }
+
+ lua_State *lstate = opts->lstate;
+
+ lua_rawgeti(lstate, LUA_REGISTRYINDEX, opts->cb);
+ lua_pushstring(lstate, logtype == TSLogTypeParse ? "parse" : "lex");
+ lua_pushstring(lstate, s);
+ if (lua_pcall(lstate, 2, 0, 0)) {
+ luaL_error(lstate, "Error executing treesitter logger callback");
+ }
+}
+
+static int parser_set_logger(lua_State *L)
+{
+ TSParser **p = parser_check(L, 1);
+ if (!p) {
+ return 0;
+ }
+
+ if (!lua_isboolean(L, 2)) {
+ return luaL_argerror(L, 2, "boolean expected");
+ }
+
+ if (!lua_isboolean(L, 3)) {
+ return luaL_argerror(L, 3, "boolean expected");
+ }
+
+ if (!lua_isfunction(L, 4)) {
+ return luaL_argerror(L, 4, "function expected");
+ }
+
+ TSLuaLoggerOpts *opts = xmalloc(sizeof(TSLuaLoggerOpts));
+ lua_pushvalue(L, 4);
+ LuaRef ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ *opts = (TSLuaLoggerOpts){
+ .lex = lua_toboolean(L, 2),
+ .parse = lua_toboolean(L, 3),
+ .cb = ref,
+ .lstate = L
+ };
+
+ TSLogger logger = {
+ .payload = (void *)opts,
+ .log = logger_cb
+ };
+
+ ts_parser_set_logger(*p, logger);
+ return 0;
+}
+
+static int parser_get_logger(lua_State *L)
+{
+ TSParser **p = parser_check(L, 1);
+ if (!p) {
+ return 0;
+ }
+
+ TSLogger logger = ts_parser_logger(*p);
+ if (logger.log) {
+ TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, opts->cb);
+ } else {
+ lua_pushnil(L);
+ }
+
return 1;
}
// Tree methods
-/// push tree interface on lua stack.
+/// Push tree interface on to the lua stack.
///
-/// This makes a copy of the tree, so ownership of the argument is unaffected.
-void push_tree(lua_State *L, TSTree *tree, bool do_copy)
+/// The tree is not copied. Ownership of the tree is transferred from C to
+/// Lua. If needed use ts_tree_copy() in the caller
+static void push_tree(lua_State *L, TSTree *tree)
{
if (tree == NULL) {
lua_pushnil(L);
return;
}
- TSTree **ud = lua_newuserdata(L, sizeof(TSTree *)); // [udata]
- if (do_copy) {
- *ud = ts_tree_copy(tree);
- } else {
- *ud = tree;
- }
+ TSLuaTree *ud = lua_newuserdata(L, sizeof(TSLuaTree)); // [udata]
+
+ ud->tree = tree;
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_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.
+ // To prevent the tree from being garbage collected, create a reference to it
+ // in the fenv which will be passed to userdata nodes of the tree.
+ // Note: environments (fenvs) associated with userdata have no meaning in Lua
+ // and are only used to associate 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]
}
-static TSTree **tree_check(lua_State *L, int index)
+static TSLuaTree *tree_check(lua_State *L, int index)
{
- TSTree **ud = luaL_checkudata(L, index, TS_META_TREE);
+ TSLuaTree *ud = luaL_checkudata(L, index, TS_META_TREE);
return ud;
}
static int tree_gc(lua_State *L)
{
- TSTree **tree = tree_check(L, 1);
- if (!tree) {
- return 0;
+ TSLuaTree *ud = tree_check(L, 1);
+ if (ud) {
+ ts_tree_delete(ud->tree);
}
-
- ts_tree_delete(*tree);
return 0;
}
@@ -653,20 +823,20 @@ static int tree_tostring(lua_State *L)
static int tree_root(lua_State *L)
{
- TSTree **tree = tree_check(L, 1);
- if (!tree) {
+ TSLuaTree *ud = tree_check(L, 1);
+ if (!ud) {
return 0;
}
- TSNode root = ts_tree_root_node(*tree);
+ TSNode root = ts_tree_root_node(ud->tree);
push_node(L, root, 1);
return 1;
}
// Node methods
-/// push node interface on lua stack
+/// Push node interface on to the Lua stack
///
-/// top of stack must either be the tree this node belongs to or another node
+/// 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, int uindex)
@@ -680,6 +850,8 @@ static void push_node(lua_State *L, TSNode node, int uindex)
*ud = node;
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_NODE); // [udata, meta]
lua_setmetatable(L, -2); // [udata]
+
+ // Copy the fenv which contains the nodes tree.
lua_getfenv(L, uindex); // [udata, reftable]
lua_setfenv(L, -2); // [udata]
}
@@ -740,12 +912,26 @@ static int node_range(lua_State *L)
if (!node_check(L, 1, &node)) {
return 0;
}
+
+ bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2);
+
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);
+
+ if (include_bytes) {
+ lua_pushinteger(L, start.row);
+ lua_pushinteger(L, start.column);
+ lua_pushinteger(L, ts_node_start_byte(node));
+ lua_pushinteger(L, end.row);
+ lua_pushinteger(L, end.column);
+ lua_pushinteger(L, ts_node_end_byte(node));
+ return 6;
+ }
+
+ lua_pushinteger(L, start.row);
+ lua_pushinteger(L, start.column);
+ lua_pushinteger(L, end.row);
+ lua_pushinteger(L, end.column);
return 4;
}
@@ -757,9 +943,9 @@ static int node_start(lua_State *L)
}
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);
+ lua_pushinteger(L, start.row);
+ lua_pushinteger(L, start.column);
+ lua_pushinteger(L, start_byte);
return 3;
}
@@ -771,9 +957,9 @@ static int node_end(lua_State *L)
}
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);
+ lua_pushinteger(L, end.row);
+ lua_pushinteger(L, end.column);
+ lua_pushinteger(L, end_byte);
return 3;
}
@@ -784,7 +970,7 @@ static int node_child_count(lua_State *L)
return 0;
}
uint32_t count = ts_node_child_count(node);
- lua_pushnumber(L, count);
+ lua_pushinteger(L, count);
return 1;
}
@@ -795,7 +981,7 @@ static int node_named_child_count(lua_State *L)
return 0;
}
uint32_t count = ts_node_named_child_count(node);
- lua_pushnumber(L, count);
+ lua_pushinteger(L, count);
return 1;
}
@@ -816,7 +1002,7 @@ static int node_symbol(lua_State *L)
return 0;
}
TSSymbol symbol = ts_node_symbol(node);
- lua_pushnumber(L, symbol);
+ lua_pushinteger(L, symbol);
return 1;
}
@@ -882,6 +1068,26 @@ static int node_missing(lua_State *L)
return 1;
}
+static int node_extra(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+ lua_pushboolean(L, ts_node_is_extra(node));
+ return 1;
+}
+
+static int node_has_changes(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+ lua_pushboolean(L, ts_node_has_changes(node));
+ return 1;
+}
+
static int node_has_error(lua_State *L)
{
TSNode node;
@@ -898,8 +1104,8 @@ static int node_child(lua_State *L)
if (!node_check(L, 1, &node)) {
return 0;
}
- long num = lua_tointeger(L, 2);
- TSNode child = ts_node_child(node, (uint32_t)num);
+ uint32_t num = (uint32_t)lua_tointeger(L, 2);
+ TSNode child = ts_node_child(node, num);
push_node(L, child, 1);
return 1;
@@ -911,8 +1117,8 @@ static int node_named_child(lua_State *L)
if (!node_check(L, 1, &node)) {
return 0;
}
- long num = lua_tointeger(L, 2);
- TSNode child = ts_node_named_child(node, (uint32_t)num);
+ uint32_t num = (uint32_t)lua_tointeger(L, 2);
+ TSNode child = ts_node_named_child(node, num);
push_node(L, child, 1);
return 1;
@@ -1108,6 +1314,19 @@ static int node_root(lua_State *L)
return 1;
}
+static int node_tree(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+
+ lua_getfenv(L, 1); // [udata, reftable]
+ lua_rawgeti(L, -1, 1); // [udata, reftable, tree_udata]
+
+ return 1;
+}
+
static int node_byte_length(lua_State *L)
{
TSNode node;
@@ -1118,7 +1337,23 @@ static int node_byte_length(lua_State *L)
uint32_t start_byte = ts_node_start_byte(node);
uint32_t end_byte = ts_node_end_byte(node);
- lua_pushnumber(L, end_byte - start_byte);
+ lua_pushinteger(L, end_byte - start_byte);
+ return 1;
+}
+
+static int node_equal(lua_State *L)
+{
+ TSNode node1;
+ if (!node_check(L, 1, &node1)) {
+ return 0;
+ }
+
+ TSNode node2;
+ if (!node_check(L, 2, &node2)) {
+ return luaL_error(L, "TSNode expected");
+ }
+
+ lua_pushboolean(L, ts_node_eq(node1, node2));
return 1;
}
@@ -1213,11 +1448,12 @@ static int node_rawquery(lua_State *L)
} else {
cursor = ts_query_cursor_new();
}
- // TODO(clason): API introduced after tree-sitter release 0.19.5
- // remove guard when minimum ts version is bumped to 0.19.6+
-#ifdef NVIM_TS_HAS_SET_MATCH_LIMIT
- ts_query_cursor_set_match_limit(cursor, 64);
+
+#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH
+ // reset the start depth
+ ts_query_cursor_set_max_start_depth(cursor, UINT32_MAX);
#endif
+ ts_query_cursor_set_match_limit(cursor, 256);
ts_query_cursor_exec(cursor, query, node);
bool captures = lua_toboolean(L, 3);
@@ -1228,6 +1464,29 @@ static int node_rawquery(lua_State *L)
ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 });
}
+ if (lua_gettop(L) >= 6 && !lua_isnil(L, 6)) {
+ if (!lua_istable(L, 6)) {
+ return luaL_error(L, "table expected");
+ }
+ lua_pushnil(L);
+ // stack: [dict, ..., nil]
+ while (lua_next(L, 6)) {
+ // stack: [dict, ..., key, value]
+ if (lua_type(L, -2) == LUA_TSTRING) {
+ char *k = (char *)lua_tostring(L, -2);
+ if (strequal("max_start_depth", k)) {
+ // TODO(lewis6991): remove ifdef when min TS version is 0.20.9
+#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH
+ uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1);
+ ts_query_cursor_set_max_start_depth(cursor, max_start_depth);
+#endif
+ }
+ }
+ lua_pop(L, 1); // pop the value; lua_next will pop the key.
+ // stack: [dict, ..., key]
+ }
+ }
+
TSLua_cursor *ud = lua_newuserdata(L, sizeof(*ud)); // [udata]
ud->cursor = cursor;
ud->predicated_match = -1;
@@ -1281,8 +1540,9 @@ int tslua_parse_query(lua_State *L)
TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type);
if (!query) {
- return luaL_error(L, "query: %s at position %d for language %s",
- query_err_string(error_type), (int)error_offset, lang_name);
+ char err_msg[IOSIZE];
+ query_err_string(src, (int)error_offset, error_type, err_msg, sizeof(err_msg));
+ return luaL_error(L, "%s", err_msg);
}
TSQuery **ud = lua_newuserdata(L, sizeof(TSQuery *)); // [udata]
@@ -1292,24 +1552,79 @@ int tslua_parse_query(lua_State *L)
return 1;
}
-static const char *query_err_string(TSQueryError err)
+static const char *query_err_to_string(TSQueryError error_type)
{
- switch (err) {
+ switch (error_type) {
case TSQueryErrorSyntax:
- return "invalid syntax";
+ return "Invalid syntax:\n";
case TSQueryErrorNodeType:
- return "invalid node type";
+ return "Invalid node type ";
case TSQueryErrorField:
- return "invalid field";
+ return "Invalid field name ";
case TSQueryErrorCapture:
- return "invalid capture";
+ return "Invalid capture name ";
case TSQueryErrorStructure:
- return "invalid structure";
+ return "Impossible pattern:\n";
default:
return "error";
}
}
+static void query_err_string(const char *src, int error_offset, TSQueryError error_type, char *err,
+ size_t errlen)
+{
+ int line_start = 0;
+ int row = 0;
+ const char *error_line = NULL;
+ int error_line_len = 0;
+
+ const char *end_str;
+ do {
+ const char *src_tmp = src + line_start;
+ end_str = strchr(src_tmp, '\n');
+ int line_length = end_str != NULL ? (int)(end_str - src_tmp) : (int)strlen(src_tmp);
+ int line_end = line_start + line_length;
+ if (line_end > error_offset) {
+ error_line = src_tmp;
+ error_line_len = line_length;
+ break;
+ }
+ line_start = line_end + 1;
+ row++;
+ } while (end_str != NULL);
+
+ int column = error_offset - line_start;
+
+ const char *type_msg = query_err_to_string(error_type);
+ snprintf(err, errlen, "Query error at %d:%d. %s", row + 1, column + 1, type_msg);
+ size_t offset = strlen(err);
+ errlen = errlen - offset;
+ err = err + offset;
+
+ // Error types that report names
+ if (error_type == TSQueryErrorNodeType
+ || error_type == TSQueryErrorField
+ || error_type == TSQueryErrorCapture) {
+ const char *suffix = src + error_offset;
+ int suffix_len = 0;
+ char c = suffix[suffix_len];
+ while (isalnum(c) || c == '_' || c == '-' || c == '.') {
+ c = suffix[++suffix_len];
+ }
+ snprintf(err, errlen, "\"%.*s\":\n", suffix_len, suffix);
+ offset = strlen(err);
+ errlen = errlen - offset;
+ err = err + offset;
+ }
+
+ if (!error_line) {
+ snprintf(err, errlen, "Unexpected EOF\n");
+ return;
+ }
+
+ snprintf(err, errlen, "%.*s\n%*s^\n", error_line_len, error_line, column, "");
+}
+
static TSQuery *query_check(lua_State *L, int index)
{
TSQuery **ud = luaL_checkudata(L, index, TS_META_QUERY);
@@ -1367,7 +1682,7 @@ static int query_inspect(lua_State *L)
&strlen);
lua_pushlstring(L, str, strlen); // [retval, patterns, pat, pred, item]
} else if (step[k].type == TSQueryPredicateStepTypeCapture) {
- lua_pushnumber(L, step[k].value_id + 1); // [..., pat, pred, item]
+ lua_pushinteger(L, step[k].value_id + 1); // [..., pat, pred, item]
} else {
abort();
}