all repos

gopher.nvim @ 55bc5787d183e5b00f07ac14fa2b78044e0d4fd8

Minimalistic plugin for Go development
10 files changed, 176 insertions(+), 69 deletions(-)
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>
Author: Smirnov Oleksandr ss2316544@gmail.com
Committed by: GitHub noreply@github.com
Committed at: 2025-03-19 18:06:33 +0200
Parent: e9f2eef
M doc/gopher.nvim.txt

@@ -129,6 +129,25 @@ ID int `yaml:id`

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 + ============================================================================== ------------------------------------------------------------------------------
M lua/gopher/init.lua

@@ -43,6 +43,7 @@

gopher.tags = { add = tags.add, rm = tags.remove, + clear = tags.clear, } gopher.test = {
M lua/gopher/struct_tags.lua

@@ -26,90 +26,96 @@ --- Name string `yaml:name`

--- } --- < -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) - - -- set user args for cmd - local cmd_args = {} - local arg = { ... } - for _, v in ipairs(arg) do - table.insert(cmd_args, v) - end +---@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) - 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 -function struct_tags.add(...) - local user_tags = { ... } - if #user_tags == 0 then - user_tags = { c.gotag.default_tag } +---@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 - local cmd_args = { "-add-tags" } - for _, v in ipairs(user_tags) do - table.insert(cmd_args, v) - 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 args = { ... } + local fpath = vim.fn.expand "%" + local bufnr = vim.api.nvim_get_current_buf() - 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
M plugin/gopher.vim

@@ -1,5 +1,6 @@

command! -nargs=* GoTagAdd :lua require"gopher".tags.add(<f-args>) command! -nargs=* GoTagRm :lua require"gopher".tags.rm(<f-args>) +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()
A 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 + } +}
A 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"` +}
A 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"` +}
A 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 + } +}
M 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 + } }
M spec/integration/struct_tags_test.lua

@@ -10,38 +10,74 @@ end,

}, } 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