[mirror] Make your go dev experience better
github.com/olexsmir/gopher.nvim
neovim
golang
1local ts = {}
2local queries = {
3 struct = [[
4 [(type_spec name: (type_identifier) @_name
5 type: (struct_type))
6 (var_declaration (var_spec
7 name: (identifier) @_name @_var
8 type: (struct_type)))
9 (short_var_declaration
10 left: (expression_list (identifier) @_name @_var)
11 right: (expression_list (composite_literal
12 type: (struct_type))))]
13 ]],
14 struct_field = [[
15 (field_declaration name: (field_identifier) @_name)
16 ]],
17 func = [[
18 [(function_declaration name: (identifier) @_name)
19 (method_declaration name: (field_identifier) @_name)
20 (method_elem name: (field_identifier) @_name)]
21 ]],
22 package = [[
23 (package_identifier) @_name
24 ]],
25 interface = [[
26 (type_spec
27 name: (type_identifier) @_name
28 type: (interface_type))
29 ]],
30 var = [[
31 [(var_declaration (var_spec name: (identifier) @_name))
32 (short_var_declaration
33 left: (expression_list (identifier) @_name @_var))]
34 ]],
35}
36
37---@param parent_type string[]
38---@param node TSNode
39---@return TSNode?
40local function get_parent_node(parent_type, node)
41 ---@type TSNode?
42 local current = node
43 while current do
44 if vim.tbl_contains(parent_type, current:type()) then
45 break
46 end
47
48 current = current:parent()
49 if current == nil then
50 return nil
51 end
52 end
53 return current
54end
55
56---@param query vim.treesitter.Query
57---@param node TSNode
58---@param bufnr integer
59---@return {name:string, is_varstruct:boolean}
60local function get_captures(query, node, bufnr)
61 local res = {}
62 for id, _node in query:iter_captures(node, bufnr) do
63 if query.captures[id] == "_name" then
64 res["name"] = vim.treesitter.get_node_text(_node, bufnr)
65 end
66
67 if query.captures[id] == "_var" then
68 res["is_varstruct"] = true
69 end
70 end
71
72 return res
73end
74
75---@class gopher.TsResult
76---@field name string Name of the struct, function, etc
77---@field start integer Line number where the declaration starts
78---@field end_ integer Line number where the declaration ends
79---@field indent integer Number of spaces/tabs in the current cursor line
80---@field is_varstruct boolean Is struct declared as `var S struct{}` or `s := struct{}{}`
81
82---@param bufnr integer
83---@param parent_type string[]
84---@param query string
85---@return gopher.TsResult
86local function do_stuff(bufnr, parent_type, query)
87 if not vim.treesitter.get_parser(bufnr, "go") then
88 error "No treesitter parser found for go"
89 end
90
91 local node = vim.treesitter.get_node { bufnr = bufnr }
92 if not node then
93 error "No nodes found under the cursor"
94 end
95
96 local parent_node = get_parent_node(parent_type, node)
97 if not parent_node then
98 error "No parent node found under the cursor"
99 end
100
101 local q = vim.treesitter.query.parse("go", query)
102 local res = get_captures(q, parent_node, bufnr)
103 assert(res.name ~= nil, "No capture name found")
104
105 local start_row, start_col, end_row, _ = parent_node:range()
106 res["indent"] = start_col
107 res["start"] = start_row + 1
108 res["end_"] = end_row + 1
109
110 return res
111end
112
113---@param bufnr integer
114function ts.get_struct_under_cursor(bufnr)
115 --- should be both type_spec and type_declaration
116 --- because in cases like `type ( T struct{}, U struct{} )`
117 ---
118 --- var_declaration is for cases like `var x struct{}`
119 --- short_var_declaration is for cases like `x := struct{}{}`
120 ---
121 --- it always chooses last struct type in the list
122 return do_stuff(bufnr, {
123 "type_spec",
124 "type_declaration",
125 "var_declaration",
126 "short_var_declaration",
127 }, queries.struct)
128end
129
130---@param bufnr integer
131function ts.get_struct_field_under_cursor(bufnr)
132 return do_stuff(bufnr, { "field_declaration" }, queries.struct_field)
133end
134
135---@param bufnr integer
136function ts.get_func_under_cursor(bufnr)
137 --- since this handles both and funcs and methods we should check for both parent nodes
138 return do_stuff(bufnr, {
139 "method_elem",
140 "function_declaration",
141 "method_declaration",
142 }, queries.func)
143end
144
145---@param bufnr integer
146function ts.get_package_under_cursor(bufnr)
147 return do_stuff(bufnr, { "package_clause" }, queries.package)
148end
149
150---@param bufnr integer
151function ts.get_interface_under_cursor(bufnr)
152 return do_stuff(bufnr, { "type_declaration" }, queries.interface)
153end
154
155---@param bufnr integer
156function ts.get_variable_under_cursor(bufnr)
157 return do_stuff(bufnr, { "var_declaration", "short_var_declaration" }, queries.var)
158end
159
160return ts