[mirror] Make your go dev experience better github.com/olexsmir/gopher.nvim
neovim golang

Compare changes

Choose any two refs to compare.

Changed files
+2031 -769
.github
doc
lua
plugin
scripts
spec
vhs
+1
.envrc
··· 1 1 dotenv 2 2 3 + # GOPHER_DIR - needed only for tests, to find the root of the project 3 4 env_vars_required GOPHER_DIR
+3 -1
.github/workflows/linters.yml
··· 53 53 run: task docgen 54 54 55 55 - name: Diff 56 - run: exit $(git status --porcelain doc | wc -l | tr -d " ") 56 + run: | 57 + git diff doc 58 + exit $(git status --porcelain doc | wc -l | tr -d " ")
+11 -9
.github/workflows/tests.yml
··· 14 14 os: [ubuntu-latest] 15 15 version: 16 16 - stable 17 - # - nightly # TODO: enable when stable 17 + - nightly 18 + - v0.10.0 19 + - v0.10.4 20 + - v0.11.0 21 + - v0.11.1 22 + - v0.11.2 23 + - v0.11.3 24 + - v0.11.4 18 25 runs-on: ${{ matrix.os }} 19 26 steps: 20 27 - name: Install Task ··· 35 42 neovim: true 36 43 version: ${{ matrix.version }} 37 44 38 - - uses: actions/checkout@v3 45 + - uses: actions/checkout@v4 39 46 40 47 - name: Cache .tests 41 48 uses: actions/cache@v4 ··· 47 54 key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} 48 55 49 56 - name: Install Go bins 50 - run: | 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 57 + run: task install-deps 56 58 57 59 - name: Run Tests 58 60 run: | 59 61 nvim --version 60 - task tests 62 + task test
-10
.luarc.json
··· 1 - { 2 - "diagnostics.globals": [ 3 - "describe", 4 - "it", 5 - "before_each", 6 - "after_each", 7 - "before_all", 8 - "after_all" 9 - ] 10 - }
+10 -12
CONTRIBUTING.md
··· 1 1 # Contributing to `gopher.nvim` 2 - 3 2 Thank you for taking the time to submit some code to gopher.nvim. It means a lot! 4 3 5 4 ### Task running 6 - 7 5 In this codebase for running tasks is used [Taskfile](https://taskfile.dev). 8 6 You can install it with: 9 7 ```bash 10 8 go install github.com/go-task/task/v3/cmd/task@latest 11 9 ``` 12 10 13 - ### Styling and formatting 14 - 11 + ### Formatting and linting 15 12 Code is formatted by [stylua](https://github.com/JohnnyMorganz/StyLua) and linted using [selene](https://github.com/Kampfkarren/selene). 16 13 You can install these with: 17 14 ··· 22 19 23 20 For formatting use this following commands, or setup your editor to integrate with selene/stylua: 24 21 ```bash 25 - task stylua 26 - task lint # lintering and format chewing 22 + task format 23 + task lint 27 24 ``` 28 25 29 26 ### Documentation 27 + Here we're using [mini.doc](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-doc.md) 28 + for generating vimhelp files based on [LuaCats](https://luals.github.io/wiki/annotations/) annotations in comments. 30 29 31 - Here we are using [mini.doc](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-doc.md) 32 - for generating help files based on EmmyLua-like annotations in comments 30 + For demo gifs in [readme](./README.md) we're using [vhs](https://github.com/charmbracelet/vhs). 31 + All files related to demos live in [/vhs](./vhs) dir. 33 32 34 33 You can generate docs with: 35 34 ```bash 36 - task docgen 35 + task docgen # generates vimhelp 36 + task vhs:generate # generates demo gifs 37 37 ``` 38 38 39 39 ### Commit messages 40 - 41 40 We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), please follow it. 42 41 43 42 ### Testing 44 - 45 43 For testing this plugins uses [mini.test](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-test.md). 46 44 All tests live in [/spec](./spec) dir. 47 45 48 46 You can run tests with: 49 47 ```bash 50 - task tests 48 + task test 51 49 ```
+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.
+122 -48
README.md
··· 4 4 5 5 Minimalistic plugin for Go development in Neovim written in Lua. 6 6 7 - It's **NOT** an LSP tool, the main goal of this plugin is to add go tooling support in Neovim. 7 + It's **NOT** an LSP tool, the goal of this plugin is to add go tooling support in Neovim. 8 8 9 - > If you want to use new and maybe undocumented, and unstable features you might use [develop](https://github.com/olexsmir/gopher.nvim/tree/develop) branch. 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 + 11 + ## Table of content 12 + * [How to install](#install-using-lazynvim) 13 + * [Features](#features) 14 + * [Configuration](#configuration) 15 + * [Troubleshooting](#troubleshooting) 16 + * [Contributing](#contributing) 10 17 11 18 ## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim)) 12 19 13 20 Requirements: 14 21 15 22 - **Neovim 0.10** or later 16 - - Treesitter `go` parser(`:TSInstall go`) 17 - - [Go](https://github.com/golang/go) installed (tested on 1.23) 23 + - Treesitter parser for `go`(`:TSInstall go` if you use [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter)) 24 + - [Go](https://github.com/golang/go) installed 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) 18 30 19 31 ```lua 32 + -- NOTE: this plugin is already lazy-loaded and adds only about 1ms 33 + -- of load time to your config 20 34 { 21 35 "olexsmir/gopher.nvim", 22 36 ft = "go", 23 37 -- branch = "develop" 24 - dependencies = { 25 - "nvim-treesitter/nvim-treesitter", 26 - }, 27 - -- (optional) will update plugin's deps on every update 38 + -- (optional) updates the plugin's dependencies on each update 28 39 build = function() 29 40 vim.cmd.GoInstallDeps() 30 41 end, 42 + ---@module "gopher" 31 43 ---@type gopher.Config 32 44 opts = {}, 33 45 } 34 46 ``` 35 47 36 - ## Configuration 37 - 38 - > [!IMPORTANT] 39 - > 40 - > If you need more info look `:h gopher.nvim` 41 - 42 - **Take a look at default options** 43 - 44 - ```lua 45 - require("gopher").setup { 46 - commands = { 47 - go = "go", 48 - gomodifytags = "gomodifytags", 49 - gotests = "gotests", 50 - impl = "impl", 51 - iferr = "iferr", 52 - dlv = "dlv", 53 - }, 54 - gotests = { 55 - -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template 56 - template = "default", 57 - -- path to a directory containing custom test code templates 58 - template_dir = nil, 59 - -- switch table tests from using slice to map (with test name for the key) 60 - named = false, 61 - }, 62 - gotag = { 63 - transform = "snakecase", 64 - }, 65 - } 66 - ``` 67 - 68 48 ## Features 69 - 70 - <!-- markdownlint-disable --> 71 49 72 50 <details> 73 51 <summary> ··· 84 62 - [impl](https://github.com/josharian/impl) 85 63 - [gotests](https://github.com/cweill/gotests) 86 64 - [iferr](https://github.com/koron/iferr) 87 - - [dlv](github.com/go-delve/delve/cmd/dlv) 65 + - [json2go](https://github.com/olexsmir/json2go) 88 66 </details> 89 67 90 68 <details> ··· 92 70 <b>Add and remove tags for structs via <a href="https://github.com/fatih/gomodifytags">gomodifytags</a></b> 93 71 </summary> 94 72 73 + ![Add tags demo](./vhs/tags.gif) 74 + 95 75 By default `json` tag will be added/removed, if not set: 96 76 97 77 ```vim 98 78 " add json tag 99 79 :GoTagAdd json 80 + 81 + " add json tag with omitempty option 82 + :GoTagAdd json=omitempty 100 83 101 84 " remove yaml tag 102 85 :GoTagRm yaml ··· 169 152 <b>Interface implementation via <a href="https://github.com/josharian/impl">impl<a></b> 170 153 </summary> 171 154 155 + ![Auto interface implementation demo](./vhs/impl.gif) 156 + 172 157 Syntax of the command: 173 158 ```vim 174 159 :GoImpl [receiver] [interface] ··· 192 177 <b>Generate boilerplate for doc comments</b> 193 178 </summary> 194 179 180 + ![Generate comments](./vhs/comment.gif) 181 + 195 182 First set a cursor on **public** package/function/interface/struct and execute: 196 183 197 184 ```vim ··· 199 186 ``` 200 187 </details> 201 188 189 + <details> 190 + <summary> 191 + <b>Convert json to Go types</b> 192 + </summary> 193 + 194 + ![Convert JSON to Go types](./vhs/json2go.gif) 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 + 202 207 203 208 <details> 204 209 <summary> 205 210 <b>Generate <code>if err != nil {</code> via <a href="https://github.com/koron/iferr">iferr</a></b> 206 211 </summary> 207 212 213 + ![Generate if err != nil {](./vhs/iferr.gif) 214 + 208 215 Set the cursor on the line with `err` and execute 209 216 210 217 ```vim ··· 212 219 ``` 213 220 </details> 214 221 215 - ## Contributing 222 + ## Configuration 223 + 224 + > [!IMPORTANT] 225 + > 226 + > If you need more info look `:h gopher.nvim` 227 + 228 + **Take a look at default options (might be a bit outdated, look `:h gopher.nvim-config`)** 229 + 230 + ```lua 231 + require("gopher").setup { 232 + -- log level, you might consider using DEBUG or TRACE for debugging the plugin 233 + log_level = vim.log.levels.INFO, 234 + 235 + -- timeout for running internal commands 236 + timeout = 2000, 237 + 238 + -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 239 + installer_timeout = 999999, 240 + 241 + -- user specified paths to binaries 242 + commands = { 243 + go = "go", 244 + gomodifytags = "gomodifytags", 245 + gotests = "gotests", 246 + impl = "impl", 247 + iferr = "iferr", 248 + }, 249 + gotests = { 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. 252 + template = "default", 253 + 254 + -- path to a directory containing custom test code templates 255 + template_dir = nil, 256 + 257 + -- use named tests(map with test name as key) in table tests(slice of structs by default) 258 + named = false, 259 + }, 260 + gotag = { 261 + transform = "snakecase", 216 262 217 - PRs are always welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) 263 + -- default tags to add to struct fields 264 + default_tag = "json", 218 265 219 - ## Thanks 266 + -- default tag option added struct fields, set to nil to disable 267 + -- e.g: `option = "json=omitempty,xml=omitempty` 268 + option = nil, 269 + }, 270 + iferr = { 271 + -- choose a custom error message, nil to use default 272 + -- e.g: `message = 'fmt.Errorf("failed to %w", err)'` 273 + message = nil, 274 + }, 275 + json2go = { 276 + -- command used to open interactive input. 277 + -- e.g: `split`, `botright split`, `tabnew` 278 + interactive_cmd = "vsplit", 220 279 221 - - [go.nvim](https://github.com/ray-x/go.nvim) 222 - - [iferr](https://github.com/koron/iferr) 280 + -- name of autogenerated struct 281 + -- e.g: "MySuperCoolName" 282 + type_name = nil, 283 + }, 284 + } 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). 293 + 294 + ## Contributing 295 + 296 + PRs are always welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md)
+24 -17
Taskfile.yml
··· 1 1 version: "3" 2 + 3 + includes: 4 + vhs: 5 + taskfile: ./vhs/Taskfile.yml 6 + dir: ./vhs 7 + 2 8 tasks: 3 9 lint: 4 - desc: runs all linters 5 10 cmds: 6 - - task: selene 11 + - selene . 7 12 - stylua --check . 8 13 9 - selene: 10 - desc: runs lua linter(selene) 11 - cmds: 12 - - selene . 14 + format: 15 + cmd: stylua . 13 16 14 - stylua: 15 - desc: runs lua formatter 16 - cmds: 17 - - stylua . 18 - 19 - tests: 20 - desc: run all tests 17 + test: 21 18 cmds: 22 19 - | 23 - nvim --headless \ 20 + nvim --clean --headless \ 24 21 -u ./scripts/minimal_init.lua \ 25 - -c "lua MiniTest.run()" 22 + -c "lua MiniTest.run()" \ 23 + -c ":qa!" 24 + nvim: 25 + cmd: nvim --clean -u "./scripts/minimal_init.lua" {{ .CLI_ARGS }} 26 26 27 27 docgen: 28 28 desc: generate vimhelp 29 29 cmds: 30 30 - | 31 - nvim --noplugin \ 32 - --headless \ 31 + nvim --clean --headless \ 33 32 -u "./scripts/minimal_init.lua" \ 34 33 -c "luafile ./scripts/docgen.lua" \ 35 34 -c ":qa!" 35 + 36 + install-deps: 37 + desc: installs go bin (used in CI) 38 + cmds: 39 + - | 40 + nvim --clean --headless \ 41 + -u "./scripts/minimal_init.lua" \ 42 + +GoInstallDepsSync +qa
+138 -79
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 - Modify 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| 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 + json2go .............................................. |gopher.nvim-json2go| 17 + Auto implementation of interface methods ................ |gopher.nvim-impl| 18 + Generating unit tests boilerplate .................... |gopher.nvim-gotests| 19 + Iferr .................................................. |gopher.nvim-iferr| 20 + Generate comments ................................... |gopher.nvim-comments| 19 21 20 22 ------------------------------------------------------------------------------ 21 - *gopher.nvim-setup* 23 + *gopher.nvim-setup()* 22 24 `gopher.setup`({user_config}) 23 25 Setup function. This method simply merges default config with opts table. 24 26 You can read more about configuration at |gopher.nvim-config| 25 27 Calling this function is optional, if you ok with default settings. 26 - See |gopher.nvim.config-defaults| 28 + See |gopher.nvim.config| 27 29 28 30 Usage ~ 29 - `require("gopher").setup {}` (replace `{}` with your `config` table) 31 + >lua 32 + require("gopher").setup {} -- use default config or replace {} with your own 33 + < 30 34 Parameters ~ 31 - {user_config} `(gopher.Config)` 35 + {user_config} `(gopher.Config)` See |gopher.nvim-config| 32 36 33 37 ------------------------------------------------------------------------------ 34 - *gopher.nvim-install-deps* 38 + *gopher.nvim-dependencies* 35 39 `gopher.install_deps` 36 - 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 to use lua api. 39 - By default dependencies will be installed asynchronously, 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. 40 45 41 46 42 47 ============================================================================== 43 48 ------------------------------------------------------------------------------ 44 49 *gopher.nvim-config* 45 - config it is the place where you can configure the plugin. 46 - also this is optional is you're ok with default settings. 47 - You can look at default options |gopher.nvim-config-defaults| 48 - 49 - ------------------------------------------------------------------------------ 50 - *gopher.nvim-config-defaults* 51 50 `default_config` 52 51 >lua 53 52 local default_config = { 54 - --minidoc_replace_end 55 - 56 53 -- log level, you might consider using DEBUG or TRACE for debugging the plugin 57 54 ---@type number 58 55 log_level = vim.log.levels.INFO, 59 56 60 - -- timeout for running commands 57 + -- timeout for running internal commands 61 58 ---@type number 62 59 timeout = 2000, 63 60 64 - --- whether to setup plugin commands or not 65 - ---@type boolean 66 - setup_commands = true, 61 + -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 62 + ---@type number 63 + installer_timeout = 999999, 67 64 68 65 -- user specified paths to binaries 69 66 ---@class gopher.ConfigCommand ··· 73 70 gotests = "gotests", 74 71 impl = "impl", 75 72 iferr = "iferr", 76 - dlv = "dlv", 73 + json2go = "json2go", 77 74 }, 78 75 ---@class gopher.ConfigGotests 79 76 gotests = { 80 - -- 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. 81 79 template = "default", 80 + 82 81 -- path to a directory containing custom test code templates 83 82 ---@type string|nil 84 83 template_dir = nil, 85 - -- 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) 86 86 named = false, 87 87 }, 88 88 ---@class gopher.ConfigGoTag ··· 92 92 93 93 -- default tags to add to struct fields 94 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, 95 100 }, 101 + ---@class gopher.ConfigIfErr 96 102 iferr = { 97 - -- 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)'` 98 105 ---@type string|nil 99 106 message = nil, 100 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 + }, 101 119 } 102 120 < 103 121 Class ~ 104 122 {gopher.Config} 123 + Fields ~ 124 + {setup} `(fun(user_config?: gopher.Config))` 125 + 126 + 127 + ============================================================================== 128 + ------------------------------------------------------------------------------ 129 + *gopher.nvim-commands* 130 + 131 + If don't want to automatically register plugins' commands, 132 + you can set `vim.g.gopher_register_commands` to `false`, before loading the plugin. 105 133 106 134 107 135 ============================================================================== 108 136 ------------------------------------------------------------------------------ 109 137 *gopher.nvim-struct-tags* 110 - struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. 111 - Usage ~ 138 + 139 + `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to 140 + struct fields. 112 141 113 - How to add/remove tags to struct fields: 142 + Usage ~ 114 143 115 - ------------------------------------------------------------------------------ 144 + How to add/remove/clear tags to struct fields: 145 + 1. Place cursor on the struct 116 146 2. Run `:GoTagAdd json` to add json tags to struct fields 117 147 3. Run `:GoTagRm json` to remove json tags to struct fields 148 + 4. Run `:GoTagClear` to clear all tags from struct fields 149 + 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 + 118 154 119 155 NOTE: if you dont specify the tag it will use `json` as default 120 156 ··· 134 170 Name string `yaml:name` 135 171 } 136 172 < 173 + 174 + ============================================================================== 137 175 ------------------------------------------------------------------------------ 138 - *struct_tags.add()* 139 - `struct_tags.add`({...}) 140 - tags to a struct under the cursor 141 - Parameters ~ 142 - {...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] 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. 143 185 186 + Alternatively, you can pass JSON directly as an argument: 187 + >vim 188 + :GoJson {"name": "Alice", "age": 30} 189 + < 144 190 ------------------------------------------------------------------------------ 145 - *struct_tags.remove()* 146 - `struct_tags.remove`({...}) 147 - tags from a struct under the cursor 191 + *json2go.transform()* 192 + `json2go.transform`({json_str}) 193 + 148 194 Parameters ~ 149 - {...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] 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. 150 198 151 199 ------------------------------------------------------------------------------ 152 - *struct_tags.clear()* 153 - `struct_tags.clear`() 154 - all tags from a struct under the cursor 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)` 155 208 156 209 157 210 ============================================================================== 158 211 ------------------------------------------------------------------------------ 159 212 *gopher.nvim-impl* 160 - impl is utilizing the `impl` tool to generate method stubs for interfaces. 213 + 214 + Integration of `impl` tool to generate method stubs for interfaces. 215 + 161 216 Usage ~ 162 217 163 218 1. Automatically implement an interface for a struct: 164 - - Place your cursor on the struct where you want to implement the interface. 165 - - Run `:GoImpl io.Reader` 166 - - 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. 167 222 168 223 2. Specify a custom receiver: 169 - - Place your cursor on the struct 170 - - Run `:GoImpl w io.Writer`, where: 171 - - `w` is the receiver. 172 - - `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. 173 228 174 229 3. Explicitly specify the receiver, struct, and interface: 175 - - No need to place the cursor on the struct if all arguments are provided. 176 - - Run `:GoImpl r RequestReader io.Reader`, where: 177 - - `r` is the receiver. 178 - - `RequestReader` is the struct. 179 - - `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. 180 235 181 236 Example: 182 237 >go ··· 197 252 Usage ~ 198 253 199 254 - Generate unit test for specific function/method: 200 - 1. Place your cursor on the desired function/method. 201 - 2. Run `:GoTestAdd` 255 + 1. Place your cursor on the desired function/method. 256 + 2. Run `:GoTestAdd` 202 257 203 258 - Generate unit tests for *all* functions/methods in current file: 204 - - run `:GoTestsAll` 259 + - run `:GoTestsAll` 205 260 206 261 - Generate unit tests *only* for *exported(public)* functions/methods: 207 - - run `:GoTestsExp` 208 - 209 - You can also specify the template to use for generating the tests. See |gopher.nvim-config| 210 - More details about templates can be found at: https://github.com/cweill/gotests 211 - 212 - ------------------------------------------------------------------------------ 213 - *gopher.nvim-gotests-named* 262 + - run `:GoTestsExp` 214 263 215 - You can enable named tests in the config if you prefer using named tests. 264 + You can also specify the template to use for generating the tests. 216 265 See |gopher.nvim-config|. 266 + More details about templates: https://github.com/cweill/gotests 267 + 268 + If you prefer named tests, you can enable them in |gopher.nvim-config|. 217 269 218 270 219 271 ============================================================================== 220 272 ------------------------------------------------------------------------------ 221 273 *gopher.nvim-iferr* 222 - If you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check. 274 + 275 + `iferr` provides a way to way to automatically insert `if err != nil` check. 276 + If you want to change `-message` option of `iferr` tool, see |gopher.nvim-config| 277 + 223 278 Usage ~ 224 279 Execute `:GoIfErr` near any `err` variable to insert the check 225 280 ··· 227 282 ============================================================================== 228 283 ------------------------------------------------------------------------------ 229 284 *gopher.nvim-comments* 285 + 286 + This module provides a way to generate comments for Go code. 287 + 230 288 Usage ~ 231 - Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. 232 - This module provides a way to generate comments for Go code. 289 + 290 + Set cursor on line with function/method/struct/etc and 291 + run `:GoCmt` to generate a comment. 233 292 234 293 235 294 vim:tw=78:ts=8:noet:ft=help:norl:
+40 -13
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 - lvl = lvl or vim.log.levels.INFO 9 - vim.defer_fn(function() 10 - vim.notify(msg, lvl, { 11 - title = c.___plugin_name, 12 - }) 13 - log.debug(msg) 14 - end, 0) 15 - end 16 - 17 - ---@param msg string 18 - ---@param lvl? number 6 + ---@param lvl? integer by default `vim.log.levels.INFO` 19 7 function utils.notify(msg, lvl) 20 8 lvl = lvl or vim.log.levels.INFO 21 9 vim.notify(msg, lvl, { 10 + ---@diagnostic disable-next-line:undefined-field 22 11 title = c.___plugin_name, 23 12 }) 24 13 log.debug(msg) ··· 40 29 end 41 30 end 42 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 39 + end 40 + 41 + -- Since indentation can be spaces or tabs, that's my hack around it 42 + ---@param line string 43 + ---@param indent integer 44 + ---@return string 45 + function utils.indent(line, indent) 46 + local char = string.sub(line, 1, 1) 47 + if char ~= " " and char ~= "\t" then 48 + char = " " 49 + end 50 + return string.rep(char, indent) 51 + end 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 43 70 end 44 71 45 72 return utils
+3 -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(...) ··· 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 ··· 43 44 float_precision = 0.01, 44 45 } 45 46 46 - ---@type Gopher.Logger 47 + ---@type gopher.Logger 47 48 ---@diagnostic disable-next-line: missing-fields 48 49 local log = {} 49 50
-51
lua/gopher/_utils/runner/gocmd.lua
··· 1 - local r = require "gopher._utils.runner" 2 - local c = require("gopher.config").commands 3 - local u = require "gopher._utils" 4 - local gocmd = {} 5 - 6 - ---@param args string[] 7 - ---@return string[] 8 - local function if_get(args) 9 - for i, arg in ipairs(args) do 10 - local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg 11 - table.remove(args, i) 12 - table.insert(args, i, m) 13 - end 14 - return args 15 - end 16 - 17 - ---@param args unknown[] 18 - ---@return string[] 19 - local function if_generate(args) 20 - if #args == 1 and args[1] == "%" then 21 - args[1] = vim.fn.expand "%" 22 - end 23 - return args 24 - end 25 - 26 - ---@param subcmd string 27 - ---@param args string[] 28 - ---@return string 29 - function gocmd.run(subcmd, args) 30 - if #args == 0 and subcmd ~= "generate" then 31 - error "please provide any arguments" 32 - end 33 - 34 - if subcmd == "get" then 35 - args = if_get(args) 36 - end 37 - 38 - if subcmd == "generate" then 39 - args = if_generate(args) 40 - end 41 - 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 49 - end 50 - 51 - return gocmd
-39
lua/gopher/_utils/runner/init.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
+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
+72 -27
lua/gopher/_utils/ts.lua
··· 1 1 local ts = {} 2 2 local queries = { 3 3 struct = [[ 4 - (type_spec name: (type_identifier) @_name 5 - type: (struct_type)) 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 + struct_field = [[ 15 + (field_declaration name: (field_identifier) @_name) 6 16 ]], 7 17 func = [[ 8 18 [(function_declaration name: (identifier) @_name) 9 - (method_declaration name: (field_identifier) @_name)] 19 + (method_declaration name: (field_identifier) @_name) 20 + (method_elem name: (field_identifier) @_name)] 10 21 ]], 11 22 package = [[ 12 23 (package_identifier) @_name ··· 16 27 name: (type_identifier) @_name 17 28 type: (interface_type)) 18 29 ]], 30 + var = [[ 31 + [(var_declaration (var_spec name: (identifier) @_name)) 32 + (short_var_declaration 33 + left: (expression_list (identifier) @_name @_var))] 34 + ]], 19 35 } 20 36 21 37 ---@param parent_type string[] 22 38 ---@param node TSNode 23 39 ---@return TSNode? 24 - local function get_parrent_node(parent_type, node) 40 + local function get_parent_node(parent_type, node) 25 41 ---@type TSNode? 26 42 local current = node 27 43 while current do ··· 40 56 ---@param query vim.treesitter.Query 41 57 ---@param node TSNode 42 58 ---@param bufnr integer 43 - ---@return {name:string} 59 + ---@return {name:string, is_varstruct:boolean} 44 60 local function get_captures(query, node, bufnr) 45 61 local res = {} 46 - for _, match, _ in query:iter_matches(node, bufnr) do 47 - for capture_id, captured_node in pairs(match) do 48 - local capture_name = query.captures[capture_id] 49 - if capture_name == "_name" then 50 - res["name"] = vim.treesitter.get_node_text(captured_node, bufnr) 51 - end 62 + for id, _node in query:iter_captures(node, bufnr) do 63 + if query.captures[id] == "_name" then 64 + res["name"] = vim.treesitter.get_node_text(_node, bufnr) 65 + end 66 + 67 + if query.captures[id] == "_var" then 68 + res["is_varstruct"] = true 52 69 end 53 70 end 54 71 ··· 56 73 end 57 74 58 75 ---@class gopher.TsResult 59 - ---@field name string 60 - ---@field start_line integer 61 - ---@field end_line integer 76 + ---@field name string Name of the struct, function, etc 77 + ---@field start integer Line number where the declaration starts 78 + ---@field end_ integer Line number where the declaration ends 79 + ---@field indent integer Number of spaces/tabs in the current cursor line 80 + ---@field is_varstruct boolean Is struct declared as `var S struct{}` or `s := struct{}{}` 62 81 63 82 ---@param bufnr integer 64 83 ---@param parent_type string[] 65 84 ---@param query string 66 85 ---@return gopher.TsResult 67 86 local function do_stuff(bufnr, parent_type, query) 68 - local node = vim.treesitter.get_node { 69 - bufnr = bufnr, 70 - } 87 + if not vim.treesitter.get_parser(bufnr, "go") then 88 + error "No treesitter parser found for go" 89 + end 90 + 91 + local node = vim.treesitter.get_node { bufnr = bufnr } 71 92 if not node then 72 - error "No nodes found under cursor" 93 + error "No nodes found under the cursor" 73 94 end 74 95 75 - local parent_node = get_parrent_node(parent_type, node) 96 + local parent_node = get_parent_node(parent_type, node) 76 97 if not parent_node then 77 - error "No parent node found under cursor" 98 + error "No parent node found under the cursor" 78 99 end 79 100 80 101 local q = vim.treesitter.query.parse("go", query) 81 102 local res = get_captures(q, parent_node, bufnr) 82 103 assert(res.name ~= nil, "No capture name found") 83 104 84 - local start_row, _, end_row, _ = parent_node:range() 85 - res["start_line"] = start_row + 1 86 - res["end_line"] = end_row + 1 105 + local start_row, start_col, end_row, _ = parent_node:range() 106 + res["indent"] = start_col 107 + res["start"] = start_row + 1 108 + res["end_"] = end_row + 1 87 109 88 110 return res 89 111 end ··· 91 113 ---@param bufnr integer 92 114 function ts.get_struct_under_cursor(bufnr) 93 115 --- should be both type_spec and type_declaration 94 - --- because in cases like `type ( T struct{}, U strict{} )` 95 - --- i will be choosing always last struct in the list 96 - return do_stuff(bufnr, { "type_spec", "type_declaration" }, queries.struct) 116 + --- because in cases like `type ( T struct{}, U struct{} )` 117 + --- 118 + --- var_declaration is for cases like `var x struct{}` 119 + --- short_var_declaration is for cases like `x := struct{}{}` 120 + --- 121 + --- it always chooses last struct type in the list 122 + return do_stuff(bufnr, { 123 + "type_spec", 124 + "type_declaration", 125 + "var_declaration", 126 + "short_var_declaration", 127 + }, queries.struct) 128 + end 129 + 130 + ---@param bufnr integer 131 + function ts.get_struct_field_under_cursor(bufnr) 132 + return do_stuff(bufnr, { "field_declaration" }, queries.struct_field) 97 133 end 98 134 99 135 ---@param bufnr integer 100 136 function ts.get_func_under_cursor(bufnr) 101 137 --- since this handles both and funcs and methods we should check for both parent nodes 102 - return do_stuff(bufnr, { "function_declaration", "method_declaration" }, queries.func) 138 + return do_stuff(bufnr, { 139 + "method_elem", 140 + "function_declaration", 141 + "method_declaration", 142 + }, queries.func) 103 143 end 104 144 105 145 ---@param bufnr integer ··· 110 150 ---@param bufnr integer 111 151 function ts.get_interface_under_cursor(bufnr) 112 152 return do_stuff(bufnr, { "type_declaration" }, queries.interface) 153 + end 154 + 155 + ---@param bufnr integer 156 + function ts.get_variable_under_cursor(bufnr) 157 + return do_stuff(bufnr, { "var_declaration", "short_var_declaration" }, queries.var) 113 158 end 114 159 115 160 return ts
+35 -19
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 7 + --- Set cursor on line with function/method/struct/etc and 8 + --- run `:GoCmt` to generate a comment. 5 9 6 10 local ts = require "gopher._utils.ts" 7 11 local log = require "gopher._utils.log" 12 + local u = require "gopher._utils" 8 13 local comment = {} 9 14 10 - ---@param name string 11 - ---@return string 12 - ---@private 13 - local function template(name) 14 - return "// " .. name .. " " 15 - end 16 - 15 + --- NOTE: The order of functions executed inside this function is IMPORTANT. 16 + --- This function is extremely fragile; run tests after making any changes. 17 + --- 17 18 ---@param bufnr integer 19 + ---@param line string 18 20 ---@return string 19 - ---@private 20 - local function generate(bufnr) 21 + ---@dochide 22 + local function generate(bufnr, line) 23 + local sf_ok, sf_res = pcall(ts.get_struct_field_under_cursor, bufnr) 24 + if sf_ok then 25 + return u.indent(line, sf_res.indent) .. "// " .. sf_res.name .. " " 26 + end 27 + 21 28 local s_ok, s_res = pcall(ts.get_struct_under_cursor, bufnr) 22 29 if s_ok then 23 - return template(s_res.name) 30 + return u.indent(line, s_res.indent) .. "// " .. s_res.name .. " " 31 + end 32 + 33 + local v_ok, v_res = pcall(ts.get_variable_under_cursor, bufnr) 34 + if v_ok then 35 + return u.indent(line, v_res.indent) .. "// " .. v_res.name .. " " 24 36 end 25 37 26 38 local f_ok, f_res = pcall(ts.get_func_under_cursor, bufnr) 27 39 if f_ok then 28 - return template(f_res.name) 40 + return u.indent(line, f_res.indent) .. "// " .. f_res.name .. " " 29 41 end 30 42 31 43 local i_ok, i_res = pcall(ts.get_interface_under_cursor, bufnr) 32 44 if i_ok then 33 - return template(i_res.name) 45 + return u.indent(line, i_res.indent) .. "// " .. i_res.name .. " " 34 46 end 35 47 36 48 local p_ok, p_res = pcall(ts.get_package_under_cursor, bufnr) ··· 43 55 44 56 function comment.comment() 45 57 local bufnr = vim.api.nvim_get_current_buf() 46 - local cmt = generate(bufnr) 47 - log.debug("generated comment: " .. cmt) 58 + local lnum = vim.fn.getcurpos()[2] 59 + local line = vim.fn.getline(lnum) 60 + local cmt = generate(bufnr, line) 61 + log.debug("generated comment:", { 62 + comment = cmt, 63 + line = line, 64 + }) 48 65 49 - local pos = vim.fn.getcurpos()[2] 50 - vim.fn.append(pos - 1, cmt) 51 - vim.fn.setpos(".", { 0, pos, #cmt }) 66 + vim.fn.append(lnum - 1, cmt) 67 + vim.fn.setpos(".", { bufnr, lnum, #cmt }) 52 68 vim.cmd "startinsert!" 53 69 end 54 70
+63 -32
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 1 ---@type gopher.Config 8 - ---@private 2 + ---@dochide 3 + ---@diagnostic disable-next-line: missing-fields .setup() gets injected later 9 4 local config = {} 10 5 11 6 ---@tag gopher.nvim-config.ConfigGoTagTransform 12 7 ---@text Possible values for |gopher.Config|.gotag.transform: 13 8 --- 14 - ---@private 9 + ---@dochide 15 10 ---@alias gopher.ConfigGoTagTransform 16 11 ---| "snakecase" "GopherUser" -> "gopher_user" 17 12 ---| "camelcase" "GopherUser" -> "gopherUser" ··· 20 15 ---| "titlecase" "GopherUser" -> "Gopher User" 21 16 ---| "keep" keeps the original field name 22 17 23 - --minidoc_replace_start { 24 - 25 - ---@tag gopher.nvim-config-defaults 18 + ---@toc_entry Config 19 + ---@tag gopher.nvim-config 26 20 ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section) 27 - --- 28 21 ---@class gopher.Config 22 + ---@field setup fun(user_config?: gopher.Config) 29 23 local default_config = { 30 - --minidoc_replace_end 31 - 32 24 -- log level, you might consider using DEBUG or TRACE for debugging the plugin 33 25 ---@type number 34 26 log_level = vim.log.levels.INFO, 35 27 36 - -- timeout for running commands 28 + -- timeout for running internal commands 37 29 ---@type number 38 30 timeout = 2000, 39 31 40 - --- whether to setup plugin commands or not 41 - ---@type boolean 42 - setup_commands = true, 32 + -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 33 + ---@type number 34 + installer_timeout = 999999, 43 35 44 36 -- user specified paths to binaries 45 37 ---@class gopher.ConfigCommand ··· 49 41 gotests = "gotests", 50 42 impl = "impl", 51 43 iferr = "iferr", 52 - dlv = "dlv", 44 + json2go = "json2go", 53 45 }, 54 46 ---@class gopher.ConfigGotests 55 47 gotests = { 56 - -- 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. 57 50 template = "default", 51 + 58 52 -- path to a directory containing custom test code templates 59 53 ---@type string|nil 60 54 template_dir = nil, 61 - -- 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) 62 57 named = false, 63 58 }, 64 59 ---@class gopher.ConfigGoTag ··· 68 63 69 64 -- default tags to add to struct fields 70 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, 71 71 }, 72 + ---@class gopher.ConfigIfErr 72 73 iferr = { 73 - -- 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)'` 74 76 ---@type string|nil 75 77 message = nil, 76 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 + }, 77 90 } 78 91 --minidoc_afterlines_end 79 92 80 93 ---@type gopher.Config 81 - ---@private 94 + ---@dochide 82 95 local _config = default_config 83 96 84 97 -- I am kinda secret so don't tell anyone about me even dont use me ··· 89 102 _config.___plugin_name = "gopher.nvim" ---@diagnostic disable-line: inject-field 90 103 91 104 ---@param user_config? gopher.Config 92 - ---@private 105 + ---@dochide 93 106 function config.setup(user_config) 94 - _config = vim.tbl_deep_extend("force", default_config, user_config or {}) 95 - end 107 + vim.validate("user_config", user_config, "table", true) 108 + 109 + _config = vim.tbl_deep_extend("force", vim.deepcopy(default_config), user_config or {}) 96 110 97 - ---@return boolean 98 - ---@private 99 - function config.should_setup_commands() 100 - return config.setup_commands 111 + vim.validate("log_level", _config.log_level, "number") 112 + vim.validate("timeout", _config.timeout, "number") 113 + vim.validate("installer_timeout", _config.timeout, "number") 114 + vim.validate("commands", _config.commands, "table") 115 + vim.validate("commands.go", _config.commands.go, "string") 116 + vim.validate("commands.gomodifytags", _config.commands.gomodifytags, "string") 117 + vim.validate("commands.gotests", _config.commands.gotests, "string") 118 + vim.validate("commands.impl", _config.commands.impl, "string") 119 + vim.validate("commands.iferr", _config.commands.iferr, "string") 120 + vim.validate("commands.json2go", _config.commands.json2go, "string") 121 + vim.validate("gotests", _config.gotests, "table") 122 + vim.validate("gotests.template", _config.gotests.template, "string") 123 + vim.validate("gotests.template_dir", _config.gotests.template_dir, { "string", "nil" }) 124 + vim.validate("gotests.named", _config.gotests.named, "boolean") 125 + vim.validate("gotag", _config.gotag, "table") 126 + vim.validate("gotag.transform", _config.gotag.transform, "string") 127 + vim.validate("gotag.default_tag", _config.gotag.default_tag, "string") 128 + vim.validate("gotag.option", _config.gotag.option, { "string", "nil" }) 129 + vim.validate("iferr", _config.iferr, "table") 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" }) 101 133 end 102 134 103 135 setmetatable(config, { ··· 106 138 end, 107 139 }) 108 140 109 - ---@return gopher.Config 110 - ---@private 141 + ---@dochide 111 142 return config
+56
lua/gopher/go.lua
··· 1 + local c = require "gopher.config" 2 + local u = require "gopher._utils" 3 + local r = require "gopher._utils.runner" 4 + local go = {} 5 + 6 + local function run(subcmd, args) 7 + local rs = r.sync { c.commands.go, subcmd, unpack(args) } 8 + if rs.code ~= 0 then 9 + error("go " .. subcmd .. " failed: " .. rs.stderr) 10 + end 11 + 12 + u.notify(c.commands.go .. " " .. subcmd .. " ran successful") 13 + return rs.stdout 14 + end 15 + 16 + ---@param args string[] 17 + function go.get(args) 18 + for i, arg in ipairs(args) do 19 + local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg 20 + table.remove(args, i) 21 + table.insert(args, i, m) 22 + end 23 + 24 + run("get", args) 25 + end 26 + 27 + ---@param args string[] 28 + function go.mod(args) 29 + run("mod", args) 30 + end 31 + 32 + ---@param args string[] 33 + function go.work(args) 34 + -- TODO: use `gopls.tidy` 35 + 36 + run("work", args) 37 + end 38 + 39 + ---Executes `go generate` 40 + ---If only argument is `%` it's going to be equivalent to `go generate <path to current file>` 41 + ---@param args string[] 42 + function go.generate(args) 43 + -- TODO: use `gopls.generate` 44 + 45 + if #args == 0 then 46 + error "please provide arguments" 47 + end 48 + 49 + if #args == 1 and args[1] == "%" then 50 + args[1] = vim.fn.expand "%" 51 + end 52 + 53 + run("generate", args) 54 + end 55 + 56 + return go
+9 -12
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 - ---@tag gopher.nvim-gotests-named 20 - ---@text 21 - --- You can enable named tests in the config if you prefer using named tests. 22 - --- See |gopher.nvim-config|. 19 + --- If you prefer named tests, you can enable them in |gopher.nvim-config|. 23 20 24 21 local c = require "gopher.config" 25 22 local ts_utils = require "gopher._utils.ts" ··· 29 26 local gotests = {} 30 27 31 28 ---@param args table 32 - ---@private 29 + ---@dochide 33 30 local function add_test(args) 34 31 if c.gotests.named then 35 32 table.insert(args, "-named")
+33 -56
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 - plugin = { 6 - { lib = "nvim-treesitter", msg = "required for everything in gopher.nvim" }, 7 - }, 5 + vim_version = "nvim-0.10", 8 6 bin = { 9 7 { 10 - bin = cmd.go, 11 - msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`", 12 - optional = false, 8 + bin = c.go, 9 + msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`, `:GoInstallDepsSync`", 13 10 }, 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 }, 17 - { 18 - bin = cmd.gotests, 19 - msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`", 20 - optional = true, 21 - }, 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`" }, 22 15 }, 23 16 treesitter = { 24 - { parser = "go", msg = "required for `gopher.nvim`" }, 17 + { parser = "go", msg = "required for most of the parts of `gopher.nvim`" }, 25 18 }, 26 19 } 27 20 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 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 33 28 end 34 29 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 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 46 38 end 47 39 48 40 function health.check() 49 - vim.health.start "required plugins" 50 - for _, plugin in ipairs(deps.plugin) do 51 - if is_lualib_found(plugin.lib) then 52 - vim.health.ok(plugin.lib .. " installed") 53 - else 54 - vim.health.error(plugin.lib .. " not found, " .. plugin.msg) 55 - end 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") 56 46 end 57 47 58 - vim.health.start "required binaries" 59 - vim.health.info "all those binaries can be installed by `:GoInstallDeps`" 48 + vim.health.start "Required binaries (those can be installed with `:GoInstallDeps`)" 60 49 for _, bin in ipairs(deps.bin) do 61 - if is_binary_found(bin.bin) then 62 - vim.health.ok(bin.bin .. " installed") 63 - else 64 - if bin.optional then 65 - vim.health.warn(bin.bin .. " not found, " .. bin.msg) 66 - else 67 - vim.health.error(bin.bin .. " not found, " .. bin.msg) 68 - end 69 - end 50 + check_binary(bin) 70 51 end 71 52 72 - vim.health.start "required treesitter parsers" 53 + vim.health.start "Treesitter" 73 54 for _, parser in ipairs(deps.treesitter) do 74 - if is_treesitter_parser_available(parser.parser) then 75 - vim.health.ok(parser.parser .. " parser installed") 76 - else 77 - vim.health.error(parser.parser .. " parser not found, " .. parser.msg) 78 - end 55 + check_treesitter(parser) 79 56 end 80 57 end 81 58
+7 -3
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. 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 + --- 4 9 ---@usage Execute `:GoIfErr` near any `err` variable to insert the check 5 10 6 11 local c = require "gopher.config" ··· 9 14 local log = require "gopher._utils.log" 10 15 local iferr = {} 11 16 12 - -- That's Lua implementation: https://github.com/koron/iferr 13 17 function iferr.iferr() 14 18 local curb = vim.fn.wordcount().cursor_bytes 15 19 local pos = vim.fn.getcurpos()[2] ··· 38 42 39 43 vim.fn.append(pos, u.remove_empty_lines(vim.split(rs.stdout, "\n"))) 40 44 vim.cmd [[silent normal! j=2j]] 41 - vim.fn.setpos(".", pos) 45 + vim.fn.setpos(".", pos --[[@as integer[] ]]) 42 46 end 43 47 44 48 return iferr
+24 -15
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. 3 + ---@text 4 + --- Integration of `impl` tool to generate method stubs for interfaces. 5 + --- 4 6 ---@usage 5 7 --- 1. Automatically implement an interface for a struct: 6 - --- - Place your cursor on the struct where you want to implement the interface. 7 - --- - Run `:GoImpl io.Reader` 8 - --- - This will automatically determine the receiver and implement the `io.Reader` interface. 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. 9 11 --- 10 12 --- 2. Specify a custom receiver: 11 - --- - Place your cursor on the struct 12 - --- - Run `:GoImpl w io.Writer`, where: 13 - --- - `w` is the receiver. 14 - --- - `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. 15 17 --- 16 18 --- 3. Explicitly specify the receiver, struct, and interface: 17 - --- - No need to place the cursor on the struct if all arguments are provided. 18 - --- - Run `:GoImpl r RequestReader io.Reader`, where: 19 - --- - `r` is the receiver. 20 - --- - `RequestReader` is the struct. 21 - --- - `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. 22 24 --- 23 25 --- Example: 24 26 --- >go ··· 43 45 local iface, recv = "", "" 44 46 local bufnr = vim.api.nvim_get_current_buf() 45 47 46 - if #args == 1 then -- :GoImpl io.Reader 48 + if #args == 0 then 49 + u.notify("arguments not provided. usage: :GoImpl f *File io.Reader", vim.log.levels.ERROR) 50 + return 51 + elseif #args == 1 then -- :GoImpl io.Reader 47 52 local st = ts_utils.get_struct_under_cursor(bufnr) 48 53 iface = args[1] 49 54 recv = string.lower(st.name) .. " *" .. st.name ··· 56 61 iface = args[3] 57 62 end 58 63 59 - local rs = r.sync { c.impl, "-dir", vim.fn.fnameescape(vim.fn.expand "%:p:h"), recv, iface } 64 + assert(iface ~= "", "interface not provided") 65 + assert(recv ~= "", "receiver not provided") 66 + 67 + local dir = vim.fn.fnameescape(vim.fn.expand "%:p:h") 68 + local rs = r.sync { c.impl, "-dir", dir, recv, iface } 60 69 if rs.code ~= 0 then 61 70 error("failed to implement interface: " .. rs.stderr) 62 71 end
+21 -28
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 go = require "gopher.go" 16 17 local gopher = {} 17 18 18 19 ---@toc_entry Setup 19 - ---@tag gopher.nvim-setup 20 + ---@tag gopher.nvim-setup() 20 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 23 --- Calling this function is optional, if you ok with default settings. 23 - --- See |gopher.nvim.config-defaults| 24 + --- See |gopher.nvim.config| 24 25 --- 25 - ---@usage `require("gopher").setup {}` (replace `{}` with your `config` table) 26 - ---@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| 27 30 gopher.setup = function(user_config) 28 31 log.debug "setting up config" 29 32 require("gopher.config").setup(user_config) ··· 31 34 end 32 35 33 36 ---@toc_entry Install dependencies 34 - ---@tag gopher.nvim-install-deps 35 - ---@text Gopher.nvim implements most of its features using third-party tools. 36 - --- To install these tools, you can run `:GoInstallDeps` command 37 - --- or call `require("gopher").install_deps()` if you want to use lua api. 38 - --- By default dependencies will be installed asynchronously, to install them synchronously pass `{sync = true}` as an argument. 37 + ---@tag gopher.nvim-dependencies 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. 39 43 gopher.install_deps = require("gopher.installer").install_deps 40 44 41 45 gopher.impl = require("gopher.impl").impl ··· 54 58 all = tests.all_tests, 55 59 } 56 60 57 - gopher.get = function(...) 58 - gocmd("get", ...) 59 - end 60 - 61 - gopher.mod = function(...) 62 - gocmd("mod", ...) 63 - end 64 - 65 - gopher.generate = function(...) 66 - gocmd("generate", ...) 67 - end 68 - 69 - gopher.work = function(...) 70 - gocmd("work", ...) 71 - end 61 + gopher.get = go.get 62 + gopher.mod = go.mod 63 + gopher.work = go.work 64 + gopher.generate = go.generate 72 65 73 66 return gopher
+21 -7
lua/gopher/installer.lua
··· 1 - local c = require("gopher.config").commands 1 + local c = require "gopher.config" 2 2 local r = require "gopher._utils.runner" 3 3 local u = require "gopher._utils" 4 4 local log = require "gopher._utils.log" ··· 9 9 impl = "github.com/josharian/impl@latest", 10 10 gotests = "github.com/cweill/gotests/...@develop", 11 11 iferr = "github.com/koron/iferr@latest", 12 + json2go = "olexsmir.xyz/json2go/cmd/json2go@latest", 12 13 } 13 14 14 15 ---@param opt vim.SystemCompleted 15 16 ---@param url string 16 17 local function handle_intall_exit(opt, url) 17 18 if opt.code ~= 0 then 18 - u.deferred_notify("go install failed: " .. url) 19 + vim.schedule(function() 20 + u.notify("go install failed: " .. url) 21 + end) 22 + 19 23 log.error("go install failed:", "url", url, "opt", vim.inspect(opt)) 20 24 return 21 25 end 22 26 23 - u.deferred_notify("go install-ed: " .. url) 27 + vim.schedule(function() 28 + u.notify("go install-ed: " .. url) 29 + end) 24 30 end 25 31 26 32 ---@param url string 27 33 local function install(url) 28 - r.async({ c.go, "install", url }, function(opt) 29 - handle_intall_exit(opt, url) 34 + vim.schedule(function() 35 + u.notify("go install-ing: " .. url) 30 36 end) 37 + 38 + r.async({ c.commands.go, "install", url }, function(opt) 39 + handle_intall_exit(opt, url) 40 + end, { timeout = c.installer_timeout }) 31 41 end 32 42 33 43 ---@param url string 34 44 local function install_sync(url) 35 - local rs = r.sync { c.go, "install", url } 45 + vim.schedule(function() 46 + u.notify("go install-ing: " .. url) 47 + end) 48 + 49 + local rs = r.sync({ c.commands.go, "install", url }, { timeout = c.installer_timeout }) 36 50 handle_intall_exit(rs, url) 37 51 end 38 52 ··· 40 54 ---@param opts? {sync:boolean} 41 55 function installer.install_deps(opts) 42 56 opts = opts or {} 43 - for url, _ in pairs(urls) do 57 + for _, url in pairs(urls) do 44 58 if opts.sync then 45 59 install_sync(url) 46 60 else
+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
+107 -28
lua/gopher/struct_tags.lua
··· 1 1 ---@toc_entry Modify struct tags 2 2 ---@tag gopher.nvim-struct-tags 3 - ---@text struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. 3 + ---@text 4 + --- `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to 5 + --- struct fields. 6 + --- 4 7 ---@usage 5 - --- How to add/remove tags to struct fields: 6 - -- 1. Place cursor on the struct 8 + --- How to add/remove/clear tags to struct fields: 9 + --- 1. Place cursor on the struct 7 10 --- 2. Run `:GoTagAdd json` to add json tags to struct fields 8 11 --- 3. Run `:GoTagRm json` to remove json tags to struct fields 12 + --- 4. Run `:GoTagClear` to clear all tags from struct fields 13 + --- 14 + --- If you want to add/remove tag with options, you can use `json=omitempty` 15 + --- (where json is tag, and omitempty is its option). 16 + --- Example: `:GoTagAdd xml json=omitempty` 17 + --- 9 18 --- 10 19 --- NOTE: if you dont specify the tag it will use `json` as default 11 20 --- ··· 29 38 local ts = require "gopher._utils.ts" 30 39 local r = require "gopher._utils.runner" 31 40 local c = require "gopher.config" 41 + local u = require "gopher._utils" 32 42 local log = require "gopher._utils.log" 33 43 local struct_tags = {} 34 44 45 + ---@dochide 46 + ---@class gopher.StructTagInput 47 + ---@field input string[] User provided tags 48 + ---@field range? gopher.StructTagRange (optional) 49 + 50 + ---@dochide 51 + ---@class gopher.StructTagRange 52 + ---@field start number 53 + ---@field end_ number 54 + 35 55 ---@param fpath string 36 56 ---@param bufnr integer 57 + ---@param range? gopher.StructTagRange 37 58 ---@param user_args string[] 38 - ---@private 39 - local function handle_tags(fpath, bufnr, user_args) 59 + ---@dochide 60 + local function handle_tags(fpath, bufnr, range, user_args) 40 61 local st = ts.get_struct_under_cursor(bufnr) 41 62 42 63 -- stylua: ignore ··· 44 65 c.commands.gomodifytags, 45 66 "-transform", c.gotag.transform, 46 67 "-format", "json", 47 - "-struct", st.name, 48 68 "-file", fpath, 49 69 "-w", 50 70 } 51 71 72 + -- `-struct` and `-line` cannot be combined, setting them separately 73 + if range or st.is_varstruct then 74 + table.insert(cmd, "-line") 75 + table.insert(cmd, string.format("%d,%d", (range or st).start, (range or st).end_)) 76 + else 77 + table.insert(cmd, "-struct") 78 + table.insert(cmd, st.name) 79 + end 80 + 52 81 for _, v in ipairs(user_args) do 53 82 table.insert(cmd, v) 54 83 end ··· 60 89 end 61 90 62 91 local res = vim.json.decode(rs.stdout) 63 - 64 92 if res["errors"] then 65 93 log.error("tags: got an error " .. vim.inspect(res)) 66 94 error("failed to set tags " .. vim.inspect(res["errors"])) 67 95 end 68 96 69 97 for i, v in ipairs(res["lines"]) do 70 - res["lines"][i] = string.gsub(v, "%s+$", "") 98 + res["lines"][i] = u.trimend(v) 71 99 end 72 100 73 101 vim.api.nvim_buf_set_lines( ··· 79 107 ) 80 108 end 81 109 110 + ---@dochide 111 + ---@param option string 112 + local function option_to_tag(option) 113 + return option:match "^(.-)=" 114 + end 115 + 116 + ---@dochide 117 + ---@param args string[] 118 + local function unwrap_if_needed(args) 119 + local out = {} 120 + for _, v in pairs(args) do 121 + for _, p in pairs(vim.split(v, ",")) do 122 + table.insert(out, p) 123 + end 124 + end 125 + return out 126 + end 127 + 128 + ---@dochide 129 + ---@class gopher.StructTagsArgs 130 + ---@field tags string 131 + ---@field options string 132 + 133 + ---@dochide 82 134 ---@param args string[] 83 - ---@return string 84 - ---@private 85 - local function handler_user_args(args) 86 - if #args == 0 then 87 - return c.gotag.default_tag 135 + ---@return gopher.StructTagsArgs 136 + function struct_tags.parse_args(args) 137 + args = unwrap_if_needed(args) 138 + 139 + local tags, options = {}, {} 140 + for _, v in pairs(args) do 141 + if string.find(v, "=") then 142 + table.insert(options, v) 143 + table.insert(tags, option_to_tag(v)) 144 + else 145 + table.insert(tags, v) 146 + end 88 147 end 89 - return table.concat(args, ",") 148 + 149 + return { 150 + tags = table.concat(u.list_unique(tags), ","), 151 + options = table.concat(u.list_unique(options), ","), 152 + } 90 153 end 91 154 92 - ---Adds tags to a struct under the cursor 93 - ---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] 94 - function struct_tags.add(...) 95 - local args = { ... } 155 + -- Adds tags to a struct under the cursor. See `:h gopher.nvim-struct-tags`. 156 + ---@param opts gopher.StructTagInput 157 + ---@dochide 158 + function struct_tags.add(opts) 159 + log.debug("adding tags", opts) 160 + 96 161 local fpath = vim.fn.expand "%" 97 162 local bufnr = vim.api.nvim_get_current_buf() 98 163 99 - local user_tags = handler_user_args(args) 100 - handle_tags(fpath, bufnr, { "-add-tags", user_tags }) 164 + local user_args = struct_tags.parse_args(opts.input) 165 + handle_tags(fpath, bufnr, opts.range, { 166 + "-add-tags", 167 + (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag, 168 + (user_args.options ~= "" or c.gotag.option) and "-add-options" or nil, 169 + (user_args.options ~= "") and user_args.options or c.gotag.option, 170 + }) 101 171 end 102 172 103 - ---Removes tags from a struct under the cursor 104 - ---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] 105 - function struct_tags.remove(...) 106 - local args = { ... } 173 + -- Removes tags from a struct under the cursor. See `:h gopher.nvim-struct-tags`. 174 + ---@dochide 175 + ---@param opts gopher.StructTagInput 176 + function struct_tags.remove(opts) 177 + log.debug("removing tags", opts) 178 + 107 179 local fpath = vim.fn.expand "%" 108 180 local bufnr = vim.api.nvim_get_current_buf() 109 181 110 - local user_tags = handler_user_args(args) 111 - handle_tags(fpath, bufnr, { "-remove-tags", user_tags }) 182 + local user_args = struct_tags.parse_args(opts.input) 183 + handle_tags(fpath, bufnr, opts.range, { 184 + "-remove-tags", 185 + (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag, 186 + (user_args.options ~= "" or c.gotag.option ~= nil) and "-remove-options" or nil, 187 + (user_args.options ~= "") and user_args.options or c.gotag.option, 188 + }) 112 189 end 113 190 114 - ---Removes all tags from a struct under the cursor 191 + -- Removes all tags from a struct under the cursor. 192 + -- See `:h gopher.nvim-struct-tags`. 193 + ---@dochide 115 194 function struct_tags.clear() 116 195 local fpath = vim.fn.expand "%" 117 196 local bufnr = vim.api.nvim_get_current_buf() 118 - handle_tags(fpath, bufnr, { "-clear-tags" }) 197 + handle_tags(fpath, bufnr, nil, { "-clear-tags" }) 119 198 end 120 199 121 200 return struct_tags
-40
nvim.toml
··· 3 3 4 4 [MiniTest] 5 5 any = true 6 - 7 - [describe] 8 - any = true 9 - [[describe.args]] 10 - type = "string" 11 - [[describe.args]] 12 - type = "function" 13 - 14 - [it] 15 - any = true 16 - [[it.args]] 17 - type = "string" 18 - [[it.args]] 19 - type = "function" 20 - 21 - [before_each] 22 - any = true 23 - [[before_each.args]] 24 - type = "function" 25 - [[after_each.args]] 26 - type = "function" 27 - 28 - [assert] 29 - any = true 30 - 31 - [assert.is_not] 32 - any = true 33 - 34 - [[assert.equals.args]] 35 - type = "any" 36 - [[assert.equals.args]] 37 - type = "any" 38 - [[assert.equals.args]] 39 - type = "any" 40 - required = false 41 - 42 - [[assert.same.args]] 43 - type = "any" 44 - [[assert.same.args]] 45 - type = "any"
+10
pkg.json
··· 1 + { 2 + "name": "gopher.nvim", 3 + "engines": { 4 + "nvim": "^0.10.0" 5 + }, 6 + "repository": { 7 + "type": "git", 8 + "url": "https://github.com/olexsmir/gopher.nvim" 9 + } 10 + }
+91 -58
plugin/gopher.lua
··· 1 - --- NOTE: runs in defer since this file before gopher.config 2 - --- I'm not sure if this is the best to do this 3 - vim.schedule(function() 4 - if require("gopher.config").should_setup_commands() then 5 - vim.api.nvim_create_user_command("GopherLog", function() 6 - vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) 7 - end, { nargs = 0 }) 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. 8 6 9 - vim.api.nvim_create_user_command("GoIfErr", function() 10 - require("gopher").iferr() 11 - end, { nargs = 0 }) 7 + if vim.g.gopher_register_commands == false then 8 + return 9 + end 12 10 13 - vim.api.nvim_create_user_command("GoCmt", function() 14 - require("gopher").comment() 15 - end, { nargs = 0 }) 11 + ---@param name string 12 + ---@param fn fun(args: table) 13 + ---@param nargs? number|"*"|"?" 14 + ---@param range? boolean 15 + ---@private 16 + local function cmd(name, fn, nargs, range) 17 + vim.api.nvim_create_user_command(name, fn, { 18 + nargs = nargs or 0, 19 + range = range or false, 20 + }) 21 + end 16 22 17 - vim.api.nvim_create_user_command("GoImpl", function(args) 18 - require("gopher").impl(unpack(args.fargs)) 19 - end, { nargs = "*" }) 23 + cmd("GopherLog", function() 24 + vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) 25 + end) 20 26 21 - -- :GoInstall 22 - vim.api.nvim_create_user_command("GoInstallDeps", function() 23 - require("gopher").install_deps() 24 - end, { nargs = 0 }) 27 + cmd("GoIfErr", function() 28 + require("gopher").iferr() 29 + end) 25 30 26 - vim.api.nvim_create_user_command("GoInstallDepsSync", function() 27 - require("gopher").install_deps { sync = true } 28 - end, { nargs = 0 }) 31 + cmd("GoCmt", function() 32 + require("gopher").comment() 33 + end) 29 34 30 - --- :GoTag 31 - vim.api.nvim_create_user_command("GoTagAdd", function(opts) 32 - require("gopher").tags.add(unpack(opts.fargs)) 33 - end, { nargs = "*" }) 35 + cmd("GoImpl", function(args) 36 + require("gopher").impl(unpack(args.fargs)) 37 + end, "*") 34 38 35 - vim.api.nvim_create_user_command("GoTagRm", function(opts) 36 - require("gopher").tags.rm(unpack(opts.fargs)) 37 - end, { nargs = "*" }) 39 + -- :GoInstall 40 + cmd("GoInstallDeps", function() 41 + require("gopher").install_deps() 42 + end) 38 43 39 - vim.api.nvim_create_user_command("GoTagClear", function() 40 - require("gopher").tags.clear() 41 - end, { nargs = 0 }) 44 + cmd("GoInstallDepsSync", function() 45 + require("gopher").install_deps { sync = true } 46 + end) 42 47 43 - --- :GoTest 44 - vim.api.nvim_create_user_command("GoTestAdd", function() 45 - require("gopher").test.add() 46 - end, { nargs = 0 }) 48 + -- :GoTag 49 + cmd("GoTagAdd", function(opts) 50 + require("gopher").tags.add { 51 + input = opts.fargs, 52 + range = (opts.count ~= -1) and { 53 + start = opts.line1, 54 + end_ = opts.line2, 55 + } or nil, 56 + } 57 + end, "*", true) 47 58 48 - vim.api.nvim_create_user_command("GoTestsAll", function() 49 - require("gopher").test.all() 50 - end, { nargs = 0 }) 59 + cmd("GoTagRm", function(opts) 60 + require("gopher").tags.rm { 61 + input = opts.fargs, 62 + range = (opts.count ~= -1) and { 63 + start = opts.line1, 64 + end_ = opts.line2, 65 + } or nil, 66 + } 67 + end, "*", true) 51 68 52 - vim.api.nvim_create_user_command("GoTestsExp", function() 53 - require("gopher").test.exported() 54 - end, { nargs = 0 }) 69 + cmd("GoTagClear", function() 70 + require("gopher").tags.clear() 71 + end) 55 72 56 - -- :Go 57 - vim.api.nvim_create_user_command("GoMod", function(opts) 58 - require("gopher").mod(opts.fargs) 59 - end, { nargs = "*" }) 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, "*") 60 78 61 - vim.api.nvim_create_user_command("GoGet", function(opts) 62 - vim.print(opts) 63 - require("gopher").get(opts.fargs) 64 - end, { nargs = "*" }) 79 + -- :GoTest 80 + cmd("GoTestAdd", function() 81 + require("gopher").test.add() 82 + end) 65 83 66 - vim.api.nvim_create_user_command("GoWork", function(opts) 67 - require("gopher").get(opts.fargs) 68 - end, { nargs = "*" }) 84 + cmd("GoTestsAll", function() 85 + require("gopher").test.all() 86 + end) 69 87 70 - vim.api.nvim_create_user_command("GoGenerate", function(opts) 71 - require("gopher").generate(opts.fargs or "") 72 - end, { nargs = "?" }) 73 - end 88 + cmd("GoTestsExp", function() 89 + require("gopher").test.exported() 74 90 end) 91 + 92 + -- :Go 93 + cmd("GoMod", function(opts) 94 + require("gopher").mod(opts.fargs) 95 + end, "*") 96 + 97 + cmd("GoGet", function(opts) 98 + require("gopher").get(opts.fargs) 99 + end, "*") 100 + 101 + cmd("GoWork", function(opts) 102 + require("gopher").get(opts.fargs) 103 + end, "*") 104 + 105 + cmd("GoGenerate", function(opts) 106 + require("gopher").generate(opts.fargs or { "" }) 107 + end, "?")
+6
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", 15 + "lua/gopher/json2go.lua", 14 16 "lua/gopher/impl.lua", 15 17 "lua/gopher/gotests.lua", 16 18 "lua/gopher/iferr.lua", ··· 27 29 table.remove(lines, 1) 28 30 29 31 return lines 32 + end 33 + 34 + hooks.sections["@dochide"] = function(s) 35 + s.parent:clear_lines() 30 36 end 31 37 32 38 MiniDoc.generate(files, "doc/gopher.nvim.txt", { hooks = hooks })
+20 -3
scripts/minimal_init.lua
··· 20 20 end 21 21 end 22 22 23 - install_plug "nvim-lua/plenary.nvim" 24 23 install_plug "nvim-treesitter/nvim-treesitter" 25 24 install_plug "echasnovski/mini.doc" -- used for docs generation 25 + install_plug "folke/tokyonight.nvim" -- theme for generating demos 26 26 install_plug "echasnovski/mini.test" 27 27 28 28 vim.env.XDG_CONFIG_HOME = root ".tests/config" ··· 30 30 vim.env.XDG_STATE_HOME = root ".tests/state" 31 31 vim.env.XDG_CACHE_HOME = root ".tests/cache" 32 32 33 - vim.cmd [[set runtimepath=$VIMRUNTIME]] 34 33 vim.opt.runtimepath:append(root()) 35 - vim.opt.packpath = { root ".tests/site" } 34 + vim.opt.packpath:append(root ".tests/site") 35 + vim.o.swapfile = false 36 + vim.o.writebackup = false 36 37 vim.notify = vim.print 37 38 38 39 -- install go treesitter parse ··· 53 54 }, 54 55 } 55 56 end 57 + 58 + -- set colorscheme only when running ui 59 + if #vim.api.nvim_list_uis() == 1 then 60 + vim.cmd.colorscheme "tokyonight-night" 61 + vim.o.cursorline = true 62 + vim.o.number = true 63 + end 64 + 65 + -- needed for tests, i dont know the reason why, but on start 66 + -- vim is not able to use treesitter for go by default 67 + vim.api.nvim_create_autocmd("FileType", { 68 + pattern = "go", 69 + callback = function(args) 70 + vim.treesitter.start(args.buf, "go") 71 + end, 72 + })
+6
spec/fixtures/comment/interface_many_method_input.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + Get(id string) int 5 + Set(id string, val int) 6 + }
+7
spec/fixtures/comment/interface_many_method_output.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + Get(id string) int 5 + // Set 6 + Set(id string, val int) 7 + }
+5
spec/fixtures/comment/interface_method_input.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + Method(input string) error 5 + }
+6
spec/fixtures/comment/interface_method_output.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + // Method 5 + Method(input string) error 6 + }
+18
spec/fixtures/comment/many_structs_fields_input.go
··· 1 + package main 2 + 3 + type ( 4 + TestOne struct { 5 + Asdf string 6 + ID int 7 + } 8 + 9 + TestTwo struct { 10 + Fesa int 11 + A bool 12 + } 13 + 14 + TestThree struct { 15 + Asufj int 16 + Fs string 17 + } 18 + )
+19
spec/fixtures/comment/many_structs_fields_output.go
··· 1 + package main 2 + 3 + type ( 4 + TestOne struct { 5 + Asdf string 6 + ID int 7 + } 8 + 9 + TestTwo struct { 10 + // Fesa 11 + Fesa int 12 + A bool 13 + } 14 + 15 + TestThree struct { 16 + Asufj int 17 + Fs string 18 + } 19 + )
+7
spec/fixtures/comment/struct_fields_input.go
··· 1 + package main 2 + 3 + type CommentStruct struct { 4 + Name string 5 + Address string 6 + Aliases []string 7 + }
+8
spec/fixtures/comment/struct_fields_output.go
··· 1 + package main 2 + 3 + type CommentStruct struct { 4 + Name string 5 + // Address 6 + Address string 7 + Aliases []string 8 + }
+5
spec/fixtures/comment/svar_input.go
··· 1 + package main 2 + 3 + func varTest() { 4 + s := "something" 5 + }
+6
spec/fixtures/comment/svar_output.go
··· 1 + package main 2 + 3 + func varTest() { 4 + // s 5 + s := "something" 6 + }
+5
spec/fixtures/comment/var_input.go
··· 1 + package main 2 + 3 + func test() { 4 + var imAVar string 5 + }
+6
spec/fixtures/comment/var_output.go
··· 1 + package main 2 + 3 + func test() { 4 + // imAVar 5 + var imAVar string 6 + }
+8
spec/fixtures/comment/var_struct_fields_input.go
··· 1 + package main 2 + 3 + func main() { 4 + var s struct { 5 + API string 6 + Key string 7 + } 8 + }
+9
spec/fixtures/comment/var_struct_fields_output.go
··· 1 + package main 2 + 3 + func main() { 4 + var s struct { 5 + API string 6 + // Key 7 + Key string 8 + } 9 + }
+2
spec/fixtures/json2go/interativly_input.go
··· 1 + package main 2 +
+5
spec/fixtures/json2go/interativly_output.go
··· 1 + package main 2 + 3 + type AutoGenerated struct { 4 + Json bool `json:"json"` 5 + }
+2
spec/fixtures/json2go/manual_input.go
··· 1 + package main 2 +
+7
spec/fixtures/json2go/manual_output.go
··· 1 + package main 2 + 3 + type AutoGenerated struct { 4 + User struct { 5 + Name string `json:"name"` 6 + } `json:"user"` 7 + }
+14
spec/fixtures/tags/add_range_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Name string 6 + Num int64 7 + Cost int 8 + Thingy []string 9 + Testing int 10 + Another struct { 11 + First int 12 + Second string 13 + } 14 + }
+14
spec/fixtures/tags/add_range_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Name string `gopher:"name"` 6 + Num int64 `gopher:"num"` 7 + Cost int `gopher:"cost"` 8 + Thingy []string 9 + Testing int 10 + Another struct { 11 + First int 12 + Second string 13 + } 14 + }
+8
spec/fixtures/tags/overwrite_default_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Another struct { 6 + Second string 7 + } 8 + }
+8
spec/fixtures/tags/overwrite_default_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `xml:"id,otheroption"` 5 + Another struct { 6 + Second string `xml:"second,otheroption"` 7 + } `xml:"another,otheroption"` 8 + }
+14
spec/fixtures/tags/remove_range_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `asdf:"id"` 5 + Name string `asdf:"name"` 6 + Num int64 `asdf:"num"` 7 + Cost int `asdf:"cost"` 8 + Thingy []string `asdf:"thingy"` 9 + Testing int `asdf:"testing"` 10 + Another struct { 11 + First int `asdf:"first"` 12 + Second string `asdf:"second"` 13 + } `asdf:"another"` 14 + }
+14
spec/fixtures/tags/remove_range_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `asdf:"id"` 5 + Name string `asdf:"name"` 6 + Num int64 7 + Cost int 8 + Thingy []string 9 + Testing int `asdf:"testing"` 10 + Another struct { 11 + First int `asdf:"first"` 12 + Second string `asdf:"second"` 13 + } `asdf:"another"` 14 + }
+11
spec/fixtures/tags/remove_with_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `json:"id,omitempty" xml:"id,someoption"` 5 + Name string `json:"name,omitempty" xml:"name,someoption"` 6 + Num int64 `json:"num,omitempty" xml:"num,someoption"` 7 + Another struct { 8 + First int `json:"first,omitempty" xml:"first,someoption"` 9 + Second string `json:"second,omitempty" xml:"second,someoption"` 10 + } `json:"another,omitempty" xml:"another,someoption"` 11 + }
+11
spec/fixtures/tags/remove_with_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `xml:"id,someoption"` 5 + Name string `xml:"name,someoption"` 6 + Num int64 `xml:"num,someoption"` 7 + Another struct { 8 + First int `xml:"first,someoption"` 9 + Second string `xml:"second,someoption"` 10 + } `xml:"another,someoption"` 11 + }
+11
spec/fixtures/tags/svar_input.go
··· 1 + package main 2 + 3 + func main() { 4 + s := struct { 5 + API string 6 + Key string 7 + }{ 8 + API: "api.com", 9 + Key: "key", 10 + } 11 + }
+11
spec/fixtures/tags/svar_output.go
··· 1 + package main 2 + 3 + func main() { 4 + s := struct { 5 + API string `xml:"api"` 6 + Key string `xml:"key"` 7 + }{ 8 + API: "api.com", 9 + Key: "key", 10 + } 11 + }
+8
spec/fixtures/tags/var_input.go
··· 1 + package main 2 + 3 + func main() { 4 + var a struct { 5 + TestField1 string 6 + TestField2 string 7 + } 8 + }
+8
spec/fixtures/tags/var_output.go
··· 1 + package main 2 + 3 + func main() { 4 + var a struct { 5 + TestField1 string `yaml:"test_field_1"` 6 + TestField2 string `yaml:"test_field_2"` 7 + } 8 + }
+8
spec/fixtures/tags/with_default_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Another struct { 6 + Second string 7 + } 8 + }
+8
spec/fixtures/tags/with_default_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `xml:"id,theoption"` 5 + Another struct { 6 + Second string `xml:"second,theoption"` 7 + } `xml:"another,theoption"` 8 + }
+11
spec/fixtures/tags/with_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Name string 6 + Num int64 7 + Another struct { 8 + First int 9 + Second string 10 + } 11 + }
+11
spec/fixtures/tags/with_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `json:"id,omitempty"` 5 + Name string `json:"name,omitempty"` 6 + Num int64 `json:"num,omitempty"` 7 + Another struct { 8 + First int `json:"first,omitempty"` 9 + Second string `json:"second,omitempty"` 10 + } `json:"another,omitempty"` 11 + }
+38 -27
spec/integration/comment_test.lua
··· 1 1 local t = require "spec.testutils" 2 - 3 - local child = MiniTest.new_child_neovim() 4 - local T = MiniTest.new_set { 5 - hooks = { 6 - post_once = child.stop, 7 - pre_case = function() 8 - child.restart { "-u", t.mininit_path } 9 - end, 10 - }, 11 - } 2 + local child, T, comment = t.setup "comment" 12 3 13 4 local function do_the_test(fixture, pos) 14 - local tmp = t.tmpfile() 15 - local fixtures = t.get_fixtures("comment/" .. fixture) 16 - t.writefile(tmp, fixtures.input) 17 - 18 - child.cmd("silent edit " .. tmp) 19 - child.fn.setpos(".", { child.fn.bufnr "%", unpack(pos) }) 5 + local rs = t.setup_test("comment/" .. fixture, child, pos) 20 6 child.cmd "GoCmt" 21 7 child.cmd "write" 22 8 23 - t.eq(t.readfile(tmp), fixtures.output) 24 - 25 - -- without it all other(not even from this module) tests are falling 26 - t.deletefile(tmp) 9 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 10 + t.cleanup(rs) 27 11 end 28 12 29 - T["comment"] = MiniTest.new_set {} 30 - T["comment"]["should add comment to package"] = function() 13 + comment["should add comment to package"] = function() 31 14 do_the_test("package", { 1, 1 }) 32 15 end 33 16 34 - T["comment"]["should add comment to struct"] = function() 17 + comment["should add comment to struct"] = function() 35 18 do_the_test("struct", { 4, 1 }) 36 19 end 37 20 38 - T["comment"]["should add comment to function"] = function() 21 + comment["should add a comment on struct field"] = function() 22 + do_the_test("struct_fields", { 5, 8 }) 23 + end 24 + 25 + comment["should add a comment on var struct field"] = function() 26 + do_the_test("var_struct_fields", { 6, 4 }) 27 + end 28 + 29 + comment["should add a comment on one field of many structs"] = function() 30 + do_the_test("many_structs_fields", { 10, 4 }) 31 + end 32 + 33 + comment["should add comment to function"] = function() 39 34 do_the_test("func", { 3, 1 }) 40 35 end 41 36 42 - T["comment"]["should add comment to method"] = function() 37 + comment["should add comment to method"] = function() 43 38 do_the_test("method", { 5, 1 }) 44 39 end 45 40 46 - T["comment"]["should add comment to interface"] = function() 41 + comment["should add comment to interface"] = function() 47 42 do_the_test("interface", { 3, 6 }) 48 43 end 49 44 50 - T["comment"]["otherwise should add // above cursor"] = function() 45 + comment["should add comment on interface method"] = function() 46 + do_the_test("interface_method", { 4, 2 }) 47 + end 48 + 49 + comment["should add a comment on interface with many method"] = function() 50 + do_the_test("interface_many_method", { 5, 2 }) 51 + end 52 + 53 + comment["should add a comment on a var"] = function() 54 + do_the_test("var", { 4, 2 }) 55 + end 56 + 57 + comment["should add a comment on a short declared var"] = function() 58 + do_the_test("svar", { 4, 8 }) 59 + end 60 + 61 + comment["otherwise should add // above cursor"] = function() 51 62 do_the_test("empty", { 1, 1 }) 52 63 end 53 64
+9 -27
spec/integration/gotests_test.lua
··· 1 1 local t = require "spec.testutils" 2 - 3 - local child = MiniTest.new_child_neovim() 4 - local T = MiniTest.new_set { 5 - hooks = { 6 - post_once = child.stop, 7 - pre_case = function() 8 - child.restart { "-u", t.mininit_path } 9 - end, 10 - }, 11 - } 12 - T["gotests"] = MiniTest.new_set {} 2 + local child, T, gotests = t.setup "gotests" 13 3 14 4 --- NOTE: :GoTestAdd is the only place that has actual logic 15 5 --- All other parts are handled `gotests` itself. ··· 20 10 return t.readfile(fpath:gsub(".go", "_test.go")) 21 11 end 22 12 23 - T["gotests"]["should add test for function under cursor"] = function() 24 - local tmp = t.tmpfile() 25 - local fixtures = t.get_fixtures "tests/function" 26 - t.writefile(tmp, fixtures.input) 27 - 28 - child.cmd("silent edit " .. tmp) 29 - child.fn.setpos(".", { child.fn.bufnr "%", 3, 6 }) 13 + gotests["should add test for function under cursor"] = function() 14 + local rs = t.setup_test("tests/function", child, { 3, 5 }) 30 15 child.cmd "GoTestAdd" 31 16 32 - t.eq(fixtures.output, read_testfile(tmp)) 17 + t.eq(rs.fixtures.output, read_testfile(rs.tmp)) 18 + t.cleanup(rs) 33 19 end 34 20 35 - T["gotests"]["should add test for method under cursor"] = function() 36 - local tmp = t.tmpfile() 37 - local fixtures = t.get_fixtures "tests/method" 38 - t.writefile(tmp, fixtures.input) 39 - 40 - child.cmd("silent edit " .. tmp) 41 - child.fn.setpos(".", { child.fn.bufnr "%", 5, 19 }) 21 + gotests["should add test for method under cursor"] = function() 22 + local rs = t.setup_test("tests/method", child, { 5, 19 }) 42 23 child.cmd "GoTestAdd" 43 24 44 - t.eq(fixtures.output, read_testfile(tmp)) 25 + t.eq(rs.fixtures.output, read_testfile(rs.tmp)) 26 + t.cleanup(rs) 45 27 end 46 28 47 29 return T
+14 -26
spec/integration/iferr_test.lua
··· 1 1 local t = require "spec.testutils" 2 - 3 - local child = MiniTest.new_child_neovim() 4 - local T = MiniTest.new_set { 5 - hooks = { 6 - post_once = child.stop, 7 - pre_case = function() 8 - child.restart { "-u", t.mininit_path } 9 - end, 10 - }, 11 - } 12 - T["iferr"] = MiniTest.new_set {} 13 - T["iferr"]["works"] = function() 14 - local tmp = t.tmpfile() 15 - local fixtures = t.get_fixtures "iferr/iferr" 16 - t.writefile(tmp, fixtures.input) 2 + local child, T, iferr = t.setup "iferr" 17 3 18 - child.cmd("silent edit " .. tmp) 19 - child.fn.setpos(".", { child.fn.bufnr "%", 8, 2, 0 }) 4 + iferr["should add if != nil {"] = function() 5 + local rs = t.setup_test("iferr/iferr", child, { 8, 2 }) 20 6 child.cmd "GoIfErr" 21 7 child.cmd "write" 22 8 23 - t.eq(t.readfile(tmp), fixtures.output) 9 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 10 + t.cleanup(rs) 24 11 end 25 12 26 - T["iferr"]["works with custom message"] = function() 27 - local tmp = t.tmpfile() 28 - local fixtures = t.get_fixtures "iferr/message" 29 - t.writefile(tmp, fixtures.input) 13 + 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 + ]] 30 19 31 - child.lua [[ require("gopher").setup { iferr = { message = 'fmt.Errorf("failed to %w", err)' } } ]] 32 - child.cmd("silent edit " .. tmp) 33 - child.fn.setpos(".", { child.fn.bufnr "%", 6, 2, 0 }) 20 + local rs = t.setup_test("iferr/message", child, { 6, 2 }) 34 21 child.cmd "GoIfErr" 35 22 child.cmd "write" 36 23 37 - t.eq(t.readfile(tmp), fixtures.output) 24 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 25 + t.cleanup(rs) 38 26 end 39 27 40 28 return T
+16 -36
spec/integration/impl_test.lua
··· 1 1 local t = require "spec.testutils" 2 - 3 - local child = MiniTest.new_child_neovim() 4 - local T = MiniTest.new_set { 5 - hooks = { 6 - post_once = child.stop, 7 - pre_case = function() 8 - child.restart { "-u", t.mininit_path } 9 - end, 10 - }, 11 - } 12 - T["impl"] = MiniTest.new_set {} 13 - T["impl"]["works w io.Writer"] = function() 14 - local tmp = t.tmpfile() 15 - local fixtures = t.get_fixtures "impl/writer" 16 - t.writefile(tmp, fixtures.input) 2 + local child, T, impl = t.setup "impl" 17 3 18 - child.cmd("silent edit " .. tmp) 19 - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 0 }) 4 + impl["should do impl with 'w io.Writer'"] = function() 5 + local rs = t.setup_test("impl/writer", child, { 3, 0 }) 20 6 child.cmd "GoImpl w io.Writer" 21 7 child.cmd "write" 22 8 23 9 -- NOTE: since "impl" won't implement interface if it's already implemented i went with this hack 24 - local rhs = fixtures.output:gsub("Test2", "Test") 25 - t.eq(t.readfile(tmp), rhs) 10 + local rhs = rs.fixtures.output:gsub("Test2", "Test") 11 + t.eq(t.readfile(rs.tmp), rhs) 12 + t.cleanup(rs) 26 13 end 27 14 28 - T["impl"]["works r Read io.Reader"] = function() 29 - local tmp = t.tmpfile() 30 - local fixtures = t.get_fixtures "impl/reader" 31 - t.writefile(tmp, fixtures.input) 32 - 33 - child.cmd("silent edit " .. tmp) 15 + impl["should work with full input, 'r Read io.Reader'"] = function() 16 + local rs = t.setup_test("impl/reader", child) 34 17 child.cmd "GoImpl r Read io.Reader" 35 18 child.cmd "write" 36 19 37 - local rhs = fixtures.output:gsub("Read2", "Read") 38 - t.eq(t.readfile(tmp), rhs) 20 + local rhs = rs.fixtures.output:gsub("Read2", "Read") 21 + t.eq(t.readfile(rs.tmp), rhs) 22 + t.cleanup(rs) 39 23 end 40 24 41 - T["impl"]["works io.Closer"] = function() 42 - local tmp = t.tmpfile() 43 - local fixtures = t.get_fixtures "impl/closer" 44 - t.writefile(tmp, fixtures.input) 45 - 46 - child.cmd("silent edit " .. tmp) 47 - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) 25 + impl["should work with minimal input 'io.Closer'"] = function() 26 + local rs = t.setup_test("impl/closer", child, { 3, 6 }) 48 27 child.cmd "GoImpl io.Closer" 49 28 child.cmd "write" 50 29 51 - local rhs = fixtures.output:gsub("Test2", "Test") 52 - t.eq(t.readfile(tmp), rhs) 30 + local rhs = rs.fixtures.output:gsub("Test2", "Test") 31 + t.eq(t.readfile(rs.tmp), rhs) 32 + t.cleanup(rs) 53 33 end 54 34 55 35 return T
+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
+105 -43
spec/integration/struct_tags_test.lua
··· 1 1 local t = require "spec.testutils" 2 + local child, T, struct_tags = t.setup "struct_tags" 2 3 3 - local child = MiniTest.new_child_neovim() 4 - local T = MiniTest.new_set { 5 - hooks = { 6 - post_once = child.stop, 7 - pre_case = function() 8 - child.restart { "-u", t.mininit_path } 9 - end, 10 - }, 11 - } 12 - T["struct_tags"] = MiniTest.new_set {} 13 - T["struct_tags"]["should add tag"] = function() 14 - local tmp = t.tmpfile() 15 - local fixtures = t.get_fixtures "tags/add" 16 - t.writefile(tmp, fixtures.input) 17 - 18 - child.cmd("silent edit " .. tmp) 19 - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6, 0 }) 4 + struct_tags["should add tag"] = function() 5 + local rs = t.setup_test("tags/add", child, { 3, 6 }) 20 6 child.cmd "GoTagAdd json" 21 7 child.cmd "write" 22 8 23 - t.eq(t.readfile(tmp), fixtures.output) 9 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 10 + t.cleanup(rs) 24 11 end 25 12 26 - T["struct_tags"]["should remove tag"] = function() 27 - local tmp = t.tmpfile() 28 - local fixtures = t.get_fixtures "tags/remove" 29 - t.writefile(tmp, fixtures.input) 30 - 31 - child.cmd("silent edit " .. tmp) 32 - child.fn.setpos(".", { child.fn.bufnr(tmp), 4, 6, 0 }) 13 + struct_tags["should remove tag"] = function() 14 + local rs = t.setup_test("tags/remove", child, { 4, 6 }) 33 15 child.cmd "GoTagRm json" 34 16 child.cmd "write" 35 17 36 - t.eq(t.readfile(tmp), fixtures.output) 18 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 19 + t.cleanup(rs) 37 20 end 38 21 39 - T["struct_tags"]["should be able to handle many structs"] = function() 40 - local tmp = t.tmpfile() 41 - local fixtures = t.get_fixtures "tags/many" 42 - t.writefile(tmp, fixtures.input) 43 - 44 - child.cmd("silent edit " .. tmp) 45 - child.fn.setpos(".", { child.fn.bufnr(tmp), 10, 3, 0 }) 22 + struct_tags["should be able to handle many structs"] = function() 23 + local rs = t.setup_test("tags/many", child, { 10, 3 }) 46 24 child.cmd "GoTagAdd testing" 47 25 child.cmd "write" 48 26 49 - t.eq(t.readfile(tmp), fixtures.output) 27 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 28 + t.cleanup(rs) 50 29 end 51 30 52 - T["struct_tags"]["should clear struct"] = function() 53 - local tmp = t.tmpfile() 54 - local fixtures = t.get_fixtures "tags/clear" 55 - t.writefile(tmp, fixtures.input) 56 - 57 - child.cmd("silent edit " .. tmp) 58 - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1, 0 }) 31 + struct_tags["should clear struct"] = function() 32 + local rs = t.setup_test("tags/clear", child, { 3, 1 }) 59 33 child.cmd "GoTagClear" 60 34 child.cmd "write" 61 35 62 - t.eq(t.readfile(tmp), fixtures.output) 36 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 37 + t.cleanup(rs) 63 38 end 64 39 65 - T["struct_tags"]["should add more than one tag"] = function() 40 + struct_tags["should add more than one tag"] = function() 66 41 local tmp = t.tmpfile() 67 42 local fixtures = t.get_fixtures "tags/add_many" 68 43 t.writefile(tmp, fixtures.input) ··· 80 55 child.cmd "write" 81 56 82 57 t.eq(t.readfile(tmp), fixtures.output) 58 + 59 + ---@diagnostic disable-next-line:missing-fields 60 + t.cleanup { tmp = tmp } 61 + end 62 + 63 + struct_tags["should add tags on var"] = function() 64 + local rs = t.setup_test("tags/var", child, { 5, 6 }) 65 + child.cmd "GoTagAdd yaml" 66 + child.cmd "write" 67 + 68 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 69 + t.cleanup(rs) 70 + end 71 + 72 + struct_tags["should add tags on short declr var"] = function() 73 + local rs = t.setup_test("tags/svar", child, { 4, 3 }) 74 + child.cmd "GoTagAdd xml" 75 + child.cmd "write" 76 + 77 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 78 + t.cleanup(rs) 79 + end 80 + 81 + struct_tags["should add tag with range"] = function() 82 + local rs = t.setup_test("tags/add_range", child, { 5, 1 }) 83 + child.cmd ".,+2GoTagAdd gopher" 84 + child.cmd "write" 85 + 86 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 87 + t.cleanup(rs) 88 + end 89 + 90 + struct_tags["should remove tag with range"] = function() 91 + local rs = t.setup_test("tags/remove_range", child, { 6, 1 }) 92 + child.cmd ".,+2GoTagRm asdf" 93 + child.cmd "write" 94 + 95 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 96 + t.cleanup(rs) 97 + end 98 + 99 + struct_tags["should add tags with option"] = function() 100 + local rs = t.setup_test("tags/with_option", child, { 3, 6 }) 101 + child.cmd "GoTagAdd json=omitempty" 102 + child.cmd "write" 103 + 104 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 105 + t.cleanup(rs) 106 + end 107 + 108 + struct_tags["should add tags with default option"] = function() 109 + child.lua [[ 110 + require("gopher").setup { 111 + gotag = { option = "xml=theoption" }, 112 + } 113 + ]] 114 + 115 + local rs = t.setup_test("tags/with_default_option", child, { 3, 6 }) 116 + child.cmd "GoTagAdd xml" 117 + child.cmd "write" 118 + 119 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 120 + t.cleanup(rs) 121 + end 122 + 123 + struct_tags["should add tags and overwrite default option"] = function() 124 + child.lua [[ 125 + require("gopher").setup { 126 + gotag = { option = "xml=theoption" }, 127 + } 128 + ]] 129 + 130 + local rs = t.setup_test("tags/overwrite_default_option", child, { 3, 6 }) 131 + child.cmd "GoTagAdd xml=otheroption" 132 + child.cmd "write" 133 + 134 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 135 + t.cleanup(rs) 136 + end 137 + 138 + struct_tags["should remove tag with specified option"] = function() 139 + local rs = t.setup_test("tags/remove_with_option", child, { 3, 6 }) 140 + child.cmd "GoTagRm json=omitempty" 141 + child.cmd "write" 142 + 143 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 144 + t.cleanup(rs) 83 145 end 84 146 85 147 return T
+61 -1
spec/testutils.lua
··· 6 6 testutils.mininit_path = vim.fs.joinpath(base_dir, "scripts", "minimal_init.lua") 7 7 testutils.fixtures_dir = vim.fs.joinpath(base_dir, "spec/fixtures") 8 8 9 + ---@param mod string Module name for which to create a nested test set. 10 + ---@return MiniTest.child child nvim client. 11 + ---@return table T root test set created by `MiniTest.new_set()`. 12 + ---@return table mod_name nested set of tests in `T[mod]`. 13 + function testutils.setup(mod) 14 + local child = MiniTest.new_child_neovim() 15 + local T = MiniTest.new_set { 16 + hooks = { 17 + post_once = child.stop, 18 + pre_case = function() 19 + child.restart { "-u", testutils.mininit_path } 20 + end, 21 + }, 22 + } 23 + 24 + T[mod] = MiniTest.new_set {} 25 + return child, T, T[mod] 26 + end 27 + 9 28 ---@generic T 10 29 ---@param a T 11 30 ---@param b T ··· 36 55 vim.fn.delete(fpath) 37 56 end 38 57 58 + ---@class gopher.TestUtilsFixtures 59 + ---@field input string 60 + ---@field output string 61 + 39 62 ---@param fixture string 40 - ---@return {input: string, output: string} 63 + ---@return gopher.TestUtilsFixtures 41 64 function testutils.get_fixtures(fixture) 42 65 return { 43 66 input = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_input.go"), 44 67 output = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_output.go"), 45 68 } 69 + end 70 + 71 + ---@class gopher.TestUtilsSetup 72 + ---@field tmp string 73 + ---@field fixtures gopher.TestUtilsFixtures 74 + ---@field bufnr number 75 + 76 + ---@param fixture string 77 + ---@param child MiniTest.child 78 + ---@param pos? number[] 79 + ---@return gopher.TestUtilsSetup 80 + function testutils.setup_test(fixture, child, pos) 81 + vim.validate("pos", pos, "table", true) 82 + 83 + local tmp = testutils.tmpfile() 84 + local fixtures = testutils.get_fixtures(fixture) 85 + 86 + testutils.writefile(tmp, fixtures.input) 87 + child.cmd("silent edit " .. tmp) 88 + 89 + local bufnr = child.fn.bufnr(tmp) 90 + if pos then 91 + assert(#pos == 2, "invalid cursor position") 92 + 93 + child.fn.setpos(".", { bufnr, unpack(pos) }) 94 + end 95 + 96 + return { 97 + tmp = tmp, 98 + bufnr = bufnr, 99 + fixtures = fixtures, 100 + } 101 + end 102 + 103 + ---@param inp gopher.TestUtilsSetup 104 + function testutils.cleanup(inp) 105 + testutils.deletefile(inp.tmp) 46 106 end 47 107 48 108 return testutils
+25
spec/unit/config_test.lua
··· 1 + local t = require "spec.testutils" 2 + local _, T, config = t.setup "config" 3 + 4 + config["can be called without any arguments passed"] = function() 5 + ---@diagnostic disable-next-line: missing-parameter 6 + require("gopher").setup() 7 + end 8 + 9 + config["can be called with empty table"] = function() 10 + ---@diagnostic disable-next-line: missing-fields 11 + require("gopher").setup {} 12 + end 13 + 14 + config["should change option"] = function() 15 + local log_level = 1234567890 16 + 17 + ---@diagnostic disable-next-line: missing-fields 18 + require("gopher").setup { 19 + log_level = log_level, 20 + } 21 + 22 + t.eq(log_level, require("gopher.config").log_level) 23 + end 24 + 25 + return T
+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
+59
spec/unit/utils_test.lua
··· 1 + local t = require "spec.testutils" 2 + local _, T, utils = t.setup "utils" 3 + 4 + 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 + 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 + utils["should .trimend()"] = function() 21 + local u = require "gopher._utils" 22 + t.eq(u.trimend " hi ", " hi") 23 + end 24 + 25 + utils["should add .indent() spaces"] = function() 26 + local u = require "gopher._utils" 27 + local line = " func Test() error {" 28 + local indent = 4 29 + 30 + t.eq(" ", u.indent(line, indent)) 31 + end 32 + 33 + utils["should add .indent() a tab"] = function() 34 + local u = require "gopher._utils" 35 + local line = "\tfunc Test() error {" 36 + local indent = 1 37 + 38 + t.eq("\t", u.indent(line, indent)) 39 + end 40 + 41 + utils["should add .indent() 2 tabs"] = function() 42 + local u = require "gopher._utils" 43 + local line = "\t\tfunc Test() error {" 44 + local indent = 2 45 + 46 + t.eq("\t\t", u.indent(line, indent)) 47 + end 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 + 59 + return T
+23
vhs/Taskfile.yml
··· 1 + version: "3" 2 + tasks: 3 + generate: 4 + deps: 5 + - comment 6 + - iferr 7 + - tags 8 + - impl 9 + 10 + comment: 11 + cmd: vhs comment.tape 12 + 13 + iferr: 14 + cmd: vhs iferr.tape 15 + 16 + tags: 17 + cmd: vhs tags.tape 18 + 19 + impl: 20 + cmd: vhs impl.tape 21 + 22 + json2go: 23 + cmd: vhs json2go.tape
vhs/comment.gif

This is a binary file and will not be displayed.

+7
vhs/comment.go
··· 1 + package demos 2 + 3 + func doSomethingImportant() {} 4 + 5 + type User struct{} 6 + 7 + type DataProvider interface{}
+29
vhs/comment.tape
··· 1 + Output comment.gif 2 + Require nvim 3 + 4 + Set FontFamily "JetBrains Mono" 5 + Set Height 800 6 + Set Width 1500 7 + Set Padding 20 8 + Set Shell "bash" 9 + Set Theme "tokyonight" 10 + Set TypingSpeed 250ms 11 + 12 + Hide Type@0ms "./nvim.sh comment.go" Enter Show 13 + 14 + # package 15 + Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 16 + 17 + # func 18 + Type@400ms "jjj" 19 + Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 20 + 21 + # struct 22 + Type@400ms "jjj" 23 + Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 24 + 25 + # interface 26 + Type@400ms "jjj" 27 + Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 28 + 29 + Sleep 5s
+3
vhs/go.mod
··· 1 + module demos 2 + 3 + go 1.25.0
vhs/iferr.gif

This is a binary file and will not be displayed.

+11
vhs/iferr.go
··· 1 + package demos 2 + 3 + func ifErr() error { 4 + out, err := doSomething() 5 + 6 + _ = out 7 + } 8 + 9 + func doSomething() (string, error) { 10 + return "", nil 11 + }
+18
vhs/iferr.tape
··· 1 + Output iferr.gif 2 + Require nvim 3 + Require iferr 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 iferr.go" Enter Show 14 + 15 + Type "3j" 16 + Type ":GoIfErr" Sleep 500ms Enter 17 + 18 + Sleep 5s
vhs/impl.gif

This is a binary file and will not be displayed.

+3
vhs/impl.go
··· 1 + package demos 2 + 3 + type CloserExample struct{}
+18
vhs/impl.tape
··· 1 + Output impl.gif 2 + Require nvim 3 + Require impl 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 impl.go" Enter Show 14 + 15 + Type "2j" 16 + Type ":GoImpl c io.Reader" Sleep 700ms Enter 17 + 18 + Sleep 5s
vhs/json2go.gif

This is a binary file and will not be displayed.

+2
vhs/json2go.go
··· 1 + package main 2 +
+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
+2
vhs/nvim.sh
··· 1 + #!/usr/bin/env bash 2 + nvim --clean -u "../scripts/minimal_init.lua" $@
vhs/tags.gif

This is a binary file and will not be displayed.

+12
vhs/tags.go
··· 1 + package demos 2 + 3 + type AddTagsToMe struct { 4 + Name string 5 + ID int 6 + Address string 7 + Aliases []string 8 + Nested struct { 9 + Foo string 10 + Bar float32 11 + } 12 + }
+31
vhs/tags.tape
··· 1 + Output tags.gif 2 + Require nvim 3 + Require gomodifytags 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 tags.go" Enter Show 14 + 15 + Type@400ms "jjj" 16 + Type ":GoTagAdd json yaml" Sleep 500ms Enter 17 + Type@120ms ":w" Enter 18 + Sleep 1s 19 + 20 + Type ":GoTagRm json" Sleep 500ms Enter 21 + Type ":w" Enter 22 + Sleep 1s 23 + 24 + Type ":GoTagClear" Sleep 500ms Enter 25 + Type ":w" Enter 26 + Sleep 1s 27 + 28 + Type@400ms "jVjj" 29 + Type ":GoTagAdd json=omitempty" Sleep 500ms Enter 30 + 31 + Sleep 5s