From 260976e47298b77e06c7046680b66dd579580de3 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Sat, 14 Mar 2026 23:33:23 +0200 Subject: [PATCH] appview/pages/markup: add Dashes extension and export NewMarkdownWith Signed-off-by: Anirudh Oppiliappan --- appview/pages/markup/extension/dashes.go | 64 ++++++++++++++++++++++++ appview/pages/markup/markdown.go | 51 +++++++++++-------- 2 files changed, 93 insertions(+), 22 deletions(-) create mode 100644 appview/pages/markup/extension/dashes.go diff --git a/appview/pages/markup/extension/dashes.go b/appview/pages/markup/extension/dashes.go new file mode 100644 index 00000000..9d7841ec --- /dev/null +++ b/appview/pages/markup/extension/dashes.go @@ -0,0 +1,64 @@ +package extension + +import ( + "github.com/yuin/goldmark" + gast "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" +) + +type dashParser struct{} + +func (p *dashParser) Trigger() []byte { + return []byte{'-'} +} + +func (p *dashParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node { + line, _ := block.PeekLine() + if len(line) < 2 || line[0] != '-' || line[1] != '-' { + return nil + } + node := gast.NewString([]byte("\u2014")) + node.SetCode(true) + block.Advance(2) + return node +} + +type digitDashParser struct{} + +func (p *digitDashParser) Trigger() []byte { + return []byte{'-'} +} + +func (p *digitDashParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node { + line, _ := block.PeekLine() + if len(line) < 2 { + return nil + } + before := block.PrecendingCharacter() + if before < '0' || before > '9' { + return nil + } + if line[1] < '0' || line[1] > '9' { + return nil + } + node := gast.NewString([]byte("\u2013")) + node.SetCode(true) + block.Advance(1) + return node +} + +type dashExt struct{} + +// Dashes replaces "--" with an em-dash (—) and a hyphen between two digits +// with an en-dash (–). Implemented as an inline parser so it operates on the +// raw byte stream, unaffected by hard-wrapped source lines. +var Dashes goldmark.Extender = &dashExt{} + +func (e *dashExt) Extend(m goldmark.Markdown) { + m.Parser().AddOptions(parser.WithInlineParsers( + util.Prioritized(&dashParser{}, 9990), + util.Prioritized(&digitDashParser{}, 9991), + )) +} diff --git a/appview/pages/markup/markdown.go b/appview/pages/markup/markdown.go index e699e08e..930681d4 100644 --- a/appview/pages/markup/markdown.go +++ b/appview/pages/markup/markdown.go @@ -13,7 +13,7 @@ import ( chromahtml "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/styles" "github.com/yuin/goldmark" - "github.com/yuin/goldmark-emoji" + emoji "github.com/yuin/goldmark-emoji" highlighting "github.com/yuin/goldmark-highlighting/v2" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/extension" @@ -53,29 +53,31 @@ type RenderContext struct { Files fs.FS } -func NewMarkdown(hostname string) goldmark.Markdown { - md := goldmark.New( - goldmark.WithExtensions( - extension.GFM, - &mermaid.Extender{ - RenderMode: mermaid.RenderModeClient, - NoScript: true, - }, - highlighting.NewHighlighting( - highlighting.WithFormatOptions( - chromahtml.Standalone(false), - chromahtml.WithClasses(true), - ), - highlighting.WithCustomStyle(styles.Get("catppuccin-latte")), - ), - extension.NewFootnote( - extension.WithFootnoteIDPrefix([]byte("footnote")), +func NewMarkdown(hostname string, extra ...goldmark.Extender) goldmark.Markdown { + exts := []goldmark.Extender{ + extension.GFM, + &mermaid.Extender{ + RenderMode: mermaid.RenderModeClient, + NoScript: true, + }, + highlighting.NewHighlighting( + highlighting.WithFormatOptions( + chromahtml.Standalone(false), + chromahtml.WithClasses(true), ), - callout.CalloutExtention, - textension.AtExt, - textension.NewTangledLinkExt(hostname), - emoji.Emoji, + highlighting.WithCustomStyle(styles.Get("catppuccin-latte")), ), + extension.NewFootnote( + extension.WithFootnoteIDPrefix([]byte("footnote")), + ), + callout.CalloutExtention, + textension.AtExt, + textension.NewTangledLinkExt(hostname), + emoji.Emoji, + } + exts = append(exts, extra...) + md := goldmark.New( + goldmark.WithExtensions(exts...), goldmark.WithParserOptions( parser.WithAutoHeadingID(), ), @@ -84,6 +86,11 @@ func NewMarkdown(hostname string) goldmark.Markdown { return md } +// NewMarkdownWith is an alias for NewMarkdown with extra extensions. +func NewMarkdownWith(hostname string, extra ...goldmark.Extender) goldmark.Markdown { + return NewMarkdown(hostname, extra...) +} + func (rctx *RenderContext) RenderMarkdown(source string) string { return rctx.RenderMarkdownWith(source, NewMarkdown(rctx.Hostname)) } -- 2.51.2