Files
nvim/docs/guides/go-workflow.md
Morten Olsen b3b70bceeb Improved flow
2026-01-26 23:04:14 +01:00

6.9 KiB

Go Development Workflow

This guide covers Go development in Neovim with gopls, testing, and debugging.

Overview

Your config includes:

  • LSP: gopls (via LazyVim go extra)
  • Linting: golangci-lint
  • Testing: go test via neotest
  • Debugging: delve via DAP
  • Formatting: gofmt/goimports

Getting Started

Project Setup

  1. Ensure Go is installed and GOPATH is set

  2. Open your Go project:

    nvim /path/to/project
    
  3. gopls will auto-start when you open a .go file

Module Initialization

If starting fresh:

go mod init myproject

LSP Features

Navigation

Key Action
gd Go to definition
gr Go to references
gI Go to implementation
gy Go to type definition
K Hover documentation

Code Actions

Key Action
<leader>ca Code actions
<leader>cr Rename symbol
<leader>cf Format (gofmt)

Go-Specific Actions

Press <leader>ca for:

  • Add import
  • Organize imports
  • Extract function
  • Extract variable
  • Generate interface stubs
  • Add struct tags
  • Fill struct

Diagnostics

Key Action
<leader>cd Line diagnostics
]d / [d Next/prev diagnostic
<leader>xx Toggle trouble

Testing

Running Tests

Key Action
<leader>tt Run test at cursor
<leader>tf Run file tests
<leader>ta Run all tests
<leader>tl Re-run last test

Test File Convention

Go tests live in *_test.go files:

// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("expected 5, got %d", result)
    }
}

Test Output

<leader>ts    Toggle test summary
<leader>to    Show test output  
<leader>tO    Toggle output panel
]t / [t       Jump to failed tests

Table-Driven Tests

Common Go pattern - cursor on any test case runs it:

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive", 2, 3, 5},
        {"negative", -1, -1, -2},
        {"zero", 0, 0, 0},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // <leader>tt here runs this specific case
            if got := Add(tt.a, tt.b); got != tt.expected {
                t.Errorf("got %d, want %d", got, tt.expected)
            }
        })
    }
}

Debugging Tests

<leader>td    Debug test at cursor

Debugging with Delve

Prerequisites

Install delve:

go install github.com/go-delve/delve/cmd/dlv@latest

Setting Breakpoints

Key Action
<leader>db Toggle breakpoint
<leader>dB Conditional breakpoint

Running Debugger

Key Action
<leader>dc Continue/Start
<leader>di Step into
<leader>do Step over
<leader>dO Step out
<leader>dt Terminate

DAP UI

<leader>du    Toggle DAP UI
<leader>de    Evaluate expression

launch.json

For custom debug configs, create .vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Package",
      "type": "go",
      "request": "launch",
      "mode": "debug",
      "program": "${fileDirname}"
    },
    {
      "name": "Debug Main",
      "type": "go",
      "request": "launch",
      "mode": "debug",
      "program": "${workspaceFolder}/cmd/myapp"
    },
    {
      "name": "Attach to Process",
      "type": "go",
      "request": "attach",
      "mode": "local",
      "processId": 0
    }
  ]
}

Code Quality

Formatting

Go code is auto-formatted on save. Manual format:

<leader>cf    Format with gofmt

Imports

goimports manages imports automatically:

  • Adds missing imports on save
  • Removes unused imports
  • Sorts imports

Linting

golangci-lint runs automatically. View issues:

<leader>xx    Toggle trouble (all diagnostics)

Struct Tags

Generate/modify struct tags via code actions:

type User struct {
    Name  string  // <leader>ca → "Add json tag"
    Email string
}

Result:

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

AI Assistance

Key Action
<leader>ka Ask opencode
<leader>ke Explain code
<leader>kR Refactor selection
<leader>kt Generate tests
<leader>kd Generate comments

Generate Tests

  1. Select function in visual mode
  2. <leader>kt
  3. Review generated table-driven tests

Common Workflows

Starting New Feature

  1. Create/open .go file
  2. <leader>H - Mark with harpoon
  3. Write code, LSP handles imports
  4. <leader>tt - Test as you go

Implementing Interface

  1. Write struct
  2. <leader>ca → "Generate interface stubs"
  3. Select interface to implement
  4. Fill in method bodies

Debugging

  1. <leader>db - Set breakpoint
  2. <leader>dc - Start debugger
  3. Use step commands to navigate
  4. <leader>de - Evaluate expressions
  5. <leader>dt - Terminate when done

Error Handling

Common Go pattern - AI can help:

  1. Write function that returns error
  2. <leader>ka - "Add proper error handling"
  3. Or use snippet (if configured):
    if err != nil {
        return err
    }
    

Project Structure

Standard Layout

myproject/
├── cmd/
│   └── myapp/
│       └── main.go
├── internal/
│   └── pkg/
├── pkg/
│   └── public/
├── go.mod
└── go.sum

go.mod

module github.com/user/myproject

go 1.21

require (
    github.com/some/dependency v1.0.0
)

Tips

Quick Run

:!go run .        Run current package
:!go run main.go  Run specific file

Or use executor:

<leader>Brs   Set command: go run .
<leader>Brr   Run it

Build

:!go build .      Build current package

Generate

:!go generate ./...   Run go generate

Module Commands

:!go mod tidy     Clean up go.mod
:!go mod download Download dependencies

Documentation

  • K on any symbol shows docs
  • Works with standard library
  • Shows function signatures

Troubleshooting

gopls Not Starting

  1. :LspInfo - Check status
  2. Verify gopls is installed:
    go install golang.org/x/tools/gopls@latest
    
  3. Check go.mod exists in project root

Imports Not Working

  1. Check gopls is running: :LspInfo
  2. Verify goimports is installed:
    go install golang.org/x/tools/cmd/goimports@latest
    

Debugger Issues

  1. Verify delve is installed:
    go install github.com/go-delve/delve/cmd/dlv@latest
    
  2. Check :DapShowLog for errors
  3. Ensure code is compiled (not just source)

Slow LSP

  1. Check for large generated files
  2. Add to .gopls.json:
    {
      "build.directoryFilters": ["-vendor", "-node_modules"]
    }