From 92625cc6e3dd68fdb82123ee4a0ec2d85d25073d Mon Sep 17 00:00:00 2001 From: Oleksandr Smirnov Date: Wed, 29 Oct 2025 13:09:19 +0200 Subject: [PATCH] feat(struct_tags): add options support --- lua/gopher/_utils/struct_tags.lua | 44 +++++++++++++++ lua/gopher/struct_tags.lua | 29 +++++----- spec/fixtures/tags/with_option_input.go | 11 ++++ spec/fixtures/tags/with_option_output.go | 11 ++++ spec/integration/struct_tags_test.lua | 9 ++++ spec/unit/struct_tag_test.lua | 68 ++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 lua/gopher/_utils/struct_tags.lua create mode 100644 spec/fixtures/tags/with_option_input.go create mode 100644 spec/fixtures/tags/with_option_output.go create mode 100644 spec/unit/struct_tag_test.lua diff --git a/lua/gopher/_utils/struct_tags.lua b/lua/gopher/_utils/struct_tags.lua new file mode 100644 index 0000000..5522840 --- /dev/null +++ b/lua/gopher/_utils/struct_tags.lua @@ -0,0 +1,44 @@ +---@param option string +local function option_to_tag(option) + return option:match "^(.-)=" +end + +---@param args string[] +local function unwrap_if_needed(args) + if #args == 1 then + return vim.split(args[1], ",") + end + return args +end + +local struct_tags = {} + +---@class gopher.StructTagsArgs +---@field tags string +---@field options string + +---@param args string[] +---@return gopher.StructTagsArgs +function struct_tags.parse_args(args) + args = unwrap_if_needed(args) + + local tags, options = {}, {} + for _, v in pairs(args) do + if string.find(v, "=") then + table.insert(options, v) + table.insert(tags, option_to_tag(v)) + else + table.insert(tags, v) + end + end + + -- todo: it's incompatible with vim lower 0.12 + tags = vim.list.unique(tags) + options = vim.list.unique(options) + return { + tags = table.concat(tags, ","), + options = table.concat(options, ","), + } +end + +return struct_tags diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 3264e7a..5ea2d01 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -35,6 +35,7 @@ local r = require "gopher._utils.runner" local c = require "gopher.config" local u = require "gopher._utils" local log = require "gopher._utils.log" +local st = require "gopher._utils.struct_tags" local struct_tags = {} ---@dochide @@ -102,16 +103,6 @@ local function handle_tags(fpath, bufnr, range, user_args) ) end ----@param args string[] ----@return string ----@dochide -local function handler_user_tags(args) - if #args == 0 then - return c.gotag.default_tag - end - return table.concat(args, ",") -end - -- Adds tags to a struct under the cursor -- See |gopher.nvim-struct-tags| ---@param opts gopher.StructTagInput @@ -122,8 +113,13 @@ function struct_tags.add(opts) local fpath = vim.fn.expand "%" local bufnr = vim.api.nvim_get_current_buf() - local user_tags = handler_user_tags(opts.tags) - handle_tags(fpath, bufnr, opts.range, { "-add-tags", user_tags }) + local user_args = st.parse_args(opts.tags) + handle_tags(fpath, bufnr, opts.range, { + "-add-tags", + (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag, + (#user_args.options ~= 0) and "-add-options" or nil, + (#user_args.options ~= 0) and user_args.options or nil, + }) end -- Removes tags from a struct under the cursor @@ -136,8 +132,13 @@ function struct_tags.remove(opts) local fpath = vim.fn.expand "%" local bufnr = vim.api.nvim_get_current_buf() - local user_tags = handler_user_tags(opts.tags) - handle_tags(fpath, bufnr, opts.range, { "-remove-tags", user_tags }) + local user_args = st.parse_args(opts.tags) + handle_tags(fpath, bufnr, opts.range, { + "-remove-tags", + (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag, + (#user_args.options ~= 0) and "-remove-options" or nil, + (#user_args.options ~= 0) and user_args.options or nil, + }) end -- Removes all tags from a struct under the cursor diff --git a/spec/fixtures/tags/with_option_input.go b/spec/fixtures/tags/with_option_input.go new file mode 100644 index 0000000..7e27a27 --- /dev/null +++ b/spec/fixtures/tags/with_option_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/with_option_output.go b/spec/fixtures/tags/with_option_output.go new file mode 100644 index 0000000..3fbbabf --- /dev/null +++ b/spec/fixtures/tags/with_option_output.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Num int64 `json:"num,omitempty"` + Another struct { + First int `json:"first,omitempty"` + Second string `json:"second,omitempty"` + } `json:"another,omitempty"` +} diff --git a/spec/integration/struct_tags_test.lua b/spec/integration/struct_tags_test.lua index ae7995b..07d0330 100644 --- a/spec/integration/struct_tags_test.lua +++ b/spec/integration/struct_tags_test.lua @@ -96,4 +96,13 @@ struct_tags["should remove tag with range"] = function() t.cleanup(rs) end +struct_tags["should add tags with option"] = function() + local rs = t.setup_test("tags/with_option", child, { 3, 6 }) + child.cmd "GoTagAdd json=omitempty" + child.cmd "write" + + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) +end + return T diff --git a/spec/unit/struct_tag_test.lua b/spec/unit/struct_tag_test.lua new file mode 100644 index 0000000..bba3276 --- /dev/null +++ b/spec/unit/struct_tag_test.lua @@ -0,0 +1,68 @@ +local t = require "spec.testutils" +local _, T, st = t.setup "struct_tags" + +st["should parse tags"] = function() + local out = require("gopher._utils.struct_tags").parse_args { "json", "yaml", "etc" } + + t.eq(out.tags, "json,yaml,etc") + t.eq(out.options, "") +end + +st["should parse tags separated by commas"] = function() + local out = require("gopher._utils.struct_tags").parse_args { "json,yaml,etc" } + + t.eq(out.tags, "json,yaml,etc") + t.eq(out.options, "") +end + +st["should parse tags separated by command and spaces"] = function() + local out = require("gopher.struct_tags").parse_args { + "json,yaml", + "json=omitempty", + "xml=something", + } + + t.eq(out.tags, "json,yaml,xml") + t.eq(out.options, "json=omitempty,xml=something") +end + +st["should parse tag with an option"] = function() + local out = require("gopher._utils.struct_tags").parse_args { + "json=omitempty", + "xml", + "xml=theoption", + } + + t.eq(out.tags, "json,xml") + t.eq(out.options, "json=omitempty,xml=theoption") +end + +st["should parse tags with an option"] = function() + local out = require("gopher._utils.struct_tags").parse_args { "json=omitempty", "yaml" } + + t.eq(out.tags, "json,yaml") + t.eq(out.options, "json=omitempty") +end + +st["should parse tags with an option separated with comma"] = function() + local out = require("gopher._utils.struct_tags").parse_args { "json=omitempty,yaml" } + + t.eq(out.tags, "json,yaml") + t.eq(out.options, "json=omitempty") +end + +st["should parse tags with options specified separately"] = function() + local out = require("gopher._utils.struct_tags").parse_args { "json", "yaml", "json=omitempty" } + + t.eq(out.tags, "json,yaml") + t.eq(out.options, "json=omitempty") +end + +st["should parse tags with options specified separately and separated by comma"] = function() + local out = require("gopher._utils.struct_tags").parse_args { "json,yaml,json=omitempty" } + + t.eq(out.tags, "json,yaml") + t.eq(out.options, "json=omitempty") +end + +return T