Go to file
2021-12-16 12:34:06 +11:00
.github/workflows bug fix for github workflow 2021-12-02 17:44:22 +11:00
doc bugfix #54 quickfix position 2021-12-15 03:59:14 +11:00
lua bugfix #56 escape '<space>' and '-' 2021-12-16 12:34:06 +11:00
plugin doc update for nvim-lsp-installer 2021-12-16 10:37:02 +11:00
.gitignore remove gofumport from go.nvim #18 2021-09-25 12:44:52 +10:00
LICENSE Create LICENSE 2021-05-10 14:13:09 +10:00
Makefile merge github action/workflow 2021-08-24 14:53:37 +10:00
README.md doc update for nvim-lsp-installer 2021-12-16 10:37:02 +11:00
samplevimrc.vim Update sample vimrc 2021-11-24 20:47:21 +11:00
selene.toml stylua/selene gotest update 2021-12-11 08:45:42 +11:00
stylua.toml stylua/selene gotest update 2021-12-11 08:45:42 +11:00
TODO.md init commit 2021-03-10 23:15:06 +11:00
vim.toml stylua/selene gotest update 2021-12-11 08:45:42 +11:00

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
  • 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 Dap UI
  • Unit test: generate unit test framework with gotests. Run test with richgo/ginkgo/go test
  • 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) Test with ginkgo, richgo inside floaterm (to enable floaterm, guihua.lua has to be installed)

install

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 '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.

autocmd BufWritePre *.go :silent! lua require('go.format').gofmt()

To startup/setup the plugin

require('go').setup()

Screenshots

Add comments

auto comments

Add/Remove tags

auto tag

GoTest in floating term

gotest

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.

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()  -- format only
require("go.format").goimport()  -- goimport + gofmt

To config format on save, in your init.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)
package foo

import "io"

func Foo() (io.Reader, error) { // the cursor on this line to add if err statement
}

auto struct

Textobject

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 binaries install and update

The following go binaries are used in go.nvim (depends on your setup):

  • gofumpt
  • golines
  • goimports
  • gorename
  • gomodifytags
  • gotests
  • iferr
  • impl
  • fillstruct
  • fixplurals
  • 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

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 -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
GoVet go vet
GoCoverage go test -coverprofile

Show test coverage:

GoTestCoverage

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
GoAddExpTest Add tests for exported funcs
GoAddAllTest Add tests for all funcs

Note: For GoTestXXX You can add avaliable arguments e.g. GoTest -tags=integration ./internal/web -bench=. -count=1 -

GoDoc

Show go doc for api in neovim floating window. e.g. GoDoc fmt.Println

Godoc

Modifytags

Modify struct tags by 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 restart restart debug session for go test file
GoDebug nearest start debug session for nearest go test function
GoDebug file same as GoDebug
GoDebug stop stop debug session and unmap debug keymap
GoBreakToggle
GoBreakCondition conditional break
GoDbgStop Stop debug session, same as GoDebug stop

Switch 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

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{}

GoModeTidy

LSP

LSP supported by 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.

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 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

 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 on all the commands provided

configuration

Configure from lua suggested, The default setup:

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","/var/log/gopls.log" }
  fillstruct = 'gopls', -- can be nil (use fillstruct, slower) and gopls
  lsp_diag_hdlr = true, -- hook lsp diag handler
  dap_debug = true, -- set to false to disable dap
  textobjects = true, -- enable default text jobects through treesittter-text-objects
  test_runner = 'go', -- richgo, go test, richgo, dlv, ginkgo
  run_in_floaterm = false, -- set to true to run in float window.
  --float term recommand if you use richgo/ginkgo with terminal color
  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
  build_tags = "tag1,tag2" -- set default build tags
})

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()")

Text object

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"
        }
      }
    }
  }

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 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.

Integrate with nvim-lsp-installer

(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


Sample vimrc

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, 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)