Neovim で Go の開発環境を作る

vimgolanglua

LSP クライアントの設定 (nvim-lspconfig)

Go の LSP サーバー gopls をインストールする。

$ go install golang.org/x/tools/gopls@latest
$ gopls version
golang.org/x/tools/gopls v0.16.2

Neovim に LSP クライアントが built-in されているので nvim-lspconfig にある gopls の設定を渡すだけではたらく。

保存時にフォーマットがかかるようにする

vim.api.nvim_create_autocmd("BufWritePre", {
  pattern = "*.go",
  callback = function()
    local params = vim.lsp.util.make_range_params()
    params.context = {only = {"source.organizeImports"}}
    -- buf_request_sync defaults to a 1000ms timeout. Depending on your
    -- machine and codebase, you may want longer. Add an additional
    -- argument after params if you find that you have to write the file
    -- twice for changes to be saved.
    -- E.g., vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 3000)
    local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params)
    for cid, res in pairs(result or {}) do
      for _, r in pairs(res.result or {}) do
        if r.edit then
          local enc = (vim.lsp.get_client_by_id(cid) or {}).offset_encoding or "utf-16"
          vim.lsp.util.apply_workspace_edit(r.edit, enc)
        end
      end
    end
    vim.lsp.buf.format({async = false})
  end
})

定義ジャンプなどの keymap を設定する。C-o で戻って C-i で進む。

vim.keymap.set('n', 'K', vim.lsp.buf.hover, { desc = 'Show hover' })
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { desc = 'Go to definition' })
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, { desc = 'Go to implementation'})
vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts, { desc = 'Show references' })

補完 (nvim-cmp)

LSP 単体でも C-x C-o で補完できるが、nvim-cmp を用いると自動で補完され godocs が併せて表示される。また、パスなど他のソースによる補完も効く。

Neovim のプラグインマネージャー lazy.nvim をインストールしてプラグインを入れる - sambaiz-net

{
	{
    'hrsh7th/nvim-cmp',
    config = function()
      local cmp = require('cmp')
      cmp.setup({
        mapping = cmp.mapping.preset.cmdline(),
        sources = {
          { name = 'nvim_lsp' },
          { name = 'buffer' },
          { name = 'path' },
        },
        mapping = cmp.mapping.preset.insert({
          ['<C-b>'] = cmp.mapping.scroll_docs(-4),
          ['<C-f>'] = cmp.mapping.scroll_docs(4),
          ['<C-Space>'] = cmp.mapping.complete(),
          ['<C-e>'] = cmp.mapping.abort(),
          ['<CR>'] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
        }),
      })
    end
  },
  {
    'hrsh7th/cmp-nvim-lsp',
    dependencies = {
      'neovim/nvim-lspconfig',
    },
    config = function()
      local lspconfig = require("lspconfig")
      local capabilities = require('cmp_nvim_lsp').default_capabilities()
      lspconfig.gopls.setup({
          capabilities = capabilities,
      })
    end
  },
  {
    'hrsh7th/cmp-buffer',
  },
  {
    'hrsh7th/cmp-path',
  },
  {
    'hrsh7th/cmp-cmdline',
  }
}

syntax highlight (nvim-treesitter)

コードをパースして syntax tree を作る tree-sitter を使うための nvim-treesitter を入れると、syntax highlight が完全になる。

{
    'nvim-treesitter/nvim-treesitter', tag = 'v0.9.3',
    config = function()
      require('nvim-treesitter.configs').setup({
        ensure_installed = { 'go', 'lua', 'typescript', 'javascript', 'json', 'yaml', 'html', 'css', 'bash', 'python', 'markdown', 'scala' },
        highlight = {
          enable = true,
        },
      })
    end
  }

テストの実行とカバレッジの可視化 (neotest, nvim-coverage)

neotest と Go の runner neotest-go をインストールしてテストを実行できるようにする。

{
  'nvim-neotest/neotest', tag = 'v5.6.1',
  dependencies = {
      'nvim-neotest/nvim-nio',
      'nvim-lua/plenary.nvim',
      'antoinemadec/FixCursorHold.nvim',
      'nvim-treesitter/nvim-treesitter',
      'nvim-neotest/neotest-go',
  },
  config = function()
      require('neotest').setup({
        adapters = {
          require('neotest-go')({
            args = { '-coverprofile=coverage.out' },
          })
        },
      })
  end,
}

vim.keymap.set('n', '<Leader>tt', ':lua require("neotest").run.run()<CR>', { desc = 'Run test' })

標準のターミナルではステータスのアイコンが文字化けしたが WezTerm では表示された。

出力した coverage profiles を nvim-coverage で可視化できる。

{
	'andythigpen/nvim-coverage',
	dependencies = {
	    'nvim-lua/plenary.nvim',
	},
	config = function()
	    require('coverage').setup({
	      lang = {
	        go = {
	          coverage_file = vim.fn.getcwd() .. '/coverage.out',
	        }
	      }
	    })
	end,
}

:Coverage で表示されるバーの色でテスト済かどうかが分かる。