[mirror] Make your go dev experience better github.com/olexsmir/gopher.nvim
neovim golang

fix(struct_tags): edge case with structs declared as var (#99)

* fix(struct_tags): edge case with structs declared as var

* test: test it and fix it

* fixup! test: test it and fix it

* fixup! fix(struct_tags): edge case with structs declared as var

* fixup! test: test it and fix it

authored by olexsmir.xyz and committed by olexsmir.xyz 969db908 a993ece5

verified
Changed files
+96 -6
lua
gopher
spec
+24 -4
lua/gopher/_utils/ts.lua
··· 1 local ts = {} 2 local queries = { 3 struct = [[ 4 - (type_spec name: (type_identifier) @_name 5 - type: (struct_type)) 6 ]], 7 func = [[ 8 [(function_declaration name: (identifier) @_name) ··· 40 ---@param query vim.treesitter.Query 41 ---@param node TSNode 42 ---@param bufnr integer 43 - ---@return {name:string} 44 local function get_captures(query, node, bufnr) 45 local res = {} 46 for _, match, _ in query:iter_matches(node, bufnr) do ··· 49 if capture_name == "_name" then 50 res["name"] = vim.treesitter.get_node_text(captured_node, bufnr) 51 end 52 end 53 end 54 ··· 59 ---@field name string 60 ---@field start_line integer 61 ---@field end_line integer 62 63 ---@param bufnr integer 64 ---@param parent_type string[] ··· 93 --- should be both type_spec and type_declaration 94 --- because in cases like `type ( T struct{}, U strict{} )` 95 --- i will be choosing always last struct in the list 96 - return do_stuff(bufnr, { "type_spec", "type_declaration" }, queries.struct) 97 end 98 99 ---@param bufnr integer
··· 1 local ts = {} 2 local 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 func = [[ 15 [(function_declaration name: (identifier) @_name) ··· 47 ---@param query vim.treesitter.Query 48 ---@param node TSNode 49 ---@param bufnr integer 50 + ---@return {name:string, is_varstruct:boolean} 51 local function get_captures(query, node, bufnr) 52 local res = {} 53 for _, match, _ in query:iter_matches(node, bufnr) do ··· 56 if capture_name == "_name" then 57 res["name"] = vim.treesitter.get_node_text(captured_node, bufnr) 58 end 59 + 60 + if capture_name == "_var" then 61 + res["is_varstruct"] = true 62 + end 63 end 64 end 65 ··· 70 ---@field name string 71 ---@field start_line integer 72 ---@field end_line integer 73 + ---@field is_varstruct boolean 74 75 ---@param bufnr integer 76 ---@param parent_type string[] ··· 105 --- should be both type_spec and type_declaration 106 --- because in cases like `type ( T struct{}, U strict{} )` 107 --- i will be choosing always last struct in the list 108 + --- 109 + --- var_declaration is for cases like `var x struct{}` 110 + --- short_var_declaration is for cases like `x := struct{}{}` 111 + return do_stuff(bufnr, { 112 + "type_spec", 113 + "type_declaration", 114 + "var_declaration", 115 + "short_var_declaration", 116 + }, queries.struct) 117 end 118 119 ---@param bufnr integer
+8 -2
lua/gopher/struct_tags.lua
··· 44 c.commands.gomodifytags, 45 "-transform", c.gotag.transform, 46 "-format", "json", 47 - "-struct", st.name, 48 "-file", fpath, 49 "-w", 50 } 51 52 for _, v in ipairs(user_args) do 53 table.insert(cmd, v) ··· 60 end 61 62 local res = vim.json.decode(rs.stdout) 63 - 64 if res["errors"] then 65 log.error("tags: got an error " .. vim.inspect(res)) 66 error("failed to set tags " .. vim.inspect(res["errors"]))
··· 44 c.commands.gomodifytags, 45 "-transform", c.gotag.transform, 46 "-format", "json", 47 "-file", fpath, 48 "-w", 49 } 50 + 51 + if st.is_varstruct then 52 + table.insert(cmd, "-line") 53 + table.insert(cmd, string.format("%d,%d", st.start_line, st.end_line)) 54 + else 55 + table.insert(cmd, "-struct") 56 + table.insert(cmd, st.name) 57 + end 58 59 for _, v in ipairs(user_args) do 60 table.insert(cmd, v) ··· 67 end 68 69 local res = vim.json.decode(rs.stdout) 70 if res["errors"] then 71 log.error("tags: got an error " .. vim.inspect(res)) 72 error("failed to set tags " .. vim.inspect(res["errors"]))
+11
spec/fixtures/tags/svar_input.go
···
··· 1 + package main 2 + 3 + func main() { 4 + s := struct { 5 + API string 6 + Key string 7 + }{ 8 + API: "api.com", 9 + Key: "key", 10 + } 11 + }
+11
spec/fixtures/tags/svar_output.go
···
··· 1 + package main 2 + 3 + func main() { 4 + s := struct { 5 + API string `xml:"api"` 6 + Key string `xml:"key"` 7 + }{ 8 + API: "api.com", 9 + Key: "key", 10 + } 11 + }
+8
spec/fixtures/tags/var_input.go
···
··· 1 + package main 2 + 3 + func main() { 4 + var a struct { 5 + TestField1 string 6 + TestField2 string 7 + } 8 + }
+8
spec/fixtures/tags/var_output.go
···
··· 1 + package main 2 + 3 + func main() { 4 + var a struct { 5 + TestField1 string `yaml:"test_field_1"` 6 + TestField2 string `yaml:"test_field_2"` 7 + } 8 + }
+26
spec/integration/struct_tags_test.lua
··· 82 t.eq(t.readfile(tmp), fixtures.output) 83 end 84 85 return T
··· 82 t.eq(t.readfile(tmp), fixtures.output) 83 end 84 85 + T["struct_tags"]["should add tags on var"] = function() 86 + local tmp = t.tmpfile() 87 + local fixtures = t.get_fixtures "tags/var" 88 + t.writefile(tmp, fixtures.input) 89 + 90 + child.cmd("silent edit " .. tmp) 91 + child.fn.setpos(".", { child.fn.bufnr(tmp), 5, 3 }) 92 + child.cmd "GoTagAdd yaml" 93 + child.cmd "write" 94 + 95 + t.eq(t.readfile(tmp), fixtures.output) 96 + end 97 + 98 + T["struct_tags"]["should add tags on short declr var"] = function() 99 + local tmp = t.tmpfile() 100 + local fixtures = t.get_fixtures "tags/svar" 101 + t.writefile(tmp, fixtures.input) 102 + 103 + child.cmd("silent edit " .. tmp) 104 + child.fn.setpos(".", { child.fn.bufnr(tmp), 4, 3 }) 105 + child.cmd "GoTagAdd xml" 106 + child.cmd "write" 107 + 108 + t.eq(t.readfile(tmp), fixtures.output) 109 + end 110 + 111 return T