📖 4 min read (~ 800 words).

Markdown descriptions

A multi-line swagger:description normally folds its body with the Option B rule: contiguous prose lines up to the first blank line, each trimmed. That’s right for a paragraph of prose, but it destroys markdown — a blank line ends the description, and leading indentation and table pipes are stripped. So a table or a multi-paragraph body never survives the trip into the spec.

Ending the annotation line with a lone | — the YAML literal block-scalar marker — opts the body into verbatim capture instead. Everything below is taken exactly as written — blank lines, indentation, table pipes and --- all preserved — until the next annotation or the end of the comment. It is opt-in per annotation; a plain swagger:description (no |) keeps the Option B behaviour unchanged.

Plain prose vs a verbatim body

These two models carry the same markdown body. The first uses an ordinary annotation; the second adds the | marker:

// Plain uses an ordinary description annotation.
//
// swagger:description
// Option B folds prose up to the first blank line, so only this sentence
// survives — the markdown table below never reaches the spec.
//
// | name | purpose |
// |------|---------|
// | foo  | bars    |
//
// swagger:model Plain
type Plain struct {
	Name string `json:"name"`
}

Full source: docs/examples/shaping/markdowndesc/markdowndesc.go

// Markdown opts into a verbatim body with the literal block marker.
//
// swagger:description |
// The body is captured **verbatim** — pipes, blank lines and all:
//
// | name | purpose |
// |------|---------|
// | foo  | bars    |
//
// - point one
// - point two
//
// swagger:model Markdown
type Markdown struct {
	// Name of the widget.
	//
	// swagger:description |
	// The name must be:
	//
	//   1. unique
	//   2. lowercase
	Name string `json:"name"`
}

Full source: docs/examples/shaping/markdowndesc/markdowndesc.go

The emitted descriptions diverge sharply:

Plain — Option B folds
{
  "description": "Option B folds prose up to the first blank line, so only this sentence\nsurvives — the markdown table below never reaches the spec.",
  "type": "object",
  "title": "Plain uses an ordinary description annotation.",
  "properties": {
    "name": {
      "type": "string",
      "x-go-name": "Name"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/markdowndesc"
}

Full source: docs/examples/shaping/markdowndesc/testdata/plain.json

swagger:description | — verbatim
{
  "description": "The body is captured **verbatim** — pipes, blank lines and all:\n\n| name | purpose |\n|------|---------|\n| foo  | bars    |\n\n- point one\n- point two",
  "type": "object",
  "title": "Markdown opts into a verbatim body with the literal block marker.",
  "properties": {
    "name": {
      "description": "The name must be:\n\n  1. unique\n  2. lowercase",
      "type": "string",
      "x-go-name": "Name"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/markdowndesc"
}

Full source: docs/examples/shaping/markdowndesc/testdata/markdown.json

  • Option B stops at the first blank line. The plain model’s description is just the opening sentence — the table that follows the blank line is dropped entirely (the original go-swagger#3211 grievance).
  • The | body is captured whole. Table leading pipes, the significant blank line, and the bullet list after it all ride through verbatim.
  • The marker never leaks. The trailing |, the swagger:description line itself, and the single godoc // convention space per line are all stripped; interior indentation and trailing whitespace (markdown hard breaks) are kept.
  • The title is unaffected. It still comes from the godoc preamble above the annotation — only the description body becomes verbatim.

It works on a field description just the same — the name property above keeps the indentation of its ordered list ( 1. unique).

Where the block ends

The literal block runs until the next annotation at the start of a line, or the end of the doc comment. In the examples above, the trailing swagger:model Widget line closes the block.

A swagger: token mid-line is ordinary prose and stays in the body — only a line that begins with an annotation terminates. Indentation doesn’t shield such a line, though: the comment prefix and leading whitespace are stripped before the check, so a line-leading swagger: inside an indented markdown code block still ends the block. Keep annotation-looking lines out of the verbatim body, or place them before the | annotation.

Note

This reframes go-swagger#3211: markdown is authored explicitly via swagger:description |, never recovered from ambient godoc prose. A plain doc comment stays plain — godoc and the spec keep their separate conventions.

What’s next