410 lines
6.9 KiB
Markdown
410 lines
6.9 KiB
Markdown
# 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:
|
|
```bash
|
|
nvim /path/to/project
|
|
```
|
|
|
|
3. gopls will auto-start when you open a `.go` file
|
|
|
|
### Module Initialization
|
|
|
|
If starting fresh:
|
|
```bash
|
|
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:
|
|
|
|
```go
|
|
// 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:
|
|
|
|
```go
|
|
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:
|
|
```bash
|
|
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`:
|
|
|
|
```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:
|
|
|
|
```go
|
|
type User struct {
|
|
Name string // <leader>ca → "Add json tag"
|
|
Email string
|
|
}
|
|
```
|
|
|
|
Result:
|
|
```go
|
|
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):
|
|
```go
|
|
if err != nil {
|
|
return err
|
|
}
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
### Standard Layout
|
|
|
|
```
|
|
myproject/
|
|
├── cmd/
|
|
│ └── myapp/
|
|
│ └── main.go
|
|
├── internal/
|
|
│ └── pkg/
|
|
├── pkg/
|
|
│ └── public/
|
|
├── go.mod
|
|
└── go.sum
|
|
```
|
|
|
|
### go.mod
|
|
|
|
```go
|
|
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:
|
|
```bash
|
|
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:
|
|
```bash
|
|
go install golang.org/x/tools/cmd/goimports@latest
|
|
```
|
|
|
|
### Debugger Issues
|
|
|
|
1. Verify delve is installed:
|
|
```bash
|
|
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`:
|
|
```json
|
|
{
|
|
"build.directoryFilters": ["-vendor", "-node_modules"]
|
|
}
|
|
```
|