+66
-9
README.md
+66
-9
README.md
···
8
8
9
9
> All development of new and maybe undocumented, and unstable features is happening on [develop](https://github.com/olexsmir/gopher.nvim/tree/develop) branch.
10
10
11
+
## Table of content
12
+
* [How to install](#install-using-lazynvim)
13
+
* [Features](#features)
14
+
* [Configuration](#configuration)
15
+
* [Troubleshooting](#troubleshooting)
16
+
* [Contributing](#contributing)
17
+
11
18
## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim))
12
19
13
20
Requirements:
···
16
23
- Treesitter parser for `go`(`:TSInstall go` if you use [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter))
17
24
- [Go](https://github.com/golang/go) installed
18
25
26
+
> [!IMPORTANT]
27
+
> If you prefer using other forges, this repository is also mirrored at:
28
+
> - [tangled.org](https://tangled.org): [`https://tangled.org/olexsmir.xyz/gopher.nvim`](https://tangled.org/olexsmir.xyz/gopher.nvim)
29
+
> - [codeberg.org](https://codeberg.org): [`https://codeberg.org/olexsmir/gopher.nvim`](https://codeberg.org/olexsmir/gopher.nvim)
30
+
19
31
```lua
20
-
-- NOTE: this plugin is already lazy-loaded, it adds only about 1ms of load
21
-
-- time to your config
32
+
-- NOTE: this plugin is already lazy-loaded and adds only about 1ms
33
+
-- of load time to your config
22
34
{
23
35
"olexsmir/gopher.nvim",
24
36
ft = "go",
25
37
-- branch = "develop"
26
-
-- (optional) will update plugin's deps on every update
38
+
-- (optional) updates the plugin's dependencies on each update
27
39
build = function()
28
40
vim.cmd.GoInstallDeps()
29
41
end,
···
50
62
- [impl](https://github.com/josharian/impl)
51
63
- [gotests](https://github.com/cweill/gotests)
52
64
- [iferr](https://github.com/koron/iferr)
65
+
- [json2go](https://github.com/olexsmir/json2go)
53
66
</details>
54
67
55
68
<details>
···
64
77
```vim
65
78
" add json tag
66
79
:GoTagAdd json
80
+
81
+
" add json tag with omitempty option
82
+
:GoTagAdd json=omitempty
67
83
68
84
" remove yaml tag
69
85
:GoTagRm yaml
···
170
186
```
171
187
</details>
172
188
189
+
<details>
190
+
<summary>
191
+
<b>Convert json to Go types</b>
192
+
</summary>
193
+
194
+

195
+
196
+
`:GoJson` opens a temporary buffer where you can paste or write JSON.
197
+
Saving the buffer (`:w` or `:wq`) automatically closes it and inserts the generated Go struct into the original buffer at the cursor position.
198
+
199
+
Alternatively, you can pass JSON directly as an argument:
200
+
```vim
201
+
:GoJson {"name": "Alice", "age": 30}
202
+
```
203
+
204
+
Additionally, `gopher.json2go` provides lua api, see `:h gopher.nvim-json2go` for details.
205
+
</details>
206
+
173
207
174
208
<details>
175
209
<summary>
···
204
238
-- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
205
239
installer_timeout = 999999,
206
240
207
-
-- restart gopls server after commands like `:GoMod`, `:GoGet`, `:GoWork`
208
-
restart_lsp = false,
209
-
241
+
-- user specified paths to binaries
210
242
commands = {
211
243
go = "go",
212
244
gomodifytags = "gomodifytags",
···
215
247
iferr = "iferr",
216
248
},
217
249
gotests = {
218
-
-- gotests doesn't have template named "default" so this plugin uses "default" to set the default template
250
+
-- a default template that gotess will use.
251
+
-- gotets doesn't have template named `default`, we use it to represent absence of the provided template.
219
252
template = "default",
253
+
220
254
-- path to a directory containing custom test code templates
221
255
template_dir = nil,
222
-
-- switch table tests from using slice to map (with test name for the key)
256
+
257
+
-- use named tests(map with test name as key) in table tests(slice of structs by default)
223
258
named = false,
224
259
},
225
260
gotag = {
226
261
transform = "snakecase",
262
+
227
263
-- default tags to add to struct fields
228
264
default_tag = "json",
265
+
266
+
-- default tag option added struct fields, set to nil to disable
267
+
-- e.g: `option = "json=omitempty,xml=omitempty`
268
+
option = nil,
229
269
},
230
270
iferr = {
231
-
-- choose a custom error message
271
+
-- choose a custom error message, nil to use default
272
+
-- e.g: `message = 'fmt.Errorf("failed to %w", err)'`
232
273
message = nil,
233
274
},
275
+
json2go = {
276
+
-- command used to open interactive input.
277
+
-- e.g: `split`, `botright split`, `tabnew`
278
+
interactive_cmd = "vsplit",
279
+
280
+
-- name of autogenerated struct
281
+
-- e.g: "MySuperCoolName"
282
+
type_name = nil,
283
+
},
234
284
}
235
285
```
286
+
287
+
## Troubleshooting
288
+
The most common issue with the plugin is missing dependencies.
289
+
Run `:checkhealth gopher` to verify that the plugin is installed correctly.
290
+
If any binaries are missing, install them using `:GoInstallDeps`.
291
+
292
+
If the issue persists, feel free to [open a new issue](https://github.com/olexsmir/gopher.nvim/issues/new).
236
293
237
294
## Contributing
238
295
+99
-33
doc/gopher.nvim.txt
+99
-33
doc/gopher.nvim.txt
···
13
13
Config ................................................ |gopher.nvim-config|
14
14
Commands ............................................ |gopher.nvim-commands|
15
15
Modify struct tags ............................... |gopher.nvim-struct-tags|
16
+
json2go .............................................. |gopher.nvim-json2go|
16
17
Auto implementation of interface methods ................ |gopher.nvim-impl|
17
18
Generating unit tests boilerplate .................... |gopher.nvim-gotests|
18
19
Iferr .................................................. |gopher.nvim-iferr|
···
36
37
------------------------------------------------------------------------------
37
38
*gopher.nvim-dependencies*
38
39
`gopher.install_deps`
39
-
Gopher.nvim implements most of its features using third-party tools.
40
-
To install these tools, you can run `:GoInstallDeps` command
41
-
or call `require("gopher").install_deps()` if you want to use lua api.
42
-
By default dependencies will be installed asynchronously,
43
-
to install them synchronously pass `{sync = true}` as an argument.
40
+
41
+
Gopher.nvim implements most of its features using third-party tools. To
42
+
install plugin's dependencies, you can run:
43
+
`:GoInstallDeps` or `:GoInstallDepsSync`
44
+
or use `require("gopher").install_deps()` if you prefer lua api.
44
45
45
46
46
47
==============================================================================
···
58
59
timeout = 2000,
59
60
60
61
-- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
62
+
---@type number
61
63
installer_timeout = 999999,
62
64
63
-
-- restart gopls server after commands like `:GoMod`, `:GoGet`, `:GoWork`
64
-
restart_lsp = false,
65
-
66
65
-- user specified paths to binaries
67
66
---@class gopher.ConfigCommand
68
67
commands = {
···
71
70
gotests = "gotests",
72
71
impl = "impl",
73
72
iferr = "iferr",
73
+
json2go = "json2go",
74
74
},
75
75
---@class gopher.ConfigGotests
76
76
gotests = {
77
-
-- gotests doesn't have template named "default" so this plugin uses "default" to set the default template
77
+
-- a default template that gotess will use.
78
+
-- gotets doesn't have template named `default`, we use it to represent absence of the provided template.
78
79
template = "default",
80
+
79
81
-- path to a directory containing custom test code templates
80
82
---@type string|nil
81
83
template_dir = nil,
82
-
-- switch table tests from using slice to map (with test name for the key)
84
+
85
+
-- use named tests(map with test name as key) in table tests(slice of structs by default)
83
86
named = false,
84
87
},
85
88
---@class gopher.ConfigGoTag
···
89
92
90
93
-- default tags to add to struct fields
91
94
default_tag = "json",
95
+
96
+
-- default tag option added struct fields, set to nil to disable
97
+
-- e.g: `option = "json=omitempty,xml=omitempty`
98
+
---@type string|nil
99
+
option = nil,
92
100
},
101
+
---@class gopher.ConfigIfErr
93
102
iferr = {
94
-
-- choose a custom error message
103
+
-- choose a custom error message, nil to use default
104
+
-- e.g: `message = 'fmt.Errorf("failed to %w", err)'`
95
105
---@type string|nil
96
106
message = nil,
97
107
},
108
+
---@class gopher.ConfigJson2Go
109
+
json2go = {
110
+
-- command used to open interactive input.
111
+
-- e.g: `split`, `botright split`, `tabnew`
112
+
interactive_cmd = "vsplit",
113
+
114
+
-- name of autogenerated struct, if nil none, will the default one of json2go.
115
+
-- e.g: "MySuperCoolName"
116
+
---@type string|nil
117
+
type_name = nil,
118
+
},
98
119
}
99
120
<
100
121
Class ~
···
115
136
------------------------------------------------------------------------------
116
137
*gopher.nvim-struct-tags*
117
138
118
-
`struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields.
139
+
`struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to
140
+
struct fields.
119
141
120
142
Usage ~
121
143
122
-
How to add/remove tags to struct fields:
144
+
How to add/remove/clear tags to struct fields:
123
145
1. Place cursor on the struct
124
146
2. Run `:GoTagAdd json` to add json tags to struct fields
125
147
3. Run `:GoTagRm json` to remove json tags to struct fields
148
+
4. Run `:GoTagClear` to clear all tags from struct fields
126
149
127
-
To clear all tags from struct run: `:GoTagClear`
150
+
If you want to add/remove tag with options, you can use `json=omitempty`
151
+
(where json is tag, and omitempty is its option).
152
+
Example: `:GoTagAdd xml json=omitempty`
153
+
128
154
129
155
NOTE: if you dont specify the tag it will use `json` as default
130
156
···
147
173
148
174
==============================================================================
149
175
------------------------------------------------------------------------------
176
+
*gopher.nvim-json2go*
177
+
178
+
Convert json to go type annotations.
179
+
180
+
Usage ~
181
+
182
+
`:GoJson` opens a temporary buffer where you can paste or write JSON.
183
+
Saving the buffer (`:w` or `:wq`) automatically closes it and inserts the
184
+
generated Go struct into the original buffer at the cursor position.
185
+
186
+
Alternatively, you can pass JSON directly as an argument:
187
+
>vim
188
+
:GoJson {"name": "Alice", "age": 30}
189
+
<
190
+
------------------------------------------------------------------------------
191
+
*json2go.transform()*
192
+
`json2go.transform`({json_str})
193
+
194
+
Parameters ~
195
+
{json_str} `(string)` Json string that is going to be converted to go type.
196
+
Return ~
197
+
`(string)` `(optional)` Go type, or nil if failed.
198
+
199
+
------------------------------------------------------------------------------
200
+
*json2go.json2go()*
201
+
`json2go.json2go`({json_str})
202
+
Converts json string to go type, and puts result under the cursor. If
203
+
[json_str] is nil, will open an interactive prompt (with cmd set in
204
+
config).
205
+
206
+
Parameters ~
207
+
{json_str} `(optional)` `(string)`
208
+
209
+
210
+
==============================================================================
211
+
------------------------------------------------------------------------------
150
212
*gopher.nvim-impl*
151
213
152
214
Integration of `impl` tool to generate method stubs for interfaces.
153
215
154
216
Usage ~
217
+
155
218
1. Automatically implement an interface for a struct:
156
-
- Place your cursor on the struct where you want to implement the interface.
157
-
- Run `:GoImpl io.Reader`
158
-
- This will automatically determine the receiver and implement the `io.Reader` interface.
219
+
- Place your cursor on the struct where you want to implement the interface.
220
+
- Run `:GoImpl io.Reader`
221
+
- This will automatically determine the receiver and implement the `io.Reader` interface.
159
222
160
223
2. Specify a custom receiver:
161
-
- Place your cursor on the struct
162
-
- Run `:GoImpl w io.Writer`, where:
163
-
- `w` is the receiver.
164
-
- `io.Writer` is the interface to implement.
224
+
- Place your cursor on the struct
225
+
- Run `:GoImpl w io.Writer`, where:
226
+
- `w` is the receiver.
227
+
- `io.Writer` is the interface to implement.
165
228
166
229
3. Explicitly specify the receiver, struct, and interface:
167
-
- No need to place the cursor on the struct if all arguments are provided.
168
-
- Run `:GoImpl r RequestReader io.Reader`, where:
169
-
- `r` is the receiver.
170
-
- `RequestReader` is the struct.
171
-
- `io.Reader` is the interface to implement.
230
+
- No need to place the cursor on the struct if all arguments are provided.
231
+
- Run `:GoImpl r RequestReader io.Reader`, where:
232
+
- `r` is the receiver.
233
+
- `RequestReader` is the struct.
234
+
- `io.Reader` is the interface to implement.
172
235
173
236
Example:
174
237
>go
···
189
252
Usage ~
190
253
191
254
- Generate unit test for specific function/method:
192
-
1. Place your cursor on the desired function/method.
193
-
2. Run `:GoTestAdd`
255
+
1. Place your cursor on the desired function/method.
256
+
2. Run `:GoTestAdd`
194
257
195
258
- Generate unit tests for *all* functions/methods in current file:
196
-
- run `:GoTestsAll`
259
+
- run `:GoTestsAll`
197
260
198
261
- Generate unit tests *only* for *exported(public)* functions/methods:
199
-
- run `:GoTestsExp`
262
+
- run `:GoTestsExp`
200
263
201
-
You can also specify the template to use for generating the tests. See |gopher.nvim-config|
202
-
More details about templates can be found at: https://github.com/cweill/gotests
264
+
You can also specify the template to use for generating the tests.
265
+
See |gopher.nvim-config|.
266
+
More details about templates: https://github.com/cweill/gotests
203
267
204
268
If you prefer named tests, you can enable them in |gopher.nvim-config|.
205
269
···
222
286
This module provides a way to generate comments for Go code.
223
287
224
288
Usage ~
225
-
Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment.
289
+
290
+
Set cursor on line with function/method/struct/etc and
291
+
run `:GoCmt` to generate a comment.
226
292
227
293
228
294
vim:tw=78:ts=8:noet:ft=help:norl:
+19
lua/gopher/_utils/init.lua
+19
lua/gopher/_utils/init.lua
···
50
50
return string.rep(char, indent)
51
51
end
52
52
53
+
---@generic T
54
+
---@param tbl T[]
55
+
---@return T[]
56
+
function utils.list_unique(tbl)
57
+
if vim.fn.has "nvim-0.12" == 1 then
58
+
return vim.list.unique(tbl)
59
+
end
60
+
61
+
for i = #tbl, 1, -1 do
62
+
for j = 1, i - 1 do
63
+
if tbl[i] == tbl[j] then
64
+
table.remove(tbl, i)
65
+
break
66
+
end
67
+
end
68
+
end
69
+
return tbl
70
+
end
71
+
53
72
return utils
+2
-2
lua/gopher/_utils/log.lua
+2
-2
lua/gopher/_utils/log.lua
···
3
3
-- for the code i have stolen(or have inspected by idk)
4
4
local c = require "gopher.config"
5
5
6
-
---@class Gopher.Logger
6
+
---@class gopher.Logger
7
7
---@field get_outfile fun():string
8
8
---@field trace fun(...)
9
9
---@field fmt_trace fun(...)
···
44
44
float_precision = 0.01,
45
45
}
46
46
47
-
---@type Gopher.Logger
47
+
---@type gopher.Logger
48
48
---@diagnostic disable-next-line: missing-fields
49
49
local log = {}
50
50
-11
lua/gopher/_utils/lsp.lua
-11
lua/gopher/_utils/lsp.lua
+3
-1
lua/gopher/comment.lua
+3
-1
lua/gopher/comment.lua
···
3
3
---@text
4
4
--- This module provides a way to generate comments for Go code.
5
5
---
6
-
---@usage Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment.
6
+
---@usage
7
+
--- Set cursor on line with function/method/struct/etc and
8
+
--- run `:GoCmt` to generate a comment.
7
9
8
10
local ts = require "gopher._utils.ts"
9
11
local log = require "gopher._utils.log"
+32
-9
lua/gopher/config.lua
+32
-9
lua/gopher/config.lua
···
30
30
timeout = 2000,
31
31
32
32
-- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync)
33
+
---@type number
33
34
installer_timeout = 999999,
34
-
35
-
-- restart gopls server after commands like `:GoMod`, `:GoGet`, `:GoWork`
36
-
restart_lsp = false,
37
35
38
36
-- user specified paths to binaries
39
37
---@class gopher.ConfigCommand
···
43
41
gotests = "gotests",
44
42
impl = "impl",
45
43
iferr = "iferr",
44
+
json2go = "json2go",
46
45
},
47
46
---@class gopher.ConfigGotests
48
47
gotests = {
49
-
-- gotests doesn't have template named "default" so this plugin uses "default" to set the default template
48
+
-- a default template that gotess will use.
49
+
-- gotets doesn't have template named `default`, we use it to represent absence of the provided template.
50
50
template = "default",
51
+
51
52
-- path to a directory containing custom test code templates
52
53
---@type string|nil
53
54
template_dir = nil,
54
-
-- switch table tests from using slice to map (with test name for the key)
55
+
56
+
-- use named tests(map with test name as key) in table tests(slice of structs by default)
55
57
named = false,
56
58
},
57
59
---@class gopher.ConfigGoTag
···
61
63
62
64
-- default tags to add to struct fields
63
65
default_tag = "json",
66
+
67
+
-- default tag option added struct fields, set to nil to disable
68
+
-- e.g: `option = "json=omitempty,xml=omitempty`
69
+
---@type string|nil
70
+
option = nil,
64
71
},
72
+
---@class gopher.ConfigIfErr
65
73
iferr = {
66
-
-- choose a custom error message
74
+
-- choose a custom error message, nil to use default
75
+
-- e.g: `message = 'fmt.Errorf("failed to %w", err)'`
67
76
---@type string|nil
68
77
message = nil,
69
78
},
79
+
---@class gopher.ConfigJson2Go
80
+
json2go = {
81
+
-- command used to open interactive input.
82
+
-- e.g: `split`, `botright split`, `tabnew`
83
+
interactive_cmd = "vsplit",
84
+
85
+
-- name of autogenerated struct, if nil none, will the default one of json2go.
86
+
-- e.g: "MySuperCoolName"
87
+
---@type string|nil
88
+
type_name = nil,
89
+
},
70
90
}
71
91
--minidoc_afterlines_end
72
92
···
91
111
vim.validate("log_level", _config.log_level, "number")
92
112
vim.validate("timeout", _config.timeout, "number")
93
113
vim.validate("installer_timeout", _config.timeout, "number")
94
-
vim.validate("restart_lsp", _config.restart_lsp, "boolean")
95
114
vim.validate("commands", _config.commands, "table")
96
115
vim.validate("commands.go", _config.commands.go, "string")
97
116
vim.validate("commands.gomodifytags", _config.commands.gomodifytags, "string")
98
117
vim.validate("commands.gotests", _config.commands.gotests, "string")
99
118
vim.validate("commands.impl", _config.commands.impl, "string")
100
119
vim.validate("commands.iferr", _config.commands.iferr, "string")
120
+
vim.validate("commands.json2go", _config.commands.json2go, "string")
101
121
vim.validate("gotests", _config.gotests, "table")
102
122
vim.validate("gotests.template", _config.gotests.template, "string")
103
-
vim.validate("gotests.template_dir", _config.gotests.template_dir, "string", true)
123
+
vim.validate("gotests.template_dir", _config.gotests.template_dir, { "string", "nil" })
104
124
vim.validate("gotests.named", _config.gotests.named, "boolean")
105
125
vim.validate("gotag", _config.gotag, "table")
106
126
vim.validate("gotag.transform", _config.gotag.transform, "string")
107
127
vim.validate("gotag.default_tag", _config.gotag.default_tag, "string")
128
+
vim.validate("gotag.option", _config.gotag.option, { "string", "nil" })
108
129
vim.validate("iferr", _config.iferr, "table")
109
-
vim.validate("iferr.message", _config.iferr.message, "string", true)
130
+
vim.validate("iferr.message", _config.iferr.message, { "string", "nil" })
131
+
vim.validate("json2go.installer_timeout", _config.json2go.interactive_cmd, "string")
132
+
vim.validate("json2go.type_name", _config.json2go.type_name, { "string", "nil" })
110
133
end
111
134
112
135
setmetatable(config, {
-10
lua/gopher/go.lua
-10
lua/gopher/go.lua
···
1
1
local c = require "gopher.config"
2
2
local u = require "gopher._utils"
3
-
local lsp = require "gopher._utils.lsp"
4
3
local r = require "gopher._utils.runner"
5
4
local go = {}
6
5
···
14
13
return rs.stdout
15
14
end
16
15
17
-
local function restart_lsp()
18
-
if c.restart_lsp then
19
-
lsp.restart()
20
-
end
21
-
end
22
-
23
16
---@param args string[]
24
17
function go.get(args)
25
18
for i, arg in ipairs(args) do
···
29
22
end
30
23
31
24
run("get", args)
32
-
restart_lsp()
33
25
end
34
26
35
27
---@param args string[]
36
28
function go.mod(args)
37
29
run("mod", args)
38
-
restart_lsp()
39
30
end
40
31
41
32
---@param args string[]
···
43
34
-- TODO: use `gopls.tidy`
44
35
45
36
run("work", args)
46
-
restart_lsp()
47
37
end
48
38
49
39
---Executes `go generate`
+7
-6
lua/gopher/gotests.lua
+7
-6
lua/gopher/gotests.lua
···
3
3
---@text gotests is utilizing the `gotests` tool to generate unit tests boilerplate.
4
4
---@usage
5
5
--- - Generate unit test for specific function/method:
6
-
--- 1. Place your cursor on the desired function/method.
7
-
--- 2. Run `:GoTestAdd`
6
+
--- 1. Place your cursor on the desired function/method.
7
+
--- 2. Run `:GoTestAdd`
8
8
---
9
9
--- - Generate unit tests for *all* functions/methods in current file:
10
-
--- - run `:GoTestsAll`
10
+
--- - run `:GoTestsAll`
11
11
---
12
12
--- - Generate unit tests *only* for *exported(public)* functions/methods:
13
-
--- - run `:GoTestsExp`
13
+
--- - run `:GoTestsExp`
14
14
---
15
-
--- You can also specify the template to use for generating the tests. See |gopher.nvim-config|
16
-
--- More details about templates can be found at: https://github.com/cweill/gotests
15
+
--- You can also specify the template to use for generating the tests.
16
+
--- See |gopher.nvim-config|.
17
+
--- More details about templates: https://github.com/cweill/gotests
17
18
---
18
19
--- If you prefer named tests, you can enable them in |gopher.nvim-config|.
19
20
+33
-37
lua/gopher/health.lua
+33
-37
lua/gopher/health.lua
···
1
+
local c = require("gopher.config").commands
1
2
local health = {}
2
-
local cmd = require("gopher.config").commands
3
3
4
4
local deps = {
5
+
vim_version = "nvim-0.10",
5
6
bin = {
6
7
{
7
-
bin = cmd.go,
8
+
bin = c.go,
8
9
msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`, `:GoInstallDepsSync`",
9
-
optional = false,
10
10
},
11
-
{ bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = true },
12
-
{ bin = cmd.impl, msg = "required for `:GoImpl`", optional = true },
13
-
{ bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = true },
14
-
{
15
-
bin = cmd.gotests,
16
-
msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`",
17
-
optional = true,
18
-
},
11
+
{ bin = c.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`" },
12
+
{ bin = c.impl, msg = "required for `:GoImpl`" },
13
+
{ bin = c.iferr, msg = "required for `:GoIfErr`" },
14
+
{ bin = c.gotests, msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`" },
19
15
},
20
16
treesitter = {
21
17
{ parser = "go", msg = "required for most of the parts of `gopher.nvim`" },
22
18
},
23
19
}
24
20
25
-
---@param bin string
26
-
---@return boolean
27
-
local function is_binary_found(bin)
28
-
return vim.fn.executable(bin) == 1
21
+
---@param bin {bin:string, msg:string, optional:boolean}
22
+
local function check_binary(bin)
23
+
if vim.fn.executable(bin.bin) == 1 then
24
+
vim.health.ok(bin.bin .. " is found oh PATH: `" .. vim.fn.exepath(bin.bin) .. "`")
25
+
else
26
+
vim.health.error(bin.bin .. " not found on PATH, " .. bin.msg)
27
+
end
29
28
end
30
29
31
-
---@param ft string
32
-
---@return boolean
33
-
local function is_treesitter_parser_available(ft)
34
-
local ok, parser = pcall(vim.treesitter.get_parser, 0, ft)
35
-
return ok and parser ~= nil
30
+
---@param ts {parser:string, msg:string}
31
+
local function check_treesitter(ts)
32
+
local ok, parser = pcall(vim.treesitter.get_parser, 0, ts.parser)
33
+
if ok and parser ~= nil then
34
+
vim.health.ok("`" .. ts.parser .. "` parser is installed")
35
+
else
36
+
vim.health.error("`" .. ts.parser .. "` parser not found")
37
+
end
36
38
end
37
39
38
40
function health.check()
39
-
vim.health.start "required binaries"
40
-
vim.health.info "all those binaries can be installed by `:GoInstallDeps`"
41
+
vim.health.start "Neovim version"
42
+
if vim.fn.has(deps.vim_version) == 1 then
43
+
vim.health.ok "Neovim version is compatible"
44
+
else
45
+
vim.health.error(deps.vim_version .. " or newer is required")
46
+
end
47
+
48
+
vim.health.start "Required binaries (those can be installed with `:GoInstallDeps`)"
41
49
for _, bin in ipairs(deps.bin) do
42
-
if is_binary_found(bin.bin) then
43
-
vim.health.ok(bin.bin .. " installed")
44
-
else
45
-
if bin.optional then
46
-
vim.health.warn(bin.bin .. " not found, " .. bin.msg)
47
-
else
48
-
vim.health.error(bin.bin .. " not found, " .. bin.msg)
49
-
end
50
-
end
50
+
check_binary(bin)
51
51
end
52
52
53
-
vim.health.start "required treesitter parsers"
53
+
vim.health.start "Treesitter"
54
54
for _, parser in ipairs(deps.treesitter) do
55
-
if is_treesitter_parser_available(parser.parser) then
56
-
vim.health.ok(parser.parser .. " parser installed")
57
-
else
58
-
vim.health.error(parser.parser .. " parser not found, " .. parser.msg)
59
-
end
55
+
check_treesitter(parser)
60
56
end
61
57
end
62
58
+1
-1
lua/gopher/iferr.lua
+1
-1
lua/gopher/iferr.lua
+14
-13
lua/gopher/impl.lua
+14
-13
lua/gopher/impl.lua
···
3
3
---@text
4
4
--- Integration of `impl` tool to generate method stubs for interfaces.
5
5
---
6
-
---@usage 1. Automatically implement an interface for a struct:
7
-
--- - Place your cursor on the struct where you want to implement the interface.
8
-
--- - Run `:GoImpl io.Reader`
9
-
--- - This will automatically determine the receiver and implement the `io.Reader` interface.
6
+
---@usage
7
+
--- 1. Automatically implement an interface for a struct:
8
+
--- - Place your cursor on the struct where you want to implement the interface.
9
+
--- - Run `:GoImpl io.Reader`
10
+
--- - This will automatically determine the receiver and implement the `io.Reader` interface.
10
11
---
11
12
--- 2. Specify a custom receiver:
12
-
--- - Place your cursor on the struct
13
-
--- - Run `:GoImpl w io.Writer`, where:
14
-
--- - `w` is the receiver.
15
-
--- - `io.Writer` is the interface to implement.
13
+
--- - Place your cursor on the struct
14
+
--- - Run `:GoImpl w io.Writer`, where:
15
+
--- - `w` is the receiver.
16
+
--- - `io.Writer` is the interface to implement.
16
17
---
17
18
--- 3. Explicitly specify the receiver, struct, and interface:
18
-
--- - No need to place the cursor on the struct if all arguments are provided.
19
-
--- - Run `:GoImpl r RequestReader io.Reader`, where:
20
-
--- - `r` is the receiver.
21
-
--- - `RequestReader` is the struct.
22
-
--- - `io.Reader` is the interface to implement.
19
+
--- - No need to place the cursor on the struct if all arguments are provided.
20
+
--- - Run `:GoImpl r RequestReader io.Reader`, where:
21
+
--- - `r` is the receiver.
22
+
--- - `RequestReader` is the struct.
23
+
--- - `io.Reader` is the interface to implement.
23
24
---
24
25
--- Example:
25
26
--- >go
+5
-5
lua/gopher/init.lua
+5
-5
lua/gopher/init.lua
···
35
35
36
36
---@toc_entry Install dependencies
37
37
---@tag gopher.nvim-dependencies
38
-
---@text Gopher.nvim implements most of its features using third-party tools.
39
-
--- To install these tools, you can run `:GoInstallDeps` command
40
-
--- or call `require("gopher").install_deps()` if you want to use lua api.
41
-
--- By default dependencies will be installed asynchronously,
42
-
--- to install them synchronously pass `{sync = true}` as an argument.
38
+
---@text
39
+
--- Gopher.nvim implements most of its features using third-party tools. To
40
+
--- install plugin's dependencies, you can run:
41
+
--- `:GoInstallDeps` or `:GoInstallDepsSync`
42
+
--- or use `require("gopher").install_deps()` if you prefer lua api.
43
43
gopher.install_deps = require("gopher.installer").install_deps
44
44
45
45
gopher.impl = require("gopher.impl").impl
+1
lua/gopher/installer.lua
+1
lua/gopher/installer.lua
+137
lua/gopher/json2go.lua
+137
lua/gopher/json2go.lua
···
1
+
---@toc_entry json2go
2
+
---@tag gopher.nvim-json2go
3
+
---@text
4
+
--- Convert json to go type annotations.
5
+
---
6
+
---@usage
7
+
--- `:GoJson` opens a temporary buffer where you can paste or write JSON.
8
+
--- Saving the buffer (`:w` or `:wq`) automatically closes it and inserts the
9
+
--- generated Go struct into the original buffer at the cursor position.
10
+
---
11
+
--- Alternatively, you can pass JSON directly as an argument:
12
+
--- >vim
13
+
--- :GoJson {"name": "Alice", "age": 30}
14
+
--- <
15
+
16
+
local c = require "gopher.config"
17
+
local log = require "gopher._utils.log"
18
+
local u = require "gopher._utils"
19
+
local r = require "gopher._utils.runner"
20
+
local json2go = {}
21
+
22
+
---@dochide
23
+
---@param bufnr integer
24
+
---@param cpos integer
25
+
---@param type_ string
26
+
local function apply(bufnr, cpos, type_)
27
+
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
28
+
local input_lines = u.remove_empty_lines(vim.split(type_, "\n"))
29
+
for i, line in pairs(input_lines) do
30
+
table.insert(lines, cpos + i, line)
31
+
end
32
+
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
33
+
end
34
+
35
+
-- Convert json string to go type.
36
+
---
37
+
---@param json_str string Json string that is going to be converted to go type.
38
+
---@return string? Go type, or nil if failed.
39
+
function json2go.transform(json_str)
40
+
local cmd = { c.commands.json2go }
41
+
if c.json2go.type_name then
42
+
table.insert(cmd, "-type", c.json2go.type_name)
43
+
end
44
+
45
+
local rs = r.sync(cmd, { stdin = json_str })
46
+
if rs.code ~= 0 then
47
+
u.notify("json2go: got this error: " .. rs.stdout, vim.log.levels.ERROR)
48
+
log.error("json2go: got this error: " .. rs.stdout)
49
+
return
50
+
end
51
+
return rs.stdout
52
+
end
53
+
54
+
---@dochide
55
+
---@param ocpos integer
56
+
local function interactive(ocpos)
57
+
local obuf = vim.api.nvim_get_current_buf()
58
+
local owin = vim.api.nvim_get_current_win()
59
+
60
+
-- setup the input window
61
+
local buf = vim.api.nvim_create_buf(false, true)
62
+
vim.cmd(c.json2go.interactive_cmd)
63
+
64
+
local win = vim.api.nvim_get_current_win()
65
+
vim.api.nvim_win_set_buf(win, buf)
66
+
vim.api.nvim_buf_set_name(buf, "[GoJson input]")
67
+
vim.api.nvim_set_option_value("filetype", "jsonc", { buf = buf })
68
+
vim.api.nvim_set_option_value("buftype", "acwrite", { buf = buf })
69
+
vim.api.nvim_set_option_value("swapfile", false, { buf = buf })
70
+
vim.api.nvim_set_option_value("bufhidden", "delete", { buf = buf })
71
+
vim.api.nvim_buf_set_lines(buf, 0, -1, false, {
72
+
"// Write your json here.",
73
+
"// Writing and quitting (:wq), will generate go struct under the cursor.",
74
+
"",
75
+
"",
76
+
})
77
+
78
+
vim.api.nvim_create_autocmd("BufLeave", { buffer = buf, command = "stopinsert" })
79
+
vim.api.nvim_create_autocmd("BufWriteCmd", {
80
+
buffer = buf,
81
+
once = true,
82
+
callback = function()
83
+
local input = vim.api.nvim_buf_get_lines(buf, 0, -1, true)
84
+
local inp = table.concat(
85
+
vim
86
+
.iter(input)
87
+
:filter(function(line)
88
+
local found = string.find(line, "^//.*")
89
+
return (not found) or (line == "")
90
+
end)
91
+
:totable(),
92
+
"\n"
93
+
)
94
+
95
+
local go_type = json2go.transform(inp)
96
+
if not go_type then
97
+
error "cound't convert json to go type"
98
+
end
99
+
100
+
vim.api.nvim_set_option_value("modified", false, { buf = buf })
101
+
apply(obuf, ocpos, go_type)
102
+
103
+
vim.api.nvim_set_current_win(owin)
104
+
vim.api.nvim_win_set_cursor(owin, { ocpos + 1, 0 })
105
+
106
+
vim.schedule(function()
107
+
pcall(vim.api.nvim_win_close, win, true)
108
+
pcall(vim.api.nvim_buf_delete, buf, {})
109
+
end)
110
+
end,
111
+
})
112
+
113
+
vim.cmd "normal! G"
114
+
vim.cmd "startinsert"
115
+
end
116
+
117
+
--- Converts json string to go type, and puts result under the cursor. If
118
+
--- [json_str] is nil, will open an interactive prompt (with cmd set in
119
+
--- config).
120
+
---
121
+
---@param json_str? string
122
+
function json2go.json2go(json_str)
123
+
local cur_line = vim.api.nvim_win_get_cursor(0)[1]
124
+
if not json_str then
125
+
interactive(cur_line)
126
+
return
127
+
end
128
+
129
+
local go_type = json2go.transform(json_str)
130
+
if not go_type then
131
+
error "cound't convert json to go type"
132
+
end
133
+
134
+
apply(0, cur_line, go_type)
135
+
end
136
+
137
+
return json2go
+9
-4
plugin/gopher.lua
+9
-4
plugin/gopher.lua
···
48
48
-- :GoTag
49
49
cmd("GoTagAdd", function(opts)
50
50
require("gopher").tags.add {
51
-
tags = opts.fargs,
51
+
input = opts.fargs,
52
52
range = (opts.count ~= -1) and {
53
53
start = opts.line1,
54
54
end_ = opts.line2,
···
58
58
59
59
cmd("GoTagRm", function(opts)
60
60
require("gopher").tags.rm {
61
-
tags = opts.fargs,
61
+
input = opts.fargs,
62
62
range = (opts.count ~= -1) and {
63
63
start = opts.line1,
64
64
end_ = opts.line2,
···
70
70
require("gopher").tags.clear()
71
71
end)
72
72
73
+
-- :GoJson
74
+
cmd("GoJson", function(opts)
75
+
local inp = ((opts.args ~= "" and opts.args) or nil)
76
+
require("gopher.json2go").json2go(inp)
77
+
end, "*")
78
+
73
79
-- :GoTest
74
80
cmd("GoTestAdd", function()
75
81
require("gopher").test.add()
···
89
95
end, "*")
90
96
91
97
cmd("GoGet", function(opts)
92
-
vim.print(opts)
93
98
require("gopher").get(opts.fargs)
94
99
end, "*")
95
100
···
98
103
end, "*")
99
104
100
105
cmd("GoGenerate", function(opts)
101
-
require("gopher").generate(opts.fargs or "")
106
+
require("gopher").generate(opts.fargs or { "" })
102
107
end, "?")
+1
scripts/docgen.lua
+1
scripts/docgen.lua
-1
scripts/minimal_init.lua
-1
scripts/minimal_init.lua
+5
spec/fixtures/json2go/interativly_output.go
+5
spec/fixtures/json2go/interativly_output.go
+7
spec/fixtures/json2go/manual_output.go
+7
spec/fixtures/json2go/manual_output.go
+2
-1
spec/integration/iferr_test.lua
+2
-1
spec/integration/iferr_test.lua
+25
spec/integration/json2go_test.lua
+25
spec/integration/json2go_test.lua
···
1
+
local t = require "spec.testutils"
2
+
local child, T, json2go = t.setup "json2go"
3
+
4
+
json2go["should convert interativly"] = function()
5
+
local rs = t.setup_test("json2go/interativly", child, { 2, 0 })
6
+
child.cmd "GoJson"
7
+
child.type_keys [[{"json": true}]]
8
+
child.type_keys "<Esc>"
9
+
child.cmd "wq" -- quit prompt
10
+
child.cmd "write" -- the fixture file
11
+
12
+
t.eq(t.readfile(rs.tmp), rs.fixtures.output)
13
+
t.cleanup(rs)
14
+
end
15
+
16
+
json2go["should convert argument"] = function()
17
+
local rs = t.setup_test("json2go/manual", child, { 2, 0 })
18
+
child.cmd [[GoJson {"user": {"name": "user-ovic"}}]]
19
+
child.cmd "write"
20
+
21
+
t.eq(t.readfile(rs.tmp), rs.fixtures.output)
22
+
t.cleanup(rs)
23
+
end
24
+
25
+
return T
+68
spec/unit/struct_tag_test.lua
+68
spec/unit/struct_tag_test.lua
···
1
+
local t = require "spec.testutils"
2
+
local _, T, st = t.setup "struct_tags"
3
+
4
+
st["should parse tags"] = function()
5
+
local out = require("gopher.struct_tags").parse_args { "json", "yaml", "etc" }
6
+
7
+
t.eq(out.tags, "json,yaml,etc")
8
+
t.eq(out.options, "")
9
+
end
10
+
11
+
st["should parse tags separated by commas"] = function()
12
+
local out = require("gopher.struct_tags").parse_args { "json,yaml,etc" }
13
+
14
+
t.eq(out.tags, "json,yaml,etc")
15
+
t.eq(out.options, "")
16
+
end
17
+
18
+
st["should parse tags separated by command and spaces"] = function()
19
+
local out = require("gopher.struct_tags").parse_args {
20
+
"json,yaml",
21
+
"json=omitempty",
22
+
"xml=something",
23
+
}
24
+
25
+
t.eq(out.tags, "json,yaml,xml")
26
+
t.eq(out.options, "json=omitempty,xml=something")
27
+
end
28
+
29
+
st["should parse tag with an option"] = function()
30
+
local out = require("gopher.struct_tags").parse_args {
31
+
"json=omitempty",
32
+
"xml",
33
+
"xml=theoption",
34
+
}
35
+
36
+
t.eq(out.tags, "json,xml")
37
+
t.eq(out.options, "json=omitempty,xml=theoption")
38
+
end
39
+
40
+
st["should parse tags with an option"] = function()
41
+
local out = require("gopher.struct_tags").parse_args { "json=omitempty", "yaml" }
42
+
43
+
t.eq(out.tags, "json,yaml")
44
+
t.eq(out.options, "json=omitempty")
45
+
end
46
+
47
+
st["should parse tags with an option separated with comma"] = function()
48
+
local out = require("gopher.struct_tags").parse_args { "json=omitempty,yaml" }
49
+
50
+
t.eq(out.tags, "json,yaml")
51
+
t.eq(out.options, "json=omitempty")
52
+
end
53
+
54
+
st["should parse tags with options specified separately"] = function()
55
+
local out = require("gopher.struct_tags").parse_args { "json", "yaml", "json=omitempty" }
56
+
57
+
t.eq(out.tags, "json,yaml")
58
+
t.eq(out.options, "json=omitempty")
59
+
end
60
+
61
+
st["should parse tags with options specified separately and separated by comma"] = function()
62
+
local out = require("gopher.struct_tags").parse_args { "json,yaml,json=omitempty" }
63
+
64
+
t.eq(out.tags, "json,yaml")
65
+
t.eq(out.options, "json=omitempty")
66
+
end
67
+
68
+
return T
+10
spec/unit/utils_test.lua
+10
spec/unit/utils_test.lua
···
46
46
t.eq("\t\t", u.indent(line, indent))
47
47
end
48
48
49
+
utils["should .list_unique on list with duplicates"] = function()
50
+
local u = require "gopher._utils"
51
+
t.eq({ "json", "xml" }, u.list_unique { "json", "xml", "json" })
52
+
end
53
+
54
+
utils["should .list_unique on list with no duplicates"] = function()
55
+
local u = require "gopher._utils"
56
+
t.eq({ "json", "xml" }, u.list_unique { "json", "xml" })
57
+
end
58
+
49
59
return T
+3
vhs/Taskfile.yml
+3
vhs/Taskfile.yml
vhs/iferr.gif
vhs/iferr.gif
This is a binary file and will not be displayed.
+1
-1
vhs/iferr.go
+1
-1
vhs/iferr.go
vhs/json2go.gif
vhs/json2go.gif
This is a binary file and will not be displayed.
+27
vhs/json2go.tape
+27
vhs/json2go.tape
···
1
+
Output json2go.gif
2
+
Require nvim
3
+
Require json2go
4
+
5
+
Set FontFamily "JetBrains Mono"
6
+
Set Height 800
7
+
Set Width 1500
8
+
Set Padding 20
9
+
Set Shell "bash"
10
+
Set Theme "tokyonight"
11
+
Set TypingSpeed 250ms
12
+
13
+
Hide Type@0ms "./nvim.sh json2go.go" Enter Show
14
+
15
+
Type@0ms "G"
16
+
Type@400ms ":GoJson" Sleep 500ms Enter
17
+
Type@70ms "{" Enter
18
+
Type@70ms `"json": true,` Enter
19
+
Type@70ms `"user": {"name": "Name", "of_age": true}` Enter
20
+
Type@70ms "}"
21
+
Escape Type@500ms ":wq" Enter
22
+
Sleep 1s
23
+
24
+
Type@25ms "G2o" Escape
25
+
Type@120ms `:GoJson {"json": true}` Enter
26
+
27
+
Sleep 5s