gopher.nvim/lua/gopher/_utils/ts.lua(view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
local ts = {}
local queries = {
struct = [[
[(type_spec name: (type_identifier) @_name
type: (struct_type))
(var_declaration (var_spec
name: (identifier) @_name @_var
type: (struct_type)))
(short_var_declaration
left: (expression_list (identifier) @_name @_var)
right: (expression_list (composite_literal
type: (struct_type))))]
]],
func = [[
[(function_declaration name: (identifier) @_name)
(method_declaration name: (field_identifier) @_name)]
]],
package = [[
(package_identifier) @_name
]],
interface = [[
(type_spec
name: (type_identifier) @_name
type: (interface_type))
]],
}
---@param parent_type string[]
---@param node TSNode
---@return TSNode?
local function get_parrent_node(parent_type, node)
---@type TSNode?
local current = node
while current do
if vim.tbl_contains(parent_type, current:type()) then
break
end
current = current:parent()
if current == nil then
return nil
end
end
return current
end
---@param query vim.treesitter.Query
---@param node TSNode
---@param bufnr integer
---@return {name:string, is_varstruct:boolean}
local function get_captures(query, node, bufnr)
local res = {}
for _, match, _ in query:iter_matches(node, bufnr) do
for capture_id, captured_node in pairs(match) do
local capture_name = query.captures[capture_id]
if capture_name == "_name" then
res["name"] = vim.treesitter.get_node_text(captured_node, bufnr)
end
if capture_name == "_var" then
res["is_varstruct"] = true
end
end
end
return res
end
---@class gopher.TsResult
---@field name string
---@field start_line integer
---@field end_line integer
---@field is_varstruct boolean
---@param bufnr integer
---@param parent_type string[]
---@param query string
---@return gopher.TsResult
local function do_stuff(bufnr, parent_type, query)
local node = vim.treesitter.get_node {
bufnr = bufnr,
}
if not node then
error "No nodes found under cursor"
end
local parent_node = get_parrent_node(parent_type, node)
if not parent_node then
error "No parent node found under cursor"
end
local q = vim.treesitter.query.parse("go", query)
local res = get_captures(q, parent_node, bufnr)
assert(res.name ~= nil, "No capture name found")
local start_row, _, end_row, _ = parent_node:range()
res["start_line"] = start_row + 1
res["end_line"] = end_row + 1
return res
end
---@param bufnr integer
function ts.get_struct_under_cursor(bufnr)
--- should be both type_spec and type_declaration
--- because in cases like `type ( T struct{}, U strict{} )`
--- i will be choosing always last struct in the list
---
--- var_declaration is for cases like `var x struct{}`
--- short_var_declaration is for cases like `x := struct{}{}`
return do_stuff(bufnr, {
"type_spec",
"type_declaration",
"var_declaration",
"short_var_declaration",
}, queries.struct)
end
---@param bufnr integer
function ts.get_func_under_cursor(bufnr)
--- since this handles both and funcs and methods we should check for both parent nodes
return do_stuff(bufnr, { "function_declaration", "method_declaration" }, queries.func)
end
---@param bufnr integer
function ts.get_package_under_cursor(bufnr)
return do_stuff(bufnr, { "package_clause" }, queries.package)
end
---@param bufnr integer
function ts.get_interface_under_cursor(bufnr)
return do_stuff(bufnr, { "type_declaration" }, queries.interface)
end
return ts
|