mirror of
https://github.com/nvim-treesitter/nvim-treesitter.git
synced 2026-07-02 11:36:54 -04:00
refacto/feat: better handling of parser updates
features:
- node_movement is moving between scopes.
- add selection initialization from normal mode
- add a decremental selection
improvements:
- attach to buffer to run tree parsing on change
- run state update on CursorMoved
- the buffer state is:
```
{
cursor_pos = { row=row, col=col },
current_node = node_under_cursor,
selection = {
range = nil, -- activates when starting a selection
nodes = {} -- filling up when starting an incremental selection
},
parser = parser, -- parser for current buffer
}
```
- refacto all the modules reliant on parsing the tree, update the current nodes, get the current nodes...
fixes:
- fix has_parser to look for .so libraries
- fix should select the whole file when selection root in selection
This commit is contained in:
parent
307c78aa1e
commit
45dcebb15f
14 changed files with 555 additions and 333 deletions
|
|
@ -1,69 +1,10 @@
|
|||
-- Utils collection for nvim-treesitter
|
||||
local api = vim.api
|
||||
local parsers = require'nvim-treesitter.parsers'
|
||||
local locals = require'nvim-treesitter.locals'
|
||||
local fn = vim.fn
|
||||
local luv = vim.loop
|
||||
local ts = vim.treesitter
|
||||
|
||||
local M = {}
|
||||
|
||||
--- Gets the smallest expression containing the current cursor position
|
||||
function M.expression_at_point(bufnr, lang)
|
||||
local bufnr = bufnr or api.nvim_get_current_buf()
|
||||
local lang = lang or api.nvim_buf_get_option(bufnr, 'ft')
|
||||
|
||||
local parser = parsers.get_parser(bufnr, lang)
|
||||
if not parser then return end
|
||||
|
||||
local tsroot = parser:parse():root()
|
||||
if not tsroot then return end
|
||||
|
||||
local curwin = api.nvim_get_current_win()
|
||||
|
||||
if api.nvim_win_get_buf(curwin) == bufnr then
|
||||
local cursor = vim.api.nvim_win_get_cursor(curwin)
|
||||
local current_node = tsroot:named_descendant_for_range(cursor[1] - 1, cursor[2], cursor[1] - 1, cursor[2])
|
||||
return current_node
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the actual text content of a node
|
||||
-- @param node the node to get the text from
|
||||
-- @param bufnr the buffer containing the node
|
||||
-- @return list of lines of text of the node
|
||||
function M.get_node_text(node, bufnr)
|
||||
local bufnr = bufnr or api.nvim_get_current_buf()
|
||||
if not node then return {} end
|
||||
|
||||
-- We have to remember that end_col is end-exclusive
|
||||
local start_row, start_col, end_row, end_col = node:range()
|
||||
if start_row ~= end_row then
|
||||
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row+1, false)
|
||||
lines[1] = string.sub(lines[1], start_col+1)
|
||||
lines[#lines] = string.sub(lines[#lines], 1, end_col)
|
||||
return lines
|
||||
else
|
||||
local line = api.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1]
|
||||
return { string.sub(line, start_col+1, end_col) }
|
||||
end
|
||||
end
|
||||
|
||||
--- Determines wether a node is the parent of another
|
||||
-- @param dest the possible parent
|
||||
-- @param source the possible child node
|
||||
function M.is_parent(dest, source)
|
||||
if not (dest and source) then return false end
|
||||
|
||||
local current = source
|
||||
while current ~= nil do
|
||||
if current == dest then
|
||||
return true
|
||||
end
|
||||
|
||||
current = current:parent()
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function M.setup_commands(mod, commands)
|
||||
for command_name, def in pairs(commands) do
|
||||
local call_fn = string.format("lua require'nvim-treesitter.%s'.commands.%s.run(<f-args>)", mod, command_name)
|
||||
|
|
@ -77,79 +18,50 @@ function M.setup_commands(mod, commands)
|
|||
end
|
||||
end
|
||||
|
||||
--- Gets the smallest scope which contains @param node
|
||||
function M.smallest_containing_scope(node, bufnr)
|
||||
local bufnr = bufnr or api.nvim_get_current_buf()
|
||||
|
||||
local root = parsers.get_parser(bufnr):parse():root()
|
||||
if not node then return root end
|
||||
|
||||
local scopes = locals.get_scopes(bufnr)
|
||||
local current = node
|
||||
while current ~= nil and not vim.tbl_contains(scopes, current) do
|
||||
current = current:parent()
|
||||
function M.get_package_path()
|
||||
for _, path in pairs(api.nvim_list_runtime_paths()) do
|
||||
if string.match(path, '.*/nvim%-treesitter') then
|
||||
return path
|
||||
end
|
||||
end
|
||||
|
||||
return current or root
|
||||
return nil, 'Plugin runtime path not found.'
|
||||
end
|
||||
|
||||
--- Get next node with same parent
|
||||
-- @param node node
|
||||
-- @param allow_switch_parents allow switching parents if last node
|
||||
-- @param allow_next_parent allow next parent if last node and next parent without children
|
||||
function M.get_next_node(node, allow_switch_parents, allow_next_parent)
|
||||
local destination_node
|
||||
local parent = node:parent()
|
||||
function M.get_cache_dir()
|
||||
local home = fn.get(fn.environ(), 'HOME')
|
||||
local xdg_cache = fn.get(fn.environ(), 'XDG_CACHE_HOME')
|
||||
|
||||
if parent then
|
||||
local found_pos = 0
|
||||
for i = 0,parent:named_child_count()-1,1 do
|
||||
if parent:named_child(i) == node then
|
||||
found_pos = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if parent:named_child_count() > found_pos + 1 then
|
||||
destination_node = parent:named_child(found_pos + 1)
|
||||
elseif allow_switch_parents then
|
||||
local next_node = M.get_next_node(node:parent())
|
||||
if next_node and next_node:named_child_count() > 0 then
|
||||
destination_node = next_node:named_child(0)
|
||||
elseif next_node and allow_next_parent then
|
||||
destination_node = next_node
|
||||
end
|
||||
end
|
||||
if xdg_cache == 0 then
|
||||
xdg_cache = home .. '/.cache'
|
||||
end
|
||||
return destination_node
|
||||
|
||||
if luv.fs_access(xdg_cache, 'RW') then
|
||||
return xdg_cache
|
||||
elseif luv.fs_access('/tmp', 'RW') then
|
||||
return '/tmp'
|
||||
end
|
||||
|
||||
return nil, 'Invalid cache rights, $XDG_CACHE_HOME or /tmp should be read/write'
|
||||
end
|
||||
|
||||
--- Get previous node with same parent
|
||||
-- @param node node
|
||||
-- @param allow_switch_parents allow switching parents if first node
|
||||
-- @param allow_previous_parent allow previous parent if first node and previous parent without children
|
||||
function M.get_previous_node(node, allow_switch_parents, allow_previous_parent)
|
||||
local destination_node
|
||||
local parent = node:parent()
|
||||
if parent then
|
||||
local found_pos = 0
|
||||
for i = 0,parent:named_child_count()-1,1 do
|
||||
if parent:named_child(i) == node then
|
||||
found_pos = i
|
||||
break
|
||||
end
|
||||
function M.has_parser(lang)
|
||||
local lang = lang or api.nvim_buf_get_option(0, 'filetype')
|
||||
return #api.nvim_get_runtime_file('parser/' .. lang .. '.so', false) > 0
|
||||
end
|
||||
|
||||
function M.get_parser(bufnr, lang)
|
||||
if M.has_parser() then
|
||||
local buf = bufnr or api.nvim_get_current_buf()
|
||||
local lang = lang or api.nvim_buf_get_option(buf, 'ft')
|
||||
if not M[buf] then
|
||||
M[buf] = {}
|
||||
end
|
||||
if 0 < found_pos then
|
||||
destination_node = parent:named_child(found_pos - 1)
|
||||
elseif allow_switch_parents then
|
||||
local previous_node = M.get_previous_node(node:parent())
|
||||
if previous_node and previous_node:named_child_count() > 0 then
|
||||
destination_node = previous_node:named_child(previous_node:named_child_count() - 1)
|
||||
elseif previous_node and allow_previous_parent then
|
||||
destination_node = previous_node
|
||||
end
|
||||
end
|
||||
if not M[buf][lang] then
|
||||
M[buf][lang] = ts.get_parser(buf, lang)
|
||||
end
|
||||
return M[buf][lang]
|
||||
end
|
||||
return destination_node
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue