mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 19:38:20 +00:00
feat(treesitter): improve query error message
This commit is contained in:

committed by
Lewis Russell

parent
4afd33e69f
commit
845d5b8b64
@@ -135,6 +135,7 @@ The following new APIs and features were added.
|
|||||||
or the parent LanguageTree's language, respectively.
|
or the parent LanguageTree's language, respectively.
|
||||||
• Added `vim.treesitter.preview_query()`, for live editing of treesitter
|
• Added `vim.treesitter.preview_query()`, for live editing of treesitter
|
||||||
queries.
|
queries.
|
||||||
|
• Improved error messages for query parsing.
|
||||||
|
|
||||||
• |vim.ui.open()| opens URIs using the system default handler (macOS `open`,
|
• |vim.ui.open()| opens URIs using the system default handler (macOS `open`,
|
||||||
Windows `explorer`, Linux `xdg-open`, etc.)
|
Windows `explorer`, Linux `xdg-open`, etc.)
|
||||||
|
@@ -1543,8 +1543,9 @@ int tslua_parse_query(lua_State *L)
|
|||||||
TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type);
|
TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type);
|
||||||
|
|
||||||
if (!query) {
|
if (!query) {
|
||||||
return luaL_error(L, "query: %s at position %d for language %s",
|
char err_msg[IOSIZE];
|
||||||
query_err_string(error_type), (int)error_offset, lang_name);
|
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]
|
TSQuery **ud = lua_newuserdata(L, sizeof(TSQuery *)); // [udata]
|
||||||
@@ -1554,24 +1555,85 @@ int tslua_parse_query(lua_State *L)
|
|||||||
return 1;
|
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:
|
case TSQueryErrorSyntax:
|
||||||
return "invalid syntax";
|
return "Invalid syntax:\n";
|
||||||
case TSQueryErrorNodeType:
|
case TSQueryErrorNodeType:
|
||||||
return "invalid node type";
|
return "Invalid node type ";
|
||||||
case TSQueryErrorField:
|
case TSQueryErrorField:
|
||||||
return "invalid field";
|
return "Invalid field name ";
|
||||||
case TSQueryErrorCapture:
|
case TSQueryErrorCapture:
|
||||||
return "invalid capture";
|
return "Invalid capture name ";
|
||||||
case TSQueryErrorStructure:
|
case TSQueryErrorStructure:
|
||||||
return "invalid structure";
|
return "Impossible pattern:\n";
|
||||||
default:
|
default:
|
||||||
return "error";
|
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;
|
||||||
|
const char *src_tmp = src;
|
||||||
|
while ((end_str = strchr(src_tmp, '\n')) != NULL) {
|
||||||
|
int line_length = (int)(end_str - src_tmp) + 1;
|
||||||
|
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;
|
||||||
|
row++;
|
||||||
|
src_tmp += line_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional check for the last line
|
||||||
|
if (line_start <= error_offset) {
|
||||||
|
error_line = src_tmp;
|
||||||
|
error_line_len = (int)strlen(src_tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
static TSQuery *query_check(lua_State *L, int index)
|
||||||
{
|
{
|
||||||
TSQuery **ud = luaL_checkudata(L, index, TS_META_QUERY);
|
TSQuery **ud = luaL_checkudata(L, index, TS_META_QUERY);
|
||||||
|
@@ -54,7 +54,7 @@ describe('print', function()
|
|||||||
-- TODO(bfredl): these look weird, print() should not use "E5114:" style errors..
|
-- TODO(bfredl): these look weird, print() should not use "E5114:" style errors..
|
||||||
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: [NULL]',
|
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: [NULL]',
|
||||||
pcall_err(command, 'lua print("foo", v_nilerr, "bar")'))
|
pcall_err(command, 'lua print("foo", v_nilerr, "bar")'))
|
||||||
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:0: abc',
|
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
|
||||||
pcall_err(command, 'lua print("foo", v_abcerr, "bar")'))
|
pcall_err(command, 'lua print("foo", v_abcerr, "bar")'))
|
||||||
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
|
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
|
||||||
pcall_err(command, 'lua print("foo", v_tblout, "bar")'))
|
pcall_err(command, 'lua print("foo", v_tblout, "bar")'))
|
||||||
@@ -84,9 +84,9 @@ describe('print', function()
|
|||||||
end
|
end
|
||||||
]])
|
]])
|
||||||
eq('', exec_capture('luafile ' .. fname))
|
eq('', exec_capture('luafile ' .. fname))
|
||||||
eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: my mistake',
|
eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:1: my mistake',
|
||||||
pcall_err(command, 'lua string_error()'))
|
pcall_err(command, 'lua string_error()'))
|
||||||
eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: 1234',
|
eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:2: 1234',
|
||||||
pcall_err(command, 'lua number_error()'))
|
pcall_err(command, 'lua number_error()'))
|
||||||
eq('Vim(lua):E5108: Error executing lua [NULL]',
|
eq('Vim(lua):E5108: Error executing lua [NULL]',
|
||||||
pcall_err(command, 'lua nil_error()'))
|
pcall_err(command, 'lua nil_error()'))
|
||||||
|
@@ -1055,4 +1055,44 @@ int x = INT_MAX;
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('fails to load queries', function()
|
||||||
|
local function test(exp, cquery)
|
||||||
|
eq(exp, pcall_err(exec_lua, "vim.treesitter.query.parse('c', ...)", cquery))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Invalid node type
|
||||||
|
test(
|
||||||
|
'.../query.lua:0: Query error at 1:2. Invalid node type "dentifier":\n'..
|
||||||
|
'(dentifier) @variable\n'..
|
||||||
|
' ^',
|
||||||
|
'(dentifier) @variable')
|
||||||
|
|
||||||
|
-- Impossible pattern
|
||||||
|
test(
|
||||||
|
'.../query.lua:0: Query error at 1:13. Impossible pattern:\n'..
|
||||||
|
'(identifier (identifier) @variable)\n'..
|
||||||
|
' ^',
|
||||||
|
'(identifier (identifier) @variable)')
|
||||||
|
|
||||||
|
-- Invalid syntax
|
||||||
|
test(
|
||||||
|
'.../query.lua:0: Query error at 1:13. Invalid syntax:\n'..
|
||||||
|
'(identifier @variable\n'..
|
||||||
|
' ^',
|
||||||
|
'(identifier @variable')
|
||||||
|
|
||||||
|
-- Invalid field name
|
||||||
|
test(
|
||||||
|
'.../query.lua:0: Query error at 1:15. Invalid field name "invalid_field":\n'..
|
||||||
|
'((identifier) invalid_field: (identifier))\n'..
|
||||||
|
' ^',
|
||||||
|
'((identifier) invalid_field: (identifier))')
|
||||||
|
|
||||||
|
-- Invalid capture name
|
||||||
|
test(
|
||||||
|
'.../query.lua:0: Query error at 1:30. Invalid capture name "ok.capture":\n'..
|
||||||
|
'((identifier) @id (#eq? @id @ok.capture))\n'..
|
||||||
|
' ^',
|
||||||
|
'((identifier) @id (#eq? @id @ok.capture))')
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@@ -188,10 +188,16 @@ function module.pcall(fn, ...)
|
|||||||
local errmsg = tostring(rv):gsub('([%s<])vim[/\\]([^%s:/\\]+):%d+', '%1\xffvim\xff%2:0')
|
local errmsg = tostring(rv):gsub('([%s<])vim[/\\]([^%s:/\\]+):%d+', '%1\xffvim\xff%2:0')
|
||||||
:gsub('[^%s<]-[/\\]([^%s:/\\]+):%d+', '.../%1:0')
|
:gsub('[^%s<]-[/\\]([^%s:/\\]+):%d+', '.../%1:0')
|
||||||
:gsub('\xffvim\xff', 'vim/')
|
:gsub('\xffvim\xff', 'vim/')
|
||||||
|
|
||||||
-- Scrub numbers in paths/stacktraces:
|
-- Scrub numbers in paths/stacktraces:
|
||||||
-- shared.lua:0: in function 'gsplit'
|
-- shared.lua:0: in function 'gsplit'
|
||||||
-- shared.lua:0: in function <shared.lua:0>'
|
-- shared.lua:0: in function <shared.lua:0>'
|
||||||
errmsg = errmsg:gsub('([^%s]):%d+', '%1:0')
|
errmsg = errmsg:gsub('([^%s].lua):%d+', '%1:0')
|
||||||
|
-- [string "<nvim>"]:0:
|
||||||
|
-- [string ":lua"]:0:
|
||||||
|
-- [string ":luado"]:0:
|
||||||
|
errmsg = errmsg:gsub('(%[string "[^"]+"%]):%d+', '%1:0')
|
||||||
|
|
||||||
-- Scrub tab chars:
|
-- Scrub tab chars:
|
||||||
errmsg = errmsg:gsub('\t', ' ')
|
errmsg = errmsg:gsub('\t', ' ')
|
||||||
-- In Lua 5.1, we sometimes get a "(tail call): ?" on the last line.
|
-- In Lua 5.1, we sometimes get a "(tail call): ?" on the last line.
|
||||||
|
Reference in New Issue
Block a user