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
- Syntax highlight & Texobject: Native treesitter support is faster and more accurate. All you need is a theme support treesitter, try aurora. Also, there are quite a few listed in 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 gopls setup lspconfig.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 nvim-dap and Dap UI.
- Load vscode launch configuration
- Unit test: generate unit test framework with gotests. Run test with richgo/ginkgo/go test
- Add and remove tag for struct with tag modify(gomodifytags)
- Code format: Supports LSP format and GoFmt(with golines)
- 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)
- Test with ginkgo, richgo inside floaterm (to enable floaterm, guihua.lua has to be installed)
- Go 1.18 support, configure your go to
go1.18
in config - GoFixPlural, FixStruct, FxiSwitch, Add comment, IfErr, ModTidy, GoGet ... Most of the tools are built on top of treesitter AST or go AST. It is fast and accurate.
Use your favorite package manager to install. The dependency treesitter
(and optionally, treesitter-objects)
should be installed the first time you use it.
Also Run TSInstall go
to install the go parser if not installed yet.
sed
is recommand to run this plugin.
Plug 'ray-x/go.nvim'
use 'ray-x/go.nvim'
Make sure the $GOPATH/bin
path is added to your $PATH
environment variable. To check this you can run
echo $PATH | grep "$GOPATH/bin"
If nothing shows up, you can add the following to your shell config file:
export PATH=$PATH:$GOPATH/bin
Add format in your vimrc.
autocmd BufWritePre *.go :silent! lua require('go.format').gofmt()
To startup/setup the plugin
require('go').setup()
Use:
:GoTermClose
To close the floating term.
gorename as an alternative to gopls rename as it supports rename across packages Command: GoRename
nvim-lsp support goimport by default.
autocmd BufWritePre (InsertLeave?) <buffer> 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
require("go.format").gofmt() -- gofmt only
require("go.format").goimport() -- goimport + gofmt
To config format on save, add one of the following to your init.lua:
-- Run gofmt on save
vim.api.nvim_exec([[ autocmd BufWritePre *.go :silent! lua require('go.format').gofmt() ]], false)
-- Run gofmt + goimport on save
vim.api.nvim_exec([[ autocmd BufWritePre *.go :silent! lua require('go.format').goimport() ]], false)
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) |
package foo
import "io"
func Foo() (io.Reader, error) { // the cursor on this line to add if err statement
}
Supported by treesitter. TS provided better parse result compared to regular expression.
See the example treesitter config file on how to setup
textobjects. Also with treesitter-objects, you can move, swap the selected blocks of codes, which is fast and accurate.
go.nvim
will load textobject with treesiteter, with default keybindings, if you what to set it up yourself, you can
set textobject
to false.
The following go binaries are used in go.nvim
(depends on your setup):
- gofumpt
- golines
- goimports
- gorename
- gomodifytags
- gotests
- iferr
- impl
- fillstruct
- fillswitch
- dlv
- ginkgo
- richgo
Normally if you run GoFmt
and the configured binary (e.g. golines) was not installed, the plugin will install it for you. But the
first run of GoFmt
may fail. It is recommended to run GoInstallBinaries
to install all binaries before using the plugin.
command | Description |
---|---|
GoInstallBinary go_binary_name | use go install go_binary_url@latest to install tool, if installed will skip |
GoUpdateBinary go_binary_name | use go install go_binary_url@latest Will force re-install if already installed, otherwise same as GoInstallBinary |
GoInstallBinaries | use go install to install all tools, skip the ones installed |
GoUpdateBinaries | use go install to update all tools to the latest version |
command | Description |
---|---|
GoMake | make |
GoBuild | |
GoGenerate | |
GoRun | e.g. GoRun equal to go run . ; or GoRun ./cmd equal to go run ./cmd |
GoStop {job_id} | stop the job started with GoRun |
GoTest | go test ./... |
GoTest -c | go test -c current_file_path |
GoTest -tags=yourtags | go test ./... -tags=yourtags |
GoTest package_path -tags=yourtags | go test packagepath -tags=yourtags |
GoTest package_path -tags=yourtags other_args | go test packagepath -tags=yourtags other_args |
GoLint | golangci-lint |
GoGet {package_url} | go get package_url and restart gopls. Note1 |
GoVet | go vet |
GoCoverage | go test -coverprofile |
GoTermClose | closes the floating term |
Note1: if package_url not provided, will check current line is a valid package url or not, if it is valid, will fetch current url
Show test coverage:
Provided wrapper for gobulild/test etc with async make Also suggest to use vim-test, which can run running tests on different granularities.
Unit test with 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 -tags=yourtag | run test for current func with -tags yourtag option |
GoTestFile | run test for current file |
GoTestFile -tags=yourtag | run test for current folder with -tags yourtag option |
GoTestPkg | run test for current package/folder |
GoTestPkg -tags=yourtag | run test for current folder with -tags yourtag option |
GoAddTest [-parallel] | Add test for current func |
GoAddExpTest [-parallel] | Add tests for exported funcs |
GoAddAllTest [-parallel] | Add tests for all funcs |
Note: For GoTestXXX
You can add avaliable arguments e.g. GoTest -tags=integration ./internal/web -bench=. -count=1 -
Show go doc for api in neovim floating window. e.g. GoDoc fmt.Println
If no argument provided, fallback to lsp.hover()
Modify struct tags by gomodifytags
and treesitter
command | Description |
---|---|
GoAddTag | |
GoRmTag | |
GoClearTag |
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 |
GoImport package_path | gopls add_import package |
generate method stubs for implementing an interface
Usage:
:GoImpl {receiver} {interface}
Also you can put cursor on struct and run
```vim
:GoImpl {interface}
e.g:
:GoImpl f *File io.Reader
command | Description |
---|---|
GoDebug | start debug session |
GoDebug -t | start debug session for go test file |
GoDebug -R | restart debug session for go test file |
GoDebug -n | start debug session for nearest go test function |
GoDebug -f | same as GoDebug |
GoDebug -p | debug package |
GoDebug -a | attach to remote process |
GoDebug -s | stop debug session and unmap debug keymap |
GoBreakToggle | GoDebug -b |
GoBreakCondition | conditional break |
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 |
Auto doc (to suppress golang-lint warning), generate comments by treesitter parsing result
type GoLintComplaining struct{}
And run
lua.require('go.comment').gen() -- or your faviourite key binding and setup placeholder "no more complaint ;P"
The code will be:
// GoLintComplaining struct no more complaint ;P
type GoLintComplaining struct{}
run go mod tidy
and restart gopls
run go mod vendor
and restart gopls
Nvim-lsp is good enough for a gopher. If you looking for a better GUI. You can install navigator, or lspsaga, and lsp-utils etc. The goal of go.nvim is more provide unique functions releated to gopls instead of a general lsp gui client. The lsp config in go.nvim has a none default setup and contains some improvement and I would suggest you to use.
The latest version enabled lsp snippets (and other setups) by default. In case you need flowing the setup from cmp README.md, please use flowing command:
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())
require('go').setup({
-- other setups ....
lsp_cfg = {
capabilities = capabilities,
-- other setups
},
})
Gopls supports code lens. To run gopls code lens action GoCodeLenAct
Note: codelens need to be enabled in gopls, check default config in
You can use native code action provided by lspconfig. If you installed guihua, you can also use a GUI version of
code action GoCodeAction
Supported by LSP, also GoLint command (by calling golangcl-lint) if you need background golangci-lint check, you can configure it with ALE
Setup for Debug provided. Need Dap and Dap UI plugin GDB style key mapping is used
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) |
Please check Vscode Launch configurations
for more info
go.nvim support launch debuger from vscode-go .vscode/launch.json configurations
If launch.json is valid, run GoDebug
will launch from the launch.json configuration.
Please use jsonls/null-ls check your launch.json is valid json file. Following syntax is not supported
- Trailing comma
- Comment
Here is a sample launch.json
Command | Description |
---|---|
GoDebug | Start debugger, to debug test, run GoDebug test , to add addition args run GoDebug arg1 arg2 |
GoDebugConfig | Open launch.json file |
GoBreakToggle | toggle break point |
BreakCondition | conditional break point |
ReplRun | dap repl run_last |
ReplToggle | dap repl toggle |
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
Plug 'mfussenegger/nvim-dap'
Plug 'rcarriga/nvim-dap-ui'
Plug 'theHamsta/nvim-dap-virtual-text'
" Plug 'nvim-telescope/telescope-dap.nvim'
Check go.lua on all the commands provided
Configure from lua suggested, The default setup:
require('go').setup({
go='go', -- go command, can be go[default] or go1.18beta1
goimport='gopls', -- goimport command, can be gopls[default] or goimport
fillstruct = 'gopls', -- can be nil (use fillstruct, slower) and gopls
gofmt = 'gofumpt', --gofmt cmd,
max_line_len = 120, -- max line length in goline format
tag_transform = false, -- can be transform option("snakecase", "camelcase", etc) check gomodifytags for details and more options
gotests_template = "", -- sets gotests -template parameter (check gotests for details)
gotests_template_dir = "", -- sets gotests -template_dir parameter (check gotests for details)
comment_placeholder = '' , -- comment_placeholder your cool placeholder e.g. ﳑ
icons = {breakpoint = '🧘', currentpos = '🏃'}, -- setup to `false` to disable icons setup
verbose = false, -- output loginf in messages
lsp_cfg = false, -- true: use non-default gopls setup specified in go/lsp.lua
-- false: do nothing
-- if lsp_cfg is a table, merge table with with non-default gopls setup in go/lsp.lua, 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 = nil, -- nil: use on_attach function defined in go/lsp.lua,
-- when lsp_cfg is true
-- if lsp_on_attach is a function: use this function as on_attach function for gopls
lsp_keymaps = true, -- set to false to disable gopls/lsp keymap
lsp_codelens = true, -- set to false to disable codelens, true by default, you can use a function
-- function(bufnr)
-- vim.api.nvim_buf_set_keymap(bufnr, "n", "<space>F", "<cmd>lua vim.lsp.buf.formatting()<CR>", {noremap=true, silent=true})
-- end
-- to setup a table of codelens
lsp_diag_hdlr = true, -- hook lsp diag handler
-- virtual text setup
lsp_diag_virtual_text = { space = 0, prefix = "" },
lsp_diag_signs = true,
lsp_diag_update_in_insert = false,
lsp_document_formatting = true,
-- set to true: use gopls to format
-- false if you want to use other formatter tool(e.g. efm, nulls)
gopls_cmd = nil, -- if you need to specify gopls path and cmd, e.g {"/home/user/lsp/gopls", "-logfile","/var/log/gopls.log" }
gopls_remote_auto = true, -- add -remote=auto to gopls
dap_debug = true, -- set to false to disable dap
dap_debug_keymap = true, -- true: use keymap for debugger defined in go/dap.lua
-- false: do not use keymap in go/dap.lua. you must define your own.
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
build_tags = "tag1,tag2", -- set default build tags
textobjects = true, -- enable default text jobects through treesittter-text-objects
test_runner = 'go', -- richgo, go test, richgo, dlv, ginkgo
verbose_tests = true, -- set to add verbose flag to tests
run_in_floaterm = false, -- set to true to run in float window. :GoTermClose closes the floatterm
-- float term recommand if you use richgo/ginkgo with terminal color
})
You will need to add keybind yourself: e.g
vim.cmd("autocmd FileType go nmap <Leader><Leader>l GoLint")
vim.cmd("autocmd FileType go nmap <Leader>gc :lua require('go.comment').gen()")
I did not provide textobject support in the plugin. Please use treesitter textobject plugin. My treesitter config:
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 = {
["<leader>a"] = "@parameter.inner"
},
swap_previous = {
["<leader>A"] = "@parameter.inner"
}
}
}
}
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 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, folke/lsp-trouble.nvim. Nvim-tree and Bufferline also introduced lsp diagnostic hooks.
(suggested by @mattbailey)
local path = require 'nvim-lsp-installer.path'
local install_root_dir = path.concat {vim.fn.stdpath 'data', 'lsp_servers'}
require('go').setup({
gopls_cmd = {install_root_dir .. '/go/gopls'},
filstruct = 'gopls',
dap_debug = true,
dap_debug_gui = true
})
If you want to use gopls setup provided by go.nvim
-- setup your go.nvim
-- make sure lsp_cfg is disabled
require('go').setup{...}
local lsp_installer_servers = require'nvim-lsp-installer.servers'
local server_available, requested_server = lsp_installer_servers.get_server("gopls")
if server_available then
requested_server:on_ready(function ()
local opts = require'go.lsp'.config() -- config() return the go.nvim gopls setup
requested_server:setup(opts)
end)
if not requested_server:is_installed() then
-- Queue the server to be installed
requested_server:install()
end
end
The following vimrc will enable all features provided by go.nvim
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/guihua.lua' " float term, codeaction and codelens gui support
Plug 'ray-x/go.nvim'
call plug#end()
lua <<EOF
require 'go'.setup({
goimport = 'gopls', -- if set to 'gopls' will use golsp format
gofmt = 'gopls', -- if set to gopls will use golsp format
max_line_len = 120,
tag_transform = false,
test_dir = '',
comment_placeholder = ' ',
lsp_cfg = true, -- false: use your own lspconfig
lsp_gofumpt = true, -- true: set default gofmt in gopls format to gofumpt
lsp_on_attach = true, -- use on_attach from go.nvim
dap_debug = true,
})
local protocol = require'vim.lsp.protocol'
EOF
This will setup gopls with non default configure provided by go.nvim (Includes lspconfig default keymaps)
Q & A:
Q: What is Toggle gc annotation details
A: This is a codelens message, please run codelens GoCodeLenAct
and get more info