···8899> All development of new and maybe undocumented, and unstable features is happening on [develop](https://github.com/olexsmir/gopher.nvim/tree/develop) branch.
10101111+## Table of content
1212+* [How to install](#install-using-lazynvim)
1313+* [Features](#features)
1414+* [Configuration](#configuration)
1515+* [Troubleshooting](#troubleshooting)
1616+* [Contributing](#contributing)
1717+1118## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim))
12191320Requirements:
···1623- Treesitter parser for `go`(`:TSInstall go` if you use [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter))
1724- [Go](https://github.com/golang/go) installed
18252626+> [!IMPORTANT]
2727+> If you prefer using other forges, this repository is also mirrored at:
2828+> - [tangled.org](https://tangled.org): [`https://tangled.org/olexsmir.xyz/gopher.nvim`](https://tangled.org/olexsmir.xyz/gopher.nvim)
2929+> - [codeberg.org](https://codeberg.org): [`https://codeberg.org/olexsmir/gopher.nvim`](https://codeberg.org/olexsmir/gopher.nvim)
3030+1931```lua
2020--- NOTE: this plugin is already lazy-loaded, it adds only about 1ms of load
2121--- time to your config
3232+-- NOTE: this plugin is already lazy-loaded and adds only about 1ms
3333+-- of load time to your config
2234{
2335 "olexsmir/gopher.nvim",
2436 ft = "go",
2537 -- branch = "develop"
2626- -- (optional) will update plugin's deps on every update
3838+ -- (optional) updates the plugin's dependencies on each update
2739 build = function()
2840 vim.cmd.GoInstallDeps()
2941 end,
···6476 ```vim
6577 " add json tag
6678 :GoTagAdd json
7979+8080+ " add json tag with omitempty option
8181+ :GoTagAdd json=omitempty
67826883 " remove yaml tag
6984 :GoTagRm yaml
···201216 -- timeout for running internal commands
202217 timeout = 2000,
203218219219+ -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
220220+ installer_timeout = 999999,
221221+222222+ -- user specified paths to binaries
204223 commands = {
205224 go = "go",
206225 gomodifytags = "gomodifytags",
···209228 iferr = "iferr",
210229 },
211230 gotests = {
212212- -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template
231231+ -- a default template that gotess will use.
232232+ -- gotets doesn't have template named `default`, we use it to represent absence of the provided template.
213233 template = "default",
234234+214235 -- path to a directory containing custom test code templates
215236 template_dir = nil,
216216- -- switch table tests from using slice to map (with test name for the key)
237237+238238+ -- use named tests(map with test name as key) in table tests(slice of structs by default)
217239 named = false,
218240 },
219241 gotag = {
220242 transform = "snakecase",
243243+221244 -- default tags to add to struct fields
222245 default_tag = "json",
246246+247247+ -- default tag option added struct fields, set to nil to disable
248248+ -- e.g: `option = "json=omitempty,xml=omitempty`
249249+ option = nil,
223250 },
224251 iferr = {
225225- -- choose a custom error message
252252+ -- choose a custom error message, nil to use default
253253+ -- e.g: `message = 'fmt.Errorf("failed to %w", err)'`
226254 message = nil,
227255 },
228256}
229257```
258258+259259+## Troubleshooting
260260+The most common issue with the plugin is missing dependencies.
261261+Run `:checkhealth gopher` to verify that the plugin is installed correctly.
262262+If any binaries are missing, install them using `:GoInstallDeps`.
263263+264264+If the issue persists, feel free to [open a new issue](https://github.com/olexsmir/gopher.nvim/issues/new).
230265231266## Contributing
232267
···3636------------------------------------------------------------------------------
3737 *gopher.nvim-dependencies*
3838 `gopher.install_deps`
3939-Gopher.nvim implements most of its features using third-party tools.
4040-To install these tools, you can run `:GoInstallDeps` command
4141-or call `require("gopher").install_deps()` if you want to use lua api.
4242-By default dependencies will be installed asynchronously,
4343-to install them synchronously pass `{sync = true}` as an argument.
3939+4040+Gopher.nvim implements most of its features using third-party tools. To
4141+install plugin's dependencies, you can run:
4242+`:GoInstallDeps` or `:GoInstallDepsSync`
4343+or use `require("gopher").install_deps()` if you prefer lua api.
444445454646==============================================================================
···5757 ---@type number
5858 timeout = 2000,
59596060- --- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
6060+ -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
6161+ ---@type number
6162 installer_timeout = 999999,
62636364 -- user specified paths to binaries
···7172 },
7273 ---@class gopher.ConfigGotests
7374 gotests = {
7474- -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template
7575+ -- a default template that gotess will use.
7676+ -- gotets doesn't have template named `default`, we use it to represent absence of the provided template.
7577 template = "default",
7878+7679 -- path to a directory containing custom test code templates
7780 ---@type string|nil
7881 template_dir = nil,
7979- -- switch table tests from using slice to map (with test name for the key)
8282+8383+ -- use named tests(map with test name as key) in table tests(slice of structs by default)
8084 named = false,
8185 },
8286 ---@class gopher.ConfigGoTag
···86908791 -- default tags to add to struct fields
8892 default_tag = "json",
9393+9494+ -- default tag option added struct fields, set to nil to disable
9595+ -- e.g: `option = "json=omitempty,xml=omitempty`
9696+ ---@type string|nil
9797+ option = nil,
8998 },
9099 iferr = {
9191- -- choose a custom error message
100100+ -- choose a custom error message, nil to use default
101101+ -- e.g: `message = 'fmt.Errorf("failed to %w", err)'`
92102 ---@type string|nil
93103 message = nil,
94104 },
···96106<
97107Class ~
98108{gopher.Config}
109109+Fields ~
110110+{setup} `(fun(user_config?: gopher.Config))`
99111100112101113==============================================================================
···110122------------------------------------------------------------------------------
111123 *gopher.nvim-struct-tags*
112124113113-`struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields.
125125+`struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to
126126+struct fields.
114127115128Usage ~
116129117117-How to add/remove tags to struct fields:
130130+How to add/remove/clear tags to struct fields:
1181311. Place cursor on the struct
1191322. Run `:GoTagAdd json` to add json tags to struct fields
1201333. Run `:GoTagRm json` to remove json tags to struct fields
134134+4. Run `:GoTagClear` to clear all tags from struct fields
121135122122-To clear all tags from struct run: `:GoTagClear`
136136+If you want to add/remove tag with options, you can use `json=omitempty`
137137+(where json is tag, and omitempty is its option).
138138+Example: `:GoTagAdd xml json=omitempty`
139139+123140124141NOTE: if you dont specify the tag it will use `json` as default
125142···147164Integration of `impl` tool to generate method stubs for interfaces.
148165149166Usage ~
167167+1501681. Automatically implement an interface for a struct:
151151- - Place your cursor on the struct where you want to implement the interface.
152152- - Run `:GoImpl io.Reader`
153153- - This will automatically determine the receiver and implement the `io.Reader` interface.
169169+ - Place your cursor on the struct where you want to implement the interface.
170170+ - Run `:GoImpl io.Reader`
171171+ - This will automatically determine the receiver and implement the `io.Reader` interface.
1541721551732. Specify a custom receiver:
156156- - Place your cursor on the struct
157157- - Run `:GoImpl w io.Writer`, where:
158158- - `w` is the receiver.
159159- - `io.Writer` is the interface to implement.
174174+ - Place your cursor on the struct
175175+ - Run `:GoImpl w io.Writer`, where:
176176+ - `w` is the receiver.
177177+ - `io.Writer` is the interface to implement.
1601781611793. Explicitly specify the receiver, struct, and interface:
162162- - No need to place the cursor on the struct if all arguments are provided.
163163- - Run `:GoImpl r RequestReader io.Reader`, where:
164164- - `r` is the receiver.
165165- - `RequestReader` is the struct.
166166- - `io.Reader` is the interface to implement.
180180+ - No need to place the cursor on the struct if all arguments are provided.
181181+ - Run `:GoImpl r RequestReader io.Reader`, where:
182182+ - `r` is the receiver.
183183+ - `RequestReader` is the struct.
184184+ - `io.Reader` is the interface to implement.
167185168186Example:
169187>go
···184202Usage ~
185203186204- Generate unit test for specific function/method:
187187- 1. Place your cursor on the desired function/method.
188188- 2. Run `:GoTestAdd`
205205+ 1. Place your cursor on the desired function/method.
206206+ 2. Run `:GoTestAdd`
189207190208- Generate unit tests for *all* functions/methods in current file:
191191- - run `:GoTestsAll`
209209+ - run `:GoTestsAll`
192210193211- Generate unit tests *only* for *exported(public)* functions/methods:
194194- - run `:GoTestsExp`
212212+ - run `:GoTestsExp`
195213196196-You can also specify the template to use for generating the tests. See |gopher.nvim-config|
197197-More details about templates can be found at: https://github.com/cweill/gotests
214214+You can also specify the template to use for generating the tests.
215215+See |gopher.nvim-config|.
216216+More details about templates: https://github.com/cweill/gotests
198217199218If you prefer named tests, you can enable them in |gopher.nvim-config|.
200219···217236This module provides a way to generate comments for Go code.
218237219238Usage ~
220220-Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment.
239239+240240+Set cursor on line with function/method/struct/etc and
241241+run `:GoCmt` to generate a comment.
221242222243223244 vim:tw=78:ts=8:noet:ft=help:norl:
-51
lua/gopher/_utils/gocmd.lua
···11-local r = require "gopher._utils.runner"
22-local c = require("gopher.config").commands
33-local u = require "gopher._utils"
44-local gocmd = {}
55-66----@param args string[]
77----@return string[]
88-local function if_get(args)
99- for i, arg in ipairs(args) do
1010- local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg
1111- table.remove(args, i)
1212- table.insert(args, i, m)
1313- end
1414- return args
1515-end
1616-1717----@param args unknown[]
1818----@return string[]
1919-local function if_generate(args)
2020- if #args == 1 and args[1] == "%" then
2121- args[1] = vim.fn.expand "%"
2222- end
2323- return args
2424-end
2525-2626----@param subcmd string
2727----@param args string[]
2828----@return string
2929-function gocmd.run(subcmd, args)
3030- if #args == 0 and subcmd ~= "generate" then
3131- error "please provide any arguments"
3232- end
3333-3434- if subcmd == "get" then
3535- args = if_get(args)
3636- end
3737-3838- if subcmd == "generate" then
3939- args = if_generate(args)
4040- end
4141-4242- local rs = r.sync { c.go, subcmd, unpack(args) }
4343- if rs.code ~= 0 then
4444- error("go " .. subcmd .. " failed: " .. rs.stderr)
4545- end
4646-4747- u.notify(c.go .. " " .. subcmd .. " ran successful")
4848- return rs.stdout
4949-end
5050-5151-return gocmd
+32-1
lua/gopher/_utils/init.lua
···33local utils = {}
4455---@param msg string
66----@param lvl? number by default `vim.log.levels.INFO`
66+---@param lvl? integer by default `vim.log.levels.INFO`
77function utils.notify(msg, lvl)
88 lvl = lvl or vim.log.levels.INFO
99 vim.notify(msg, lvl, {
···3636function utils.trimend(s)
3737 local r, _ = string.gsub(s, "%s+$", "")
3838 return r
3939+end
4040+4141+-- Since indentation can be spaces or tabs, that's my hack around it
4242+---@param line string
4343+---@param indent integer
4444+---@return string
4545+function utils.indent(line, indent)
4646+ local char = string.sub(line, 1, 1)
4747+ if char ~= " " and char ~= "\t" then
4848+ char = " "
4949+ end
5050+ return string.rep(char, indent)
5151+end
5252+5353+---@generic T
5454+---@param tbl T[]
5555+---@return T[]
5656+function utils.list_unique(tbl)
5757+ if vim.fn.has "nvim-0.12" == 1 then
5858+ return vim.list.unique(tbl)
5959+ end
6060+6161+ for i = #tbl, 1, -1 do
6262+ for j = 1, i - 1 do
6363+ if tbl[i] == tbl[j] then
6464+ table.remove(tbl, i)
6565+ break
6666+ end
6767+ end
6868+ end
6969+ return tbl
3970end
40714172return utils
+2-2
lua/gopher/_utils/log.lua
···33-- for the code i have stolen(or have inspected by idk)
44local c = require "gopher.config"
5566----@class Gopher.Logger
66+---@class gopher.Logger
77---@field get_outfile fun():string
88---@field trace fun(...)
99---@field fmt_trace fun(...)
···4444 float_precision = 0.01,
4545}
46464747----@type Gopher.Logger
4747+---@type gopher.Logger
4848---@diagnostic disable-next-line: missing-fields
4949local log = {}
5050
+40-16
lua/gopher/_utils/ts.lua
···1111 right: (expression_list (composite_literal
1212 type: (struct_type))))]
1313 ]],
1414+ struct_field = [[
1515+ (field_declaration name: (field_identifier) @_name)
1616+ ]],
1417 func = [[
1518 [(function_declaration name: (identifier) @_name)
1616- (method_declaration name: (field_identifier) @_name)]
1919+ (method_declaration name: (field_identifier) @_name)
2020+ (method_elem name: (field_identifier) @_name)]
1721 ]],
1822 package = [[
1923 (package_identifier) @_name
···2327 name: (type_identifier) @_name
2428 type: (interface_type))
2529 ]],
3030+ var = [[
3131+ [(var_declaration (var_spec name: (identifier) @_name))
3232+ (short_var_declaration
3333+ left: (expression_list (identifier) @_name @_var))]
3434+ ]],
2635}
27362837---@param parent_type string[]
2938---@param node TSNode
3039---@return TSNode?
3131-local function get_parrent_node(parent_type, node)
4040+local function get_parent_node(parent_type, node)
3241 ---@type TSNode?
3342 local current = node
3443 while current do
···6473end
65746675---@class gopher.TsResult
6767----@field name string
6868----@field start integer
6969----@field end_ integer
7070----@field is_varstruct boolean
7676+---@field name string Name of the struct, function, etc
7777+---@field start integer Line number where the declaration starts
7878+---@field end_ integer Line number where the declaration ends
7979+---@field indent integer Number of spaces/tabs in the current cursor line
8080+---@field is_varstruct boolean Is struct declared as `var S struct{}` or `s := struct{}{}`
71817282---@param bufnr integer
7383---@param parent_type string[]
···7888 error "No treesitter parser found for go"
7989 end
80908181- local node = vim.treesitter.get_node {
8282- bufnr = bufnr,
8383- }
9191+ local node = vim.treesitter.get_node { bufnr = bufnr }
8492 if not node then
8585- error "No nodes found under cursor"
9393+ error "No nodes found under the cursor"
8694 end
87958888- local parent_node = get_parrent_node(parent_type, node)
9696+ local parent_node = get_parent_node(parent_type, node)
8997 if not parent_node then
9090- error "No parent node found under cursor"
9898+ error "No parent node found under the cursor"
9199 end
9210093101 local q = vim.treesitter.query.parse("go", query)
94102 local res = get_captures(q, parent_node, bufnr)
95103 assert(res.name ~= nil, "No capture name found")
961049797- local start_row, _, end_row, _ = parent_node:range()
105105+ local start_row, start_col, end_row, _ = parent_node:range()
106106+ res["indent"] = start_col
98107 res["start"] = start_row + 1
99108 res["end_"] = end_row + 1
100109···104113---@param bufnr integer
105114function ts.get_struct_under_cursor(bufnr)
106115 --- should be both type_spec and type_declaration
107107- --- because in cases like `type ( T struct{}, U strict{} )`
108108- --- i will be choosing always last struct in the list
116116+ --- because in cases like `type ( T struct{}, U struct{} )`
109117 ---
110118 --- var_declaration is for cases like `var x struct{}`
111119 --- short_var_declaration is for cases like `x := struct{}{}`
120120+ ---
121121+ --- it always chooses last struct type in the list
112122 return do_stuff(bufnr, {
113123 "type_spec",
114124 "type_declaration",
···118128end
119129120130---@param bufnr integer
131131+function ts.get_struct_field_under_cursor(bufnr)
132132+ return do_stuff(bufnr, { "field_declaration" }, queries.struct_field)
133133+end
134134+135135+---@param bufnr integer
121136function ts.get_func_under_cursor(bufnr)
122137 --- since this handles both and funcs and methods we should check for both parent nodes
123123- return do_stuff(bufnr, { "function_declaration", "method_declaration" }, queries.func)
138138+ return do_stuff(bufnr, {
139139+ "method_elem",
140140+ "function_declaration",
141141+ "method_declaration",
142142+ }, queries.func)
124143end
125144126145---@param bufnr integer
···131150---@param bufnr integer
132151function ts.get_interface_under_cursor(bufnr)
133152 return do_stuff(bufnr, { "type_declaration" }, queries.interface)
153153+end
154154+155155+---@param bufnr integer
156156+function ts.get_variable_under_cursor(bufnr)
157157+ return do_stuff(bufnr, { "var_declaration", "short_var_declaration" }, queries.var)
134158end
135159136160return ts
+31-17
lua/gopher/comment.lua
···33---@text
44--- This module provides a way to generate comments for Go code.
55---
66----@usage Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment.
66+---@usage
77+--- Set cursor on line with function/method/struct/etc and
88+--- run `:GoCmt` to generate a comment.
79810local ts = require "gopher._utils.ts"
911local log = require "gopher._utils.log"
1212+local u = require "gopher._utils"
1013local comment = {}
11141212----@param name string
1313----@return string
1414----@dochide
1515-local function template(name)
1616- return "// " .. name .. " "
1717-end
1818-1515+--- NOTE: The order of functions executed inside this function is IMPORTANT.
1616+--- This function is extremely fragile; run tests after making any changes.
1717+---
1918---@param bufnr integer
1919+---@param line string
2020---@return string
2121---@dochide
2222-local function generate(bufnr)
2222+local function generate(bufnr, line)
2323+ local sf_ok, sf_res = pcall(ts.get_struct_field_under_cursor, bufnr)
2424+ if sf_ok then
2525+ return u.indent(line, sf_res.indent) .. "// " .. sf_res.name .. " "
2626+ end
2727+2328 local s_ok, s_res = pcall(ts.get_struct_under_cursor, bufnr)
2429 if s_ok then
2525- return template(s_res.name)
3030+ return u.indent(line, s_res.indent) .. "// " .. s_res.name .. " "
3131+ end
3232+3333+ local v_ok, v_res = pcall(ts.get_variable_under_cursor, bufnr)
3434+ if v_ok then
3535+ return u.indent(line, v_res.indent) .. "// " .. v_res.name .. " "
2636 end
27372838 local f_ok, f_res = pcall(ts.get_func_under_cursor, bufnr)
2939 if f_ok then
3030- return template(f_res.name)
4040+ return u.indent(line, f_res.indent) .. "// " .. f_res.name .. " "
3141 end
32423343 local i_ok, i_res = pcall(ts.get_interface_under_cursor, bufnr)
3444 if i_ok then
3535- return template(i_res.name)
4545+ return u.indent(line, i_res.indent) .. "// " .. i_res.name .. " "
3646 end
37473848 local p_ok, p_res = pcall(ts.get_package_under_cursor, bufnr)
···45554656function comment.comment()
4757 local bufnr = vim.api.nvim_get_current_buf()
4848- local cmt = generate(bufnr)
4949- log.debug("generated comment: " .. cmt)
5858+ local lnum = vim.fn.getcurpos()[2]
5959+ local line = vim.fn.getline(lnum)
6060+ local cmt = generate(bufnr, line)
6161+ log.debug("generated comment:", {
6262+ comment = cmt,
6363+ line = line,
6464+ })
50655151- local pos = vim.fn.getcurpos()[2]
5252- vim.fn.append(pos - 1, cmt)
5353- vim.fn.setpos(".", { 0, pos, #cmt })
6666+ vim.fn.append(lnum - 1, cmt)
6767+ vim.fn.setpos(".", { bufnr, lnum, #cmt })
5468 vim.cmd "startinsert!"
5569end
5670
+22-8
lua/gopher/config.lua
···11+---@type gopher.Config
22+---@dochide
33+---@diagnostic disable-next-line: missing-fields .setup() gets injected later
14local config = {}
2536---@tag gopher.nvim-config.ConfigGoTagTransform
···1619---@tag gopher.nvim-config
1720---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
1821---@class gopher.Config
2222+---@field setup fun(user_config?: gopher.Config)
1923local default_config = {
2024 -- log level, you might consider using DEBUG or TRACE for debugging the plugin
2125 ---@type number
···2529 ---@type number
2630 timeout = 2000,
27312828- --- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
3232+ -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
3333+ ---@type number
2934 installer_timeout = 999999,
30353136 -- user specified paths to binaries
···3944 },
4045 ---@class gopher.ConfigGotests
4146 gotests = {
4242- -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template
4747+ -- a default template that gotess will use.
4848+ -- gotets doesn't have template named `default`, we use it to represent absence of the provided template.
4349 template = "default",
5050+4451 -- path to a directory containing custom test code templates
4552 ---@type string|nil
4653 template_dir = nil,
4747- -- switch table tests from using slice to map (with test name for the key)
5454+5555+ -- use named tests(map with test name as key) in table tests(slice of structs by default)
4856 named = false,
4957 },
5058 ---@class gopher.ConfigGoTag
···54625563 -- default tags to add to struct fields
5664 default_tag = "json",
6565+6666+ -- default tag option added struct fields, set to nil to disable
6767+ -- e.g: `option = "json=omitempty,xml=omitempty`
6868+ ---@type string|nil
6969+ option = nil,
5770 },
5871 iferr = {
5959- -- choose a custom error message
7272+ -- choose a custom error message, nil to use default
7373+ -- e.g: `message = 'fmt.Errorf("failed to %w", err)'`
6074 ---@type string|nil
6175 message = nil,
6276 },
···92106 vim.validate("commands.iferr", _config.commands.iferr, "string")
93107 vim.validate("gotests", _config.gotests, "table")
94108 vim.validate("gotests.template", _config.gotests.template, "string")
9595- vim.validate("gotests.template_dir", _config.gotests.template_dir, "string", true)
109109+ vim.validate("gotests.template_dir", _config.gotests.template_dir, { "string", "nil" })
96110 vim.validate("gotests.named", _config.gotests.named, "boolean")
97111 vim.validate("gotag", _config.gotag, "table")
98112 vim.validate("gotag.transform", _config.gotag.transform, "string")
99113 vim.validate("gotag.default_tag", _config.gotag.default_tag, "string")
114114+ vim.validate("gotag.option", _config.gotag.option, { "string", "nil" })
100115 vim.validate("iferr", _config.iferr, "table")
101101- vim.validate("iferr.message", _config.iferr.message, "string", true)
116116+ vim.validate("iferr.message", _config.iferr.message, { "string", "nil" })
102117end
103118104119setmetatable(config, {
···108123})
109124110125---@dochide
111111----@return gopher.Config
112112-return config --[[ @as gopher.Config ]]
126126+return config
+56
lua/gopher/go.lua
···11+local c = require "gopher.config"
22+local u = require "gopher._utils"
33+local r = require "gopher._utils.runner"
44+local go = {}
55+66+local function run(subcmd, args)
77+ local rs = r.sync { c.commands.go, subcmd, unpack(args) }
88+ if rs.code ~= 0 then
99+ error("go " .. subcmd .. " failed: " .. rs.stderr)
1010+ end
1111+1212+ u.notify(c.commands.go .. " " .. subcmd .. " ran successful")
1313+ return rs.stdout
1414+end
1515+1616+---@param args string[]
1717+function go.get(args)
1818+ for i, arg in ipairs(args) do
1919+ local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg
2020+ table.remove(args, i)
2121+ table.insert(args, i, m)
2222+ end
2323+2424+ run("get", args)
2525+end
2626+2727+---@param args string[]
2828+function go.mod(args)
2929+ run("mod", args)
3030+end
3131+3232+---@param args string[]
3333+function go.work(args)
3434+ -- TODO: use `gopls.tidy`
3535+3636+ run("work", args)
3737+end
3838+3939+---Executes `go generate`
4040+---If only argument is `%` it's going to be equivalent to `go generate <path to current file>`
4141+---@param args string[]
4242+function go.generate(args)
4343+ -- TODO: use `gopls.generate`
4444+4545+ if #args == 0 then
4646+ error "please provide arguments"
4747+ end
4848+4949+ if #args == 1 and args[1] == "%" then
5050+ args[1] = vim.fn.expand "%"
5151+ end
5252+5353+ run("generate", args)
5454+end
5555+5656+return go
+7-6
lua/gopher/gotests.lua
···33---@text gotests is utilizing the `gotests` tool to generate unit tests boilerplate.
44---@usage
55--- - Generate unit test for specific function/method:
66---- 1. Place your cursor on the desired function/method.
77---- 2. Run `:GoTestAdd`
66+--- 1. Place your cursor on the desired function/method.
77+--- 2. Run `:GoTestAdd`
88---
99--- - Generate unit tests for *all* functions/methods in current file:
1010---- - run `:GoTestsAll`
1010+--- - run `:GoTestsAll`
1111---
1212--- - Generate unit tests *only* for *exported(public)* functions/methods:
1313---- - run `:GoTestsExp`
1313+--- - run `:GoTestsExp`
1414---
1515---- You can also specify the template to use for generating the tests. See |gopher.nvim-config|
1616---- More details about templates can be found at: https://github.com/cweill/gotests
1515+--- You can also specify the template to use for generating the tests.
1616+--- See |gopher.nvim-config|.
1717+--- More details about templates: https://github.com/cweill/gotests
1718---
1819--- If you prefer named tests, you can enable them in |gopher.nvim-config|.
1920
+33-37
lua/gopher/health.lua
···11+local c = require("gopher.config").commands
12local health = {}
22-local cmd = require("gopher.config").commands
3344local deps = {
55+ vim_version = "nvim-0.10",
56 bin = {
67 {
77- bin = cmd.go,
88+ bin = c.go,
89 msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`, `:GoInstallDepsSync`",
99- optional = false,
1010 },
1111- { bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = true },
1212- { bin = cmd.impl, msg = "required for `:GoImpl`", optional = true },
1313- { bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = true },
1414- {
1515- bin = cmd.gotests,
1616- msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`",
1717- optional = true,
1818- },
1111+ { bin = c.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`" },
1212+ { bin = c.impl, msg = "required for `:GoImpl`" },
1313+ { bin = c.iferr, msg = "required for `:GoIfErr`" },
1414+ { bin = c.gotests, msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`" },
1915 },
2016 treesitter = {
2117 { parser = "go", msg = "required for most of the parts of `gopher.nvim`" },
2218 },
2319}
24202525----@param bin string
2626----@return boolean
2727-local function is_binary_found(bin)
2828- return vim.fn.executable(bin) == 1
2121+---@param bin {bin:string, msg:string, optional:boolean}
2222+local function check_binary(bin)
2323+ if vim.fn.executable(bin.bin) == 1 then
2424+ vim.health.ok(bin.bin .. " is found oh PATH: `" .. vim.fn.exepath(bin.bin) .. "`")
2525+ else
2626+ vim.health.error(bin.bin .. " not found on PATH, " .. bin.msg)
2727+ end
2928end
30293131----@param ft string
3232----@return boolean
3333-local function is_treesitter_parser_available(ft)
3434- local ok, parser = pcall(vim.treesitter.get_parser, 0, ft)
3535- return ok and parser ~= nil
3030+---@param ts {parser:string, msg:string}
3131+local function check_treesitter(ts)
3232+ local ok, parser = pcall(vim.treesitter.get_parser, 0, ts.parser)
3333+ if ok and parser ~= nil then
3434+ vim.health.ok("`" .. ts.parser .. "` parser is installed")
3535+ else
3636+ vim.health.error("`" .. ts.parser .. "` parser not found")
3737+ end
3638end
37393840function health.check()
3939- vim.health.start "required binaries"
4040- vim.health.info "all those binaries can be installed by `:GoInstallDeps`"
4141+ vim.health.start "Neovim version"
4242+ if vim.fn.has(deps.vim_version) == 1 then
4343+ vim.health.ok "Neovim version is compatible"
4444+ else
4545+ vim.health.error(deps.vim_version .. " or newer is required")
4646+ end
4747+4848+ vim.health.start "Required binaries (those can be installed with `:GoInstallDeps`)"
4149 for _, bin in ipairs(deps.bin) do
4242- if is_binary_found(bin.bin) then
4343- vim.health.ok(bin.bin .. " installed")
4444- else
4545- if bin.optional then
4646- vim.health.warn(bin.bin .. " not found, " .. bin.msg)
4747- else
4848- vim.health.error(bin.bin .. " not found, " .. bin.msg)
4949- end
5050- end
5050+ check_binary(bin)
5151 end
52525353- vim.health.start "required treesitter parsers"
5353+ vim.health.start "Treesitter"
5454 for _, parser in ipairs(deps.treesitter) do
5555- if is_treesitter_parser_available(parser.parser) then
5656- vim.health.ok(parser.parser .. " parser installed")
5757- else
5858- vim.health.error(parser.parser .. " parser not found, " .. parser.msg)
5959- end
5555+ check_treesitter(parser)
6056 end
6157end
6258
···33---@text
44--- Integration of `impl` tool to generate method stubs for interfaces.
55---
66----@usage 1. Automatically implement an interface for a struct:
77---- - Place your cursor on the struct where you want to implement the interface.
88---- - Run `:GoImpl io.Reader`
99---- - This will automatically determine the receiver and implement the `io.Reader` interface.
66+---@usage
77+--- 1. Automatically implement an interface for a struct:
88+--- - Place your cursor on the struct where you want to implement the interface.
99+--- - Run `:GoImpl io.Reader`
1010+--- - This will automatically determine the receiver and implement the `io.Reader` interface.
1011---
1112--- 2. Specify a custom receiver:
1212---- - Place your cursor on the struct
1313---- - Run `:GoImpl w io.Writer`, where:
1414---- - `w` is the receiver.
1515---- - `io.Writer` is the interface to implement.
1313+--- - Place your cursor on the struct
1414+--- - Run `:GoImpl w io.Writer`, where:
1515+--- - `w` is the receiver.
1616+--- - `io.Writer` is the interface to implement.
1617---
1718--- 3. Explicitly specify the receiver, struct, and interface:
1818---- - No need to place the cursor on the struct if all arguments are provided.
1919---- - Run `:GoImpl r RequestReader io.Reader`, where:
2020---- - `r` is the receiver.
2121---- - `RequestReader` is the struct.
2222---- - `io.Reader` is the interface to implement.
1919+--- - No need to place the cursor on the struct if all arguments are provided.
2020+--- - Run `:GoImpl r RequestReader io.Reader`, where:
2121+--- - `r` is the receiver.
2222+--- - `RequestReader` is the struct.
2323+--- - `io.Reader` is the interface to implement.
2324---
2425--- Example:
2526--- >go
+10-21
lua/gopher/init.lua
···1313local log = require "gopher._utils.log"
1414local tags = require "gopher.struct_tags"
1515local tests = require "gopher.gotests"
1616-local gocmd = require("gopher._utils.gocmd").run
1616+local go = require "gopher.go"
1717local gopher = {}
18181919---@toc_entry Setup
···35353636---@toc_entry Install dependencies
3737---@tag gopher.nvim-dependencies
3838----@text Gopher.nvim implements most of its features using third-party tools.
3939---- To install these tools, you can run `:GoInstallDeps` command
4040---- or call `require("gopher").install_deps()` if you want to use lua api.
4141---- By default dependencies will be installed asynchronously,
4242---- to install them synchronously pass `{sync = true}` as an argument.
3838+---@text
3939+--- Gopher.nvim implements most of its features using third-party tools. To
4040+--- install plugin's dependencies, you can run:
4141+--- `:GoInstallDeps` or `:GoInstallDepsSync`
4242+--- or use `require("gopher").install_deps()` if you prefer lua api.
4343gopher.install_deps = require("gopher.installer").install_deps
44444545gopher.impl = require("gopher.impl").impl
···5858 all = tests.all_tests,
5959}
60606161-gopher.get = function(...)
6262- gocmd("get", ...)
6363-end
6464-6565-gopher.mod = function(...)
6666- gocmd("mod", ...)
6767-end
6868-6969-gopher.generate = function(...)
7070- gocmd("generate", ...)
7171-end
7272-7373-gopher.work = function(...)
7474- gocmd("work", ...)
7575-end
6161+gopher.get = go.get
6262+gopher.mod = go.mod
6363+gopher.work = go.work
6464+gopher.generate = go.generate
76657766return gopher
+67-19
lua/gopher/struct_tags.lua
···11---@toc_entry Modify struct tags
22---@tag gopher.nvim-struct-tags
33---@text
44---- `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields.
44+--- `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to
55+--- struct fields.
56---
67---@usage
77---- How to add/remove tags to struct fields:
88+--- How to add/remove/clear tags to struct fields:
89--- 1. Place cursor on the struct
910--- 2. Run `:GoTagAdd json` to add json tags to struct fields
1011--- 3. Run `:GoTagRm json` to remove json tags to struct fields
1212+--- 4. Run `:GoTagClear` to clear all tags from struct fields
1113---
1212---- To clear all tags from struct run: `:GoTagClear`
1414+--- If you want to add/remove tag with options, you can use `json=omitempty`
1515+--- (where json is tag, and omitempty is its option).
1616+--- Example: `:GoTagAdd xml json=omitempty`
1717+---
1318---
1419--- NOTE: if you dont specify the tag it will use `json` as default
1520---
···39444045---@dochide
4146---@class gopher.StructTagInput
4242----@field tags string[] User provided tags
4747+---@field input string[] User provided tags
4348---@field range? gopher.StructTagRange (optional)
44494550---@dochide
···102107 )
103108end
104109110110+---@dochide
111111+---@param option string
112112+local function option_to_tag(option)
113113+ return option:match "^(.-)="
114114+end
115115+116116+---@dochide
105117---@param args string[]
106106----@return string
118118+local function unwrap_if_needed(args)
119119+ local out = {}
120120+ for _, v in pairs(args) do
121121+ for _, p in pairs(vim.split(v, ",")) do
122122+ table.insert(out, p)
123123+ end
124124+ end
125125+ return out
126126+end
127127+128128+---@dochide
129129+---@class gopher.StructTagsArgs
130130+---@field tags string
131131+---@field options string
132132+107133---@dochide
108108-local function handler_user_tags(args)
109109- if #args == 0 then
110110- return c.gotag.default_tag
134134+---@param args string[]
135135+---@return gopher.StructTagsArgs
136136+function struct_tags.parse_args(args)
137137+ args = unwrap_if_needed(args)
138138+139139+ local tags, options = {}, {}
140140+ for _, v in pairs(args) do
141141+ if string.find(v, "=") then
142142+ table.insert(options, v)
143143+ table.insert(tags, option_to_tag(v))
144144+ else
145145+ table.insert(tags, v)
146146+ end
111147 end
112112- return table.concat(args, ",")
148148+149149+ return {
150150+ tags = table.concat(u.list_unique(tags), ","),
151151+ options = table.concat(u.list_unique(options), ","),
152152+ }
113153end
114154115115--- Adds tags to a struct under the cursor
116116--- See |gopher.nvim-struct-tags|
155155+-- Adds tags to a struct under the cursor. See `:h gopher.nvim-struct-tags`.
117156---@param opts gopher.StructTagInput
118157---@dochide
119158function struct_tags.add(opts)
···122161 local fpath = vim.fn.expand "%"
123162 local bufnr = vim.api.nvim_get_current_buf()
124163125125- local user_tags = handler_user_tags(opts.tags)
126126- handle_tags(fpath, bufnr, opts.range, { "-add-tags", user_tags })
164164+ local user_args = struct_tags.parse_args(opts.input)
165165+ handle_tags(fpath, bufnr, opts.range, {
166166+ "-add-tags",
167167+ (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag,
168168+ (user_args.options ~= "" or c.gotag.option) and "-add-options" or nil,
169169+ (user_args.options ~= "") and user_args.options or c.gotag.option,
170170+ })
127171end
128172129129--- Removes tags from a struct under the cursor
130130--- See `:h gopher.nvim-struct-tags`
173173+-- Removes tags from a struct under the cursor. See `:h gopher.nvim-struct-tags`.
131174---@dochide
132175---@param opts gopher.StructTagInput
133176function struct_tags.remove(opts)
···136179 local fpath = vim.fn.expand "%"
137180 local bufnr = vim.api.nvim_get_current_buf()
138181139139- local user_tags = handler_user_tags(opts.tags)
140140- handle_tags(fpath, bufnr, opts.range, { "-remove-tags", user_tags })
182182+ local user_args = struct_tags.parse_args(opts.input)
183183+ handle_tags(fpath, bufnr, opts.range, {
184184+ "-remove-tags",
185185+ (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag,
186186+ (user_args.options ~= "" or c.gotag.option ~= nil) and "-remove-options" or nil,
187187+ (user_args.options ~= "") and user_args.options or c.gotag.option,
188188+ })
141189end
142190143143--- Removes all tags from a struct under the cursor
144144--- See `:h gopher.nvim-struct-tags`
191191+-- Removes all tags from a struct under the cursor.
192192+-- See `:h gopher.nvim-struct-tags`.
145193---@dochide
146194function struct_tags.clear()
147195 local fpath = vim.fn.expand "%"
···11+package main
22+33+type Test struct {
44+ ID int `xml:"id,otheroption"`
55+ Another struct {
66+ Second string `xml:"second,otheroption"`
77+ } `xml:"another,otheroption"`
88+}
+11
spec/fixtures/tags/remove_with_option_input.go
···11+package main
22+33+type Test struct {
44+ ID int `json:"id,omitempty" xml:"id,someoption"`
55+ Name string `json:"name,omitempty" xml:"name,someoption"`
66+ Num int64 `json:"num,omitempty" xml:"num,someoption"`
77+ Another struct {
88+ First int `json:"first,omitempty" xml:"first,someoption"`
99+ Second string `json:"second,omitempty" xml:"second,someoption"`
1010+ } `json:"another,omitempty" xml:"another,someoption"`
1111+}
+11
spec/fixtures/tags/remove_with_option_output.go
···11+package main
22+33+type Test struct {
44+ ID int `xml:"id,someoption"`
55+ Name string `xml:"name,someoption"`
66+ Num int64 `xml:"num,someoption"`
77+ Another struct {
88+ First int `xml:"first,someoption"`
99+ Second string `xml:"second,someoption"`
1010+ } `xml:"another,someoption"`
1111+}
+8
spec/fixtures/tags/with_default_option_input.go
···11+package main
22+33+type Test struct {
44+ ID int
55+ Another struct {
66+ Second string
77+ }
88+}
+8
spec/fixtures/tags/with_default_option_output.go
···11+package main
22+33+type Test struct {
44+ ID int `xml:"id,theoption"`
55+ Another struct {
66+ Second string `xml:"second,theoption"`
77+ } `xml:"another,theoption"`
88+}
+11
spec/fixtures/tags/with_option_input.go
···11+package main
22+33+type Test struct {
44+ ID int
55+ Name string
66+ Num int64
77+ Another struct {
88+ First int
99+ Second string
1010+ }
1111+}
+11
spec/fixtures/tags/with_option_output.go
···11+package main
22+33+type Test struct {
44+ ID int `json:"id,omitempty"`
55+ Name string `json:"name,omitempty"`
66+ Num int64 `json:"num,omitempty"`
77+ Another struct {
88+ First int `json:"first,omitempty"`
99+ Second string `json:"second,omitempty"`
1010+ } `json:"another,omitempty"`
1111+}
+28
spec/integration/comment_test.lua
···1818 do_the_test("struct", { 4, 1 })
1919end
20202121+comment["should add a comment on struct field"] = function()
2222+ do_the_test("struct_fields", { 5, 8 })
2323+end
2424+2525+comment["should add a comment on var struct field"] = function()
2626+ do_the_test("var_struct_fields", { 6, 4 })
2727+end
2828+2929+comment["should add a comment on one field of many structs"] = function()
3030+ do_the_test("many_structs_fields", { 10, 4 })
3131+end
3232+2133comment["should add comment to function"] = function()
2234 do_the_test("func", { 3, 1 })
2335end
···28402941comment["should add comment to interface"] = function()
3042 do_the_test("interface", { 3, 6 })
4343+end
4444+4545+comment["should add comment on interface method"] = function()
4646+ do_the_test("interface_method", { 4, 2 })
4747+end
4848+4949+comment["should add a comment on interface with many method"] = function()
5050+ do_the_test("interface_many_method", { 5, 2 })
5151+end
5252+5353+comment["should add a comment on a var"] = function()
5454+ do_the_test("var", { 4, 2 })
5555+end
5656+5757+comment["should add a comment on a short declared var"] = function()
5858+ do_the_test("svar", { 4, 8 })
3159end
32603361comment["otherwise should add // above cursor"] = function()