Parse converts Markdown source to HTML.
{
flags := blackfriday.CommonHTMLFlags &^ blackfriday.Smartypants &^ blackfriday.SmartypantsFractions &^ blackfriday.SmartypantsDashes &^ blackfriday.SmartypantsLatexDashes
renderer := blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{Flags: flags})
out := blackfriday.Run(
[]byte(src),
blackfriday.WithExtensions(blackfriday.CommonExtensions),
blackfriday.WithRenderer(renderer),
)
return string(out)
}
Heading represents a parsed heading.
{
slug := strings.ToLower(text)
slug = slugRe.ReplaceAllString(slug, "")
slug = strings.TrimSpace(slug)
slug = strings.ReplaceAll(slug, " ", "-")
if n, ok := s.seen[slug]; ok {
s.seen[slug] = n + 1
return fmt.Sprintf("%s-%d", slug, n)
}
s.seen[slug] = 1
return slug
}
{ return &slugger{seen: make(map[string]int)} }
Headings extracts headings from Markdown and generates ids.
{
parser := blackfriday.New(blackfriday.WithExtensions(blackfriday.CommonExtensions))
root := parser.Parse([]byte(src))
headings := []Heading{}
slug := newSlugger()
var buf bytes.Buffer
var collectText func(n *blackfriday.Node)
collectText = func(n *blackfriday.Node) {
switch n.Type {
case blackfriday.Text, blackfriday.Code:
buf.Write(n.Literal)
}
for c := n.FirstChild; c != nil; c = c.Next {
collectText(c)
}
}
root.Walk(func(n *blackfriday.Node, entering bool) blackfriday.WalkStatus {
if !entering {
return blackfriday.GoToNext
}
if n.Type == blackfriday.Heading {
buf.Reset()
for c := n.FirstChild; c != nil; c = c.Next {
collectText(c)
if c.Next != nil {
buf.WriteByte(' ')
}
}
text := strings.TrimSpace(buf.String())
level := n.HeadingData.Level
headings = append(headings, Heading{
Text: text,
Depth: int(level),
ID: slug.slug(text),
})
}
return blackfriday.GoToNext
})
return headings
}
{
md := "# Title"
html := Parse(md)
if html == "" || html[:4] != "<h1>" {
t.Fatalf("unexpected html: %q", html)
}
}
{
md := "# Title\n## Sub"
hs := Headings(md)
if len(hs) != 2 {
t.Fatalf("expected 2 headings, got %d", len(hs))
}
if hs[0].Text != "Title" || hs[0].Depth != 1 {
t.Fatalf("unexpected first heading: %+v", hs[0])
}
}
TestParsePreservesQuotes ensures markdown.Parse keeps plain quotes so
component include directives remain parseable.
{
src := `@include:Comp:{code:"/path/file.go", uri:"/demo"}`
html := Parse(src)
if !strings.Contains(html, `code:"/path/file.go"`) {
t.Fatalf("expected quoted code path, got %q", html)
}
if strings.Contains(html, "“") || strings.Contains(html, "”") {
t.Fatalf("unexpected smart quotes in %q", html)
}
}
import "bytes"
import "fmt"
import "regexp"
import "strings"
import "github.com/russross/blackfriday/v2"
blackfriday
import "strings"
import "testing"