gopls: Navigation, Refactoring & Diagnostics
gopls is the official Go language server.
It turns your editor into a type-aware client: jump to definitions, rename symbols across packages, surface diagnostics, and apply fixes before you run go build.
Summary
gopls loads your module (or go.work workspace), type-checks packages incrementally, and answers LSP requests from the editor.
Navigation commands resolve through the type checker, so they respect imports, build tags, and generics.
Refactoring operations (rename, add import, extract function) edit the AST with type-checked patches rather than blind search-and-replace.
Diagnostics merge compiler errors from go/types with selected analyzers (unused variables, printf mistakes, and more).
Recipe
Quick-reference recipe card - copy-paste ready.
Editor actions (names vary by IDE):
| Action | Typical shortcut | gopls method |
|---|---|---|
| Go to definition | F12 / Cmd+click | textDocument/definition |
| Find references | Shift+F12 | textDocument/references |
| Rename symbol | F2 | textDocument/rename |
| Organize imports | on save | textDocument/codeAction |
| Show diagnostics | automatic | textDocument/publishDiagnostics |
When to reach for this:
- Tracing a symbol from HTTP handler to store implementation across packages.
- Renaming an exported function without missing importers.
- Fixing import cycles and unused imports before commit.
- Reading squiggles that mirror
go buildand vet-style issues early.
Working Example
Consider a small module:
// example.com/demo/internal/greet/greet.go
package greet
func Hello(name string) string {
return "hello, " + name
}// example.com/demo/cmd/app/main.go
package main
import (
"fmt"
"example.com/demo/internal/greet"
)
func main() {
fmt.Println(greet.Hello("world"))
}With gopls running at the module root:
- Go to definition on
greet.Helloinmain.goopensgreet.goon the function body. - Find references on
Hellolistsmain.goand any test files importinggreet. - Rename
HellotoGreetupdates both files and adjusts import usage if the symbol moves packages (gopls may prompt or refuse if it would break visibility). - Organize imports removes unused
fmtif you delete the println, adds missing imports when you reference new packages.
What this demonstrates:
- Cross-package navigation follows import paths, not directory proximity.
- Rename is safe because gopls knows all references from type-checked metadata.
- Diagnostics appear on unused imports and wrong arity calls before
go run.
Deep Dive
How It Works
- gopls maintains a session per workspace root (
go.modorgo.work). - File changes trigger incremental recomputation of package syntax and types.
- LSP clients send URI-based positions; gopls maps them to AST nodes and type objects.
- Results are cached; unchanged packages skip rework.
Navigation Features
| Feature | Behavior |
|---|---|
| Definition | Jumps to declaring identifier; for interfaces, may offer type definition vs implementation |
| Implementation | Lists concrete types implementing an interface method |
| References | All identifier uses in scope of workspace, including tests |
| Document symbol | Outline of functions, types, and constants in current file |
| Workspace symbol | Fuzzy search across package-qualified names |
Refactoring Features
| Feature | Notes |
|---|---|
| Rename | Fails safely if shadowing or cross-module constraints would break |
| Add/import organize | Groups stdlib, third-party, local; applies goimports logic |
| Extract function | Lifts a block to a new function with inferred parameters |
| Generate tests | Scaffolds _test.go with table-driven stubs (editor dependent) |
Diagnostics and Code Actions
gopls publishes diagnostics with severity (error, warning, hint).
Many include code actions: quick fixes for missing imports, suggested types, or trivial refactors.
Analyzer diagnostics approximate go vet and friends; they are not guaranteed to match every CI linter you enable later.
Go Notes
// build tags affect what gopls type-checks
//go:build integration
package myappAlign gopls.build.buildFlags or GOFLAGS with CI so tagged files do not flip between green locally and red in CI.
Gotchas
- Wrong workspace root - Opening a parent folder without
go.modleaves gopls guessing; open the module orgo.workdirectory. Fix: File → Open Folder on module root. - Stale diagnostics after git branch switch - gopls cache may lag. Fix: Restart language server or run "Go: Restart Language Server" in VS Code.
- Rename across modules without workspace - Without
go.workor a published version, cross-module rename may be incomplete. Fix: Addgo.workfor local multi-module edits. - Build tags mismatch - Files behind
//go:buildtags show as excluded or error in editor. Fix: SetbuildFlags: ["-tags=integration"]in gopls settings to match CI. - Generated code noise - Protobuf or
stringeroutputs clutter navigation. Fix: Exclude*.pb.gofrom editor features or mark generated directories ingoplsdirectory filters. - gopls vs golangci-lint gap - Editor may be clean while CI fails on stricter linters. Fix: Run
golangci-lint runlocally with the committed config.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Plain text search (rg) | Exploring comments, strings, or non-Go files | Renaming symbols or finding typed references |
go doc / pkg.go.dev | Reading public API docs offline | Navigating private internal/ across your module |
| IDE without gopls | Unsupported editor | You need accurate Go refactors (prefer gopls-backed clients) |
guru (legacy) | Maintaining very old workflows | Starting new projects (gopls superseded it) |
FAQs
Why does gopls say "packages.Load error" on a fresh clone?
Run go mod download and ensure the folder opened is the module root.
Corporate proxies may block sumdb; set GOPROXY consistently with CI.
Can gopls rename exported identifiers across repositories?
Only for modules in the same workspace or those your editor resolves as dependencies.
It will not rename consumers in repos you do not have open.
What is the difference between go to definition and go to type definition?
Definition jumps to the identifier's declaration site.
Type definition jumps to the underlying type for aliases and interface methods.
Why are imports reorganized on save?
That is usually the editor running goimports or gopls code actions.
Disable format-on-save or organizeImports if it fights your flow.
Does gopls work with generics?
Yes - navigation and rename understand type parameters and instantiated types in Go 1.18+.
How do I see implementation of an interface method?
Use "Go to implementations" (LSP textDocument/implementation) on the interface method or type.
Why is gopls slow in a large monorepo?
Initial indexing type-checks many packages.
Use gopls memory limits, narrow workspace folders, and exclude vendor trees.
Can I run gopls from the command line?
Yes - gopls check and gopls vulncheck exist for diagnostics without an IDE.
Daily editing still benefits from LSP integration.
Do diagnostics block `go build`?
No - they are editor hints.
The compiler remains authoritative; fix red squiggles that mirror build errors first.
Should I commit gopls settings?
Commit editor-agnostic team guidance (build tags, formatters).
Personal UI settings can stay local; share .vscode/settings.json only when the team standardizes on VS Code.
Related
- Go Developer Tooling: LSP, Debugger, and Analyzers - toolchain overview
- Go Tooling Setup Basics - install and configure gopls
- Debugging with Delve - runtime inspection when types are not enough
- staticcheck & golangci-lint in CI - CI analyzers beyond editor subset
- gopls & Analysis Tooling Best Practices - team editor hygiene
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).