cs-midi-docs/internal/render/markdown.go

90 lines
2.1 KiB
Go

package render
import (
"bytes"
"fmt"
"regexp"
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/yuin/goldmark"
highlighting "github.com/yuin/goldmark-highlighting/v2"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
)
var (
md goldmark.Markdown
platformRe = regexp.MustCompile("^```(\\w+)\\s+(pico|esp32s3|esp32c3)\\s*$")
)
func init() {
md = goldmark.New(
goldmark.WithExtensions(
extension.GFM,
highlighting.NewHighlighting(
highlighting.WithFormatOptions(
chromahtml.WithClasses(true),
chromahtml.ClassPrefix("hl-"),
),
),
),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
)
}
// preprocessPlatformBlocks rewrites fenced code blocks tagged with a platform
// (e.g. ```cpp pico) into an HTML wrapper div + untagged fence so Goldmark
// renders them normally while preserving the data-platform attribute.
func preprocessPlatformBlocks(src []byte) []byte {
lines := bytes.Split(src, []byte("\n"))
var out [][]byte
inFence := false
platformFence := false
for _, line := range lines {
trimmed := bytes.TrimSpace(line)
if !inFence {
if m := platformRe.FindSubmatch(trimmed); m != nil {
out = append(out, []byte(fmt.Sprintf(`<div data-platform="%s">`, m[2])))
out = append(out, []byte{})
out = append(out, []byte("```"+string(m[1])))
inFence = true
platformFence = true
continue
}
if bytes.HasPrefix(trimmed, []byte("```")) {
inFence = true
}
} else if bytes.Equal(trimmed, []byte("```")) {
inFence = false
if platformFence {
out = append(out, line)
out = append(out, []byte{})
out = append(out, []byte("</div>"))
platformFence = false
continue
}
}
out = append(out, line)
}
return bytes.Join(out, []byte("\n"))
}
func Markdown(src []byte) ([]byte, error) {
src = preprocessPlatformBlocks(src)
var buf bytes.Buffer
if err := md.Convert(src, &buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}