mirror of
https://github.com/neovim/neovim.git
synced 2025-09-12 22:38:16 +00:00
tree-sitter: initial tree-sitter support
This commit is contained in:
@@ -405,6 +405,11 @@ if(MSGPACK_HAS_FLOAT32)
|
|||||||
add_definitions(-DNVIM_MSGPACK_HAS_FLOAT32)
|
add_definitions(-DNVIM_MSGPACK_HAS_FLOAT32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(TREESITTER_PATH "${DEPS_BUILD_DIR}/build/src/treesitter")
|
||||||
|
set(TREESITTER_C_PATH "${DEPS_BUILD_DIR}/build/src/treesitter-c")
|
||||||
|
set(TREESITTER_JAVASCRIPT_PATH "${DEPS_BUILD_DIR}/build/src/treesitter-javascript")
|
||||||
|
set(LIBUTF8PROC_INCLUDE_DIRS ${DEPS_BUILD_DIR}/build/src/utf8proc)
|
||||||
|
|
||||||
option(FEAT_TUI "Enable the Terminal UI" ON)
|
option(FEAT_TUI "Enable the Terminal UI" ON)
|
||||||
|
|
||||||
if(FEAT_TUI)
|
if(FEAT_TUI)
|
||||||
|
119
runtime/lua/treesitter_rt.lua
Normal file
119
runtime/lua/treesitter_rt.lua
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
local a = vim.api
|
||||||
|
|
||||||
|
if __treesitter_rt_ns == nil then
|
||||||
|
__treesitter_rt_ns = a.nvim_buf_add_highlight(0, 0, "", 0, 0, 0)
|
||||||
|
__treesitter_rt_syn_ns = a.nvim_buf_add_highlight(0, 0, "", 0, 0, 0)
|
||||||
|
end
|
||||||
|
local my_ns = __treesitter_rt_ns
|
||||||
|
local my_syn_ns = __treesitter_rt_syn_ns
|
||||||
|
|
||||||
|
local path = '.deps/build/src/treesitter-javascript/src/highlights.json'
|
||||||
|
a.nvim_set_var("_ts_path", path)
|
||||||
|
obj = a.nvim_eval("json_decode(readfile(g:_ts_path,'b'))")
|
||||||
|
|
||||||
|
|
||||||
|
--luadev = require'luadev'
|
||||||
|
--i = require'inspect'
|
||||||
|
|
||||||
|
|
||||||
|
function parse_tree(tsstate, force)
|
||||||
|
if tsstate.valid and not force then
|
||||||
|
return tsstate.tree
|
||||||
|
end
|
||||||
|
tsstate.tree = tsstate.parser:parse_buf(tsstate.bufnr)
|
||||||
|
tsstate.valid = true
|
||||||
|
return tsstate.tree
|
||||||
|
end
|
||||||
|
|
||||||
|
function the_cb(tsstate, ev, bufnr, tick, start_row, oldstopline, stop_row)
|
||||||
|
local start_byte = a.nvim_buf_get_offset(bufnr,start_row)
|
||||||
|
-- a bit messy, should we expose edited but not reparsed tree?
|
||||||
|
-- are multiple edits safe in general?
|
||||||
|
local root = tsstate.parser:tree():root()
|
||||||
|
-- TODO: add proper lookup function!
|
||||||
|
local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0)
|
||||||
|
local edit
|
||||||
|
if inode == nil then
|
||||||
|
local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row)
|
||||||
|
tsstate.parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0)
|
||||||
|
else
|
||||||
|
local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start()
|
||||||
|
local fake_rows = fakeoldstoprow-oldstopline
|
||||||
|
local fakestop = stop_row+fake_rows
|
||||||
|
local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol
|
||||||
|
tsstate.parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol)
|
||||||
|
end
|
||||||
|
tsstate.valid = false
|
||||||
|
--luadev.append_buf({i{edit.start_byte,edit.old_end_byte,edit.new_end_byte},
|
||||||
|
-- i{edit.start_point, edit.old_end_point, edit.new_end_point}})
|
||||||
|
end
|
||||||
|
|
||||||
|
function attach_buf(tsstate)
|
||||||
|
local function cb(ev, ...)
|
||||||
|
return the_cb(tsstate, ev, ...)
|
||||||
|
end
|
||||||
|
a.nvim_buf_attach(tsstate.bufnr, false, {on_lines=cb})
|
||||||
|
end
|
||||||
|
|
||||||
|
function create_parser(bufnr)
|
||||||
|
if bufnr == 0 then
|
||||||
|
bufnr = a.nvim_get_current_buf()
|
||||||
|
end
|
||||||
|
local ft = a.nvim_buf_get_option(bufnr, "filetype")
|
||||||
|
local tsstate = {}
|
||||||
|
tsstate.bufnr = bufnr
|
||||||
|
tsstate.parser = vim.ts_parser(ft)
|
||||||
|
parse_tree(tsstate)
|
||||||
|
attach_buf(tsstate)
|
||||||
|
return tsstate
|
||||||
|
end
|
||||||
|
|
||||||
|
function ts_inspect_pos(row,col)
|
||||||
|
local tree = parse_tree(theparser)
|
||||||
|
local root = tree:root()
|
||||||
|
local node = root:descendant_for_point_range(row,col,row,col)
|
||||||
|
show_node(node)
|
||||||
|
end
|
||||||
|
|
||||||
|
function show_node(node)
|
||||||
|
if node == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
a.nvim_buf_clear_highlight(0, my_ns, 0, -1)
|
||||||
|
shown_node = node
|
||||||
|
print(node:type())
|
||||||
|
local start_row, start_col, end_row, end_col = node:range()
|
||||||
|
|
||||||
|
a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", start_row, start_col, start_col+1)
|
||||||
|
|
||||||
|
if end_col >= 1 then
|
||||||
|
end_col = end_col - 1
|
||||||
|
end
|
||||||
|
a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", end_row, end_col, end_col+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ts_expand_node()
|
||||||
|
if shown_node == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
parent = shown_node:parent()
|
||||||
|
show_node(parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ts_cursor()
|
||||||
|
local row, col = unpack(a.nvim_win_get_cursor(0))
|
||||||
|
ts_inspect_pos(row-1, col)
|
||||||
|
end
|
||||||
|
|
||||||
|
if false then
|
||||||
|
ctree = theparser.tree
|
||||||
|
root = ctree:root()
|
||||||
|
cursor = root:to_cursor()
|
||||||
|
node = cursor:forward(5000) if true then return node end
|
||||||
|
print(#root)
|
||||||
|
c = root:child(50)
|
||||||
|
print(require'inspect'{c:extent()})
|
||||||
|
type(ctree.__tostring)
|
||||||
|
root:__tostring()
|
||||||
|
print(_tslua_debug())
|
||||||
|
end
|
32
runtime/plugin/ts_test.vim
Normal file
32
runtime/plugin/ts_test.vim
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
let g:ts_test_path = expand("<sfile>:p:h:h")
|
||||||
|
let g:has_ts = v:false
|
||||||
|
|
||||||
|
func! TSTest()
|
||||||
|
if g:has_ts
|
||||||
|
return
|
||||||
|
end
|
||||||
|
" TODO: module!
|
||||||
|
lua require'treesitter_rt'
|
||||||
|
lua theparser = create_parser(vim.api.nvim_get_current_buf())
|
||||||
|
let g:has_ts = v:true
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! TSCursor()
|
||||||
|
" disable matchparen
|
||||||
|
NoMatchParen
|
||||||
|
call TSTest()
|
||||||
|
au CursorMoved <buffer> lua ts_cursor()
|
||||||
|
au CursorMovedI <buffer> lua ts_cursor()
|
||||||
|
map <buffer> <Plug>(ts-expand) <cmd>lua ts_expand_node()<cr>
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! TSSyntax()
|
||||||
|
" disable matchparen
|
||||||
|
set syntax=
|
||||||
|
call TSTest()
|
||||||
|
lua ts_syntax()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
command! TSTest call TSTest()
|
||||||
|
command! TSCursor call TSCursor()
|
||||||
|
command! TSSyntax call TSSyntax()
|
@@ -74,6 +74,8 @@ include_directories(${GENERATED_DIR})
|
|||||||
include_directories(${CACHED_GENERATED_DIR})
|
include_directories(${CACHED_GENERATED_DIR})
|
||||||
include_directories(${GENERATED_INCLUDES_DIR})
|
include_directories(${GENERATED_INCLUDES_DIR})
|
||||||
|
|
||||||
|
include_directories(${LIBUTF8PROC_INCLUDE_DIRS})
|
||||||
|
|
||||||
file(MAKE_DIRECTORY ${TOUCHES_DIR})
|
file(MAKE_DIRECTORY ${TOUCHES_DIR})
|
||||||
file(MAKE_DIRECTORY ${GENERATED_DIR})
|
file(MAKE_DIRECTORY ${GENERATED_DIR})
|
||||||
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
|
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
|
||||||
@@ -85,6 +87,10 @@ file(GLOB NVIM_HEADERS *.h)
|
|||||||
file(GLOB XDIFF_SOURCES xdiff/*.c)
|
file(GLOB XDIFF_SOURCES xdiff/*.c)
|
||||||
file(GLOB XDIFF_HEADERS xdiff/*.h)
|
file(GLOB XDIFF_HEADERS xdiff/*.h)
|
||||||
|
|
||||||
|
# when LIBUTF8PROC build is fixed, don't use lib.c with amalgamated utf8proc.c
|
||||||
|
#file(GLOB TS_SOURCES tree_sitter/*.c)
|
||||||
|
file(GLOB TS_SOURCES ../tree_sitter/lib.c)
|
||||||
|
|
||||||
foreach(subdir
|
foreach(subdir
|
||||||
os
|
os
|
||||||
api
|
api
|
||||||
@@ -141,6 +147,7 @@ set(CONV_SOURCES
|
|||||||
ex_cmds.c
|
ex_cmds.c
|
||||||
ex_docmd.c
|
ex_docmd.c
|
||||||
fileio.c
|
fileio.c
|
||||||
|
lua/tree_sitter.c
|
||||||
mbyte.c
|
mbyte.c
|
||||||
memline.c
|
memline.c
|
||||||
message.c
|
message.c
|
||||||
@@ -158,7 +165,7 @@ foreach(sfile ${CONV_SOURCES})
|
|||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
# xdiff: inlined external project, we don't maintain it. #9306
|
# xdiff: inlined external project, we don't maintain it. #9306
|
||||||
list(APPEND CONV_SOURCES ${XDIFF_SOURCES})
|
list(APPEND CONV_SOURCES ${XDIFF_SOURCES} ${TS_SOURCES})
|
||||||
|
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
set_source_files_properties(
|
set_source_files_properties(
|
||||||
@@ -414,7 +421,7 @@ endif()
|
|||||||
|
|
||||||
add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
||||||
${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}
|
${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}
|
||||||
${XDIFF_SOURCES} ${XDIFF_HEADERS})
|
${XDIFF_SOURCES} ${XDIFF_HEADERS} ${TS_SOURCES})
|
||||||
target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES})
|
target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES})
|
||||||
install_helper(TARGETS nvim)
|
install_helper(TARGETS nvim)
|
||||||
|
|
||||||
@@ -500,7 +507,7 @@ add_library(
|
|||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
|
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
|
||||||
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
||||||
${XDIFF_SOURCES} ${XDIFF_HEADERS}
|
${XDIFF_SOURCES} ${XDIFF_HEADERS} ${TS_SOURCES}
|
||||||
)
|
)
|
||||||
set_property(TARGET libnvim APPEND PROPERTY
|
set_property(TARGET libnvim APPEND PROPERTY
|
||||||
INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS})
|
INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS})
|
||||||
@@ -525,7 +532,7 @@ else()
|
|||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
|
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
|
||||||
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
||||||
${XDIFF_SOURCES} ${XDIFF_HEADERS}
|
${XDIFF_SOURCES} ${XDIFF_HEADERS} ${TS_SOURCES}
|
||||||
${UNIT_TEST_FIXTURES}
|
${UNIT_TEST_FIXTURES}
|
||||||
)
|
)
|
||||||
target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES})
|
target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES})
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "nvim/lua/executor.h"
|
#include "nvim/lua/executor.h"
|
||||||
#include "nvim/lua/converter.h"
|
#include "nvim/lua/converter.h"
|
||||||
|
#include "nvim/lua/tree_sitter.h"
|
||||||
|
|
||||||
#include "luv/luv.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_setfield(lstate, -2, "luv");
|
||||||
lua_pop(lstate, 3);
|
lua_pop(lstate, 3);
|
||||||
|
|
||||||
|
// internal vim._treesitter... API
|
||||||
|
nlua_add_treesitter(lstate);
|
||||||
|
|
||||||
lua_setglobal(lstate, "vim");
|
lua_setglobal(lstate, "vim");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,3 +821,40 @@ void ex_luafile(exarg_T *const eap)
|
|||||||
return;
|
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");
|
||||||
|
}
|
||||||
|
472
src/nvim/lua/tree_sitter.c
Normal file
472
src/nvim/lua/tree_sitter.c
Normal file
@@ -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 <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#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, "<parser>");
|
||||||
|
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, "<tree>");
|
||||||
|
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, "<node ");
|
||||||
|
lua_pushstring(L, ts_node_type(node));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
10
src/nvim/lua/tree_sitter.h
Normal file
10
src/nvim/lua/tree_sitter.h
Normal file
@@ -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
|
6
third-party/CMakeLists.txt
vendored
6
third-party/CMakeLists.txt
vendored
@@ -195,6 +195,9 @@ set(GETTEXT_SHA256 ff942af0e438ced4a8b0ea4b0b6e0d6d657157c5e2364de57baa279c1c125
|
|||||||
set(LIBICONV_URL https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz)
|
set(LIBICONV_URL https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz)
|
||||||
set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178)
|
set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178)
|
||||||
|
|
||||||
|
set(UTF8PROC_URL https://github.com/JuliaStrings/utf8proc/archive/v2.2.0.tar.gz)
|
||||||
|
set(UTF8PROC_SHA256 3f8fd1dbdb057ee5ba584a539d5cd1b3952141c0338557cb0bdf8cb9cfed5dbf)
|
||||||
|
|
||||||
if(USE_BUNDLED_UNIBILIUM)
|
if(USE_BUNDLED_UNIBILIUM)
|
||||||
include(BuildUnibilium)
|
include(BuildUnibilium)
|
||||||
endif()
|
endif()
|
||||||
@@ -246,6 +249,9 @@ if(USE_BUNDLED_LIBICONV)
|
|||||||
include(BuildLibiconv)
|
include(BuildLibiconv)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# TODO: build it as a normal lib, so it can be used as a distro lib when available
|
||||||
|
include(BuildUtf8proc)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
include(GetBinaryDeps)
|
include(GetBinaryDeps)
|
||||||
|
|
||||||
|
16
third-party/cmake/BuildUtf8proc.cmake
vendored
Normal file
16
third-party/cmake/BuildUtf8proc.cmake
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
ExternalProject_Add(utf8proc
|
||||||
|
PREFIX ${DEPS_BUILD_DIR}
|
||||||
|
URL ${UTF8PROC_URL}
|
||||||
|
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/utf8proc
|
||||||
|
DOWNLOAD_COMMAND ${CMAKE_COMMAND}
|
||||||
|
-DPREFIX=${DEPS_BUILD_DIR}
|
||||||
|
-DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/utf8proc
|
||||||
|
-DURL=${UTF8PROC_URL}
|
||||||
|
-DEXPECTED_SHA256=${UTF8PROC_SHA256}
|
||||||
|
-DTARGET=utf8proc
|
||||||
|
-DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
|
||||||
|
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
|
||||||
|
CONFIGURE_COMMAND true
|
||||||
|
BUILD_COMMAND true
|
||||||
|
INSTALL_COMMAND true
|
||||||
|
)
|
Reference in New Issue
Block a user