summaryrefslogtreecommitdiff
path: root/lua/nvim-treesitter/ts_utils.lua
diff options
context:
space:
mode:
authorkiyan42 <yazdani.kiyan@protonmail.com>2020-05-08 11:22:59 +0200
committerkiyan42 <yazdani.kiyan@protonmail.com>2020-05-12 16:16:48 +0200
commit45dcebb15f1a954eba2bcb3ae4c1a03f710a1f2a (patch)
tree50cbca8e9615d69a8ff9aca6b33a35abb32f9363 /lua/nvim-treesitter/ts_utils.lua
parent307c78aa1e2cc5e499469fe892108b7fcf6cdb5e (diff)
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
Diffstat (limited to 'lua/nvim-treesitter/ts_utils.lua')
-rw-r--r--lua/nvim-treesitter/ts_utils.lua208
1 files changed, 208 insertions, 0 deletions
diff --git a/lua/nvim-treesitter/ts_utils.lua b/lua/nvim-treesitter/ts_utils.lua
new file mode 100644
index 00000000..1445418e
--- /dev/null
+++ b/lua/nvim-treesitter/ts_utils.lua
@@ -0,0 +1,208 @@
+local api = vim.api
+
+local locals = require'nvim-treesitter.locals'
+
+local M = {}
+
+--- 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
+
+--- 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()
+
+ if not parent then return end
+ 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
+
+ return destination_node
+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 not parent then return end
+
+ 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 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
+ return destination_node
+end
+
+function M.parent_scope(node, cursor_pos)
+ local bufnr = api.nvim_get_current_buf()
+
+ local scopes = locals.get_scopes(bufnr)
+ if not node or not scopes then return end
+
+ local row = cursor_pos.row
+ local col = cursor_pos.col
+ local iter_node = node
+
+ while iter_node ~= nil do
+ local row_, col_ = iter_node:start()
+ if vim.tbl_contains(scopes, iter_node) and (row_+1 ~= row or col_ ~= col) then
+ return iter_node
+ end
+ iter_node = iter_node:parent()
+ end
+end
+
+function M.containing_scope(node)
+ local bufnr = api.nvim_get_current_buf()
+
+ local scopes = locals.get_scopes(bufnr)
+ if not node or not scopes then return end
+
+ local iter_node = node
+
+ while iter_node ~= nil and not vim.tbl_contains(scopes, iter_node) do
+ iter_node = iter_node:parent()
+ end
+
+ return iter_node or node
+end
+
+function M.get_named_children(node)
+ local nodes = {}
+ for i=0,node:named_child_count() - 1,1 do
+ nodes[i+1] = node:named_child(i)
+ end
+ return nodes
+end
+
+function M.nested_scope(node, cursor_pos)
+ local bufnr = api.nvim_get_current_buf()
+
+ local scopes = locals.get_scopes(bufnr)
+ if not node or not scopes then return end
+
+ local row = cursor_pos.row
+ local col = cursor_pos.col
+ local scope = M.containing_scope(node)
+
+ for _, child in ipairs(M.get_named_children(scope)) do
+ local row_, col_ = child:start()
+ if vim.tbl_contains(scopes, child) and ((row_+1 == row and col_ > col) or row_+1 > row) then
+ return child
+ end
+ end
+end
+
+function M.next_scope(node)
+ local bufnr = api.nvim_get_current_buf()
+
+ local scopes = locals.get_scopes(bufnr)
+ if not node or not scopes then return end
+
+ local scope = M.containing_scope(node)
+
+ local parent = scope:parent()
+ if not parent then return end
+
+ local is_prev = true
+ for _, child in ipairs(M.get_named_children(parent)) do
+ if child == scope then
+ is_prev = false
+ elseif not is_prev and vim.tbl_contains(scopes, child) then
+ return child
+ end
+ end
+end
+
+function M.previous_scope(node)
+ local bufnr = api.nvim_get_current_buf()
+
+ local scopes = locals.get_scopes(bufnr)
+ if not node or not scopes then return end
+
+ local scope = M.containing_scope(node)
+
+ local parent = scope:parent()
+ if not parent then return end
+
+ local is_prev = true
+ local children = M.get_named_children(parent)
+ for i=#children,1,-1 do
+ if children[i] == scope then
+ is_prev = false
+ elseif not is_prev and vim.tbl_contains(scopes, children[i]) then
+ return children[i]
+ end
+ end
+end
+
+return M