Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

md source pure

Package md provides helper functions for generating Markdown content programmatically.

Overview

Package md provides helper functions for generating Markdown content programmatically.

It includes utilities for text formatting, creating lists, blockquotes, code blocks, links, images, and more.

Highlights: - Supports basic Markdown syntax such as bold, italic, strikethrough, headers, and lists. - Manages multiline support in lists (e.g., bullet, ordered, and todo lists). - Includes advanced helpers like inline images with links and nested list prefixes.

For a comprehensive example of how to use these helpers, see: https://gno.land/r/docs/moul_md

Sanitization contract

Some helpers in this package sanitize their user-derived arguments INTERNALLY (via p/nt/markdown/sanitize/v0). When using these, pass raw user input — do NOT pre-wrap with sanitize.*, or you will double-wrap (the escapers are not idempotent and double-wrap is a bug):

Example
1Link, UserLink, Image, InlineImageWithLink, FootnoteDefinition,
2LinkReferenceDefinition, CollapsibleSection (title only),
3InlineCode, CodeBlock, LanguageCodeBlock, Blockquote

The other helpers DO NOT sanitize — they are pure builders that wrap their input in markdown chrome. User-derived input reaches the output unmodified, so callers MUST wrap with sanitize.* at the call site:

Example
1Bold, Italic, Strikethrough, H1-H6, BulletList, BulletItem,
2OrderedList, TodoList, TodoItem, Nested, Paragraph, Columns,
3ColumnsN, HorizontalRule

Examples:

Example
1// Sanitizing helper — pass raw:
2out += md.Link(post.Title, post.URL)                                 // good
3out += md.Link(sanitize.InlineText(post.Title), sanitize.URL(post.URL)) // BAD: double-wrap
4
5// Non-sanitizing helper — wrap once:
6out += md.H2(sanitize.InlineText(post.Title))                        // good
7out += md.H2(post.Title)                                             // BAD: raw user input
8out += md.H2(sanitize.InlineText(sanitize.InlineText(post.Title)))   // BAD: double-wrap

Composition: outputs of sanitizing helpers are safe markdown chrome and can be embedded inside non-sanitizing helpers freely:

Example
1out += md.H2(md.Link(post.Title, post.URL))   // good — H2 doesn't re-escape Link's output

The reverse is unsafe: do NOT embed a non-sanitizing helper's markdown chrome inside a sanitizing helper's arg, or the inner markdown gets re-escaped:

Example
1out += md.Link(md.Bold(post.Title), post.URL) // BAD: Link's internal sanitize
2                                              // escapes the ** chars from md.Bold

Functions 32

func Blockquote

1func Blockquote(text string) string
source

Blockquote returns the text as a CommonMark blockquote. Example: Blockquote("foo\nbar") => "\n> foo\n> bar\n\n"

Delegates to sanitize.Blockquote, which cleans the content (bidi-strip, CR/CRLF/U+2028/U+2029/NEL line-ending normalize, LRD strip, ref-link escape, block-marker escape, fence auto-close), line-prefixes each line with "> ", and wraps with "\n" / "\n\n" so the quote opens cleanly and cannot pull appended chrome into the quote via CM §5.2 lazy continuation. Callers do NOT need to pre-wrap the input.

func Bold

1func Bold(text string) string
source

Bold returns bold text for markdown. Example: Bold("foo") => "**foo**"

func BulletItem

1func BulletItem(item string) string
source

BulletItem returns a bullet item for markdown. Example: BulletItem("foo") => "- foo\n"

func BulletList

1func BulletList(items []string) string
source

BulletList returns a bullet list for markdown. Example: BulletList([]string{"foo", "bar"}) => "- foo\n- bar\n"

func CodeBlock

1func CodeBlock(content string) string
source

CodeBlock creates a markdown code block. Example: CodeBlock("foo") => "```\nfoo\n```"

Delegates to sanitize.CodeBlock, which cleans the content (bidi-strip, CR/CRLF normalize, NEL/U+2028/U+2029 fold, NUL→U+FFFD) and picks a fence wide enough to outscan any backticks in content. Callers do NOT need to pre-wrap content with another sanitize helper.

func CollapsibleSection

1func CollapsibleSection(title, content string) string
source

CollapsibleSection creates a collapsible section for markdown using HTML <details> and <summary> tags. Example: CollapsibleSection("Click to expand", "Hidden content") => <details><summary>Click to expand</summary>

Hidden content </details>

The title argument is sanitized via sanitize.HTMLEscape (it lands in an HTML element body inside <summary>, not a markdown context — so HTML entity escaping is the correct policy, not markdown backslash escaping). The content argument is passed through unchanged because <details> with a blank-line-separated body allows markdown inside (CM §4.6); callers must pre-wrap content with sanitize.Block if it derives from user input.

func Columns

1func Columns(contentByColumn []string, padded bool) string
source

Columns returns a formatted row of columns using the Gno syntax. If you want a specific number of columns per row (<=4), use ColumnsN. Check /r/docs/markdown#columns for more info. If padded=true & the final <gno-columns> tag is missing column content, an empty column element will be placed to keep the cols per row constant. Padding works only with colsPerRow > 0.

Example:

Example
1Columns([]string{"A", "B"}, false)
2// Returns:
3// <gno-columns>
4// A
5// <gno-columns-sep>
6// B
7// </gno-columns>

func ColumnsN

1func ColumnsN(content []string, colsPerRow int, padded bool) string
source

ColumnsN splits content into multiple rows of N columns each and formats them. If colsPerRow <= 0, all items are placed in one <gno-columns> block. If padded=true & the final <gno-columns> tag is missing column content, an empty column element will be placed to keep the cols per row constant. Padding works only with colsPerRow > 0. Note: On standard-size screens, gnoweb handles a max of 4 cols per row.

Example:

Example
 1ColumnsN([]string{"A", "B", "C"}, 2, false)
 2// Returns:
 3// <gno-columns>
 4// A
 5// <gno-columns-sep>
 6// B
 7// </gno-columns>
 8// <gno-columns>
 9// C
10// </gno-columns>

func EscapeText

1func EscapeText(text string) string
source

EscapeText escapes special Markdown characters in regular text for use in inline contexts.

Deprecated: use sanitize.InlineText directly. EscapeText was INCOMPLETE — it missed \, #, <, & and did not strip bidi/zero-width characters, replace NUL with U+FFFD, or normalize line endings. User input could inject backslash escapes (\* cancels neighboring escapes), autolinks (<https://x>), raw HTML (<script>), HTML entity references (&), and bidi spoofing (RLO before an address). It now delegates to sanitize.InlineText. The other helpers in this package (Link, UserLink, Image, InlineImageWithLink, CollapsibleSection) sanitize their text args internally now, so you rarely need to call any inline-text-escape helper directly.

Behavior change vs. the original implementation: \, #, <, & are now escaped; | is no longer escaped (the original was over-escaping — outside GFM table-cell context, | is markdown-inert; for table cells use sanitize.TableCell which adds the | escape on top of the inline-text set). Bidi controls are stripped, NUL becomes U+FFFD, and line endings normalize.

func EscapeURL

1func EscapeURL(url string) string
source

EscapeURL escapes characters in a URL for use in markdown link syntax.

Deprecated: use sanitize.URL (for link href) or sanitize.ImageURL (for image src) directly. EscapeURL previously only percent-encoded ( and ) and did not validate the URL scheme — a security footgun (EscapeURL("javascript:alert(1)") returned a working XSS payload). It now delegates to sanitize.URL, which allowlists schemes (rejects javascript:, data:text/html, vbscript:, blob:, etc.) and percent-encodes all unsafe bytes (including the ( ) this function handled). The other helpers in this package (Link, UserLink, Image, InlineImageWithLink) sanitize their URL args internally now, so you rarely need to call any URL-escape helper directly.

Behavior change vs. the original implementation: invalid schemes now return "" instead of passing through with ( ) escaped. Non-ASCII bytes get percent-encoded to standard RFC 3986 wire form instead of passing through as raw UTF-8.

func FootnoteDefinition

1func FootnoteDefinition(name, text string) string
source

FootnoteDefinition emits a GFM footnote definition — `[^name]: body` — for a footnote that is referenced elsewhere in the document by `[^name]`.

Example: FootnoteDefinition("note1", "Long form of the citation.") renders as:

Example
1[^note1]:
2    Long form of the citation.

The `name` is validated as a FootnoteLabel (^[A-Za-z0-9_-]{1,64}$); `text` is user-supplied multi-paragraph prose, sanitized via Block. An invalid name or empty body returns "".

Delegates to sanitize.FootnoteDefinition. Callers do NOT need to pre-wrap either argument.

func H1

1func H1(text string) string
source

H1 returns a level 1 header for markdown. Example: H1("foo") => "# foo\n"

func H2

1func H2(text string) string
source

H2 returns a level 2 header for markdown. Example: H2("foo") => "## foo\n"

func H3

1func H3(text string) string
source

H3 returns a level 3 header for markdown. Example: H3("foo") => "### foo\n"

func H4

1func H4(text string) string
source

H4 returns a level 4 header for markdown. Example: H4("foo") => "#### foo\n"

func H5

1func H5(text string) string
source

H5 returns a level 5 header for markdown. Example: H5("foo") => "##### foo\n"

func H6

1func H6(text string) string
source

H6 returns a level 6 header for markdown. Example: H6("foo") => "###### foo\n"

func HorizontalRule

1func HorizontalRule() string
source

HorizontalRule returns a horizontal rule for markdown. Example: HorizontalRule() => "---\n"

func Image

1func Image(altText, url string) string
source

Image returns an image for markdown. Example: Image("foo", "http://example.com") => "![foo](http://example.com)"

altText is sanitized via sanitize.InlineText, url via sanitize.ImageURL (allowlists http/https/relative + data:image/*; rejects mailto:, javascript:, data:text/html, etc.). Callers do NOT need to pre-wrap either argument.

func InlineCode

1func InlineCode(code string) string
source

InlineCode wraps the given text as a CommonMark inline code span. Example: InlineCode("foo") => "`foo`"

Delegates to sanitize.InlineCode, which cleans the input (bidi-strip, CR/CRLF + NEL + U+2028/U+2029 folded to single space, NUL→U+FFFD) and picks a backtick-run length that outscans any internal backticks. Callers do NOT need to pre-wrap the input.

func Italic

1func Italic(text string) string
source

Italic returns italicized text for markdown. Example: Italic("foo") => "*foo*"

func LanguageCodeBlock

1func LanguageCodeBlock(language, content string) string
source

LanguageCodeBlock creates a markdown code block with language-specific syntax highlighting. Example: LanguageCodeBlock("go", "foo") => "```go\nfoo\n```"

Delegates to sanitize.LanguageCodeBlock, which validates the language tag (charset ^[a-zA-Z0-9_+-]{1,32}$, falling back to a tagless fence if invalid) and cleans the content as CodeBlock does. Callers do NOT need to pre-wrap either argument.

func LinkReferenceDefinition

1func LinkReferenceDefinition(label, url, title string) string
source

LinkReferenceDefinition emits a CommonMark link reference definition (CM §4.7) — `[label]: url "title"` — for a reference link that is invoked elsewhere by `[text][label]` or by the shortcut form `[label]`.

Example: LinkReferenceDefinition("r/docs/help", "/r/docs/help", "") renders as:

Example
1[r/docs/help]: /r/docs/help

The `label` is validated as a FootnoteLabel (^[A-Za-z0-9_-]{1,64}$); `url` is sanitized via URL (allowlist); `title` is sanitized via LinkTitle. An invalid label or rejected URL returns "".

Realms should choose a namespaced label using dashes (e.g. `r-myrealm-help`) so that shortcut-reference invocations from user content can't collide with bare words a user is likely to write. `/` is not in the FootnoteLabel charset.

Delegates to sanitize.LinkReferenceDefinition. Callers do NOT need to pre-wrap any argument.

func Nested

1func Nested(content, prefix string) string
source

Nested prefixes each line with a given prefix, enabling nested lists. Example: Nested("- foo\n- bar", " ") => " - foo\n - bar\n"

func OrderedList

1func OrderedList(items []string) string
source

OrderedList returns an ordered list for markdown. Example: OrderedList([]string{"foo", "bar"}) => "1. foo\n2. bar\n"

func Paragraph

1func Paragraph(content string) string
source

Paragraph wraps the given text in a Markdown paragraph. Example: Paragraph("foo") => "foo\n"

func Strikethrough

1func Strikethrough(text string) string
source

Strikethrough returns strikethrough text for markdown. Example: Strikethrough("foo") => "~~foo~~"

func TodoItem

1func TodoItem(item string, done bool) string
source

TodoItem returns a todo item with checkbox for markdown. Example: TodoItem("foo", true) => "- [x] foo\n"

func TodoList

1func TodoList(items []string, done []bool) string
source

TodoList returns a list of todo items with checkboxes for markdown. Example: TodoList([]string{"foo", "bar\nmore bar"}, []bool{true, false}) => "- [x] foo\n- [ ] bar\n more bar\n"

Imports 3

Source Files 4