# go.nvim A modern go neovim plugin based on treesitter, nvim-lsp and dap debugger. It is written in Lua and async as much as possible. PR & Suggestions welcome. The plugin covers most features required for a gopher. - Async jobs with libuv - Syntex highlight & Texobject: Native treesitter support is faster and more accurate. All you need is a theme support treesitter, try [aurora](https://github.com/ray-x/aurora). Also, there are quite a few listed in [awesome-neovim](https://github.com/rockerBOO/awesome-neovim) - All the GoToXxx (E.g reference, implementation, definition, goto doc, peek code/doc etc) You need lspconfig setup. There are lots of posts on how to set it up. You can also check my [navigator](https://github.com/ray-x/navigator.lua) gopls setup [lspconfig.lua](https://github.com/ray-x/navigator.lua/blob/master/lua/navigator/lspclient/clients.lua) - Runtime lint/vet/compile: Supported by lsp (once you setup up your lsp client), GoLint with golangci-lint also supported - Build/Make/Test: Go.nvim provides supports for these by an async job wrapper. - Test coverage: run test coverage and show coverage sign - Dlv Debug: with Dap UI - Unit test: Support [gotests](https://github.com/cweill/gotests) - tag modify: Supports gomodifytags - Code format: Supports LSP format and GoFmt - CodeLens : gopls codelens and codelens action support - Comments: Add autodocument for your package/function/struct/interface. This feature is unique and can help you suppress golint errors... Go to alternative go file (between test and source) ## install make sure the `$GOPATH/bin` path is added to your `$PATH` environment variable. To check this you can run ```bash echo $PATH | grep "$GOPATH/bin" ``` if nothing shows up, you can add the following to your shell config file ```bash export PATH=$PATH:$GOPATH/bin ``` add 'ray-x/go.nvim' to your package manager, the dependency is `treesitter` (and optionally, treesitter-objects) related binaries will be installed the first time you use it Add format in your vimrc. ```vim autocmd BufWritePre *.go :silent! lua require('go.format').gofmt() ``` To startup/setup the plugin ```lua require('go').setup() ``` ## Screenshots ### Add comments ![auto comments](https://github.com/ray-x/files/blob/master/img/go.nvim/comment.gif?raw=true) ### Add/Remove tags ![auto tag](https://github.com/ray-x/files/blob/master/img/go.nvim/tags.gif?raw=true) ## refactor gorename gorename as an alternative to gopls rename as it supports rename across packages Command: GoRename ## code format nvim-lsp support goimport by default. ```vim autocmd BufWritePre (InsertLeave?) lua vim.lsp.buf.formatting_sync(nil,500) ``` The plugin provides code format, by default is goline + gofumpt (stricter version of gofmt) Use following code to format go code ```lua require("go.format").gofmt() -- format only require("go.format").goimport() -- goimport + gofmt ``` To config format on save, in your init.lua: ```lua -- Format on save vim.api.nvim_exec([[ autocmd BufWritePre *.go :silent! lua require('go.format').gofmt() ]], false) -- Import on save vim.api.nvim_exec([[ autocmd BufWritePre *.go :silent! lua require('go.format').goimport() ]], false) ``` ## Auto fill Note: auto fill struct also supported by gopls lsp-action | command | Description | | ------------ | ------------------------------------------------------------- | | GoFillStruct | auto fill struct | | GoFillSwitch | fill switch | | GoIfErr | Add if err | | GoFixPlurals | change func foo(b int, a int, r int) -> func foo(b, a, r int) | ```go package foo import "io" func Foo() (io.Reader, error) { // the cursor on this line to add if err statement } ``` ![auto struct](https://github.com/ray-x/files/blob/master/img/go.nvim/fstruct.gif?raw=true) ## Textobject Supported by treesitter. TS provided better parse result compared to regular expression. Check [my treesitter config file](https://github.com/ray-x/dotfiles/blob/master/nvim/lua/modules/lang/treesitter.lua) on how to setup textobjects. Also with treesitter-objects, you can move, swap the selected blocks of codes, which is fast and accurate. ## Build and test | command | Description | | --------------- | ------------------------------------------------------------------------ | | GoMake | make | | GoBuild | | | GoGenerate | | | GoRun | e.g. GoRun equal to `go run .`; or `GoRun ./cmd` equal to `go run ./cmd` | | GoTest | go test ./... | | GoTest yourtags | go test ./... -tags=yourtags | | GoLint | golangci-lint | | GoCoverage | go test -coverprofile | Show test coverage: GoTestCoverage Provided wrapper for gobulild/test etc with async make Also suggest to use [vim-test](https://github.com/vim-test/vim-test), which can run running tests on different granularities. ## Unit test with [gotests](https://github.com/cweill/gotests) and testify Support table based unit test auto generate, parse current function/method name using treesitter | command | Description | | ------------------ | ------------------------------------------------------- | | GoTestFunc | run test for current func | | GoTestFunc yourtag | run test for current func with `-tags yourtag` option | | GoTestFile | run test for current file folder | | GoTestFile yourtag | run test for current folder with `-tags yourtag` option | | GoAddTest | | | GoAddExpTest | Add tests for exported funcs | | GoAddAllTest | Add tests for all funcs | ## GoDoc Show go doc for api in neovim floating window. e.g. `GoDoc fmt.Println` ![Godoc](https://user-images.githubusercontent.com/1681295/133886804-cc110fae-6fbf-4218-9c22-07fc9d6a64d2.jpg) ## Modifytags Modify struct tags by [`gomodifytags`](https://github.com/fatih/gomodifytags) and treesitter | command | Description | | ---------- | ----------- | | GoAddTag | | | GoRmTag | | | GoClearTag | | ## GoFmt nvim-lsp support goimport by default. The plugin provided a new formatter, goline + gofumpt (stricter version of gofmt) | command | Description | | -------- | --------------------------- | | GoFmt | goline + gofumpt | | GoImport | goline + goimport + gofumpt | ## GoImpl generate method stubs for implementing an interface Usage: ``` :GoImpl {receiver} {interface} ``` e.g: ``` :GoImpl f *File io.Reader ``` ## Debug | command | Description | | ---------------- | ------------------------------------------------ | | GoDebug | start debug session | | GoDebug test | start debug session for go test file | | GoDebug nearest | start debug session for nearest go test function | | GoBreakToggle | | | GoBreakCondition | conditional break | | GoDbgStop | Stop debug session | ## Swtich between go and test file | command | Description | | ---------------- | ------------------------------------------------------- | | GoAlt / GoAlt! | open alternative go file (use ! to create if not exist) | | GoAltS / GoAltS! | open alternative go file in split | | GoAltV / GoAltV! | open alternative go file in vertical split | ## Comments and Doc Auto doc (to suppress golang-lint warning), generate comments by treesitter parsing result ```go type GoLintComplaining struct{} ``` And run ```lua lua.require('go.comment').gen() -- or your faviourite key binding and setup placeholder "no more complaint ;P" ``` The code will be: ```go // GoLintComplaining struct no more complaint ;P type GoLintComplaining struct{} ``` ## GoModeTidy ## LSP LSP supported by nvim-lsp is good enough for a gopher. If you looking for a better GUI. You can install [navigator](https://github.com/ray-x/navigator.lua), or lspsaga, and lsp-utils etc. ## LSP CodeLens Gopls supports code lens. To run gopls code lens action `GoCodeLenAct` Note: codelens need to be enabled in gopls, check default config in ## Lint Supported by LSP, also GoLint command (by calling golangcl-lint) if you need background golangci-lint check, you can configure it with ALE ## Debug with dlv Setup for Debug provided. Need Dap and Dap UI plugin ![dap](https://user-images.githubusercontent.com/1681295/125160289-743ba080-e1bf-11eb-804f-6a6d227ec33b.jpg) GDB style key mapping is used ### Keymaps | key | Description | | --- | ---------------------------------------- | | c | continue | | n | next | | s | step | | o | stepout | | S | cap S: stop debug | | u | up | | D | cap D: down | | C | cap C: run to cursor | | b | toggle breakpoint | | P | cap P: pause | | p | print, hover value (also in visual mode) | ### Commands | Command | Description | | -------------- | ------------------------------------------------------------------------------------------- | | GoDebug | Start debugger, to debug test, run `Debug test`, to add addition args run `Debug arg1 arg2` | | GoBreakToggle | toggle break point | | BreakCondition | conditional break point | | ReplRun | dap repl run_last | | ReplToggle | dap repl toggle | ### Required DAP plugins The plugin will setup debugger. But you need to install - dap - 'mfussenegger/nvim-dap' - dap ui (optional) - 'rcarriga/nvim-dap-ui' - dap virtual text (optional) - 'theHamsta/nvim-dap-virtual-text' Also you can check telescope dap extension : nvim-telescope/telescope-dap.nvim Sample vimrc for DAP ```viml Plug 'mfussenegger/nvim-dap' Plug 'rcarriga/nvim-dap-ui' Plug 'theHamsta/nvim-dap-virtual-text' " Plug 'nvim-telescope/telescope-dap.nvim' ``` ## Commands Check [go.lua](https://github.com/ray-x/go.nvim/blob/master/lua/go.lua) on all the commands provided ## configuration Configure from lua suggested, The default setup: ```lua require('go').setup({ goimport='gopls', -- goimport command, can be gopls[default] or goimport gofmt = 'gofumpt', --gofmt cmd, max_line_len = 120, -- max line length in goline format tag_transform = false, -- tag_transfer check gomodifytags for details test_template = '', -- default to testify if not set; g:go_nvim_tests_template check gotests for details test_template_dir = '', -- default to nil if not set; g:go_nvim_tests_template_dir check gotests for details comment_placeholder = '' , -- comment_placeholder your cool placeholder e.g. ﳑ     icons = {breakpoint = '🧘', currentpos = '🏃'}, verbose = false, -- output loginf in messages lsp_cfg = false, -- true: apply go.nvim non-default gopls setup, if it is a list, will merge with gopls setup e.g. -- lsp_cfg = {settings={gopls={matcher='CaseInsensitive', ['local'] = 'your_local_module_path', gofumpt = true }}} lsp_gofumpt = false, -- true: set default gofmt in gopls format to gofumpt lsp_on_attach = true, -- if a on_attach function provided: attach on_attach function to gopls -- true: will use go.nvim on_attach if true -- nil/false do nothing lsp_codelens = true, -- set to false to disable codelens, true by default gopls_remote_auto = true, -- add -remote=auto to gopls gopls_cmd = nil, -- if you need to specify gopls path and cmd, e.g {"/home/user/lsp/gopls", "-logfile", fillstruct = 'gopls', -- can be nil (use fillstruct, slower) and gopls "/var/log/gopls.log" } lsp_diag_hdlr = true, -- hook lsp diag handler dap_debug = true, -- set to false to disable dap dap_debug_keymap = true, -- set keymaps for debugger dap_debug_gui = true, -- set to true to enable dap gui, highly recommand dap_debug_vt = true, -- set to true to enable dap virtual text }) ``` You will need to add keybind yourself: e.g ```lua vim.cmd("autocmd FileType go nmap l GoLint") vim.cmd("autocmd FileType go nmap gc :lua require('go.comment').gen()") ``` ## Text object I did not provide textobject support in the plugin. Please use treesitter textobject plugin. My treesitter config: ```lua require "nvim-treesitter.configs".setup { incremental_selection = { enable = enable, keymaps = { -- mappings for incremental selection (visual mappings) init_selection = "gnn", -- maps in normal mode to init the node/scope selection node_incremental = "grn", -- increment to the upper named parent scope_incremental = "grc", -- increment to the upper scope (as defined in locals.scm) node_decremental = "grm" -- decrement to the previous node } }, textobjects = { -- syntax-aware textobjects enable = enable, lsp_interop = { enable = enable, peek_definition_code = { ["DF"] = "@function.outer", ["DF"] = "@class.outer" } }, keymaps = { ["iL"] = { -- you can define your own textobjects directly here go = "(function_definition) @function", }, -- or you use the queries from supported languages with textobjects.scm ["af"] = "@function.outer", ["if"] = "@function.inner", ["aC"] = "@class.outer", ["iC"] = "@class.inner", ["ac"] = "@conditional.outer", ["ic"] = "@conditional.inner", ["ae"] = "@block.outer", ["ie"] = "@block.inner", ["al"] = "@loop.outer", ["il"] = "@loop.inner", ["is"] = "@statement.inner", ["as"] = "@statement.outer", ["ad"] = "@comment.outer", ["am"] = "@call.outer", ["im"] = "@call.inner" }, move = { enable = enable, set_jumps = true, -- whether to set jumps in the jumplist goto_next_start = { ["]m"] = "@function.outer", ["]]"] = "@class.outer" }, goto_next_end = { ["]M"] = "@function.outer", ["]["] = "@class.outer" }, goto_previous_start = { ["[m"] = "@function.outer", ["[["] = "@class.outer" }, goto_previous_end = { ["[M"] = "@function.outer", ["[]"] = "@class.outer" } }, select = { enable = enable, keymaps = { -- You can use the capture groups defined in textobjects.scm ["af"] = "@function.outer", ["if"] = "@function.inner", ["ac"] = "@class.outer", ["ic"] = "@class.inner", -- Or you can define your own textobjects like this ["iF"] = { python = "(function_definition) @function", cpp = "(function_definition) @function", c = "(function_definition) @function", java = "(method_declaration) @function", go = "(method_declaration) @function" } } }, swap = { enable = enable, swap_next = { ["a"] = "@parameter.inner" }, swap_previous = { ["A"] = "@parameter.inner" } } } } ``` ## Nvim LSP setup go.nvim provided a better non-default setup for gopls (includes debounce, staticcheck, diagnosticsDelay etc) This gopls setup provided by go.nvim works perfectly fine for most of the cases. You can also install [navigator.lua](https://github.com/ray-x/navigator.lua) which can auto setup all lsp clients and provides a better GUI. For diagnostic issue, you can use the default setup. There are also quite a few plugins that you can use to explore issues, e.g. [navigator.lua](https://github.com/ray-x/navigator.lua), [folke/lsp-trouble.nvim](https://github.com/folke/lsp-trouble.nvim). [Nvim-tree](https://github.com/kyazdani42/nvim-tree.lua) and [Bufferline](https://github.com/akinsho/nvim-bufferline.lua) also introduced lsp diagnostic hooks. ## Sample vimrc The following vimrc will enable all features provided by go.nvim ```viml set termguicolors call plug#begin('~/.vim/plugged') Plug 'neovim/nvim-lspconfig' Plug 'nvim-treesitter/nvim-treesitter' Plug 'mfussenegger/nvim-dap' Plug 'rcarriga/nvim-dap-ui' Plug 'theHamsta/nvim-dap-virtual-text' Plug 'ray-x/go.nvim' call plug#end() lua <