Skip to main content
This configuration provides comprehensive Go support using gopls (the official Go language server), along with formatters, linters, and Go-specific plugins.

Language server

The gopls language server is enabled:
-- From lua/plugins/lsp/init.lua:44
vim.lsp.enable({
  "gopls", -- Go (you might see 2 processes, one is likely telemetry)
})
You may see two gopls processes running. One is the main server, the other is typically telemetry. Check with pgrep -a gopls.

gopls configuration

Custom configuration is in after/lsp/gopls.lua:

Explicit command

-- From after/lsp/gopls.lua:2
return {
  cmd = { "gopls" },
}
This avoids duplicate processes from PATH resolution issues.

Code lenses

Most code lenses are disabled to reduce CPU usage:
-- From after/lsp/gopls.lua:6
codelenses = {
  generate = false,
  gc_details = false, -- Very CPU intensive
  test = false,
  tidy = false,
  vendor = false, -- Rarely used
  regenerate_cgo = false,
  upgrade_dependency = false, -- Triggers on-demand anyway
  run_govulncheck = false, -- Very CPU intensive, run manually
}
Code lenses like gc_details and run_govulncheck are CPU-intensive. They’re disabled by default but can be run manually when needed.

Inlay hints

-- From after/lsp/gopls.lua:16
hints = {
  assignVariableTypes = true,
  compositeLiteralFields = true,
  compositeLiteralTypes = true,
  constantValues = true,
  functionTypeParameters = true,
  parameterNames = true,
  rangeVariableTypes = true,
}
Inlay hints show:
  • Variable types
  • Composite literal fields and types
  • Constant values
  • Function type parameters
  • Parameter names
  • Range variable types
Toggle hints with <leader>ih.

Analyses

-- From after/lsp/gopls.lua:25
analyses = {
  nilness = true,
  unusedparams = true,
  unusedwrite = true,
  ST1003 = false, -- Disable naming convention checks (Ts -> TS, url -> URL)
  undeclaredname = true,
  fillreturns = true,
  nonewvars = true,
  useany = false,
  unreachable = false,
  unusedresult = false,
  simplifyslice = false,
  simplifyrange = false,
  simplifycompositelit = false,
  shadow = false,
  printf = false,
  structtag = true,
  modernize = false,
  stylecheck = false,
  gocritic = false,
  deprecated = false,
}
Naming convention checks (ST1003) are disabled because they can be noisy (e.g., suggesting url should be URL).

Performance settings

-- From after/lsp/gopls.lua:47
usePlaceholders = false,
completeUnimported = true,
staticcheck = false, -- Disable to save CPU/memory (use <leader>ll for linting)
matcher = "Fuzzy",
diagnosticsDelay = "1000ms", -- Increase delay to reduce CPU spikes
symbolMatcher = "fuzzy",
Why staticcheck is disabled: Staticcheck analysis is CPU and memory intensive. Instead, use golangci-lint manually with <leader>ll.

Directory filters

-- From after/lsp/gopls.lua:53
directoryFilters = {
  "-.git",
  "-.vscode",
  "-.idea",
  "-.vscode-test",
  "-node_modules",
}
Excludes common directories from workspace scanning.

Formatting

-- From after/lsp/gopls.lua:61
semanticTokens = true,
gofumpt = true,
gopls uses gofumpt for stricter formatting.

Formatters

Go files use both goimports and gofumpt:
-- From lua/plugins/formatter.lua:71
go = { "goimports", "gofumpt" }
Both formatters run in sequence:
  1. goimports - Organize and add missing imports
  2. gofumpt - Stricter formatting than standard gofmt
Installation:
go install golang.org/x/tools/cmd/goimports@latest
go install mvdan.cc/gofumpt@latest

Linting

Go linting uses golangci-lint:
-- From lua/plugins/linting.lua:60
go = { "golangcilint" }
Trigger with <leader>ll.

golangci-lint configuration

The linter is configured to find project-specific config files:
-- From lua/plugins/linting.lua:36
function()
  -- Find .golangci.yaml or .golangci.yml in project root
  local config_patterns =
    { ".golangci.yaml", ".golangci.yml", ".golangci.toml", ".golangci.json" }
  for _, pattern in ipairs(config_patterns) do
    local config_file = vim.fs.find(pattern, {
      upward = true,
      path = vim.fn.expand("%:p:h"),
      stop = vim.env.HOME,
    })[1]
    if config_file then
      return "-c=" .. config_file
    end
  end
  return nil
end
It automatically finds:
  • .golangci.yaml
  • .golangci.yml
  • .golangci.toml
  • .golangci.json
Installation:
brew install golangci-lint
# or see https://golangci-lint.run/welcome/install/

go.nvim plugin

The go.nvim plugin provides Go-specific features:
-- From lua/plugins/languages/lessgo.lua:2
{
  "ray-x/go.nvim",
  enabled = true,
  dependencies = {
    "ray-x/guihua.lua",
    "nvim-treesitter/nvim-treesitter",
    "neovim/nvim-lspconfig",
    "saghen/blink.cmp",
  },
  ft = { "go", "gomod" },
}

go.nvim configuration

The plugin is configured to work alongside the main LSP setup:
-- From lua/plugins/languages/lessgo.lua:12
require("go").setup({
  lsp_keymaps = false,
  lsp_codelens = false,
  lsp_on_attach = false,
  lsp_cfg = false, -- Disable go.nvim LSP, using lspconfig instead
  lsp_gofumpt = false,
  lsp_inlay_hints = {
    enable = false,
  },
})
go.nvim’s LSP features are disabled because we use the main LSP configuration. go.nvim provides additional Go tooling commands.

golangci-lint integration

-- From lua/plugins/languages/lessgo.lua:21
golangci_lint = {
  default = "none", -- Disabled, using nvim-lint instead
  flags = {
    "--output.json.path=stdout",
    "--issues-exit-code=0",
    "--show-stats=false",
    "--allow-parallel-runners",
  },
  severity = vim.diagnostic.severity.INFO,
}

Diagnostics

-- From lua/plugins/languages/lessgo.lua:38
diagnostic = false,
lsp_diag_virtual_text = false,
lsp_diag_signs = false,
lsp_diag_update_in_insert = false,
Diagnostics are handled by tiny-diagnostics plugin.

no-go.nvim plugin

Enhances error handling visualization:
-- From lua/plugins/languages/lessgo.lua:50
{
  "snehilshah/no-go.nvim",
  branch = "no-no-go",
  enabled = true,
  ft = "go",
  opts = {
    identifiers = { "err", "error" },
    import_virtual_text = {
      prefix = " ",
      suffix = " imports ",
    },
    reveal_on_cursor = false,
    keys = {
      down = "j",
      up = "k",
      toggle = "<M-o>",
    },
  },
}
Features:
  • Conceals if err != nil blocks for cleaner reading
  • Shows import counts as virtual text
  • Toggle error block visibility with <M-o>

Common workflows

Running tests

Use go.nvim commands:
:GoTest         " Run tests in current file
:GoTestFunc     " Run test under cursor
:GoTestFile     " Run all tests in file
:GoTestPkg      " Run all tests in package

Adding imports

  1. Type the package name
  2. Save the file (goimports runs on save)
  3. Missing imports are automatically added

Filling struct

  1. Place cursor in empty struct literal
  2. Press ga or <leader>ca
  3. Select “Fill struct”

Generating code

Use gopls code actions:
  1. Press ga or <leader>ca
  2. Select from:
    • “Generate constructor”
    • “Generate method stubs”
    • “Add missing imports”

Viewing documentation

  1. Place cursor on symbol
  2. Press K
Shows documentation from comments and standard library.

Keymaps

All standard LSP keymaps work:
KeymapAction
KHover documentation
<C-k>Signature help
gdGo to definition
grFind references
giGo to implementation
gtGo to type definition
gaCode action
<leader>rnRename symbol
<leader>ihToggle inlay hints
<leader>fbFormat buffer
<leader>llLint with golangci-lint
<M-o>Toggle error blocks (no-go.nvim)
]d / [dNext/prev diagnostic

Installation summary

1

Install gopls

go install golang.org/x/tools/gopls@latest
2

Install formatters

go install golang.org/x/tools/cmd/goimports@latest
go install mvdan.cc/gofumpt@latest
3

Install linter

brew install golangci-lint
4

Verify installation

gopls version
goimports -h
gofumpt --version
golangci-lint --version

Troubleshooting

gopls not starting

  1. Check gopls is installed: gopls version
  2. Run :LspInfoCustom to see client status
  3. Check logs: :LspLog
  4. Look for duplicate processes: pgrep -a gopls

Slow performance

  1. Check CPU usage: Some analyses are disabled in config
  2. Increase diagnostics delay in after/lsp/gopls.lua:51
  3. Consider excluding more directories in directoryFilters
  4. Disable inlay hints: <leader>ih

Imports not organizing

  1. Check goimports is installed: goimports -h
  2. Run :ConformInfo to see formatter status
  3. Manually format: <leader>fb

golangci-lint not working

  1. Check it’s installed: golangci-lint --version
  2. Trigger manually: <leader>ll
  3. Check for config file in project root
  4. Look at linter output in :messages

Next steps

TypeScript

TypeScript/JavaScript configuration

Lua

Lua language configuration

Formatting

Learn more about formatters

Linting

Learn more about linters

Build docs developers (and LLMs) love