When working with Go in Neovim, you want both gopls (the official Go LSP) and golangci-lint. gopls provides language features like autocomplete and go-to-definition, while golangci-lint-langserver acts as an LSP wrapper around golangci-lint to run multiple linters that catch issues gopls doesn’t.

TL;DR: Use Mason to install both gopls and golangci-lint-langserver. Configure them with nvim-lspconfig. They work together - gopls for language features, golangci-lint for comprehensive linting.

Install Neovim plugins

Using vim-plug, add these to your init.vim:

Plug 'williamboman/mason.nvim'
Plug 'williamboman/mason-lspconfig.nvim'
Plug 'neovim/nvim-lspconfig'

Run :PlugInstall to install them.

Install language servers with Mason

Open Neovim and run:

:MasonInstall gopls golangci-lint-langserver

Mason handles the installation and PATH management for you.

Configure both language servers

In your Neovim config (init.lua or in a separate lua file):

require("mason").setup()
require("mason-lspconfig").setup({
  ensure_installed = { "gopls", "golangci_lint_ls" }
})

local lspconfig = require('lspconfig')

-- Setup gopls for Go language features
lspconfig.gopls.setup({
  settings = {
    gopls = {
      analyses = {
        unusedparams = true,
      },
      staticcheck = true,
    },
  },
})

-- Setup golangci-lint for comprehensive linting
lspconfig.golangci_lint_ls.setup({
  init_options = {
    command = { "golangci-lint", "run", "--out-format", "json" },
  },
})

Configure golangci-lint rules

Create a .golangci.yml in your project root to customize which linters run:

linters:
  enable:
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - unused

Now when you open a Go file, you get language features from gopls and linting from golangci-lint working together.


Legacy setup (manual installation)

If you’re not using Mason, you can install the tools manually with go install:

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/nametake/golangci-lint-langserver@latest

Then configure nvim-lspconfig directly:

local lspconfig = require('lspconfig')

lspconfig.golangci_lint_ls.setup({
  cmd = {'golangci-lint-langserver'},
  root_dir = lspconfig.util.root_pattern('.git', 'go.mod'),
  init_options = {
    command = { "golangci-lint", "run", "--fast", "--out-format", "json" },
  },
  filetypes = {'go', 'gomod'}
})

When you open a Go file, nvim-lspconfig will dispatch to golangci-lint-langserver for linting. By default, it looks for .golangci.yml in your project root for linting configuration.

Summary

You need both gopls and golangci-lint for a complete Go development experience in Neovim. Use Mason to manage installations and nvim-lspconfig to configure them. gopls handles language features, golangci-lint runs multiple linters. They complement each other and work together seamlessly.