+4
.envrc
+4
.envrc
+42
-3
.github/workflows/linters.yml
+42
-3
.github/workflows/linters.yml
···
1
1
name: linters
2
-
on: [push, pull_request]
2
+
3
+
on:
4
+
push:
5
+
branches:
6
+
- main
7
+
- develop
8
+
pull_request:
3
9
4
10
jobs:
5
11
linters:
6
-
name: linters
12
+
name: Lua
7
13
runs-on: ubuntu-latest
8
14
steps:
9
-
- uses: actions/checkout@v3
15
+
- uses: actions/checkout@v4
10
16
- uses: JohnnyMorganz/stylua-action@v3
11
17
with:
12
18
token: ${{ secrets.GITHUB_TOKEN }}
···
17
23
with:
18
24
token: ${{ secrets.GITHUB_TOKEN }}
19
25
args: .
26
+
27
+
docs:
28
+
name: Docs
29
+
runs-on: ubuntu-latest
30
+
steps:
31
+
- uses: actions/checkout@v4
32
+
33
+
- name: Install Task
34
+
uses: arduino/setup-task@v1
35
+
with:
36
+
version: 3.x
37
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
38
+
39
+
- name: Install NeoVim
40
+
uses: rhysd/action-setup-vim@v1
41
+
with:
42
+
neovim: true
43
+
version: stable
44
+
45
+
- name: Cache .tests
46
+
uses: actions/cache@v4
47
+
with:
48
+
path: |
49
+
${{ github.workspace }}/.tests
50
+
key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }}
51
+
52
+
- name: Generate docs
53
+
run: task docgen
54
+
55
+
- name: Diff
56
+
run: |
57
+
git diff doc
58
+
exit $(git status --porcelain doc | wc -l | tr -d " ")
+38
-23
.github/workflows/tests.yml
+38
-23
.github/workflows/tests.yml
···
1
1
name: tests
2
-
on: [push, pull_request]
2
+
3
+
on:
4
+
push:
5
+
branches:
6
+
- main
7
+
- develop
8
+
pull_request:
3
9
4
10
jobs:
5
11
tests:
6
12
strategy:
7
13
matrix:
8
14
os: [ubuntu-latest]
9
-
nvim_version:
15
+
version:
16
+
- stable
10
17
- nightly
11
-
- v0.7.0
12
-
- v0.7.2
13
-
- v0.8.0
14
-
- v0.8.1
15
-
- v0.8.2
16
-
- v0.8.3
17
-
- v0.9.0
18
-
- v0.9.1
19
-
- v0.9.2
20
-
- v0.9.4
21
-
- v0.9.5
22
-
- v0.10.0
23
18
runs-on: ${{ matrix.os }}
24
19
steps:
25
20
- name: Install Task
···
28
23
version: 3.x
29
24
repo-token: ${{ secrets.GITHUB_TOKEN }}
30
25
31
-
- uses: actions/checkout@v3
26
+
- name: Install Go
27
+
uses: actions/setup-go@v5
28
+
with:
29
+
go-version: "1.24.0"
30
+
check-latest: false
31
+
32
+
- name: Install NeoVim
33
+
uses: rhysd/action-setup-vim@v1
34
+
with:
35
+
neovim: true
36
+
version: ${{ matrix.version }}
32
37
33
-
- name: Install Neovim
38
+
- uses: actions/checkout@v4
39
+
40
+
- name: Cache .tests
41
+
uses: actions/cache@v4
42
+
with:
43
+
path: |
44
+
${{ github.workspace }}/.tests
45
+
~/.cache/go-build
46
+
~/go/pkg/mod
47
+
key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }}
48
+
49
+
- name: Install Go bins
34
50
run: |
35
-
mkdir -p /tmp/nvim
36
-
wget -q https://github.com/neovim/neovim/releases/download/${{ matrix.nvim_version }}/nvim.appimage -O /tmp/nvim/nvim.appimage
37
-
cd /tmp/nvim
38
-
chmod a+x ./nvim.appimage
39
-
./nvim.appimage --appimage-extract
40
-
echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH
51
+
# TODO: install with :GoInstallDeps
52
+
go install github.com/fatih/gomodifytags@latest
53
+
go install github.com/josharian/impl@latest
54
+
go install github.com/cweill/gotests/...@latest
55
+
go install github.com/koron/iferr@latest
41
56
42
57
- name: Run Tests
43
58
run: |
44
59
nvim --version
45
-
task test
60
+
task tests
-10
.luarc.json
-10
.luarc.json
+5
-9
CONTRIBUTING.md
+5
-9
CONTRIBUTING.md
···
18
18
```bash
19
19
sudo pacman -S selene stylua
20
20
# or whatever is your package manager
21
-
# or way of installing pkgs
22
21
```
23
22
24
23
For formatting use this following commands, or setup your editor to integrate with selene/stylua:
25
24
```bash
26
-
task format
27
-
task format:check # will check if your code formatted
28
-
task lint
25
+
task stylua
26
+
task lint # lintering and format chewing
29
27
```
30
28
31
29
### Documentation
···
39
37
```
40
38
41
39
### Commit messages
40
+
42
41
We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), please follow it.
43
42
44
43
### Testing
45
44
46
-
For testing this plugins uses [plenary.nvim](https://github.com/nvim-lua/plenary.nvim).
47
-
All tests live in [/spec](https://github.com/olexsmir/gopher.nvim/tree/main/spec) dir.
45
+
For testing this plugins uses [mini.test](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-test.md).
46
+
All tests live in [/spec](./spec) dir.
48
47
49
48
You can run tests with:
50
49
```bash
51
-
task test
52
-
# also there are some aliases for that
53
50
task tests
54
-
task spec
55
51
```
+21
LICENSE
+21
LICENSE
···
1
+
MIT License
2
+
3
+
Copyright (c) 2025 Oleksandr Smirnov
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy
6
+
of this software and associated documentation files (the "Software"), to deal
7
+
in the Software without restriction, including without limitation the rights
8
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+
copies of the Software, and to permit persons to whom the Software is
10
+
furnished to do so, subject to the following conditions:
11
+
12
+
The above copyright notice and this permission notice shall be included in all
13
+
copies or substantial portions of the Software.
14
+
15
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+
SOFTWARE.
+19
-27
README.md
+19
-27
README.md
···
10
10
11
11
## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim))
12
12
13
-
Pre-dependency:
13
+
Requirements:
14
14
15
-
- [Go](https://github.com/golang/go)
16
-
- `go` treesitter parser, install by `:TSInstall go`
15
+
- **Neovim 0.10** or later
16
+
- Treesitter `go` parser(`:TSInstall go`)
17
+
- [Go](https://github.com/golang/go) installed (tested on 1.23)
17
18
18
19
```lua
19
20
{
20
21
"olexsmir/gopher.nvim",
21
22
ft = "go",
22
-
-- branch = "develop", -- if you want develop branch
23
-
-- keep in mind, it might break everything
23
+
-- branch = "develop"
24
24
dependencies = {
25
-
"nvim-lua/plenary.nvim",
26
25
"nvim-treesitter/nvim-treesitter",
27
-
"mfussenegger/nvim-dap", -- (optional) only if you use `gopher.dap`
28
26
},
29
27
-- (optional) will update plugin's deps on every update
30
28
build = function()
···
35
33
}
36
34
```
37
35
38
-
## Configuratoin
36
+
## Configuration
39
37
40
38
> [!IMPORTANT]
41
39
>
42
40
> If you need more info look `:h gopher.nvim`
43
41
44
-
**Take a look at default options**
42
+
**Take a look at default options (might be a bit outdated, look `:h gopher.nvim-config`)**
45
43
46
44
```lua
47
45
require("gopher").setup {
46
+
-- log level, you might consider using DEBUG or TRACE for debugging the plugin
47
+
log_level = vim.log.levels.INFO,
48
+
49
+
-- timeout for running internal commands
50
+
timeout = 2000,
51
+
48
52
commands = {
49
53
go = "go",
50
54
gomodifytags = "gomodifytags",
51
55
gotests = "gotests",
52
56
impl = "impl",
53
57
iferr = "iferr",
54
-
dlv = "dlv",
55
58
},
56
59
gotests = {
57
60
-- gotests doesn't have template named "default" so this plugin uses "default" to set the default template
···
59
62
-- path to a directory containing custom test code templates
60
63
template_dir = nil,
61
64
-- switch table tests from using slice to map (with test name for the key)
62
-
-- works only with gotests installed from develop branch
63
65
named = false,
64
66
},
65
67
gotag = {
66
68
transform = "snakecase",
69
+
-- default tags to add to struct fields
70
+
default_tag = "json",
71
+
},
72
+
iferr = {
73
+
-- choose a custom error message
74
+
message = nil,
67
75
},
68
76
}
69
77
```
···
87
95
- [impl](https://github.com/josharian/impl)
88
96
- [gotests](https://github.com/cweill/gotests)
89
97
- [iferr](https://github.com/koron/iferr)
90
-
- [dlv](github.com/go-delve/delve/cmd/dlv)
91
98
</details>
92
99
93
100
<details>
···
215
222
```
216
223
</details>
217
224
218
-
<details>
219
-
<summary>
220
-
<b>Setup <a href="https://github.com/mfussenegger/nvim-dap">nvim-dap</a> for go in one line</b>
221
-
</summary>
222
-
223
-
THIS FEATURE WILL BE REMOVED IN `0.1.6`
224
-
225
-
note [nvim-dap](https://github.com/mfussenegger/nvim-dap) has to be installed
226
-
227
-
```lua
228
-
require("gopher.dap").setup()
229
-
```
230
-
</details>
231
-
232
225
## Contributing
233
226
234
227
PRs are always welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md)
···
236
229
## Thanks
237
230
238
231
- [go.nvim](https://github.com/ray-x/go.nvim)
239
-
- [nvim-dap-go](https://github.com/leoluz/nvim-dap-go)
240
232
- [iferr](https://github.com/koron/iferr)
+7
-22
Taskfile.yml
+7
-22
Taskfile.yml
···
1
1
version: "3"
2
2
tasks:
3
-
format:
4
-
desc: formats all lua files in repo
5
-
cmds:
6
-
- stylua .
7
-
8
3
lint:
9
4
desc: runs all linters
10
5
cmds:
11
6
- task: selene
12
-
- task: stylua:check
7
+
- stylua --check .
13
8
14
9
selene:
15
10
desc: runs lua linter(selene)
16
11
cmds:
17
12
- selene .
18
13
19
-
stylua:check:
20
-
desc: runs stylua in check mode
21
-
cmds:
22
-
- stylua --check .
23
-
24
14
stylua:
25
15
desc: runs lua formatter
26
16
cmds:
27
17
- stylua .
28
18
29
-
test:
30
-
desc: runs all tests
31
-
aliases: [tests, spec]
19
+
tests:
20
+
desc: run all tests
32
21
cmds:
33
22
- |
34
-
nvim --headless \
35
-
-u ./scripts/minimal_init.lua \
36
-
-c "PlenaryBustedDirectory spec \
37
-
{minimal_init='./scripts/minimal_init.lua' \
38
-
,sequential=true}" \
39
-
-c ":qa!"
23
+
nvim --clean --headless \
24
+
-u ./scripts/minimal_init.lua \
25
+
-c "lua MiniTest.run()"
40
26
41
27
docgen:
42
28
desc: generate vimhelp
43
29
cmds:
44
30
- |
45
-
nvim --noplugin \
46
-
--headless \
31
+
nvim --clean --headless \
47
32
-u "./scripts/minimal_init.lua" \
48
33
-c "luafile ./scripts/docgen.lua" \
49
34
-c ":qa!"
-3
autoload/health/gopher.vim
-3
autoload/health/gopher.vim
+94
-94
doc/gopher.nvim.txt
+94
-94
doc/gopher.nvim.txt
···
1
-
*gopher.nvim*
1
+
*gopher.nvim* Enhance your golang experience
2
+
3
+
MIT License Copyright (c) 2025 Oleksandr Smirnov
2
4
3
5
==============================================================================
4
6
5
7
gopher.nvim is a minimalistic plugin for Go development in Neovim written in Lua.
6
8
It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim.
7
9
8
-
------------------------------------------------------------------------------
9
-
*gopher.nvim-table-of-contents*
10
10
Table of Contents
11
-
Setup....................................................|gopher.nvim-setup|
12
-
Install dependencies..............................|gopher.nvim-install-deps|
13
-
Configuration...........................................|gopher.nvim-config|
14
-
Modifty struct tags................................|gopher.nvim-struct-tags|
15
-
Auto implementation of interface methods..................|gopher.nvim-impl|
16
-
Generating unit tests boilerplate......................|gopher.nvim-gotests|
17
-
Iferr....................................................|gopher.nvim-iferr|
18
-
Generate comments.....................................|gopher.nvim-comments|
19
-
Setup `nvim-dap` for Go......................................|gopher.nvim-dap|
11
+
Setup ................................................ |gopher.nvim-setup()|
12
+
Install dependencies ............................ |gopher.nvim-dependencies|
13
+
Config ................................................ |gopher.nvim-config|
14
+
Commands ............................................ |gopher.nvim-commands|
15
+
Modify struct tags ............................... |gopher.nvim-struct-tags|
16
+
Auto implementation of interface methods ................ |gopher.nvim-impl|
17
+
Generating unit tests boilerplate .................... |gopher.nvim-gotests|
18
+
Iferr .................................................. |gopher.nvim-iferr|
19
+
Generate comments ................................... |gopher.nvim-comments|
20
20
21
21
------------------------------------------------------------------------------
22
-
*gopher.nvim-setup*
22
+
*gopher.nvim-setup()*
23
23
`gopher.setup`({user_config})
24
-
Setup function. This method simply merges default configs with opts table.
24
+
Setup function. This method simply merges default config with opts table.
25
25
You can read more about configuration at |gopher.nvim-config|
26
-
Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults|
26
+
Calling this function is optional, if you ok with default settings.
27
+
See |gopher.nvim.config|
27
28
28
29
Usage ~
29
-
`require("gopher").setup {}` (replace `{}` with your `config` table)
30
+
>lua
31
+
require("gopher").setup {} -- use default config or replace {} with your own
32
+
<
30
33
Parameters ~
31
-
{user_config} gopher.Config
34
+
{user_config} `(gopher.Config)` See |gopher.nvim-config|
32
35
33
36
------------------------------------------------------------------------------
34
-
*gopher.nvim-install-deps*
37
+
*gopher.nvim-dependencies*
35
38
`gopher.install_deps`
36
39
Gopher.nvim implements most of its features using third-party tools.
37
-
To install these tools, you can run `:GoInstallDeps` command
38
-
or call `require("gopher").install_deps()` if you want ues lua api.
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.
39
44
40
45
41
46
==============================================================================
42
47
------------------------------------------------------------------------------
43
48
*gopher.nvim-config*
44
-
config it is the place where you can configure the plugin.
45
-
also this is optional is you're ok with default settings.
46
-
You can look at default options |gopher.nvim-config-defaults|
47
-
48
-
------------------------------------------------------------------------------
49
-
*gopher.nvim-config-defaults*
50
49
`default_config`
51
50
>lua
52
51
local default_config = {
53
-
--minidoc_replace_end
54
-
55
-
-- log level, you might consider using DEBUG or TRACE for degugging the plugin
52
+
-- log level, you might consider using DEBUG or TRACE for debugging the plugin
56
53
---@type number
57
54
log_level = vim.log.levels.INFO,
55
+
56
+
-- timeout for running internal commands
57
+
---@type number
58
+
timeout = 2000,
58
59
59
60
-- user specified paths to binaries
60
61
---@class gopher.ConfigCommand
···
64
65
gotests = "gotests",
65
66
impl = "impl",
66
67
iferr = "iferr",
67
-
dlv = "dlv",
68
68
},
69
69
---@class gopher.ConfigGotests
70
70
gotests = {
···
74
74
---@type string|nil
75
75
template_dir = nil,
76
76
-- switch table tests from using slice to map (with test name for the key)
77
-
-- works only with gotests installed from develop branch
78
77
named = false,
79
78
},
80
79
---@class gopher.ConfigGoTag
81
80
gotag = {
82
81
---@type gopher.ConfigGoTagTransform
83
82
transform = "snakecase",
83
+
84
+
-- default tags to add to struct fields
85
+
default_tag = "json",
86
+
},
87
+
iferr = {
88
+
-- choose a custom error message
89
+
---@type string|nil
90
+
message = nil,
84
91
},
85
92
}
86
93
<
···
90
97
91
98
==============================================================================
92
99
------------------------------------------------------------------------------
100
+
*gopher.nvim-commands*
101
+
102
+
If don't want to automatically register plugins' commands,
103
+
you can set `vim.g.gopher_register_commands` to `false`, before loading the plugin.
104
+
105
+
106
+
==============================================================================
107
+
------------------------------------------------------------------------------
93
108
*gopher.nvim-struct-tags*
94
-
struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields.
109
+
110
+
`struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields.
111
+
95
112
Usage ~
96
-
- put your coursor on the struct
97
-
- run `:GoTagAdd json` to add json tags to struct fields
98
-
- run `:GoTagRm json` to remove json tags to struct fields
113
+
114
+
How to add/remove tags to struct fields:
115
+
1. Place cursor on the struct
116
+
2. Run `:GoTagAdd json` to add json tags to struct fields
117
+
3. Run `:GoTagRm json` to remove json tags to struct fields
118
+
119
+
To clear all tags from struct run: `:GoTagClear`
99
120
100
-
note: if you dont spesify the tag it will use `json` as default
121
+
NOTE: if you dont specify the tag it will use `json` as default
101
122
102
-
simple example:
123
+
Example:
103
124
>go
104
125
// before
105
126
type User struct {
···
116
137
}
117
138
<
118
139
119
-
120
140
==============================================================================
121
141
------------------------------------------------------------------------------
122
142
*gopher.nvim-impl*
123
-
impl is utilizing the `impl` tool to generate method stubs for interfaces.
143
+
144
+
Integration of `impl` tool to generate method stubs for interfaces.
145
+
124
146
Usage ~
147
+
1. Automatically implement an interface for a struct:
148
+
- Place your cursor on the struct where you want to implement the interface.
149
+
- Run `:GoImpl io.Reader`
150
+
- This will automatically determine the receiver and implement the `io.Reader` interface.
125
151
126
-
1. put your coursor on the struct on which you want implement the interface
127
-
and run `:GoImpl io.Reader`
128
-
which will automatically choose the reciver for the methods and
129
-
implement the `io.Reader` interface
130
-
2. same as previous but with custom receiver, so put your coursor on the struct
131
-
run `:GoImpl w io.Writer`
132
-
where `w` is the receiver and `io.Writer` is the interface
133
-
3. specift receiver, struct, and interface
134
-
there's no need to put your coursor on the struct if you specify all arguments
135
-
`:GoImpl r RequestReader io.Reader`
136
-
where `r` is the receiver, `RequestReader` is the struct and `io.Reader` is the interface
152
+
2. Specify a custom receiver:
153
+
- Place your cursor on the struct
154
+
- Run `:GoImpl w io.Writer`, where:
155
+
- `w` is the receiver.
156
+
- `io.Writer` is the interface to implement.
157
+
158
+
3. Explicitly specify the receiver, struct, and interface:
159
+
- No need to place the cursor on the struct if all arguments are provided.
160
+
- Run `:GoImpl r RequestReader io.Reader`, where:
161
+
- `r` is the receiver.
162
+
- `RequestReader` is the struct.
163
+
- `io.Reader` is the interface to implement.
137
164
138
-
simple example:
165
+
Example:
139
166
>go
140
167
type BytesReader struct{}
141
168
// ^ put your cursor here
···
143
170
144
171
// this is what you will get
145
172
func (b *BytesReader) Read(p []byte) (n int, err error) {
146
-
panic("not implemented") // TODO: Implement
173
+
panic("not implemented") // TODO: Implement
147
174
}
148
175
<
149
-
150
176
151
177
==============================================================================
152
178
------------------------------------------------------------------------------
···
154
180
gotests is utilizing the `gotests` tool to generate unit tests boilerplate.
155
181
Usage ~
156
182
157
-
- generate unit test for spesisfic function/method
158
-
- to specift the function/method put your cursor on it
159
-
- run `:GoTestAdd`
183
+
- Generate unit test for specific function/method:
184
+
1. Place your cursor on the desired function/method.
185
+
2. Run `:GoTestAdd`
160
186
161
-
- generate unit tests for all functions/methods in current file
187
+
- Generate unit tests for *all* functions/methods in current file:
162
188
- run `:GoTestsAll`
163
189
164
-
- generate unit tests only for exported(public) functions/methods
190
+
- Generate unit tests *only* for *exported(public)* functions/methods:
165
191
- run `:GoTestsExp`
166
192
167
-
you can also specify the template to use for generating the tests. see |gopher.nvim-config|
168
-
more details about templates can be found at: https://github.com/cweill/gotests
169
-
170
-
171
-
------------------------------------------------------------------------------
172
-
*gopher.nvim-gotests-named*
173
-
174
-
if you prefare using named tests, you can enable it in the config.
175
-
but you would need to install `gotests@develop` because stable version doesn't support this feature.
176
-
you can do it with:
177
-
>lua
178
-
-- simply run go get in your shell:
179
-
go install github.com/cweill/gotests/...@develop
193
+
You can also specify the template to use for generating the tests. See |gopher.nvim-config|
194
+
More details about templates can be found at: https://github.com/cweill/gotests
180
195
181
-
-- if you want to install it within neovim, you can use one of this:
182
-
183
-
vim.fn.jobstart("go install github.com/cweill/gotests/...@develop")
184
-
185
-
-- or if you want to use mason:
186
-
require("mason-tool-installer").setup {
187
-
ensure_installed = {
188
-
{ "gotests", version = "develop" },
189
-
}
190
-
}
191
-
<
192
-
193
-
if you choose to install `gotests` within neovim, i recommend adding it to your `build` section in your |lazy.nvim|
196
+
If you prefer named tests, you can enable them in |gopher.nvim-config|.
194
197
195
198
196
199
==============================================================================
197
200
------------------------------------------------------------------------------
198
201
*gopher.nvim-iferr*
199
-
if you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check.
202
+
203
+
`iferr` provides a way to way to automatically insert `if err != nil` check.
204
+
If you want to change `-message` option of `iferr` tool, see |gopher.nvim-config|
205
+
200
206
Usage ~
201
-
execute `:GoIfErr` near any err variable to insert the check
207
+
Execute `:GoIfErr` near any `err` variable to insert the check
202
208
203
209
204
210
==============================================================================
205
211
------------------------------------------------------------------------------
206
212
*gopher.nvim-comments*
207
-
Usage ~
208
-
Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line.
213
+
209
214
This module provides a way to generate comments for Go code.
210
215
211
-
212
-
==============================================================================
213
-
------------------------------------------------------------------------------
214
-
*gopher.nvim-dap*
215
-
This module sets up `nvim-dap` for Go.
216
216
Usage ~
217
-
just call `require("gopher.dap").setup()`, and you're good to go.
217
+
Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment.
218
218
219
219
220
220
vim:tw=78:ts=8:noet:ft=help:norl:
-33
lua/gopher/_utils/health_util.lua
-33
lua/gopher/_utils/health_util.lua
···
1
-
local h = vim.health or require "health"
2
-
local health = {}
3
-
4
-
health.start = h.start or h.report_start
5
-
health.ok = h.ok or h.report_ok
6
-
health.warn = h.warn or h.report_warn
7
-
health.error = h.error or h.report_error
8
-
health.info = h.info or h.report_info
9
-
10
-
---@param module string
11
-
---@return boolean
12
-
function health.is_lualib_found(module)
13
-
local is_found, _ = pcall(require, module)
14
-
return is_found
15
-
end
16
-
17
-
---@param bin string
18
-
---@return boolean
19
-
function health.is_binary_found(bin)
20
-
if vim.fn.executable(bin) == 1 then
21
-
return true
22
-
end
23
-
return false
24
-
end
25
-
26
-
---@param ft string
27
-
---@return boolean
28
-
function health.is_treesitter_parser_available(ft)
29
-
local ok, parser = pcall(vim.treesitter.get_parser, 0, ft)
30
-
return ok and parser ~= nil
31
-
end
32
-
33
-
return health
+24
-17
lua/gopher/_utils/init.lua
+24
-17
lua/gopher/_utils/init.lua
···
3
3
local utils = {}
4
4
5
5
---@param msg string
6
-
---@param lvl number
7
-
function utils.deferred_notify(msg, lvl)
8
-
vim.defer_fn(function()
9
-
vim.notify(msg, lvl, {
10
-
title = c.___plugin_name,
11
-
})
12
-
log.debug(msg)
13
-
end, 0)
14
-
end
15
-
16
-
---@param msg string
17
6
---@param lvl? number
18
7
function utils.notify(msg, lvl)
19
8
lvl = lvl or vim.log.levels.INFO
20
9
vim.notify(msg, lvl, {
10
+
---@diagnostic disable-next-line:undefined-field
21
11
title = c.___plugin_name,
22
12
})
23
13
log.debug(msg)
24
14
end
25
15
26
-
-- safe require
27
-
---@param module string module name
28
-
function utils.sreq(module)
29
-
local ok, m = pcall(require, module)
30
-
assert(ok, string.format("gopher.nvim dependency error: %s not installed", module))
31
-
return m
16
+
---@param path string
17
+
---@return string
18
+
function utils.readfile_joined(path)
19
+
return table.concat(vim.fn.readfile(path), "\n")
20
+
end
21
+
22
+
---@param t string[]
23
+
---@return string[]
24
+
function utils.remove_empty_lines(t)
25
+
local res = {}
26
+
for _, line in ipairs(t) do
27
+
if line ~= "" then
28
+
table.insert(res, line)
29
+
end
30
+
end
31
+
return res
32
+
end
33
+
34
+
---@param s string
35
+
---@return string
36
+
function utils.trimend(s)
37
+
local r, _ = string.gsub(s, "%s+$", "")
38
+
return r
32
39
end
33
40
34
41
return utils
+2
-1
lua/gopher/_utils/log.lua
+2
-1
lua/gopher/_utils/log.lua
···
18
18
19
19
local config = {
20
20
-- Name of the plugin. Prepended to log messages
21
+
---@diagnostic disable-next-line:undefined-field
21
22
name = c.___plugin_name,
22
23
23
24
-- Should print the output to neovim while running
···
91
92
local log_at_level = function(level_config, message_maker, ...)
92
93
-- Return early if we're below the current_log_level
93
94
--
94
-
-- the log level source get from config directly because otherwise it doesnt work
95
+
-- the log level source get from config directly because otherwise it doesn't work
95
96
if level_config.level < c.log_level then
96
97
return
97
98
end
+39
lua/gopher/_utils/runner.lua
+39
lua/gopher/_utils/runner.lua
···
1
+
local c = require "gopher.config"
2
+
local runner = {}
3
+
4
+
---@class gopher.RunnerOpts
5
+
---@field cwd? string
6
+
---@field timeout? number
7
+
---@field stdin? boolean|string|string[]
8
+
---@field text? boolean
9
+
10
+
---@param cmd (string|number)[]
11
+
---@param on_exit fun(out:vim.SystemCompleted)
12
+
---@param opts? gopher.RunnerOpts
13
+
---@return vim.SystemObj
14
+
function runner.async(cmd, on_exit, opts)
15
+
opts = opts or {}
16
+
return vim.system(cmd, {
17
+
cwd = opts.cwd or nil,
18
+
timeout = opts.timeout or c.timeout,
19
+
stdin = opts.stdin or nil,
20
+
text = opts.text or true,
21
+
}, on_exit)
22
+
end
23
+
24
+
---@param cmd (string|number)[]
25
+
---@param opts? gopher.RunnerOpts
26
+
---@return vim.SystemCompleted
27
+
function runner.sync(cmd, opts)
28
+
opts = opts or {}
29
+
return vim
30
+
.system(cmd, {
31
+
cwd = opts.cwd or nil,
32
+
timeout = opts.timeout or c.timeout,
33
+
stdin = opts.stdin or nil,
34
+
text = opts.text or true,
35
+
})
36
+
:wait()
37
+
end
38
+
39
+
return runner
+10
-12
lua/gopher/_utils/runner/gocmd.lua
lua/gopher/_utils/gocmd.lua
+10
-12
lua/gopher/_utils/runner/gocmd.lua
lua/gopher/_utils/gocmd.lua
···
25
25
26
26
---@param subcmd string
27
27
---@param args string[]
28
-
---@return string[]|nil
28
+
---@return string
29
29
function gocmd.run(subcmd, args)
30
-
if #args == 0 then
31
-
error "please provice any arguments"
30
+
if #args == 0 and subcmd ~= "generate" then
31
+
error "please provide any arguments"
32
32
end
33
33
34
34
if subcmd == "get" then
···
39
39
args = if_generate(args)
40
40
end
41
41
42
-
return r.sync(c.go, {
43
-
args = { subcmd, unpack(args) },
44
-
on_exit = function(data, status)
45
-
if status ~= 0 then
46
-
error("gocmd failed: " .. data)
47
-
end
48
-
u.notify(c.go .. " " .. subcmd .. " successful runned")
49
-
end,
50
-
})
42
+
local rs = r.sync { c.go, subcmd, unpack(args) }
43
+
if rs.code ~= 0 then
44
+
error("go " .. subcmd .. " failed: " .. rs.stderr)
45
+
end
46
+
47
+
u.notify(c.go .. " " .. subcmd .. " ran successful")
48
+
return rs.stdout
51
49
end
52
50
53
51
return gocmd
-33
lua/gopher/_utils/runner/init.lua
-33
lua/gopher/_utils/runner/init.lua
···
1
-
local Job = require "plenary.job"
2
-
local runner = {}
3
-
4
-
---@class gopher.RunnerOpts
5
-
---@field args? string[]
6
-
---@field cwd? string?
7
-
---@field on_exit? fun(data:string, status:number)
8
-
9
-
---@param cmd string
10
-
---@param opts gopher.RunnerOpts
11
-
---@return string[]|nil
12
-
function runner.sync(cmd, opts)
13
-
local output
14
-
Job:new({
15
-
command = cmd,
16
-
args = opts.args,
17
-
cwd = opts.cwd,
18
-
on_stderr = function(_, data)
19
-
vim.print(data)
20
-
end,
21
-
on_exit = function(data, status)
22
-
output = data:result()
23
-
vim.schedule(function()
24
-
if opts.on_exit then
25
-
opts.on_exit(output, status)
26
-
end
27
-
end)
28
-
end,
29
-
}):sync(60000 --[[1 min]])
30
-
return output
31
-
end
32
-
33
-
return runner
+136
lua/gopher/_utils/ts.lua
+136
lua/gopher/_utils/ts.lua
···
1
+
local ts = {}
2
+
local queries = {
3
+
struct = [[
4
+
[(type_spec name: (type_identifier) @_name
5
+
type: (struct_type))
6
+
(var_declaration (var_spec
7
+
name: (identifier) @_name @_var
8
+
type: (struct_type)))
9
+
(short_var_declaration
10
+
left: (expression_list (identifier) @_name @_var)
11
+
right: (expression_list (composite_literal
12
+
type: (struct_type))))]
13
+
]],
14
+
func = [[
15
+
[(function_declaration name: (identifier) @_name)
16
+
(method_declaration name: (field_identifier) @_name)]
17
+
]],
18
+
package = [[
19
+
(package_identifier) @_name
20
+
]],
21
+
interface = [[
22
+
(type_spec
23
+
name: (type_identifier) @_name
24
+
type: (interface_type))
25
+
]],
26
+
}
27
+
28
+
---@param parent_type string[]
29
+
---@param node TSNode
30
+
---@return TSNode?
31
+
local function get_parrent_node(parent_type, node)
32
+
---@type TSNode?
33
+
local current = node
34
+
while current do
35
+
if vim.tbl_contains(parent_type, current:type()) then
36
+
break
37
+
end
38
+
39
+
current = current:parent()
40
+
if current == nil then
41
+
return nil
42
+
end
43
+
end
44
+
return current
45
+
end
46
+
47
+
---@param query vim.treesitter.Query
48
+
---@param node TSNode
49
+
---@param bufnr integer
50
+
---@return {name:string, is_varstruct:boolean}
51
+
local function get_captures(query, node, bufnr)
52
+
local res = {}
53
+
for id, _node in query:iter_captures(node, bufnr) do
54
+
if query.captures[id] == "_name" then
55
+
res["name"] = vim.treesitter.get_node_text(_node, bufnr)
56
+
end
57
+
58
+
if query.captures[id] == "_var" then
59
+
res["is_varstruct"] = true
60
+
end
61
+
end
62
+
63
+
return res
64
+
end
65
+
66
+
---@class gopher.TsResult
67
+
---@field name string
68
+
---@field start_line integer
69
+
---@field end_line integer
70
+
---@field is_varstruct boolean
71
+
72
+
---@param bufnr integer
73
+
---@param parent_type string[]
74
+
---@param query string
75
+
---@return gopher.TsResult
76
+
local function do_stuff(bufnr, parent_type, query)
77
+
if not vim.treesitter.get_parser(bufnr, "go") then
78
+
error "No treesitter parser found for go"
79
+
end
80
+
81
+
local node = vim.treesitter.get_node {
82
+
bufnr = bufnr,
83
+
}
84
+
if not node then
85
+
error "No nodes found under cursor"
86
+
end
87
+
88
+
local parent_node = get_parrent_node(parent_type, node)
89
+
if not parent_node then
90
+
error "No parent node found under cursor"
91
+
end
92
+
93
+
local q = vim.treesitter.query.parse("go", query)
94
+
local res = get_captures(q, parent_node, bufnr)
95
+
assert(res.name ~= nil, "No capture name found")
96
+
97
+
local start_row, _, end_row, _ = parent_node:range()
98
+
res["start_line"] = start_row + 1
99
+
res["end_line"] = end_row + 1
100
+
101
+
return res
102
+
end
103
+
104
+
---@param bufnr integer
105
+
function ts.get_struct_under_cursor(bufnr)
106
+
--- should be both type_spec and type_declaration
107
+
--- because in cases like `type ( T struct{}, U strict{} )`
108
+
--- i will be choosing always last struct in the list
109
+
---
110
+
--- var_declaration is for cases like `var x struct{}`
111
+
--- short_var_declaration is for cases like `x := struct{}{}`
112
+
return do_stuff(bufnr, {
113
+
"type_spec",
114
+
"type_declaration",
115
+
"var_declaration",
116
+
"short_var_declaration",
117
+
}, queries.struct)
118
+
end
119
+
120
+
---@param bufnr integer
121
+
function ts.get_func_under_cursor(bufnr)
122
+
--- since this handles both and funcs and methods we should check for both parent nodes
123
+
return do_stuff(bufnr, { "function_declaration", "method_declaration" }, queries.func)
124
+
end
125
+
126
+
---@param bufnr integer
127
+
function ts.get_package_under_cursor(bufnr)
128
+
return do_stuff(bufnr, { "package_clause" }, queries.package)
129
+
end
130
+
131
+
---@param bufnr integer
132
+
function ts.get_interface_under_cursor(bufnr)
133
+
return do_stuff(bufnr, { "type_declaration" }, queries.interface)
134
+
end
135
+
136
+
return ts
-104
lua/gopher/_utils/ts/init.lua
-104
lua/gopher/_utils/ts/init.lua
···
1
-
---@diagnostic disable: param-type-mismatch
2
-
local nodes = require "gopher._utils.ts.nodes"
3
-
local u = require "gopher._utils"
4
-
local ts = {
5
-
querys = {
6
-
struct_block = [[((type_declaration (type_spec name:(type_identifier) @struct.name type: (struct_type)))@struct.declaration)]],
7
-
em_struct_block = [[(field_declaration name:(field_identifier)@struct.name type: (struct_type)) @struct.declaration]],
8
-
package = [[(package_clause (package_identifier)@package.name)@package.clause]],
9
-
interface = [[((type_declaration (type_spec name:(type_identifier) @interface.name type:(interface_type)))@interface.declaration)]],
10
-
method_name = [[((method_declaration receiver: (parameter_list)@method.receiver name: (field_identifier)@method.name body:(block))@method.declaration)]],
11
-
func = [[((function_declaration name: (identifier)@function.name) @function.declaration)]],
12
-
},
13
-
}
14
-
15
-
---@return table
16
-
local function get_name_defaults()
17
-
return {
18
-
["func"] = "function",
19
-
["if"] = "if",
20
-
["else"] = "else",
21
-
["for"] = "for",
22
-
}
23
-
end
24
-
25
-
---@param row string
26
-
---@param col string
27
-
---@param bufnr string|nil
28
-
---@param do_notify boolean|nil
29
-
---@return table|nil
30
-
function ts.get_struct_node_at_pos(row, col, bufnr, do_notify)
31
-
local notify = do_notify or true
32
-
local query = ts.querys.struct_block .. " " .. ts.querys.em_struct_block
33
-
local bufn = bufnr or vim.api.nvim_get_current_buf()
34
-
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
35
-
if ns == nil then
36
-
if notify then
37
-
u.deferred_notify("struct not found", vim.log.levels.WARN)
38
-
end
39
-
else
40
-
return ns[#ns]
41
-
end
42
-
end
43
-
44
-
---@param row string
45
-
---@param col string
46
-
---@param bufnr string|nil
47
-
---@param do_notify boolean|nil
48
-
---@return table|nil
49
-
function ts.get_func_method_node_at_pos(row, col, bufnr, do_notify)
50
-
local notify = do_notify or true
51
-
local query = ts.querys.func .. " " .. ts.querys.method_name
52
-
local bufn = bufnr or vim.api.nvim_get_current_buf()
53
-
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
54
-
if ns == nil then
55
-
if notify then
56
-
u.deferred_notify("function not found", vim.log.levels.WARN)
57
-
end
58
-
else
59
-
return ns[#ns]
60
-
end
61
-
end
62
-
63
-
---@param row string
64
-
---@param col string
65
-
---@param bufnr string|nil
66
-
---@param do_notify boolean|nil
67
-
---@return table|nil
68
-
function ts.get_package_node_at_pos(row, col, bufnr, do_notify)
69
-
local notify = do_notify or true
70
-
-- stylua: ignore
71
-
if row > 10 then return end
72
-
local query = ts.querys.package
73
-
local bufn = bufnr or vim.api.nvim_get_current_buf()
74
-
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
75
-
if ns == nil then
76
-
if notify then
77
-
u.deferred_notify("package not found", vim.log.levels.WARN)
78
-
return nil
79
-
end
80
-
else
81
-
return ns[#ns]
82
-
end
83
-
end
84
-
85
-
---@param row string
86
-
---@param col string
87
-
---@param bufnr string|nil
88
-
---@param do_notify boolean|nil
89
-
---@return table|nil
90
-
function ts.get_interface_node_at_pos(row, col, bufnr, do_notify)
91
-
local notify = do_notify or true
92
-
local query = ts.querys.interface
93
-
local bufn = bufnr or vim.api.nvim_get_current_buf()
94
-
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
95
-
if ns == nil then
96
-
if notify then
97
-
u.deferred_notify("interface not found", vim.log.levels.WARN)
98
-
end
99
-
else
100
-
return ns[#ns]
101
-
end
102
-
end
103
-
104
-
return ts
-143
lua/gopher/_utils/ts/nodes.lua
-143
lua/gopher/_utils/ts/nodes.lua
···
1
-
local ts_query = require "nvim-treesitter.query"
2
-
local parsers = require "nvim-treesitter.parsers"
3
-
local locals = require "nvim-treesitter.locals"
4
-
local u = require "gopher._utils"
5
-
local M = {}
6
-
7
-
local function intersects(row, col, sRow, sCol, eRow, eCol)
8
-
if sRow > row or eRow < row then
9
-
return false
10
-
end
11
-
12
-
if sRow == row and sCol > col then
13
-
return false
14
-
end
15
-
16
-
if eRow == row and eCol < col then
17
-
return false
18
-
end
19
-
20
-
return true
21
-
end
22
-
23
-
---@param nodes table
24
-
---@param row string
25
-
---@param col string
26
-
---@return table
27
-
function M.intersect_nodes(nodes, row, col)
28
-
local found = {}
29
-
for idx = 1, #nodes do
30
-
local node = nodes[idx]
31
-
local sRow = node.dim.s.r
32
-
local sCol = node.dim.s.c
33
-
local eRow = node.dim.e.r
34
-
local eCol = node.dim.e.c
35
-
36
-
if intersects(row, col, sRow, sCol, eRow, eCol) then
37
-
table.insert(found, node)
38
-
end
39
-
end
40
-
41
-
return found
42
-
end
43
-
44
-
---@param nodes table
45
-
---@return table
46
-
function M.sort_nodes(nodes)
47
-
table.sort(nodes, function(a, b)
48
-
return M.count_parents(a) < M.count_parents(b)
49
-
end)
50
-
51
-
return nodes
52
-
end
53
-
54
-
---@param query string
55
-
---@param lang string
56
-
---@param bufnr integer
57
-
---@param pos_row string
58
-
---@return string
59
-
function M.get_all_nodes(query, lang, _, bufnr, pos_row, _)
60
-
bufnr = bufnr or 0
61
-
pos_row = pos_row or 30000
62
-
63
-
local ok, parsed_query = pcall(function()
64
-
return vim.treesitter.query.parse(lang, query)
65
-
end)
66
-
if not ok then
67
-
return nil
68
-
end
69
-
70
-
local parser = parsers.get_parser(bufnr, lang)
71
-
local root = parser:parse()[1]:root()
72
-
local start_row, _, end_row, _ = root:range()
73
-
local results = {}
74
-
75
-
for match in ts_query.iter_prepared_matches(parsed_query, root, bufnr, start_row, end_row) do
76
-
local sRow, sCol, eRow, eCol, declaration_node
77
-
local type, name, op = "", "", ""
78
-
locals.recurse_local_nodes(match, function(_, node, path)
79
-
local idx = string.find(path, ".[^.]*$")
80
-
op = string.sub(path, idx + 1, #path)
81
-
type = string.sub(path, 1, idx - 1)
82
-
83
-
if op == "name" then
84
-
name = vim.treesitter.get_node_text(node, bufnr)
85
-
elseif op == "declaration" or op == "clause" then
86
-
declaration_node = node
87
-
sRow, sCol, eRow, eCol = node:range()
88
-
sRow = sRow + 1
89
-
eRow = eRow + 1
90
-
sCol = sCol + 1
91
-
eCol = eCol + 1
92
-
end
93
-
end)
94
-
95
-
if declaration_node ~= nil then
96
-
table.insert(results, {
97
-
declaring_node = declaration_node,
98
-
dim = { s = { r = sRow, c = sCol }, e = { r = eRow, c = eCol } },
99
-
name = name,
100
-
operator = op,
101
-
type = type,
102
-
})
103
-
end
104
-
end
105
-
106
-
return results
107
-
end
108
-
109
-
---@param query string
110
-
---@param default string
111
-
---@param bufnr string
112
-
---@param row string
113
-
---@param col string
114
-
---@return table
115
-
function M.nodes_at_cursor(query, default, bufnr, row, col)
116
-
bufnr = bufnr or vim.api.nvim_get_current_buf()
117
-
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
118
-
if row == nil or col == nil then
119
-
row, col = unpack(vim.api.nvim_win_get_cursor(0))
120
-
end
121
-
122
-
local nodes = M.get_all_nodes(query, ft, default, bufnr, row, col)
123
-
if nodes == nil then
124
-
u.deferred_notify(
125
-
"Unable to find any nodes. Place your cursor on a go symbol and try again",
126
-
vim.log.levels.DEBUG
127
-
)
128
-
return nil
129
-
end
130
-
131
-
nodes = M.sort_nodes(M.intersect_nodes(nodes, row, col))
132
-
if nodes == nil or #nodes == 0 then
133
-
u.deferred_notify(
134
-
"Unable to find any nodes at pos. " .. tostring(row) .. ":" .. tostring(col),
135
-
vim.log.levels.DEBUG
136
-
)
137
-
return nil
138
-
end
139
-
140
-
return nodes
141
-
end
142
-
143
-
return M
+39
-41
lua/gopher/comment.lua
+39
-41
lua/gopher/comment.lua
···
1
1
---@toc_entry Generate comments
2
2
---@tag gopher.nvim-comments
3
-
---@usage Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line.
4
-
---@text This module provides a way to generate comments for Go code.
3
+
---@text
4
+
--- This module provides a way to generate comments for Go code.
5
+
---
6
+
---@usage Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment.
5
7
8
+
local ts = require "gopher._utils.ts"
6
9
local log = require "gopher._utils.log"
10
+
local comment = {}
7
11
8
-
local function generate(row, col)
9
-
local ts_utils = require "gopher._utils.ts"
10
-
local comment, ns = nil, nil
12
+
---@param name string
13
+
---@return string
14
+
---@dochide
15
+
local function template(name)
16
+
return "// " .. name .. " "
17
+
end
11
18
12
-
ns = ts_utils.get_package_node_at_pos(row, col, nil, false)
13
-
if ns ~= nil then
14
-
comment = "// Package " .. ns.name .. " provides " .. ns.name
15
-
return comment, ns
19
+
---@param bufnr integer
20
+
---@return string
21
+
---@dochide
22
+
local function generate(bufnr)
23
+
local s_ok, s_res = pcall(ts.get_struct_under_cursor, bufnr)
24
+
if s_ok then
25
+
return template(s_res.name)
16
26
end
17
27
18
-
ns = ts_utils.get_struct_node_at_pos(row, col, nil, false)
19
-
if ns ~= nil then
20
-
comment = "// " .. ns.name .. " " .. ns.type .. " "
21
-
return comment, ns
28
+
local f_ok, f_res = pcall(ts.get_func_under_cursor, bufnr)
29
+
if f_ok then
30
+
return template(f_res.name)
22
31
end
23
32
24
-
ns = ts_utils.get_func_method_node_at_pos(row, col, nil, false)
25
-
if ns ~= nil then
26
-
comment = "// " .. ns.name .. " " .. ns.type .. " "
27
-
return comment, ns
33
+
local i_ok, i_res = pcall(ts.get_interface_under_cursor, bufnr)
34
+
if i_ok then
35
+
return template(i_res.name)
28
36
end
29
37
30
-
ns = ts_utils.get_interface_node_at_pos(row, col, nil, false)
31
-
if ns ~= nil then
32
-
comment = "// " .. ns.name .. " " .. ns.type .. " "
33
-
return comment, ns
38
+
local p_ok, p_res = pcall(ts.get_package_under_cursor, bufnr)
39
+
if p_ok then
40
+
return "// Package " .. p_res.name .. " provides "
34
41
end
35
42
36
-
return "// ", {}
43
+
return "// "
37
44
end
38
45
39
-
return function()
40
-
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
41
-
local comment, ns = generate(row + 1, col + 1)
42
-
43
-
log.debug("generated comment: " .. comment)
44
-
45
-
vim.api.nvim_win_set_cursor(0, {
46
-
ns.dim.s.r,
47
-
ns.dim.s.c,
48
-
})
49
-
50
-
---@diagnostic disable-next-line: param-type-mismatch
51
-
vim.fn.append(row - 1, comment)
52
-
53
-
vim.api.nvim_win_set_cursor(0, {
54
-
ns.dim.s.r,
55
-
#comment + 1,
56
-
})
46
+
function comment.comment()
47
+
local bufnr = vim.api.nvim_get_current_buf()
48
+
local cmt = generate(bufnr)
49
+
log.debug("generated comment: " .. cmt)
57
50
58
-
vim.cmd [[startinsert!]]
51
+
local pos = vim.fn.getcurpos()[2]
52
+
vim.fn.append(pos - 1, cmt)
53
+
vim.fn.setpos(".", { 0, pos, #cmt })
54
+
vim.cmd "startinsert!"
59
55
end
56
+
57
+
return comment
+46
-25
lua/gopher/config.lua
+46
-25
lua/gopher/config.lua
···
1
-
---@toc_entry Configuration
2
-
---@tag gopher.nvim-config
3
-
---@text config it is the place where you can configure the plugin.
4
-
--- also this is optional is you're ok with default settings.
5
-
--- You can look at default options |gopher.nvim-config-defaults|
6
-
7
-
---@type gopher.Config
8
-
---@private
9
1
local config = {}
10
2
11
3
---@tag gopher.nvim-config.ConfigGoTagTransform
12
4
---@text Possible values for |gopher.Config|.gotag.transform:
13
5
---
14
-
---@private
6
+
---@dochide
15
7
---@alias gopher.ConfigGoTagTransform
16
8
---| "snakecase" "GopherUser" -> "gopher_user"
17
9
---| "camelcase" "GopherUser" -> "gopherUser"
···
20
12
---| "titlecase" "GopherUser" -> "Gopher User"
21
13
---| "keep" keeps the original field name
22
14
23
-
--minidoc_replace_start {
24
-
25
-
---@tag gopher.nvim-config-defaults
26
-
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section):gsub(">", ">lua")
27
-
---
15
+
---@toc_entry Config
16
+
---@tag gopher.nvim-config
17
+
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
28
18
---@class gopher.Config
29
19
local default_config = {
30
-
--minidoc_replace_end
31
-
32
-
-- log level, you might consider using DEBUG or TRACE for degugging the plugin
20
+
-- log level, you might consider using DEBUG or TRACE for debugging the plugin
33
21
---@type number
34
22
log_level = vim.log.levels.INFO,
23
+
24
+
-- timeout for running internal commands
25
+
---@type number
26
+
timeout = 2000,
35
27
36
28
-- user specified paths to binaries
37
29
---@class gopher.ConfigCommand
···
41
33
gotests = "gotests",
42
34
impl = "impl",
43
35
iferr = "iferr",
44
-
dlv = "dlv",
45
36
},
46
37
---@class gopher.ConfigGotests
47
38
gotests = {
···
51
42
---@type string|nil
52
43
template_dir = nil,
53
44
-- switch table tests from using slice to map (with test name for the key)
54
-
-- works only with gotests installed from develop branch
55
45
named = false,
56
46
},
57
47
---@class gopher.ConfigGoTag
58
48
gotag = {
59
49
---@type gopher.ConfigGoTagTransform
60
50
transform = "snakecase",
51
+
52
+
-- default tags to add to struct fields
53
+
default_tag = "json",
54
+
},
55
+
iferr = {
56
+
-- choose a custom error message
57
+
---@type string|nil
58
+
message = nil,
61
59
},
62
60
}
63
61
--minidoc_afterlines_end
64
62
65
63
---@type gopher.Config
66
-
---@private
64
+
---@dochide
67
65
local _config = default_config
68
66
69
-
-- I am kinda secret so don't tell anyone about me
70
-
-- even dont use me
67
+
-- I am kinda secret so don't tell anyone about me even dont use me
71
68
--
72
-
-- if you don't belive me that i am secret see
69
+
-- if you don't believe me that i am secret see
73
70
-- the line below it says @private
74
71
---@private
75
72
_config.___plugin_name = "gopher.nvim" ---@diagnostic disable-line: inject-field
76
73
77
74
---@param user_config? gopher.Config
78
-
---@private
75
+
---@dochide
79
76
function config.setup(user_config)
80
-
_config = vim.tbl_deep_extend("force", default_config, user_config or {})
77
+
vim.validate { user_config = { user_config, "table", true } }
78
+
79
+
_config = vim.tbl_deep_extend("force", vim.deepcopy(default_config), user_config or {})
80
+
81
+
vim.validate {
82
+
log_level = { _config.log_level, "number" },
83
+
timeout = { _config.timeout, "number" },
84
+
["commands"] = { _config.commands, "table" },
85
+
["commands.go"] = { _config.commands.go, "string" },
86
+
["commands.gomodifytags"] = { _config.commands.gomodifytags, "string" },
87
+
["commands.gotests"] = { _config.commands.gotests, "string" },
88
+
["commands.impl"] = { _config.commands.impl, "string" },
89
+
["commands.iferr"] = { _config.commands.iferr, "string" },
90
+
["gotests"] = { _config.gotests, "table" },
91
+
["gotests.template"] = { _config.gotests.template, "string" },
92
+
["gotests.template_dir"] = { _config.gotests.template, "string", true },
93
+
["gotests.named"] = { _config.gotests.named, "boolean" },
94
+
["gotag"] = { _config.gotag, "table" },
95
+
["gotag.transform"] = { _config.gotag.transform, "string" },
96
+
["gotag.default_tag"] = { _config.gotag.default_tag, "string" },
97
+
["iferr"] = { _config.iferr, "table" },
98
+
["iferr.message"] = { _config.iferr.message, "string", true },
99
+
}
81
100
end
82
101
83
102
setmetatable(config, {
···
86
105
end,
87
106
})
88
107
108
+
---@dochide
109
+
---@return gopher.Config
89
110
return config
-129
lua/gopher/dap.lua
-129
lua/gopher/dap.lua
···
1
-
---@toc_entry Setup `nvim-dap` for Go
2
-
---@tag gopher.nvim-dap
3
-
---@text This module sets up `nvim-dap` for Go.
4
-
---@usage just call `require("gopher.dap").setup()`, and you're good to go.
5
-
6
-
local c = require "gopher.config"
7
-
local dap = {}
8
-
9
-
dap.adapter = function(callback, config)
10
-
local host = config.host or "127.0.0.1"
11
-
local port = config.port or "38697"
12
-
local addr = string.format("%s:%s", host, port)
13
-
14
-
local handle, pid_or_err
15
-
local stdout = assert(vim.loop.new_pipe(false))
16
-
local opts = {
17
-
stdio = { nil, stdout },
18
-
args = { "dap", "-l", addr },
19
-
detached = true,
20
-
}
21
-
22
-
handle, pid_or_err = vim.loop.spawn(c.commands.dlv, opts, function(status)
23
-
if not stdout or not handle then
24
-
return
25
-
end
26
-
27
-
stdout:close()
28
-
handle:close()
29
-
if status ~= 0 then
30
-
print("dlv exited with code", status)
31
-
end
32
-
end)
33
-
34
-
assert(handle, "Error running dlv: " .. tostring(pid_or_err))
35
-
if stdout then
36
-
stdout:read_start(function(err, chunk)
37
-
assert(not err, err)
38
-
if chunk then
39
-
vim.schedule(function()
40
-
require("dap.repl").append(chunk)
41
-
end)
42
-
end
43
-
end)
44
-
end
45
-
46
-
-- wait for delve to start
47
-
vim.defer_fn(function()
48
-
callback { type = "server", host = "127.0.0.1", port = port }
49
-
end, 100)
50
-
end
51
-
52
-
local function args_input()
53
-
vim.ui.input({ prompt = "Args: " }, function(input)
54
-
return vim.split(input or "", " ")
55
-
end)
56
-
end
57
-
58
-
local function get_arguments()
59
-
local co = coroutine.running()
60
-
if co then
61
-
return coroutine.create(function()
62
-
local args = args_input()
63
-
coroutine.resume(co, args)
64
-
end)
65
-
else
66
-
return args_input()
67
-
end
68
-
end
69
-
70
-
dap.configuration = {
71
-
{
72
-
type = "go",
73
-
name = "Debug",
74
-
request = "launch",
75
-
program = "${file}",
76
-
},
77
-
{
78
-
type = "go",
79
-
name = "Debug (Arguments)",
80
-
request = "launch",
81
-
program = "${file}",
82
-
args = get_arguments,
83
-
},
84
-
{
85
-
type = "go",
86
-
name = "Debug Package",
87
-
request = "launch",
88
-
program = "${fileDirname}",
89
-
},
90
-
{
91
-
type = "go",
92
-
name = "Attach",
93
-
mode = "local",
94
-
request = "attach",
95
-
processId = require("dap.utils").pick_process,
96
-
},
97
-
{
98
-
type = "go",
99
-
name = "Debug test",
100
-
request = "launch",
101
-
mode = "test",
102
-
program = "${file}",
103
-
},
104
-
{
105
-
type = "go",
106
-
name = "Debug test (go.mod)",
107
-
request = "launch",
108
-
mode = "test",
109
-
program = "./${relativeFileDirname}",
110
-
},
111
-
}
112
-
113
-
-- sets ups nvim-dap for Go in one function call.
114
-
function dap.setup()
115
-
vim.deprecate(
116
-
"gopher.dap",
117
-
"you might consider setting up `nvim-dap` manually, or using another plugin(https://github.com/leoluz/nvim-dap-go)",
118
-
"v0.1.6",
119
-
"gopher"
120
-
)
121
-
122
-
local ok, d = pcall(require, "dap")
123
-
assert(ok, "gopher.nvim dependency error: dap not installed")
124
-
125
-
d.adapters.go = dap.adapter
126
-
d.configurations.go = dap.configuration
127
-
end
128
-
129
-
return dap
+17
-46
lua/gopher/gotests.lua
+17
-46
lua/gopher/gotests.lua
···
2
2
---@tag gopher.nvim-gotests
3
3
---@text gotests is utilizing the `gotests` tool to generate unit tests boilerplate.
4
4
---@usage
5
-
--- - generate unit test for spesisfic function/method
6
-
--- - to specift the function/method put your cursor on it
7
-
--- - run `:GoTestAdd`
5
+
--- - Generate unit test for specific function/method:
6
+
--- 1. Place your cursor on the desired function/method.
7
+
--- 2. Run `:GoTestAdd`
8
8
---
9
-
--- - generate unit tests for all functions/methods in current file
9
+
--- - Generate unit tests for *all* functions/methods in current file:
10
10
--- - run `:GoTestsAll`
11
11
---
12
-
--- - generate unit tests only for exported(public) functions/methods
12
+
--- - Generate unit tests *only* for *exported(public)* functions/methods:
13
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. See |gopher.nvim-config|
16
+
--- More details about templates can be found at: https://github.com/cweill/gotests
17
17
---
18
-
19
-
---@tag gopher.nvim-gotests-named
20
-
---@text
21
-
--- if you prefare using named tests, you can enable it in the config.
22
-
--- but you would need to install `gotests@develop` because stable version doesn't support this feature.
23
-
--- you can do it with:
24
-
--- >lua
25
-
--- -- simply run go get in your shell:
26
-
--- go install github.com/cweill/gotests/...@develop
27
-
---
28
-
--- -- if you want to install it within neovim, you can use one of this:
29
-
---
30
-
--- vim.fn.jobstart("go install github.com/cweill/gotests/...@develop")
31
-
---
32
-
--- -- or if you want to use mason:
33
-
--- require("mason-tool-installer").setup {
34
-
--- ensure_installed = {
35
-
--- { "gotests", version = "develop" },
36
-
--- }
37
-
--- }
38
-
--- <
39
-
---
40
-
--- if you choose to install `gotests` within neovim, i recommend adding it to your `build` section in your |lazy.nvim|
18
+
--- If you prefer named tests, you can enable them in |gopher.nvim-config|.
41
19
42
20
local c = require "gopher.config"
43
21
local ts_utils = require "gopher._utils.ts"
···
47
25
local gotests = {}
48
26
49
27
---@param args table
50
-
---@private
28
+
---@dochide
51
29
local function add_test(args)
52
30
if c.gotests.named then
53
31
table.insert(args, "-named")
···
68
46
69
47
log.debug("generating tests with args: ", args)
70
48
71
-
return r.sync(c.commands.gotests, {
72
-
args = args,
73
-
on_exit = function(data, status)
74
-
if not status == 0 then
75
-
error("gotests failed: " .. data)
76
-
end
49
+
local rs = r.sync { c.commands.gotests, unpack(args) }
50
+
if rs.code ~= 0 then
51
+
error("gotests failed: " .. rs.stderr)
52
+
end
77
53
78
-
u.notify "unit test(s) generated"
79
-
end,
80
-
})
54
+
u.notify "unit test(s) generated"
81
55
end
82
56
83
57
-- generate unit test for one function
84
58
function gotests.func_test()
85
-
local ns = ts_utils.get_func_method_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0)))
86
-
if ns == nil or ns.name == nil then
87
-
u.notify("cursor on func/method and execute the command again", vim.log.levels.WARN)
88
-
return
89
-
end
59
+
local bufnr = vim.api.nvim_get_current_buf()
60
+
local func = ts_utils.get_func_under_cursor(bufnr)
90
61
91
-
add_test { "-only", ns.name }
62
+
add_test { "-only", func.name }
92
63
end
93
64
94
65
-- generate unit tests for all functions in current file
+40
-28
lua/gopher/health.lua
+40
-28
lua/gopher/health.lua
···
1
1
local health = {}
2
2
local cmd = require("gopher.config").commands
3
-
local u = require "gopher._utils.health_util"
4
3
5
4
local deps = {
6
5
plugin = {
7
-
{ lib = "dap", msg = "required for `gopher.dap`", optional = true },
8
-
{ lib = "plenary", msg = "required for everyting in gopher.nvim", optional = false },
9
-
{ lib = "nvim-treesitter", msg = "required for everyting in gopher.nvim", optional = false },
6
+
{ lib = "nvim-treesitter", msg = "required for everything in gopher.nvim" },
10
7
},
11
8
bin = {
12
9
{
···
14
11
msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`",
15
12
optional = false,
16
13
},
17
-
{ bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = false },
18
-
{ bin = cmd.impl, msg = "required for `:GoImpl`", optional = false },
19
-
{ bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = false },
14
+
{ bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = true },
15
+
{ bin = cmd.impl, msg = "required for `:GoImpl`", optional = true },
16
+
{ bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = true },
20
17
{
21
18
bin = cmd.gotests,
22
19
msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`",
23
-
optional = false,
20
+
optional = true,
24
21
},
25
-
{ bin = cmd.dlv, msg = "required for debugging, (`nvim-dap`, `gopher.dap`)", optional = true },
26
22
},
27
23
treesitter = {
28
-
{ parser = "go", msg = "required for `gopher.nvim`", optional = false },
24
+
{ parser = "go", msg = "required for `gopher.nvim`" },
29
25
},
30
26
}
31
27
28
+
---@param module string
29
+
---@return boolean
30
+
local function is_lualib_found(module)
31
+
local is_found, _ = pcall(require, module)
32
+
return is_found
33
+
end
34
+
35
+
---@param bin string
36
+
---@return boolean
37
+
local function is_binary_found(bin)
38
+
return vim.fn.executable(bin) == 1
39
+
end
40
+
41
+
---@param ft string
42
+
---@return boolean
43
+
local function is_treesitter_parser_available(ft)
44
+
local ok, parser = pcall(vim.treesitter.get_parser, 0, ft)
45
+
return ok and parser ~= nil
46
+
end
47
+
32
48
function health.check()
33
-
u.start "required plugins"
49
+
vim.health.start "required plugins"
34
50
for _, plugin in ipairs(deps.plugin) do
35
-
if u.is_lualib_found(plugin.lib) then
36
-
u.ok(plugin.lib .. " installed")
51
+
if is_lualib_found(plugin.lib) then
52
+
vim.health.ok(plugin.lib .. " installed")
37
53
else
38
-
if plugin.optional then
39
-
u.warn(plugin.lib .. " not found, " .. plugin.msg)
40
-
else
41
-
u.error(plugin.lib .. " not found, " .. plugin.msg)
42
-
end
54
+
vim.health.error(plugin.lib .. " not found, " .. plugin.msg)
43
55
end
44
56
end
45
57
46
-
u.start "required binaries"
47
-
u.info "all those binaries can be installed by `:GoInstallDeps`"
58
+
vim.health.start "required binaries"
59
+
vim.health.info "all those binaries can be installed by `:GoInstallDeps`"
48
60
for _, bin in ipairs(deps.bin) do
49
-
if u.is_binary_found(bin.bin) then
50
-
u.ok(bin.bin .. " installed")
61
+
if is_binary_found(bin.bin) then
62
+
vim.health.ok(bin.bin .. " installed")
51
63
else
52
64
if bin.optional then
53
-
u.warn(bin.bin .. " not found, " .. bin.msg)
65
+
vim.health.warn(bin.bin .. " not found, " .. bin.msg)
54
66
else
55
-
u.error(bin.bin .. " not found, " .. bin.msg)
67
+
vim.health.error(bin.bin .. " not found, " .. bin.msg)
56
68
end
57
69
end
58
70
end
59
71
60
-
u.start "required treesitter parsers"
72
+
vim.health.start "required treesitter parsers"
61
73
for _, parser in ipairs(deps.treesitter) do
62
-
if u.is_treesitter_parser_available(parser.parser) then
63
-
u.ok(parser.parser .. " parser installed")
74
+
if is_treesitter_parser_available(parser.parser) then
75
+
vim.health.ok(parser.parser .. " parser installed")
64
76
else
65
-
u.error(parser.parser .. " parser not found, " .. parser.msg)
77
+
vim.health.error(parser.parser .. " parser not found, " .. parser.msg)
66
78
end
67
79
end
68
80
end
+28
-12
lua/gopher/iferr.lua
+28
-12
lua/gopher/iferr.lua
···
1
+
-- Thanks https://github.com/koron/iferr for vim implementation
2
+
1
3
---@toc_entry Iferr
2
4
---@tag gopher.nvim-iferr
3
-
---@text if you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check.
4
-
---@usage execute `:GoIfErr` near any err variable to insert the check
5
+
---@text
6
+
--- `iferr` provides a way to way to automatically insert `if err != nil` check.
7
+
--- If you want to change `-message` option of `iferr` tool, see |gopher.nvim-config|
8
+
---
9
+
---@usage Execute `:GoIfErr` near any `err` variable to insert the check
5
10
6
11
local c = require "gopher.config"
12
+
local u = require "gopher._utils"
13
+
local r = require "gopher._utils.runner"
7
14
local log = require "gopher._utils.log"
8
15
local iferr = {}
9
16
10
-
-- That's Lua implementation: github.com/koron/iferr
11
17
function iferr.iferr()
12
-
local boff = vim.fn.wordcount().cursor_bytes
18
+
local curb = vim.fn.wordcount().cursor_bytes
13
19
local pos = vim.fn.getcurpos()[2]
20
+
local fpath = vim.fn.expand "%"
14
21
15
-
local data = vim.fn.systemlist((c.commands.iferr .. " -pos " .. boff), vim.fn.bufnr "%")
16
-
if vim.v.shell_error ~= 0 then
17
-
if string.find(data[1], "no functions at") then
18
-
vim.print "no function found"
19
-
log.warn("iferr: no function at " .. boff)
22
+
local cmd = { c.commands.iferr, "-pos", curb }
23
+
if c.iferr.message ~= nil and type(c.iferr.message) == "string" then
24
+
table.insert(cmd, "-message")
25
+
table.insert(cmd, c.iferr.message)
26
+
end
27
+
28
+
local rs = r.sync(cmd, {
29
+
stdin = u.readfile_joined(fpath),
30
+
})
31
+
32
+
if rs.code ~= 0 then
33
+
if string.find(rs.stderr, "no functions at") then
34
+
u.notify("iferr: no function at " .. curb, vim.log.levels.ERROR)
35
+
log.warn("iferr: no function at " .. curb)
20
36
return
21
37
end
22
38
23
-
log.error("failed. output: " .. vim.inspect(data))
24
-
error("iferr failed: " .. vim.inspect(data))
39
+
log.error("ferr: failed. output: " .. rs.stderr)
40
+
error("iferr failed: " .. rs.stderr)
25
41
end
26
42
27
-
vim.fn.append(pos, data)
43
+
vim.fn.append(pos, u.remove_empty_lines(vim.split(rs.stdout, "\n")))
28
44
vim.cmd [[silent normal! j=2j]]
29
45
vim.fn.setpos(".", pos)
30
46
end
+40
-66
lua/gopher/impl.lua
+40
-66
lua/gopher/impl.lua
···
1
1
---@toc_entry Auto implementation of interface methods
2
2
---@tag gopher.nvim-impl
3
-
---@text impl is utilizing the `impl` tool to generate method stubs for interfaces.
4
-
---@usage
5
-
--- 1. put your coursor on the struct on which you want implement the interface
6
-
--- and run `:GoImpl io.Reader`
7
-
--- which will automatically choose the reciver for the methods and
8
-
--- implement the `io.Reader` interface
9
-
--- 2. same as previous but with custom receiver, so put your coursor on the struct
10
-
--- run `:GoImpl w io.Writer`
11
-
--- where `w` is the receiver and `io.Writer` is the interface
12
-
--- 3. specift receiver, struct, and interface
13
-
--- there's no need to put your coursor on the struct if you specify all arguments
14
-
--- `:GoImpl r RequestReader io.Reader`
15
-
--- where `r` is the receiver, `RequestReader` is the struct and `io.Reader` is the interface
3
+
---@text
4
+
--- Integration of `impl` tool to generate method stubs for interfaces.
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.
10
+
---
11
+
--- 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.
16
+
---
17
+
--- 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.
16
23
---
17
-
--- simple example:
24
+
--- Example:
18
25
--- >go
19
26
--- type BytesReader struct{}
20
27
--- // ^ put your cursor here
···
22
29
---
23
30
--- // this is what you will get
24
31
--- func (b *BytesReader) Read(p []byte) (n int, err error) {
25
-
--- panic("not implemented") // TODO: Implement
32
+
--- panic("not implemented") // TODO: Implement
26
33
--- }
27
34
--- <
28
35
···
32
39
local u = require "gopher._utils"
33
40
local impl = {}
34
41
35
-
---@return string
36
-
---@private
37
-
local function get_struct()
38
-
local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0)))
39
-
if ns == nil then
40
-
u.deferred_notify("put cursor on a struct or specify a receiver", vim.log.levels.INFO)
41
-
return ""
42
-
end
43
-
44
-
vim.api.nvim_win_set_cursor(0, {
45
-
ns.dim.e.r,
46
-
ns.dim.e.c,
47
-
})
48
-
49
-
return ns.name
50
-
end
51
-
52
42
function impl.impl(...)
53
43
local args = { ... }
54
-
local iface, recv_name = "", ""
55
-
local recv = get_struct()
44
+
local iface, recv = "", ""
45
+
local bufnr = vim.api.nvim_get_current_buf()
56
46
57
-
if #args == 0 then
58
-
iface = vim.fn.input "impl: generating method stubs for interface: "
59
-
vim.cmd "redraw!"
60
-
if iface == "" then
61
-
u.deferred_notify("usage: GoImpl f *File io.Reader", vim.log.levels.INFO)
62
-
return
63
-
end
64
-
elseif #args == 1 then -- :GoImpl io.Reader
65
-
recv = string.lower(recv) .. " *" .. recv
66
-
vim.cmd "redraw!"
67
-
iface = select(1, ...)
47
+
if #args == 1 then -- :GoImpl io.Reader
48
+
local st = ts_utils.get_struct_under_cursor(bufnr)
49
+
iface = args[1]
50
+
recv = string.lower(st.name) .. " *" .. st.name
68
51
elseif #args == 2 then -- :GoImpl w io.Writer
69
-
recv_name = select(1, ...)
70
-
recv = string.format("%s *%s", recv_name, recv)
71
-
iface = select(#args, ...)
72
-
elseif #args > 2 then
73
-
iface = select(#args, ...)
74
-
recv = select(#args - 1, ...)
75
-
recv_name = select(#args - 2, ...)
76
-
recv = string.format("%s %s", recv_name, recv)
52
+
local st = ts_utils.get_struct_under_cursor(bufnr)
53
+
iface = args[2]
54
+
recv = args[1] .. " *" .. st.name
55
+
elseif #args == 3 then -- :GoImpl r Struct io.Reader
56
+
recv = args[1] .. " *" .. args[2]
57
+
iface = args[3]
77
58
end
78
59
79
-
local output = r.sync(c.impl, {
80
-
args = {
81
-
"-dir",
82
-
vim.fn.fnameescape(vim.fn.expand "%:p:h" --[[@as string]]),
83
-
recv,
84
-
iface,
85
-
},
86
-
on_exit = function(data, status)
87
-
if not status == 0 then
88
-
error("impl failed: " .. data)
89
-
end
90
-
end,
91
-
})
60
+
local rs = r.sync { c.impl, "-dir", vim.fn.fnameescape(vim.fn.expand "%:p:h"), recv, iface }
61
+
if rs.code ~= 0 then
62
+
error("failed to implement interface: " .. rs.stderr)
63
+
end
92
64
93
65
local pos = vim.fn.getcurpos()[2]
66
+
local output = u.remove_empty_lines(vim.split(rs.stdout, "\n"))
67
+
94
68
table.insert(output, 1, "")
95
69
vim.fn.append(pos, output)
96
70
end
+24
-17
lua/gopher/init.lua
+24
-17
lua/gopher/init.lua
···
1
-
--- *gopher.nvim*
1
+
--- *gopher.nvim* Enhance your golang experience
2
+
---
3
+
--- MIT License Copyright (c) 2025 Oleksandr Smirnov
2
4
---
3
5
--- ==============================================================================
4
6
---
5
7
--- gopher.nvim is a minimalistic plugin for Go development in Neovim written in Lua.
6
8
--- It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim.
7
-
9
+
---
8
10
--- Table of Contents
9
-
---@tag gopher.nvim-table-of-contents
10
11
---@toc
11
12
12
13
local log = require "gopher._utils.log"
13
14
local tags = require "gopher.struct_tags"
14
15
local tests = require "gopher.gotests"
15
-
local gocmd = require("gopher._utils.runner.gocmd").run
16
+
local gocmd = require("gopher._utils.gocmd").run
16
17
local gopher = {}
17
18
18
19
---@toc_entry Setup
19
-
---@tag gopher.nvim-setup
20
-
---@text Setup function. This method simply merges default configs with opts table.
20
+
---@tag gopher.nvim-setup()
21
+
---@text Setup function. This method simply merges default config with opts table.
21
22
--- You can read more about configuration at |gopher.nvim-config|
22
-
--- Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults|
23
+
--- Calling this function is optional, if you ok with default settings.
24
+
--- See |gopher.nvim.config|
23
25
---
24
-
---@usage `require("gopher").setup {}` (replace `{}` with your `config` table)
25
-
---@param user_config gopher.Config
26
+
---@usage >lua
27
+
--- require("gopher").setup {} -- use default config or replace {} with your own
28
+
--- <
29
+
---@param user_config gopher.Config See |gopher.nvim-config|
26
30
gopher.setup = function(user_config)
27
31
log.debug "setting up config"
28
32
require("gopher.config").setup(user_config)
···
30
34
end
31
35
32
36
---@toc_entry Install dependencies
33
-
---@tag gopher.nvim-install-deps
37
+
---@tag gopher.nvim-dependencies
34
38
---@text Gopher.nvim implements most of its features using third-party tools.
35
-
--- To install these tools, you can run `:GoInstallDeps` command
36
-
--- or call `require("gopher").install_deps()` if you want ues lua api.
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.
37
43
gopher.install_deps = require("gopher.installer").install_deps
38
44
39
45
gopher.impl = require("gopher.impl").impl
40
46
gopher.iferr = require("gopher.iferr").iferr
41
-
gopher.comment = require "gopher.comment"
47
+
gopher.comment = require("gopher.comment").comment
42
48
43
49
gopher.tags = {
44
50
add = tags.add,
45
51
rm = tags.remove,
52
+
clear = tags.clear,
46
53
}
47
54
48
55
gopher.test = {
···
52
59
}
53
60
54
61
gopher.get = function(...)
55
-
gocmd("get", { ... })
62
+
gocmd("get", ...)
56
63
end
57
64
58
65
gopher.mod = function(...)
59
-
gocmd("mod", { ... })
66
+
gocmd("mod", ...)
60
67
end
61
68
62
69
gopher.generate = function(...)
63
-
gocmd("generate", { ... })
70
+
gocmd("generate", ...)
64
71
end
65
72
66
73
gopher.work = function(...)
67
-
gocmd("work", { ... })
74
+
gocmd("work", ...)
68
75
end
69
76
70
77
return gopher
+42
-21
lua/gopher/installer.lua
+42
-21
lua/gopher/installer.lua
···
1
1
local c = require("gopher.config").commands
2
2
local r = require "gopher._utils.runner"
3
3
local u = require "gopher._utils"
4
+
local log = require "gopher._utils.log"
4
5
local installer = {}
5
6
6
7
local urls = {
7
-
gomodifytags = "github.com/fatih/gomodifytags",
8
-
impl = "github.com/josharian/impl",
9
-
gotests = "github.com/cweill/gotests/...",
10
-
iferr = "github.com/koron/iferr",
11
-
dlv = "github.com/go-delve/delve/cmd/dlv",
8
+
gomodifytags = "github.com/fatih/gomodifytags@latest",
9
+
impl = "github.com/josharian/impl@latest",
10
+
gotests = "github.com/cweill/gotests/...@develop",
11
+
iferr = "github.com/koron/iferr@latest",
12
12
}
13
13
14
-
---@param pkg string
15
-
local function install(pkg)
16
-
local url = urls[pkg] .. "@latest"
17
-
r.sync(c.go, {
18
-
args = { "install", url },
19
-
on_exit = function(data, status)
20
-
if not status == 0 then
21
-
error("go install failed: " .. data)
22
-
return
23
-
end
24
-
u.notify("installed: " .. url)
25
-
end,
26
-
})
14
+
---@param opt vim.SystemCompleted
15
+
---@param url string
16
+
local function handle_intall_exit(opt, url)
17
+
if opt.code ~= 0 then
18
+
vim.schedule(function()
19
+
u.notify("go install failed: " .. url)
20
+
end)
21
+
22
+
log.error("go install failed:", "url", url, "opt", vim.inspect(opt))
23
+
return
24
+
end
25
+
26
+
vim.schedule(function()
27
+
u.notify("go install-ed: " .. url)
28
+
end)
29
+
end
30
+
31
+
---@param url string
32
+
local function install(url)
33
+
r.async({ c.go, "install", url }, function(opt)
34
+
handle_intall_exit(opt, url)
35
+
end)
36
+
end
37
+
38
+
---@param url string
39
+
local function install_sync(url)
40
+
local rs = r.sync { c.go, "install", url }
41
+
handle_intall_exit(rs, url)
27
42
end
28
43
29
44
---Install required go deps
30
-
function installer.install_deps()
31
-
for pkg, _ in pairs(urls) do
32
-
install(pkg)
45
+
---@param opts? {sync:boolean}
46
+
function installer.install_deps(opts)
47
+
opts = opts or {}
48
+
for _, url in pairs(urls) do
49
+
if opts.sync then
50
+
install_sync(url)
51
+
else
52
+
install(url)
53
+
end
33
54
end
34
55
end
35
56
+3
nvim.toml
+3
nvim.toml
+13
pkg.json
+13
pkg.json
+87
plugin/gopher.lua
+87
plugin/gopher.lua
···
1
+
---@toc_entry Commands
2
+
---@tag gopher.nvim-commands
3
+
---@text
4
+
--- If don't want to automatically register plugins' commands,
5
+
--- you can set `vim.g.gopher_register_commands` to `false`, before loading the plugin.
6
+
7
+
if vim.g.gopher_register_commands == false then
8
+
return
9
+
end
10
+
11
+
---@param name string
12
+
---@param fn fun(args: table)
13
+
---@param nargs? number|"*"|"?"
14
+
---@private
15
+
local function cmd(name, fn, nargs)
16
+
nargs = nargs or 0
17
+
vim.api.nvim_create_user_command(name, fn, { nargs = nargs })
18
+
end
19
+
20
+
cmd("GopherLog", function()
21
+
vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile())
22
+
end)
23
+
24
+
cmd("GoIfErr", function()
25
+
require("gopher").iferr()
26
+
end)
27
+
28
+
cmd("GoCmt", function()
29
+
require("gopher").comment()
30
+
end)
31
+
32
+
cmd("GoImpl", function(args)
33
+
require("gopher").impl(unpack(args.fargs))
34
+
end, "*")
35
+
36
+
-- :GoInstall
37
+
cmd("GoInstallDeps", function()
38
+
require("gopher").install_deps()
39
+
end)
40
+
41
+
cmd("GoInstallDepsSync", function()
42
+
require("gopher").install_deps { sync = true }
43
+
end)
44
+
45
+
-- :GoTag
46
+
cmd("GoTagAdd", function(opts)
47
+
require("gopher").tags.add(unpack(opts.fargs))
48
+
end, "*")
49
+
50
+
cmd("GoTagRm", function(opts)
51
+
require("gopher").tags.rm(unpack(opts.fargs))
52
+
end, "*")
53
+
54
+
cmd("GoTagClear", function()
55
+
require("gopher").tags.clear()
56
+
end)
57
+
58
+
-- :GoTest
59
+
cmd("GoTestAdd", function()
60
+
require("gopher").test.add()
61
+
end)
62
+
63
+
cmd("GoTestsAll", function()
64
+
require("gopher").test.all()
65
+
end)
66
+
67
+
cmd("GoTestsExp", function()
68
+
require("gopher").test.exported()
69
+
end)
70
+
71
+
-- :Go
72
+
cmd("GoMod", function(opts)
73
+
require("gopher").mod(opts.fargs)
74
+
end, "*")
75
+
76
+
cmd("GoGet", function(opts)
77
+
vim.print(opts)
78
+
require("gopher").get(opts.fargs)
79
+
end, "*")
80
+
81
+
cmd("GoWork", function(opts)
82
+
require("gopher").get(opts.fargs)
83
+
end, "*")
84
+
85
+
cmd("GoGenerate", function(opts)
86
+
require("gopher").generate(opts.fargs or "")
87
+
end, "?")
-14
plugin/gopher.vim
-14
plugin/gopher.vim
···
1
-
command! -nargs=* GoTagAdd :lua require"gopher".tags.add(<f-args>)
2
-
command! -nargs=* GoTagRm :lua require"gopher".tags.rm(<f-args>)
3
-
command! GoTestAdd :lua require"gopher".test.add()
4
-
command! GoTestsAll :lua require"gopher".test.all()
5
-
command! GoTestsExp :lua require"gopher".test.exported()
6
-
command! -nargs=* GoMod :lua require"gopher".mod(<f-args>)
7
-
command! -nargs=* GoGet :lua require"gopher".get(<f-args>)
8
-
command! -nargs=* GoWork :lua require"gopher".work(<f-args>)
9
-
command! -nargs=* GoImpl :lua require"gopher".impl(<f-args>)
10
-
command! -nargs=* GoGenerate :lua require"gopher".generate(<f-args>)
11
-
command! GoCmt :lua require"gopher".comment()
12
-
command! GoIfErr :lua require"gopher".iferr()
13
-
command! GoInstallDeps :lua require"gopher".install_deps()
14
-
command! GopherLog :lua vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile())
+5
-1
scripts/docgen.lua
+5
-1
scripts/docgen.lua
···
10
10
local files = {
11
11
"lua/gopher/init.lua",
12
12
"lua/gopher/config.lua",
13
+
"plugin/gopher.lua",
13
14
"lua/gopher/struct_tags.lua",
14
15
"lua/gopher/impl.lua",
15
16
"lua/gopher/gotests.lua",
16
17
"lua/gopher/iferr.lua",
17
18
"lua/gopher/comment.lua",
18
-
"lua/gopher/dap.lua",
19
19
}
20
20
21
21
minidoc.setup()
···
28
28
table.remove(lines, 1)
29
29
30
30
return lines
31
+
end
32
+
33
+
hooks.sections["@dochide"] = function(s)
34
+
s.parent:clear_lines()
31
35
end
32
36
33
37
MiniDoc.generate(files, "doc/gopher.nvim.txt", { hooks = hooks })
+43
-14
scripts/minimal_init.lua
+43
-14
scripts/minimal_init.lua
···
6
6
local function install_plug(plugin)
7
7
local name = plugin:match ".*/(.*)"
8
8
local package_root = root ".tests/site/pack/deps/start/"
9
-
if not vim.loop.fs_stat(package_root .. name) then
9
+
if not vim.uv.fs_stat(package_root .. name) then
10
10
print("Installing " .. plugin)
11
-
vim.fn.mkdir(package_root, "p")
12
-
vim.fn.system {
13
-
"git",
14
-
"clone",
15
-
"--depth=1",
16
-
"https://github.com/" .. plugin .. ".git",
17
-
package_root .. "/" .. name,
18
-
}
11
+
vim
12
+
.system({
13
+
"git",
14
+
"clone",
15
+
"--depth=1",
16
+
"https://github.com/" .. plugin .. ".git",
17
+
package_root .. "/" .. name,
18
+
})
19
+
:wait()
19
20
end
20
21
end
21
22
22
-
vim.cmd [[set runtimepath=$VIMRUNTIME]]
23
-
vim.opt.runtimepath:append(root())
24
-
vim.opt.packpath = { root ".tests/site" }
25
-
vim.notify = print
26
-
27
23
install_plug "nvim-lua/plenary.nvim"
28
24
install_plug "nvim-treesitter/nvim-treesitter"
29
25
install_plug "echasnovski/mini.doc" -- used for docs generation
26
+
install_plug "echasnovski/mini.test"
30
27
31
28
vim.env.XDG_CONFIG_HOME = root ".tests/config"
32
29
vim.env.XDG_DATA_HOME = root ".tests/data"
33
30
vim.env.XDG_STATE_HOME = root ".tests/state"
34
31
vim.env.XDG_CACHE_HOME = root ".tests/cache"
32
+
33
+
vim.opt.runtimepath:append(root())
34
+
vim.opt.packpath:append(root ".tests/site")
35
+
vim.notify = vim.print
36
+
37
+
-- install go treesitter parse
38
+
require("nvim-treesitter.install").ensure_installed_sync "go"
39
+
40
+
require("gopher").setup {
41
+
log_level = vim.log.levels.OFF,
42
+
timeout = 4000,
43
+
}
44
+
45
+
-- setup mini.test only when running headless nvim
46
+
if #vim.api.nvim_list_uis() == 0 then
47
+
require("mini.test").setup {
48
+
collect = {
49
+
find_files = function()
50
+
return vim.fn.globpath("spec", "**/*_test.lua", true, true)
51
+
end,
52
+
},
53
+
}
54
+
end
55
+
56
+
-- needed for tests, i dont know the reason why, but on start
57
+
-- vim is not able to use treesitter for go by default
58
+
vim.api.nvim_create_autocmd("FileType", {
59
+
pattern = "go",
60
+
callback = function(args)
61
+
vim.treesitter.start(args.buf, "go")
62
+
end,
63
+
})
spec/fixtures/comment/empty_input.go
spec/fixtures/comment/empty_input.go
This is a binary file and will not be displayed.
+5
spec/fixtures/comment/func_input.go
+5
spec/fixtures/comment/func_input.go
+6
spec/fixtures/comment/func_output.go
+6
spec/fixtures/comment/func_output.go
+4
spec/fixtures/comment/interface_output.go
+4
spec/fixtures/comment/interface_output.go
+7
spec/fixtures/comment/method_input.go
+7
spec/fixtures/comment/method_input.go
+8
spec/fixtures/comment/method_output.go
+8
spec/fixtures/comment/method_output.go
+1
spec/fixtures/comment/package_input.go
+1
spec/fixtures/comment/package_input.go
···
1
+
package main
+4
spec/fixtures/comment/struct_output.go
+4
spec/fixtures/comment/struct_output.go
+9
spec/fixtures/iferr/iferr_input.go
+9
spec/fixtures/iferr/iferr_input.go
+12
spec/fixtures/iferr/iferr_output.go
+12
spec/fixtures/iferr/iferr_output.go
+7
spec/fixtures/iferr/message_input.go
+7
spec/fixtures/iferr/message_input.go
+10
spec/fixtures/iferr/message_output.go
+10
spec/fixtures/iferr/message_output.go
+7
spec/fixtures/impl/closer_output.go
+7
spec/fixtures/impl/closer_output.go
+7
spec/fixtures/impl/reader_output.go
+7
spec/fixtures/impl/reader_output.go
+7
spec/fixtures/impl/writer_output.go
+7
spec/fixtures/impl/writer_output.go
+5
spec/fixtures/tests/function_input.go
+5
spec/fixtures/tests/function_input.go
+24
spec/fixtures/tests/function_output.go
+24
spec/fixtures/tests/function_output.go
···
1
+
package fortest
2
+
3
+
import "testing"
4
+
5
+
func TestAdd(t *testing.T) {
6
+
type args struct {
7
+
x int
8
+
y int
9
+
}
10
+
tests := []struct {
11
+
name string
12
+
args args
13
+
want int
14
+
}{
15
+
// TODO: Add test cases.
16
+
}
17
+
for _, tt := range tests {
18
+
t.Run(tt.name, func(t *testing.T) {
19
+
if got := Add(tt.args.x, tt.args.y); got != tt.want {
20
+
t.Errorf("Add() = %v, want %v", got, tt.want)
21
+
}
22
+
})
23
+
}
24
+
}
+7
spec/fixtures/tests/method_input.go
+7
spec/fixtures/tests/method_input.go
+26
spec/fixtures/tests/method_output.go
+26
spec/fixtures/tests/method_output.go
···
1
+
package fortest
2
+
3
+
import "testing"
4
+
5
+
func TestForTest_Add(t *testing.T) {
6
+
type args struct {
7
+
x int
8
+
y int
9
+
}
10
+
tests := []struct {
11
+
name string
12
+
tr *ForTest
13
+
args args
14
+
want int
15
+
}{
16
+
// TODO: Add test cases.
17
+
}
18
+
for _, tt := range tests {
19
+
t.Run(tt.name, func(t *testing.T) {
20
+
tr := &ForTest{}
21
+
if got := tr.Add(tt.args.x, tt.args.y); got != tt.want {
22
+
t.Errorf("ForTest.Add() = %v, want %v", got, tt.want)
23
+
}
24
+
})
25
+
}
26
+
}
+37
spec/integration/comment_test.lua
+37
spec/integration/comment_test.lua
···
1
+
local t = require "spec.testutils"
2
+
local child, T = t.setup "comment"
3
+
4
+
local function do_the_test(fixture, pos)
5
+
local rs = t.setup_test("comment/" .. fixture, child, pos)
6
+
child.cmd "GoCmt"
7
+
child.cmd "write"
8
+
9
+
t.eq(t.readfile(rs.tmp), rs.fixtures.output)
10
+
t.cleanup(rs)
11
+
end
12
+
13
+
T["comment"]["should add comment to package"] = function()
14
+
do_the_test("package", { 1, 1 })
15
+
end
16
+
17
+
T["comment"]["should add comment to struct"] = function()
18
+
do_the_test("struct", { 4, 1 })
19
+
end
20
+
21
+
T["comment"]["should add comment to function"] = function()
22
+
do_the_test("func", { 3, 1 })
23
+
end
24
+
25
+
T["comment"]["should add comment to method"] = function()
26
+
do_the_test("method", { 5, 1 })
27
+
end
28
+
29
+
T["comment"]["should add comment to interface"] = function()
30
+
do_the_test("interface", { 3, 6 })
31
+
end
32
+
33
+
T["comment"]["otherwise should add // above cursor"] = function()
34
+
do_the_test("empty", { 1, 1 })
35
+
end
36
+
37
+
return T
+29
spec/integration/gotests_test.lua
+29
spec/integration/gotests_test.lua
···
1
+
local t = require "spec.testutils"
2
+
local child, T = t.setup "gotests"
3
+
4
+
--- NOTE: :GoTestAdd is the only place that has actual logic
5
+
--- All other parts are handled `gotests` itself.
6
+
7
+
---@param fpath string
8
+
---@return string
9
+
local function read_testfile(fpath)
10
+
return t.readfile(fpath:gsub(".go", "_test.go"))
11
+
end
12
+
13
+
T["gotests"]["should add test for function under cursor"] = function()
14
+
local rs = t.setup_test("tests/function", child, { 3, 5 })
15
+
child.cmd "GoTestAdd"
16
+
17
+
t.eq(rs.fixtures.output, read_testfile(rs.tmp))
18
+
t.cleanup(rs)
19
+
end
20
+
21
+
T["gotests"]["should add test for method under cursor"] = function()
22
+
local rs = t.setup_test("tests/method", child, { 5, 19 })
23
+
child.cmd "GoTestAdd"
24
+
25
+
t.eq(rs.fixtures.output, read_testfile(rs.tmp))
26
+
t.cleanup(rs)
27
+
end
28
+
29
+
return T
+27
spec/integration/iferr_test.lua
+27
spec/integration/iferr_test.lua
···
1
+
local t = require "spec.testutils"
2
+
local child, T = t.setup "iferr"
3
+
4
+
T["iferr"]["should add if != nil {"] = function()
5
+
local rs = t.setup_test("iferr/iferr", child, { 8, 2 })
6
+
child.cmd "GoIfErr"
7
+
child.cmd "write"
8
+
9
+
t.eq(t.readfile(rs.tmp), rs.fixtures.output)
10
+
t.cleanup(rs)
11
+
end
12
+
13
+
T["iferr"]["should add if err with custom message"] = function()
14
+
child.lua [[
15
+
require("gopher").setup {
16
+
iferr = { message = 'fmt.Errorf("failed to %w", err)' }
17
+
} ]]
18
+
19
+
local rs = t.setup_test("iferr/message", child, { 6, 2 })
20
+
child.cmd "GoIfErr"
21
+
child.cmd "write"
22
+
23
+
t.eq(t.readfile(rs.tmp), rs.fixtures.output)
24
+
t.cleanup(rs)
25
+
end
26
+
27
+
return T
+35
spec/integration/impl_test.lua
+35
spec/integration/impl_test.lua
···
1
+
local t = require "spec.testutils"
2
+
local child, T = t.setup "impl"
3
+
4
+
T["impl"]["should do impl with 'w io.Writer'"] = function()
5
+
local rs = t.setup_test("impl/writer", child, { 3, 0 })
6
+
child.cmd "GoImpl w io.Writer"
7
+
child.cmd "write"
8
+
9
+
-- NOTE: since "impl" won't implement interface if it's already implemented i went with this hack
10
+
local rhs = rs.fixtures.output:gsub("Test2", "Test")
11
+
t.eq(t.readfile(rs.tmp), rhs)
12
+
t.cleanup(rs)
13
+
end
14
+
15
+
T["impl"]["should work with full input, 'r Read io.Reader'"] = function()
16
+
local rs = t.setup_test("impl/reader", child)
17
+
child.cmd "GoImpl r Read io.Reader"
18
+
child.cmd "write"
19
+
20
+
local rhs = rs.fixtures.output:gsub("Read2", "Read")
21
+
t.eq(t.readfile(rs.tmp), rhs)
22
+
t.cleanup(rs)
23
+
end
24
+
25
+
T["impl"]["should work with minimal input 'io.Closer'"] = function()
26
+
local rs = t.setup_test("impl/closer", child, { 3, 6 })
27
+
child.cmd "GoImpl io.Closer"
28
+
child.cmd "write"
29
+
30
+
local rhs = rs.fixtures.output:gsub("Test2", "Test")
31
+
t.eq(t.readfile(rs.tmp), rhs)
32
+
t.cleanup(rs)
33
+
end
34
+
35
+
return T
+102
spec/testutils.lua
+102
spec/testutils.lua
···
1
+
local base_dir = vim.env.GOPHER_DIR or vim.fn.expand "%:p:h"
2
+
3
+
---@class gopher.TestUtils
4
+
local testutils = {}
5
+
6
+
testutils.mininit_path = vim.fs.joinpath(base_dir, "scripts", "minimal_init.lua")
7
+
testutils.fixtures_dir = vim.fs.joinpath(base_dir, "spec/fixtures")
8
+
9
+
---@param name string
10
+
---@return MiniTest.child, table
11
+
function testutils.setup(name)
12
+
local child = MiniTest.new_child_neovim()
13
+
local T = MiniTest.new_set {
14
+
hooks = {
15
+
post_once = child.stop,
16
+
pre_case = function()
17
+
child.restart { "-u", testutils.mininit_path }
18
+
end,
19
+
},
20
+
}
21
+
22
+
T[name] = MiniTest.new_set {}
23
+
return child, T
24
+
end
25
+
26
+
---@generic T
27
+
---@param a T
28
+
---@param b T
29
+
---@return boolean
30
+
function testutils.eq(a, b)
31
+
return MiniTest.expect.equality(a, b)
32
+
end
33
+
34
+
---@return string
35
+
function testutils.tmpfile()
36
+
return vim.fn.tempname() .. ".go"
37
+
end
38
+
39
+
---@param path string
40
+
---@return string
41
+
function testutils.readfile(path)
42
+
return vim.fn.join(vim.fn.readfile(path), "\n")
43
+
end
44
+
45
+
---@param fpath string
46
+
---@param contents string
47
+
function testutils.writefile(fpath, contents)
48
+
vim.fn.writefile(vim.split(contents, "\n"), fpath)
49
+
end
50
+
51
+
---@param fpath string
52
+
function testutils.deletefile(fpath)
53
+
vim.fn.delete(fpath)
54
+
end
55
+
56
+
---@class gopher.TestUtilsFixtures
57
+
---@field input string
58
+
---@field output string
59
+
60
+
---@param fixture string
61
+
---@return gopher.TestUtilsFixtures
62
+
function testutils.get_fixtures(fixture)
63
+
return {
64
+
input = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_input.go"),
65
+
output = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_output.go"),
66
+
}
67
+
end
68
+
69
+
---@class gopher.TestUtilsSetup
70
+
---@field tmp string
71
+
---@field fixtures gopher.TestUtilsFixtures
72
+
---@field bufnr number
73
+
74
+
---@param fixture string
75
+
---@param child MiniTest.child
76
+
---@param pos? number[]
77
+
---@return gopher.TestUtilsSetup
78
+
function testutils.setup_test(fixture, child, pos)
79
+
local tmp = testutils.tmpfile()
80
+
local fixtures = testutils.get_fixtures(fixture)
81
+
82
+
testutils.writefile(tmp, fixtures.input)
83
+
child.cmd("silent edit " .. tmp)
84
+
85
+
local bufnr = child.fn.bufnr(tmp)
86
+
if pos then
87
+
child.fn.setpos(".", { bufnr, unpack(pos) })
88
+
end
89
+
90
+
return {
91
+
tmp = tmp,
92
+
bufnr = bufnr,
93
+
fixtures = fixtures,
94
+
}
95
+
end
96
+
97
+
---@param inp gopher.TestUtilsSetup
98
+
function testutils.cleanup(inp)
99
+
testutils.deletefile(inp.tmp)
100
+
end
101
+
102
+
return testutils
+25
spec/unit/utils_test.lua
+25
spec/unit/utils_test.lua
···
1
+
local t = require "spec.testutils"
2
+
local _, T = t.setup "utils"
3
+
4
+
T["utils"]["should .remove_empty_lines()"] = function()
5
+
local u = require "gopher._utils"
6
+
local inp = { "hi", "", "a", "", "", "asdf" }
7
+
8
+
t.eq(u.remove_empty_lines(inp), { "hi", "a", "asdf" })
9
+
end
10
+
11
+
T["utils"]["should .readfile_joined()"] = function()
12
+
local data = "line1\nline2\nline3"
13
+
local tmp = t.tmpfile()
14
+
local u = require "gopher._utils"
15
+
16
+
t.writefile(tmp, data)
17
+
t.eq(u.readfile_joined(tmp), data)
18
+
end
19
+
20
+
T["utils"]["should .trimend()"] = function()
21
+
local u = require "gopher._utils"
22
+
t.eq(u.trimend " hi ", " hi")
23
+
end
24
+
25
+
return T
-29
spec/units/config_spec.lua
-29
spec/units/config_spec.lua
···
1
-
describe("gopher.config", function()
2
-
it(".setup() should provide default when .setup() is not called", function()
3
-
local c = require "gopher.config"
4
-
5
-
assert.are.same(c.commands.go, "go")
6
-
assert.are.same(c.commands.gomodifytags, "gomodifytags")
7
-
assert.are.same(c.commands.gotests, "gotests")
8
-
assert.are.same(c.commands.impl, "impl")
9
-
assert.are.same(c.commands.iferr, "iferr")
10
-
assert.are.same(c.commands.dlv, "dlv")
11
-
end)
12
-
13
-
it(".setup() should change options on users config", function()
14
-
local c = require "gopher.config"
15
-
c.setup {
16
-
commands = {
17
-
go = "go1.420",
18
-
gomodifytags = "iDontUseRustBtw",
19
-
},
20
-
}
21
-
22
-
assert.are.same(c.commands.go, "go1.420")
23
-
assert.are.same(c.commands.gomodifytags, "iDontUseRustBtw")
24
-
assert.are.same(c.commands.gotests, "gotests")
25
-
assert.are.same(c.commands.impl, "impl")
26
-
assert.are.same(c.commands.iferr, "iferr")
27
-
assert.are.same(c.commands.dlv, "dlv")
28
-
end)
29
-
end)
-15
spec/units/utils_spec.lua
-15
spec/units/utils_spec.lua
···
1
-
describe("gopher._utils", function()
2
-
local u = require "gopher._utils"
3
-
4
-
describe(".sreq()", function()
5
-
it("can require existing module", function()
6
-
assert.are.same(require "gopher", u.sreq "gopher")
7
-
end)
8
-
9
-
it("cannot require non-existing module", function()
10
-
assert.has.errors(function()
11
-
u.sreq "iDontExistBtw"
12
-
end)
13
-
end)
14
-
end)
15
-
end)