From 55bc5787d183e5b00f07ac14fa2b78044e0d4fd8 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Wed, 19 Mar 2025 18:06:33 +0200 Subject: [PATCH] refactor: struct tags (#94) * refactor(struct_tags): finally my hands got to this * feat(struct_tags): trim output * feat(struct_tags): add :GoTagClear * docgen * refactor(struct_tags): error on out-of-bounds * feat(struct_tags): add support for working with multiple tags at the once * test(struct_tags): test both possible inputs * refactor(struct_tags): add type annotation, dont force write * refactor(struct_tags): optimization ig * docs: fix * fixup! refactor(struct_tags): add type annotation, dont force write * task docgen --------- Co-authored-by: Oliver <1571880470@qq.com> --- doc/gopher.nvim.txt | 19 ++++ lua/gopher/init.lua | 1 + lua/gopher/struct_tags.lua | 122 ++++++++++++++------------ plugin/gopher.vim | 1 + spec/fixtures/tags/add_many_input.go | 11 +++ spec/fixtures/tags/add_many_output.go | 11 +++ spec/fixtures/tags/clear_input.go | 11 +++ spec/fixtures/tags/clear_output.go | 11 +++ spec/fixtures/tags/remove_output.go | 12 +-- spec/integration/struct_tags_test.lua | 48 ++++++++-- 10 files changed, 177 insertions(+), 70 deletions(-) create mode 100644 spec/fixtures/tags/add_many_input.go create mode 100644 spec/fixtures/tags/add_many_output.go create mode 100644 spec/fixtures/tags/clear_input.go create mode 100644 spec/fixtures/tags/clear_output.go diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 4c3fd65..377a4f4 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -129,6 +129,25 @@ Example: Name string `yaml:name` } < +------------------------------------------------------------------------------ + *struct_tags.add()* + `struct_tags.add`({...}) +tags to a struct under the cursor +Parameters ~ +{...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] + +------------------------------------------------------------------------------ + *struct_tags.remove()* + `struct_tags.remove`({...}) +tags from a struct under the cursor +Parameters ~ +{...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] + +------------------------------------------------------------------------------ + *struct_tags.clear()* + `struct_tags.clear`() +all tags from a struct under the cursor + ============================================================================== ------------------------------------------------------------------------------ diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index 0de883b..bb9888b 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -43,6 +43,7 @@ gopher.comment = require("gopher.comment").comment gopher.tags = { add = tags.add, rm = tags.remove, + clear = tags.clear, } gopher.test = { diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 73c7612..4545640 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -26,90 +26,96 @@ --- } --- < -local ts_utils = require "gopher._utils.ts" +local ts = require "gopher._utils.ts" local r = require "gopher._utils.runner" local c = require "gopher.config" +local log = require "gopher._utils.log" local struct_tags = {} -local function modify(...) - local fpath = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter - local bufnr = vim.api.nvim_get_current_buf() - local struct = ts_utils.get_struct_under_cursor(bufnr) +---@param fpath string +---@param bufnr integer +---@param user_args string[] +---@private +local function handle_tags(fpath, bufnr, user_args) + local st = ts.get_struct_under_cursor(bufnr) - -- set user args for cmd - local cmd_args = {} - local arg = { ... } - for _, v in ipairs(arg) do - table.insert(cmd_args, v) - end - - local rs = r.sync { + -- stylua: ignore + local cmd = { c.commands.gomodifytags, - "-transform", - c.gotag.transform, - "-format", - "json", - "-struct", - struct.name, + "-transform", c.gotag.transform, + "-format", "json", + "-struct", st.name, + "-file", fpath, "-w", - "-file", - fpath, - unpack(cmd_args), } + for _, v in ipairs(user_args) do + table.insert(cmd, v) + end + + local rs = r.sync(cmd) if rs.code ~= 0 then + log.error("tags: failed to set tags " .. rs.stderr) error("failed to set tags " .. rs.stderr) end - -- decode value - local tagged = vim.json.decode(rs.stdout) - if - tagged.errors ~= nil - or tagged.lines == nil - or tagged["start"] == nil - or tagged["start"] == 0 - then - error("failed to set tags " .. vim.inspect(tagged)) + local res = vim.json.decode(rs.stdout) + + if res["errors"] then + log.error("tags: got an error " .. vim.inspect(res)) + error("failed to set tags " .. vim.inspect(res["errors"])) + end + + for i, v in ipairs(res["lines"]) do + res["lines"][i] = string.gsub(v, "%s+$", "") end vim.api.nvim_buf_set_lines( - 0, - tagged.start - 1, - tagged.start - 1 + #tagged.lines, - false, - tagged.lines + bufnr, + res["start"] - 1, + res["start"] - 1 + #res["lines"], + true, + res["lines"] ) - vim.cmd "write" end --- add tags to struct under cursor +---@param args string[] +---@return string +---@private +local function handler_user_args(args) + if #args == 0 then + return c.gotag.default_tag + end + return table.concat(args, ",") +end + +---Adds tags to a struct under the cursor +---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] function struct_tags.add(...) - local user_tags = { ... } - if #user_tags == 0 then - user_tags = { c.gotag.default_tag } - end + local args = { ... } + local fpath = vim.fn.expand "%" + local bufnr = vim.api.nvim_get_current_buf() - local cmd_args = { "-add-tags" } - for _, v in ipairs(user_tags) do - table.insert(cmd_args, v) - end - - modify(unpack(cmd_args)) + local user_tags = handler_user_args(args) + handle_tags(fpath, bufnr, { "-add-tags", user_tags }) end --- remove tags to struct under cursor +---Removes tags from a struct under the cursor +---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] function struct_tags.remove(...) - local user_tags = { ... } - if #user_tags == 0 then - user_tags = { c.gotag.default_tag } - end + local args = { ... } + local fpath = vim.fn.expand "%" + local bufnr = vim.api.nvim_get_current_buf() - local cmd_args = { "-remove-tags" } - for _, v in ipairs(user_tags) do - table.insert(cmd_args, v) - end + local user_tags = handler_user_args(args) + handle_tags(fpath, bufnr, { "-remove-tags", user_tags }) +end - modify(unpack(cmd_args)) +---Removes all tags from a struct under the cursor +function struct_tags.clear() + local fpath = vim.fn.expand "%" + local bufnr = vim.api.nvim_get_current_buf() + handle_tags(fpath, bufnr, { "-clear-tags" }) end return struct_tags diff --git a/plugin/gopher.vim b/plugin/gopher.vim index b61ed6e..dc924b9 100644 --- a/plugin/gopher.vim +++ b/plugin/gopher.vim @@ -1,5 +1,6 @@ command! -nargs=* GoTagAdd :lua require"gopher".tags.add() command! -nargs=* GoTagRm :lua require"gopher".tags.rm() +command! GoTagClear :lua require"gopher".tags.clear() command! GoTestAdd :lua require"gopher".test.add() command! GoTestsAll :lua require"gopher".test.all() command! GoTestsExp :lua require"gopher".test.exported() diff --git a/spec/fixtures/tags/add_many_input.go b/spec/fixtures/tags/add_many_input.go new file mode 100644 index 0000000..7e27a27 --- /dev/null +++ b/spec/fixtures/tags/add_many_input.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int + Name string + Num int64 + Another struct { + First int + Second string + } +} diff --git a/spec/fixtures/tags/add_many_output.go b/spec/fixtures/tags/add_many_output.go new file mode 100644 index 0000000..9a43e62 --- /dev/null +++ b/spec/fixtures/tags/add_many_output.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int `test4:"id" test5:"id" test1:"id" test2:"id"` + Name string `test4:"name" test5:"name" test1:"name" test2:"name"` + Num int64 `test4:"num" test5:"num" test1:"num" test2:"num"` + Another struct { + First int `test4:"first" test5:"first" test1:"first" test2:"first"` + Second string `test4:"second" test5:"second" test1:"second" test2:"second"` + } `test4:"another" test5:"another" test1:"another" test2:"another"` +} diff --git a/spec/fixtures/tags/clear_input.go b/spec/fixtures/tags/clear_input.go new file mode 100644 index 0000000..050b327 --- /dev/null +++ b/spec/fixtures/tags/clear_input.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int `json:"id" yaml:"id" xml:"id" db:"id"` + Name string `json:"name" yaml:"name" xml:"name" db:"name"` + Num int64 `json:"num" yaml:"num" xml:"num" db:"num"` + Another struct { + First int `json:"first" yaml:"first" xml:"first" db:"first"` + Second string `json:"second" yaml:"second" xml:"second" db:"second"` + } `json:"another" yaml:"another" xml:"another" db:"another"` +} diff --git a/spec/fixtures/tags/clear_output.go b/spec/fixtures/tags/clear_output.go new file mode 100644 index 0000000..7e27a27 --- /dev/null +++ b/spec/fixtures/tags/clear_output.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int + Name string + Num int64 + Another struct { + First int + Second string + } +} diff --git a/spec/fixtures/tags/remove_output.go b/spec/fixtures/tags/remove_output.go index 6d9fe70..7e27a27 100644 --- a/spec/fixtures/tags/remove_output.go +++ b/spec/fixtures/tags/remove_output.go @@ -1,11 +1,11 @@ package main type Test struct { - ID int - Name string - Num int64 + ID int + Name string + Num int64 Another struct { - First int - Second string - } + First int + Second string + } } diff --git a/spec/integration/struct_tags_test.lua b/spec/integration/struct_tags_test.lua index e8f8606..4ac245b 100644 --- a/spec/integration/struct_tags_test.lua +++ b/spec/integration/struct_tags_test.lua @@ -10,38 +10,74 @@ local T = MiniTest.new_set { }, } T["struct_tags"] = MiniTest.new_set {} -T["struct_tags"]["works add"] = function() +T["struct_tags"]["should add tag"] = function() local tmp = t.tmpfile() local fixtures = t.get_fixtures "tags/add" t.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 3, 6, 0 }) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6, 0 }) child.cmd "GoTagAdd json" + child.cmd "write" t.eq(t.readfile(tmp), fixtures.output) end -T["struct_tags"]["works remove"] = function() +T["struct_tags"]["should remove tag"] = function() local tmp = t.tmpfile() local fixtures = t.get_fixtures "tags/remove" t.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 4, 6, 0 }) + child.fn.setpos(".", { child.fn.bufnr(tmp), 4, 6, 0 }) child.cmd "GoTagRm json" + child.cmd "write" t.eq(t.readfile(tmp), fixtures.output) end -T["struct_tags"]["works many structs"] = function() +T["struct_tags"]["should be able to handle many structs"] = function() local tmp = t.tmpfile() local fixtures = t.get_fixtures "tags/many" t.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 10, 3, 0 }) + child.fn.setpos(".", { child.fn.bufnr(tmp), 10, 3, 0 }) child.cmd "GoTagAdd testing" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + +T["struct_tags"]["should clear struct"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/clear" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1, 0 }) + child.cmd "GoTagClear" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + +T["struct_tags"]["should add more than one tag"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/add_many" + t.writefile(tmp, fixtures.input) + + --- with comma, like gomodifytags + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1 }) + child.cmd "GoTagAdd test4,test5" + child.cmd "write" + + -- without comma + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1 }) + child.cmd "GoTagAdd test1 test2" + child.cmd "write" t.eq(t.readfile(tmp), fixtures.output) end