protoc plugin that generates markdown documentation from protobuf files
just build # builds to bin/protoc-gen-mdgo test ./... # run all tests
go test ./internal/registry # run tests for specific package
go test -v ./... # verbose output
go test -run TestName ./... # run single test by name
go test -cover ./... # with coveragego vet ./... # standard go vet
gofmt -s -w . # format code (use -s for simplification)go mod tidy # clean up dependencies
go mod download # download dependenciescmd/protoc-gen-md/ - main entry point for protoc plugin
internal/
generator/ - core generation logic, namespace handling
mdgen/ - markdown generation (services, messages, fields, enums)
registry/ - protobuf descriptor registry, comment indexing
walk/ - file descriptor traversal utilities
wellknown/ - well-known type documentation URLs
- Standard library first
- Third-party packages second
- Local packages last
- Groups separated by blank lines
- No named imports unless necessary for disambiguation
Example:
import (
"fmt"
"strings"
"github.com/Masterminds/sprig/v3"
"google.golang.org/protobuf/types/descriptorpb"
"github.com/roostmoe/protomd/internal/registry"
)- Use short, descriptive variable names:
mfor message,ffor field,sfor service - Exported types/functions start with capital letter
- Unexported helpers in lowercase
- Acronyms stay uppercase:
URL,HTTP,RPC(notUrl,Http,Rpc) - Registry keys use fully-qualified names with leading dot:
.pkg.Message - Source code paths use int32 slices matching descriptor.proto field numbers
- Prefer
*descriptorpb.DescriptorProtooverinterface{} - Use pointer receivers for methods that modify state
- Document exported types with godoc comments
- Use type aliases sparingly
- Use
fmt.Errorfwith%wfor wrapping errors - Use
panic()for unrecoverable plugin errors (protoc expects error status via stderr) - Check errors immediately after calls
- No naked returns in error paths
Example:
if err := doSomething(); err != nil {
return fmt.Errorf("do something: %w", err)
}- Godoc format for exported symbols
- Leading comments describe why not what
- Use
//for single-line comments - Inline phase comments for complex multi-step logic (see main.go)
- Keep functions focused and small
- Exported functions get godoc comments
- Helper functions grouped logically
- Prefer explicit parameters over global state
- Use
make()with capacity hints for large collections - Append idiom:
slice = append(slice, item) - Clone slices before mutation:
append([]int32{}, path...) - Map iteration order is undefined - sort keys if order matters
- Source code paths are
[]int32indexes into descriptor tree - Path format:
[field_number, index, field_number, index, ...] - Comments keyed by comma-joined path string:
"6,0,2,1" - Get descriptors with
proto.GetName(),proto.GetPackage(), etc. - Check extensions with
proto.HasExtension()beforeproto.GetExtension()
- Templates live in
internal/mdgen/templates/ - Use sprig functions for string manipulation
- Custom template functions in mdgen.go init()
- Execute with
tmpl.ExecuteTemplate(w, "template.md", data)
- Use
generator.addFile(path, content)to add output files - Paths must be relative (no leading
/) - Content is raw string written by protoc
- One namespace = one index.md + service/method .md files
- Registry built once from CodeGeneratorRequest
- Messages/Enums/Services indexed by fully-qualified name
- Comments indexed per-file by source path
- Use registry for cross-references and type lookups
- No tests currently in codebase
- When adding tests: use
_test.gosuffix - Use table-driven tests for multiple cases
- Mock registry for unit tests of mdgen components
- Update
Fieldstruct ininternal/mdgen/field.go - Extend
NewFieldconstructor logic - Update field template if needed
- Add function to
internal/mdgen/mdgen.go - Register in
init()funcMap - Use in template with
{{ functionName args }}
- Import extension from
google.golang.org/genproto - Use
getExtension()orgetMethodExtension()helpers - Add to relevant mdgen struct (Service, Method, Field, etc.)
- Build:
just build - Run protoc with plugin on test .proto files
- Inspect generated .md files
- Check source paths match expected descriptor tree structure
- Requires: 1.25.6 (see mise.toml and go.mod)
- Use mise or install manually
- google.golang.org/protobuf - protobuf descriptor types
- google.golang.org/genproto - googleapis annotations
- github.com/Masterminds/sprig/v3 - template functions