markdown

package
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Code

func Code(text string) string

Code wraps text in markdown inline code backticks

func CodeLink(url string, text ...string) string

CodeLink creates a markdown link with code-formatted display text If text is empty, uses url as the display text url is the link URL text is the optional display text (will be wrapped in backticks)

func FenceMeta

func FenceMeta(fenceLine string) []string

FenceMeta returns the space-separated metadata tokens after "ipmt" in a ```ipmt … fence info-string (e.g. "```ipmt unresolved" → ["unresolved"]). Empty for a bare ```ipmt fence, or when "ipmt" is not the complete language token (e.g. "```ipmtX", "```ipmt-x" → nil — those are not ipmt fences).

func FencedByteRanges

func FencedByteRanges(text string) [][2]int

FencedByteRanges returns the half-open byte ranges [start,end) of text covered by fenced code blocks, delimiter lines included. Text is walked as-is (no newline normalization) so the offsets align with the input — FindInlineIpmt uses this to drop matches whose marker or code span sits inside a fence, where they are literal text. An unterminated fence extends to len(text).

func Generate

func Generate(title string) string

Generate creates a GitHub-like, ASCII-only markdown anchor slug from a heading title. Rules:

  • Convert to lowercase
  • Replace spaces with hyphens
  • Remove special characters except hyphens, slashes, and dots (slashes and dots are retained, unlike GitHub which strips them)
  • Collapse consecutive slashes into single slash
  • Remove leading/trailing hyphens
  • Collapse multiple hyphens into one

Note: this is ASCII-only. Unlike GitHub, all non-ASCII letters are dropped (e.g. "Über Café" → "ber-caf", "日本語" → ""). Callers compare two Generate outputs against each other, so the divergence is self-consistent for matching, but it is NOT byte-equal to a GitHub heading anchor.

func IsIPMTFenceStart

func IsIPMTFenceStart(line string) bool

IsIPMTFenceStart reports whether line opens an ```ipmt``` fenced block (3+ backticks; tilde fences never carry ipmt). "ipmt" must be the complete CommonMark info-string language token, so ```ipmt and ```ipmt <metadata> match, but ```ipmtX and ```ipmt-x (a different language) do not.

NOTE: a true opener also requires that no longer fence is already open — callers that scan whole documents must track fence state (see ParseFenceOpen / SkipFence) or use ScanIPMTBlocks, which does.

func IsIPMTInfo

func IsIPMTInfo(info string) bool

IsIPMTInfo reports whether a fence info string (as returned by ParseFenceOpen) declares the ipmt language: "ipmt" exactly, optionally followed by whitespace-separated metadata ("ipmt unresolved"). A different language that merely starts with the letters ("ipmtX", "ipmt-x") does not match.

func LineStartInlineIpmt

func LineStartInlineIpmt(text string) []int

LineStartInlineIpmt returns the 1-based numbers of lines whose first non-space content is an inline ipmt marker comment (`<!--ipmt-->` or `<!--ipmt:as-token:NAME-->`), with at most 3 spaces of indentation and outside any fenced code block.

Such a line renders BROKEN in static HTML and the VS Code preview alike: CommonMark parses a line beginning (≤3 spaces indent) with `<!--` as an HTML block (type 2, comment), so the markdown engine passes the WHOLE line through verbatim — the adjacent backtick code span never becomes inline `<code>`, and the inline-ipmt rewrite (which keys on `<!--…--><code>`) never fires. Marker and code span both leak out raw.

Fix in the source: put at least one non-marker character before the first marker on the line — lead with a word, or let table/list syntax occupy column 0 (a `| <!--ipmt…` table cell is inline, so it's fine).

Limitation: markers nested inside a blockquote (`> <!--ipmt…`) are NOT detected here — `>` sits at column 0 — yet they break the same way. Callers that care can strip blockquote markers before calling.

func Link(url string, text ...string) string

Link creates a complete markdown link string [text](url) If text is empty, uses url as the display text url is the link URL text is the optional display text

func NormalizeLF

func NormalizeLF(s string) string

NormalizeLF rewrites all line endings in s to "\n".

func RelPath

func RelPath(absPath string, baseDir string) string

RelPath creates a relative path from baseDir to absPath absPath is the absolute path to the target file baseDir is the absolute path to the base directory Returns the relative path suitable for markdown links

func RelPathWithDisplay

func RelPathWithDisplay(absPath string, outputDir string, projectRoot string) (string, string)

RelPathWithDisplay creates relative paths for both display and link absPath is the absolute path to the target file outputDir is the absolute path to the directory containing the output markdown file projectRoot is the absolute path to the project root for generating display text Returns: display text (relative to projectRoot), link path (relative to outputDir)

func SkipFence

func SkipFence(lines []string, open int, f Fence) int

SkipFence returns the line index just past the fence opened at lines[open], i.e. the index after its closing delimiter — or len(lines) when the fence never closes (the rest of the document is literal fence content).

Types

type Fence

type Fence struct {
	Char byte // '`' or '~'
	Len  int  // characters in the opening run, >= 3
}

Fence identifies an open fenced-code-block delimiter: the fence character and the length of the opening run. The zero value is not a valid fence.

func ParseFenceOpen

func ParseFenceOpen(line string) (f Fence, info string, ok bool)

ParseFenceOpen reports whether line opens a fenced code block and, if so, returns the fence delimiter and its info string (trimmed; "" for a bare fence). See the package comment for the exact rules.

func (Fence) ClosedBy

func (f Fence) ClosedBy(line string) bool

ClosedBy reports whether line closes this fence: a run of at least f.Len of f.Char with nothing but whitespace around it.

type IPMTBlock

type IPMTBlock struct {
	Index     int      // 1-based block index within the source
	StartLine int      // 0-based line index of the opening fence
	EndLine   int      // 0-based line index of the closing fence; -1 if unterminated
	Content   string   // text between fences, joined with "\n"; no trailing newline
	Meta      []string // info-string tokens after "ipmt" (e.g. ["unresolved"]); empty for a bare fence
}

IPMTBlock describes a fenced ```ipmt``` code block found in a markdown source.

func ScanIPMTBlocks

func ScanIPMTBlocks(text string) (lines []string, blocks []IPMTBlock)

ScanIPMTBlocks splits text into LF-normalized lines and extracts all ```ipmt``` fenced code blocks. Unterminated blocks have EndLine == -1 and their Content holds everything after the opening fence.

CommonMark-faithful on nesting (see fence.go): a ```ipmt line inside another fenced block — e.g. a ````md documentation example — is literal text, NOT a block; such wrapper fences are skipped wholesale. An ipmt block's closing fence must be at least as long as its opener with no info string, so a ```text line inside the block stays content.

type InlineIpmt

type InlineIpmt struct {
	// Byte offsets in the source markdown:
	MarkerStart int // start of the `<!--…-->` comment
	MarkerEnd   int // end (exclusive) of the comment
	CodeStart   int // start of the opening backtick run
	CodeEnd     int // end (exclusive) of the closing backtick run
	// ContentStart..ContentEnd is the inline-code content between the
	// opening and closing backtick runs.
	ContentStart int
	ContentEnd   int
	Code         string // the content between the backticks (== src[ContentStart:ContentEnd])

	// AsToken, when non-empty, is the NAME from an
	// `<!--ipmt:as-token:NAME-->` marker — a palette class suffix
	// (`e-marker`, `L`, `type-marker`, …). The whole visible Code is
	// painted in that style; Code is NOT parsed as ipmt. Empty means a
	// bare `<!--ipmt-->` marker (tokenize Code as a valid fragment).
	AsToken string
}

InlineIpmt describes one inline ipmt marker in a markdown source: an HTML-comment marker immediately before a backtick-delimited inline code span. Two marker forms:

`<!--ipmt-->` `t-shirt ::c`            ← bare: tokenize the code as ipmt
`<!--ipmt:as-token:e-marker-->` `::e`  ← paint the code in style NAME

The marker MUST come before the code. Whitespace (including tabs and newlines) between marker and the first backtick is tolerated; no other token may appear. The marker is case-insensitive on `ipmt`/`as-token`; the NAME is taken verbatim (case-sensitive, matching the palette class suffix — e.g. `L`, `e-marker`, `type-marker`).

func FindInlineIpmt

func FindInlineIpmt(text string) []InlineIpmt

FindInlineIpmt scans `text` for inline ipmt markers and the adjacent backtick-delimited code spans they apply to. Returns all matches in source order.

Recognition: the marker matches inlineIpmtMarkerRE; after it, only whitespace may appear before the opening backtick run. The opening run is N backticks; the closing run must be exactly N (CommonMark inline code). A match whose marker sits INSIDE a fenced code block — e.g. a ```md example showing the marker syntax — is literal text and is skipped, as is a marker whose code span would cross into a fence (a marker immediately above a ``` opener would otherwise swallow the fence delimiter as its "inline code").

Shared between cmd/ipm-rpc (inline tokens in ipm.embedBuffer) and pkg/mdhtml (static-HTML output). Single source of truth for the marker grammar lives HERE.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL