Skip to content

Instantly share code, notes, and snippets.

@bzon
Created February 22, 2026 10:44
Show Gist options
  • Select an option

  • Save bzon/8fb832deceaac78f1d972f00bb9d6f0e to your computer and use it in GitHub Desktop.

Select an option

Save bzon/8fb832deceaac78f1d972f00bb9d6f0e to your computer and use it in GitHub Desktop.
LazyVim for Go development starter

Getting LazyVim set up for senior-level Go development is less about piling on dozens of random plugins and more about orchestrating the "Big Three": LSP (gopls), Linting (golangci-lint), and Debugging (delve).


Senior Go Developer LazyVim Setup Guide

This guide transforms a stock LazyVim installation into a production-ready Go IDE.

1. Prerequisites

Before touching Neovim, ensure the industry-standard binaries are on your $PATH:

# Install Delve for debugging and Golangci-lint for senior-level linting
go install github.com/go-delve/delve/cmd/dlv@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Install gofumpt for stricter formatting (optional but recommended)
go install mvdan.cc/gofumpt@latest

2. Enable the Go "Extra"

LazyVim has a pre-configured Go module. This handles gopls, nvim-dap-go, and neotest-go automatically.

  1. Open Neovim: nvim
  2. Type :LazyExtras
  3. Find lang.go and press x to enable it.
  4. Restart Neovim.

3. The Custom Configuration (lua/plugins/go.lua)

For a "Senior" setup, we need more than defaults. Create a new file at ~/.config/nvim/lua/plugins/go.lua and paste the following. This adds gomodifytags (for JSON tags) and impl (for implementing interfaces).

return {
  -- Core LSP & Tooling Configuration
  {
    "neovim/nvim-lspconfig",
    opts = {
      servers = {
        gopls = {
          settings = {
            gopls = {
              gofumpt = true,
              codelenses = {
                gc_details = false,
                generate = true,
                regenerate_cgo = true,
                run_govulncheck = true,
                test = true,
                tidy = true,
                upgrade_dependency = true,
                vendor = true,
              },
              hints = {
                assignVariableTypes = true,
                compositeLiteralFields = true,
                compositeLiteralTypes = true,
                constantValues = true,
                functionTypeParameters = true,
                parameterNames = true,
                rangeVariableTypes = true,
              },
              analyses = {
                fieldalignment = true,
                nilness = true,
                unusedparams = true,
                unusedwrite = true,
                useany = true,
              },
              usePlaceholders = true,
              completeUnimported = true,
              staticcheck = true,
              directoryFilters = { "-.git", "-.vscode", "-.idea", "-.vscode-test", "-node_modules" },
              semanticTokens = true,
            },
          },
        },
      },
      setup = {
        gopls = function(_, opts)
          -- workaround for gopls not supporting semanticTokensProvider
          -- https://github.com/golang/go/issues/54531#issuecomment-1464982242
          LazyVim.lsp.on_attach(function(client, _)
            if client.name == "gopls" then
              if not client.server_capabilities.semanticTokensProvider then
                local semantic = client.config.capabilities.textDocument.semanticTokens
                client.server_capabilities.semanticTokensProvider = {
                  full = true,
                  legend = {
                    tokenTypes = semantic.tokenTypes,
                    tokenModifiers = semantic.tokenModifiers,
                  },
                  range = true,
                }
              end
            end
          end)
        end,
      },
    },
  },

  -- Add specific Go tools to Mason
  {
    "williamboman/mason.nvim",
    opts = {
      ensure_installed = {
        "goimports",
        "gofumpt",
        "gomodifytags",
        "impl",
        "delve",
      },
    },
  },
}

4. Keybindings for the "Senior" Workflow

Add these to ~/.config/nvim/lua/config/keymaps.lua to speed up common tasks like adding JSON tags or generating interface stubs.

local map = vim.keymap.set

-- Go specific mappings
map("n", "<leader>gsj", "<cmd>GoTagAdd json<cr>", { desc = "Add JSON struct tags" })
map("n", "<leader>gsy", "<cmd>GoTagAdd yaml<cr>", { desc = "Add YAML struct tags" })
map("n", "<leader>gie", "<cmd>GoIfErr<cr>", { desc = "Generate if err != nil" })

5. Testing the Setup

Don't just trust the config; verify the pipeline.

Test 1: LSP & Formatting

  • Create a main.go.
  • Write a messy function and save. It should auto-format via gofumpt and organize imports via goimports.
  • Check for Inlay Hints (e.g., parameter names showing up in gray text).

Test 2: The Debugger (DAP)

  • Set a breakpoint on a line using <leader>db.
  • Start the debugger with <leader>dc.
  • Requirement: Ensure you have a go.mod file in the directory, or Delve will be cranky.

Test 3: The Test Runner (Neotest)

  • Open a _test.go file.
  • Hover over a test function and press <leader>tr (Test Run).
  • Press <leader>ts to see the summary panel on the right.

Final Tip

Since you're on a fresh install, run :checkhealth frequently in the first hour. It will tell you exactly which binary (like gopls or dlv) Neovim is failing to find.

Would you like me to provide a snippet to configure golangci-lint to run automatically on every save?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment