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 are welcome.
The plugin covers most features required for a gopher.
//go:build integration
)go template
, gohtmltmpl
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
recommended to run this plugin.
Plug 'nvim-treesitter/nvim-treesitter'
Plug 'neovim/nvim-lspconfig'
Plug 'ray-x/go.nvim'
Plug 'ray-x/guihua.lua' ; recommended if need floating window support
use 'ray-x/go.nvim'
use 'ray-x/guihua.lua' -- recommended if need floating window support
use 'neovim/nvim-lspconfig'
use 'nvim-treesitter/nvim-treesitter'
{
"ray-x/go.nvim",
dependencies = { -- optional packages
"ray-x/guihua.lua",
"neovim/nvim-lspconfig",
"nvim-treesitter/nvim-treesitter",
},
config = function()
require("go").setup()
end,
event = {"CmdlineEnter"},
ft = {"go", 'gomod'},
build = ':lua require("go.install").update_all_sync()' -- if you need to install/update all binaries
}
The go.nvim load speed is fast and you can enable it by default
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.
lua <<EOF
local format_sync_grp = vim.api.nvim_create_augroup("GoFormat", {})
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go",
callback = function()
require('go.format').goimports()
end,
group = format_sync_grp,
})
EOF
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 Note: use with care Command: GoRename
nvim-lsp support goimports 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").goimports() -- goimports + gofmt
To config format on save, add one of the following to your init.lua:
-- Run gofmt on save
local format_sync_grp = vim.api.nvim_create_augroup("GoFormat", {})
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go",
callback = function()
require('go.format').gofmt()
end,
group = format_sync_grp,
})
-- Run gofmt + goimports on save
local format_sync_grp = vim.api.nvim_create_augroup("goimports", {})
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go",
callback = function()
require('go.format').goimports()
end,
group = format_sync_grp,
})
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):
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. 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/update 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 | async make, use with other commands |
GoBuild args | go build args (-g: enable debug, %: expand to current file, %:h expand to current package) |
GoGenerate | |
GoRun {args} -a {cmd_args} | e.g. GoRun equal to go run . ; or GoRun ./cmd equal to go run ./cmd, Additional args: -F run in floaterm |
GoRun -a {cmd_args} | specify additional arguments pass to your main(), see notes 3 |
GoStop {job_id} | stop the job started with GoRun |
GoTest | go test ./... |
GoTestSum {pkgname} {gotestsum arguments} | run gotestsum and show result in side panel |
GoTestSum -w | run gotestsum in watch mode |
GoTest -v | go test -v current_file_path |
GoTest -c | go test -c current_file_path |
GoTest -n | test nearest, see GoTestFunc |
GoTest -f | test current file, see GoTestFile |
GoTest -n 1 | -count=1 flag |
GoTest -p {pkgname} | test package, see GoTestPkg, test current package if {pkgname} not specified |
GoTest -parallel {number} | test current package with parallel number |
GoTest -b {build_flags} | run go test with build flags e.g. -b -gcflags="all-N\ -l" |
GoTest -t yourtags | go test ./... -tags=yourtags, see notes |
GoTest -F ./... | awk '{$1=$1};1' | delta | pipe the test output to awk and then delta/diff-so-fancy to show diff output of go test (e.g. testify) |
GoTest -a your_args | go test ./... -args=yourargs, see notes |
GoTest package_path -t yourtags | go test packagepath -tags=yourtags |
GoTest package_path -t 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 |
GoWork {run | use} {pkgpath} |
GoCoverage | go test -coverprofile |
GoCoverage -p | go test -coverprofile (only tests package for current buffer) |
GoCoverage -f coverage_file_name | load coverage file |
GoCoverage {flags} | -t : toggle, -r: remove signs, -R remove sings from all files, -m show metrics |
GoCoverage {flags} {go test flags} | e.g: GoCoverage -p -coverpkg 'yourpackagename' |
GoTermClose | closes the floating term |
Note:
//+build tags
exist it will be added automaticallyShow 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.
Support table based unit test auto generate, parse current function/method name using treesitter
command | Description |
---|---|
GoTestFunc | run test for current func |
GoTestFunc -s | select the test function you want to run |
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 |
GoTestXXX Arguments
arguments | Description |
---|---|
-v | verbose mode |
-c | compile |
-C | coverprofile |
-n | count |
-t | tags |
-f | fuzz |
-bench | bench test |
-m | metric |
-s | select |
-p | package |
-F | floaterm mode |
-a | args |
Note: For GoTestXXX
You can add available arguments with long name or character flag e.g.
GoTest -tags=integration ./internal/web -b=. -count=1 -
You can also add other unmapped arguments after the -a
or -args
flag GoTest -a mock=true
Show cheat.sh for api in neovim new buffer. e.g. GoCheat sort
Show go doc for api in neovim floating window. e.g. GoDoc fmt.Println
If no argument provided, fallback to lsp.hover()
A symbole outline for all symbols (var, const, func, struct, interface etc) inside a package You can still use navigator or sidebar plugins (e.g. vista, symbols-outline) to check outline within a file. But it is more useful for go to check the symbols in a package, as those symbols are visuals inside package, also the method can be defined in different source file.
Command format: GoPkgOutline {options} options: -f: show in floatwing window (default side panel, both require quihua.lua) -p package_name: the package you want to list. e.g. GoPkgOutline -p json; default package is current file's package If guihua not installed fallback to loclist
Modify struct tags by gomodifytags
and treesitter
command | Description |
---|---|
GoAddTag | |
GoRmTag | |
GoClearTag |
Options: -transform/-t: transform the tag -add-options/-a: add options to the tag
nvim-lsp support goimports by default. The plugin provided a new formatter, goline + gofumpt (stricter version of gofmt)
command | Description |
---|---|
GoFmt {opts} | default: gofumpt |
GoImports | default: goimports |
GoImports package_path | gopls add_import package |
{opts} : -a
format all buffers
nvim-lsp/gopls support implementation by default. The plugin provides this command for people migrate from vim-go
generate method stubs for implementing an interface
Usage:
:GoImpl {receiver} {interface}
Also, you can put the cursor on the struct and run
:GoImpl {interface}
e.g:
:GoImpl f *File io.Reader
or simply put your cursor in a struct and do
:GoImpl io.Reader
or simply your cursor on a interface and specify a receiver type
:GoImpl MyType
command | Description |
---|---|
GoDebug | start debug session, Note 1 |
GoDebug -h | show helps info |
GoDebug -c | compile only |
GoDebug -t | start debug session for go test file, Note 2 |
GoDebug -R | restart debug session |
GoDebug -n | start debug session for nearest go test function |
GoDebug -p | launch package test and start debug |
GoDebug -e program | dap exec program |
GoDebug -a | attach to remote process |
GoDebug -s | stop debug session and unmap debug keymap |
GoDebug -A args | debug session with args |
GoDbgKeys | show debug keymaps in a floating window (guihua) |
GoBreakToggle | GoDebug -b |
GoDbgStop | Same as GoDebug -s |
GoDbgContinue | Continue debug session |
BreakCondition | conditional break |
Notes:
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 |
command | Description |
---|---|
GoMockGen | default: generate mocks for current file |
GoMockGen -s | source mode(default) |
GoMockGen -i | interface mode, provide interface name or put the cursor on interface -p package |
GoMockGen -d | destination directory, default: ./mocks |
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 favorite key binding and setup placeholder "no more complaint ;P"
The code will be:
// GoLintComplaining struct no more complaint ;P
type GoLintComplaining struct{}
command | Description |
---|---|
GoCmt | Add comment |
command | Description |
---|---|
GoModInit | run go mod init and restart gopls |
GoModTidy | run go mod tidy and restart gopls |
GoModVendor | run go mod vendor and restart gopls |
run go mod tidy
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').default_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
, or with visual selection :'<,'>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(adapter) for go included. Need Dap and Dap UI plugin nvim-dap nvim-dap-ui 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 debugger 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
Here is a sample launch.json
GoJson2Struct youStructName
-bang will put result to register a
if ["x] specified, will put get json from clipboarddap_enrich_config
function, to modify the selected launch.json configuration on the fly,
as suggested by https://github.com/mfussenegger/nvim-dap/discussions/548#discussioncomment-8778225: dap_enrich_config = function(config, on_config)
local final_config = vim.deepcopy(finalConfig)
final_config.env['NEW_ENV_VAR'] = 'env-var-value'
-- load .env file for your project
local workspacefolder = vim.lsp.buf.list_workspace_folders()[1] or vim.fn.getcwd()
local envs_from_file = require('go.env').load_env(workspacefolder .. 'your_project_dot_env_file_name')
final_config = vim.tbl_extend("force", final_config, envs_from_file)
on_config(final_config)
end
create return value for current function e.g. if we have
func Foo() (int, error) {
return 1, nil
}
and in your code you cursor on Foo
Foo()
will generate
i, err := Foo()
if err != nil {
return
}
GoNew ./pkg/string.go
will create string.go
with template file GoNew also support using gonew
command to create new file with template file
gonew cli, e.g GoNew hello package_name/folder
is same as
gonew golang.org/x/example/hello package_name/folder
if package_name/folder not provided, a hello project will be
created in current folderArg | Description |
---|---|
run | |
watch | |
build | |
bootstrap | |
labels | |
outline |
Command | Description |
---|---|
GoDebug | Start debugger, to debug test, run GoDebug test , to add addition args run GoDebug arg1 arg2 |
GoDebugConfig | Open launch.json file |
GoBreakSave | save all breakpoints to project file |
GoBreakLoad | load all breakpoints from project 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
dap ui (optional)
dap virtual text (optional)
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 'nvim-neotest/nvim-nio'
Plug 'theHamsta/nvim-dap-virtual-text'
" Plug 'nvim-telescope/telescope-dap.nvim'
Check commands.lua on all the commands provided
Check gopls.lua on all the gopls commands provided, some of them are not exposed to user, but you can still use it in your lua setup.
Configure from lua suggested, The default setup:
require('go').setup({
disable_defaults = false, -- true|false when true set false to all boolean settings and replace all tables
-- settings with {}; string will be set to ''. user need to setup ALL the settings
-- It is import to set ALL values in your own config if set value to true otherwise the plugin may not work
go='go', -- go command, can be go[default] or e.g. go1.18beta1
goimports ='gopls', -- goimports command, can be gopls[default] or either goimports or golines if need to split long lines
gofmt = 'gopls', -- gofmt through gopls: alternative is gofumpt, goimports, golines, gofmt, etc
fillstruct = 'gopls', -- set to fillstruct if gopls fails to fill struct
max_line_len = 0, -- max line length in golines format, Target maximum line length for golines
tag_transform = false, -- can be transform option("snakecase", "camelcase", etc) check gomodifytags for details and more options
tag_options = 'json=omitempty', -- sets options sent to gomodifytags, i.e., json=omitempty
gotests_template = "", -- sets gotests -template parameter (check gotests for details)
gotests_template_dir = "", -- sets gotests -template_dir parameter (check gotests for details)
gotest_case_exact_match = true, -- true: run test with ^Testname$, false: run test with TestName
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_semantic_highlights = true, -- use highlights from gopls
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 = true, -- true: set default gofmt in gopls format to gofumpt
-- false: do not 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
null_ls = { -- set to false to disable null-ls setup
golangci_lint = {
method = {"NULL_LS_DIAGNOSTICS_ON_SAVE", "NULL_LS_DIAGNOSTICS_ON_OPEN"}, -- when it should run
-- disable = {'errcheck', 'staticcheck'}, -- linters to disable empty by default
-- enable = {'govet', 'ineffassign','revive', 'gosimple'}, -- linters to enable; empty by default
severity = vim.diagnostic.severity.INFO, -- severity level of the diagnostics
},
},
diagnostic = { -- set diagnostic to false to disable vim.diagnostic.config setup,
-- true: default nvim setup
hdlr = false, -- hook lsp diag handler and send diag to quickfix
underline = true,
virtual_text = { spacing = 2, prefix = '' }, -- virtual text setup
signs = {'', '', '', ''}, -- set to true to use default signs, an array of 4 to specify custom signs
update_in_insert = false,
},
-- if you need to setup your ui for input and select, you can do it here
-- go_input = require('guihua.input').input -- set to vim.ui.input to disable guihua input
-- go_select = require('guihua.select').select -- vim.ui.select to disable guihua select
lsp_document_formatting = true,
-- set to true: use gopls to format
-- false if you want to use other formatter tool(e.g. efm, nulls)
lsp_inlay_hints = {
enable = true, -- this is the only field apply to neovim > 0.10
-- following are used for neovim < 0.10 which does not implement inlay hints
-- hint style, set to 'eol' for end-of-line hints, 'inlay' for inline hints
style = 'inlay',
-- Note: following setup only works for style = 'eol', you do not need to set it for 'inlay'
-- Only show inlay hints for the current line
only_current_line = false,
-- Event which triggers a refersh of the inlay hints.
-- You can make this "CursorMoved" or "CursorMoved,CursorMovedI" but
-- not that this may cause higher CPU usage.
-- This option is only respected when only_current_line and
-- autoSetHints both are true.
only_current_line_autocmd = "CursorHold",
-- whether to show variable name before type hints with the inlay hints or not
-- default: false
show_variable_name = true,
-- prefix for parameter hints
parameter_hints_prefix = " ",
show_parameter_hints = true,
-- prefix for all the other hints (type, chaining)
other_hints_prefix = "=> ",
-- whether to align to the length of the longest line in the file
max_len_align = false,
-- padding from the left if max_len_align is true
max_len_align_padding = 1,
-- whether to align to the extreme right or not
right_align = false,
-- padding from the right if right_align is true
right_align_padding = 6,
-- The color of the hints
highlight = "Comment",
},
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
gocoverage_sign = "█",
sign_priority = 5, -- change to a higher number to override other signs
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.
-- Windows: Use Visual Studio keymap
dap_debug_gui = {}, -- bool|table put your dap-ui setup here set to false to disable
dap_debug_vt = { enabled = true, enabled_commands = true, all_frames = true }, -- bool|table put your dap-virtual-text setup here set to false to disable
dap_port = 38697, -- can be set to a number, if set to -1 go.nvim will pick up a random port
dap_timeout = 15, -- see dap option initialize_timeout_sec = 15,
dap_retries = 20, -- see dap option max_retries
dap_enrich_config = nil, -- see dap option enrich_config
build_tags = "tag1,tag2", -- set default build tags
textobjects = true, -- enable default text objects through treesittter-text-objects
test_runner = 'go', -- one of {`go`, `dlv`, `ginkgo`, `gotestsum`}
verbose_tests = true, -- set to add verbose flag to tests deprecated, see '-v' option
run_in_floaterm = false, -- set to true to run in a float window. :GoTermClose closes the floatterm
-- float term recommend if you use gotestsum ginkgo with terminal color
floaterm = { -- position
posititon = 'auto', -- one of {`top`, `bottom`, `left`, `right`, `center`, `auto`}
width = 0.45, -- width of float window if not auto
height = 0.98, -- height of float window if not auto
title_colors = 'nord', -- default to nord, one of {'nord', 'tokyo', 'dracula', 'rainbow', 'solarized ', 'monokai'}
-- can also set to a list of colors to define colors to choose from
-- e.g {'#D8DEE9', '#5E81AC', '#88C0D0', '#EBCB8B', '#A3BE8C', '#B48EAD'}
},
trouble = false, -- true: use trouble to open quickfix
test_efm = false, -- errorfomat for quickfix, default mix mode, set to true will be efm only
luasnip = false, -- enable included luasnip snippets. you can also disable while add lua/snips folder to luasnip load
-- Do not enable this if you already added the path, that will duplicate the entries
on_jobstart = function(cmd) _=cmd end, -- callback for stdout
on_stdout = function(err, data) _, _ = err, data end, -- callback when job started
on_stderr = function(err, data) _, _ = err, data end, -- callback for stderr
on_exit = function(code, signal, output) _, _, _ = code, signal, output end, -- callback for jobexit, output : string
iferr_vertical_shift = 4 -- defines where the cursor will end up vertically from the begining of if err statement
})
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()")
go.nvim
allow you override your setup by a project file. Put .gonvim/init.lua
in your root folder. It is a small lua
script and will be run durning go.setup(). The return value is used to override go.nvim
setup. The sample project
setup. You can check the youtube video here on how to use this feature.
-- .gonvim/init.lua project config
return {
go = "go", -- set to go1.18beta1 if necessary
goimports = "gopls", -- if set to 'gopls' will use gopls format, also goimports
gofmt = "gofumpt", -- if set to gopls will use gopls format
null_ls_document_formatting_disable = true
}
This will override your global go.nvim
setup
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 provides a better snippet support for go. Please check snippets for all languages and snippets for go
For a video demo, please check this: go.nvim new features work through
If you are not familiar with luasnip, please checkout LuaSnip Tutorial and TJ's Introduction to LuaSnip
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.
[!IMPORTANT] I will integrate more gopls functions into go.nvim, please make sure you have the latest version installed Also, enable gopls experimental features if it is configure somewhere other than go.nvim Otherwise, set
lsp_cfg
totrue
in your go.nvim setup to enable gopls setup in go.nvim
gopls = {
capabilities = {
textDocument = {
completion = {
completionItem = {
commitCharactersSupport = true,
deprecatedSupport = true,
documentationFormat = { 'markdown', 'plaintext' },
preselectSupport = true,
insertReplaceSupport = true,
labelDetailsSupport = true,
snippetSupport = true,
resolveSupport = {
properties = {
'documentation',
'details',
'additionalTextEdits',
},
},
},
contextSupport = true,
dynamicRegistration = true,
},
},
},
filetypes = { 'go', 'gomod', 'gosum', 'gotmpl', 'gohtmltmpl', 'gotexttmpl' },
message_level = vim.lsp.protocol.MessageType.Error,
cmd = {
'gopls', -- share the gopls instance if there is one already
'-remote.debug=:0',
},
root_dir = function(fname)
local has_lsp, lspconfig = pcall(require, 'lspconfig')
if has_lsp then
local util = lspconfig.util
return util.root_pattern('go.work', 'go.mod')(fname)
or util.root_pattern('.git')(fname)
or util.path.dirname(fname)
end
end,
flags = { allow_incremental_sync = true, debounce_text_changes = 500 },
settings = {
gopls = {
-- more settings: https://github.com/golang/tools/blob/master/gopls/doc/settings.md
-- not supported
analyses = {
unreachable = true,
nilness = true,
unusedparams = true,
useany = true,
unusedwrite = true,
ST1003 = true,
undeclaredname = true,
fillreturns = true,
nonewvars = true,
fieldalignment = false,
shadow = true,
},
codelenses = {
generate = true, -- show the `go generate` lens.
gc_details = true, -- Show a code lens toggling the display of gc's choices.
test = true,
tidy = true,
vendor = true,
regenerate_cgo = true,
upgrade_dependency = true,
},
hints = {
assignVariableTypes = true,
compositeLiteralFields = true,
compositeLiteralTypes = true,
constantValues = true,
functionTypeParameters = true,
parameterNames = true,
rangeVariableTypes = true,
},
usePlaceholders = true,
completeUnimported = true,
staticcheck = true,
matcher = 'Fuzzy',
diagnosticsDelay = '500ms',
symbolMatcher = 'fuzzy',
semanticTokens = true,
noSemanticTokens = true, -- disable semantic string tokens so we can use treesitter highlight injection
['local'] = get_current_gomod(),
gofumpt = _GO_NVIM_CFG.lsp_gofumpt or false, -- true|false, -- turn on for new repos, gofmpt is good but also create code turmoils
buildFlags = { '-tags', 'integration' },
},
},
-- NOTE: it is important to add handler to formatting handlers
-- the async formatter will call these handlers when gopls responed
-- without these handlers, the file will not be saved
handlers = {
[range_format] = function(...)
vim.lsp.handlers[range_format](...)
if vfn.getbufinfo('%')[1].changed == 1 then
vim.cmd('noautocmd write')
end
end,
[formatting] = function(...)
vim.lsp.handlers[formatting](...)
if vfn.getbufinfo('%')[1].changed == 1 then
vim.cmd('noautocmd write')
end
end,
},
}
require("mason").setup()
require("mason-lspconfig").setup()
require('lspconfig').gopls.setup({
-- your gopls setup
})
If you want to use gopls setup provided by go.nvim
-- setup your go.nvim
-- make sure lsp_cfg is disabled
require("mason").setup()
require("mason-lspconfig").setup()
require('go').setup{
lsp_cfg = false
-- other setups...
}
local cfg = require'go.lsp'.config() -- config() return the go.nvim gopls setup
require('lspconfig').gopls.setup(cfg)
You can install tree-sitter parsers for gomod, gosum and gotmpl
:TSInstall gomod gosum gotmpl
The plugin injects the tmpl to html syntax so you should see this:
To get highlighting for other templated languages check out the docs of tree-sitter-go-template.
gotest
LSP diagnostic source for null-lsgolangci_lint
A async version of golangci-lint null-ls lintgotest_action
LSP test code action for null-lsGotest allow you run go test <package>
when you save your go file and add diagnostics to nvim
local null_ls = require("null-ls")
local sources = {
null_ls.builtins.diagnostics.revive,
null_ls.builtins.formatting.golines.with({
extra_args = {
"--max-len=180",
"--base-formatter=gofumpt",
},
})
}
-- for go.nvim
local gotest = require("go.null_ls").gotest()
local gotest_codeaction = require("go.null_ls").gotest_action()
local golangci_lint = require("go.null_ls").golangci_lint()
table.insert(sources, gotest)
table.insert(sources, golangci_lint)
table.insert(sources, gotest_codeaction)
null_ls.setup({ sources = sources, debounce = 1000, default_timeout = 5000 })
-- alternatively
null_ls.register(gotest)
You will see the failed tests flagged
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({
goimports = 'gopls', -- if set to 'gopls' will use golsp format
gofmt = 'gopls', -- if set to gopls will use golsp format
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: What is Toggle gc annotation details
A: This is a codelens message, please run codelens GoCodeLenAct
and get more info