forked from tangled.org/core
Monorepo for Tangled

appview: pages/markup: render markdown with transformations

anirudh.fi 11d58231 f5f430be

verified
Changed files
+74 -1
appview
pages
+74 -1
appview/pages/markup/markdown.go
··· 3 3 4 4 import ( 5 5 "bytes" 6 + "path" 6 7 7 8 "github.com/yuin/goldmark" 9 + "github.com/yuin/goldmark/ast" 8 10 "github.com/yuin/goldmark/extension" 9 11 "github.com/yuin/goldmark/parser" 12 + "github.com/yuin/goldmark/text" 13 + "github.com/yuin/goldmark/util" 10 14 ) 11 15 12 - func RenderMarkdown(source string) string { 16 + // RendererType defines the type of renderer to use based on context 17 + type RendererType int 18 + 19 + const ( 20 + // RendererTypeRepoMarkdown is for repository documentation markdown files 21 + RendererTypeRepoMarkdown RendererType = iota 22 + // RendererTypeIssueComment is for issue comments 23 + RendererTypeIssueComment 24 + // RendererTypePullComment is for pull request comments 25 + RendererTypePullComment 26 + // RendererTypeDefault is the default renderer with minimal transformations 27 + RendererTypeDefault 28 + ) 29 + 30 + // RenderContext holds the contextual data for rendering markdown. 31 + // It can be initialized empty, and that'll skip any transformations 32 + // and use the default renderer (RendererTypeDefault). 33 + type RenderContext struct { 34 + Ref string 35 + FullRepoName string 36 + RendererType RendererType 37 + } 38 + 39 + func (rctx *RenderContext) RenderMarkdown(source string) string { 13 40 md := goldmark.New( 14 41 goldmark.WithExtensions(extension.GFM), 15 42 goldmark.WithParserOptions( 16 43 parser.WithAutoHeadingID(), 17 44 ), 18 45 ) 46 + 47 + if rctx != nil { 48 + var transformers []util.PrioritizedValue 49 + 50 + transformers = append(transformers, util.Prioritized(&MarkdownTransformer{rctx: rctx}, 10000)) 51 + 52 + md.Parser().AddOptions( 53 + parser.WithASTTransformers(transformers...), 54 + ) 55 + } 56 + 19 57 var buf bytes.Buffer 20 58 if err := md.Convert([]byte(source), &buf); err != nil { 21 59 return source 22 60 } 23 61 return buf.String() 24 62 } 63 + 64 + type MarkdownTransformer struct { 65 + rctx *RenderContext 66 + } 67 + 68 + func (a *MarkdownTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { 69 + _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { 70 + if !entering { 71 + return ast.WalkContinue, nil 72 + } 73 + 74 + switch a.rctx.RendererType { 75 + case RendererTypeRepoMarkdown: 76 + a.rctx.relativeLinkTransformer(n.(*ast.Link)) 77 + case RendererTypeDefault: 78 + a.rctx.relativeLinkTransformer(n.(*ast.Link)) 79 + // more types here like RendererTypeIssue/Pull etc. 80 + } 81 + 82 + return ast.WalkContinue, nil 83 + }) 84 + } 85 + 86 + func (rctx *RenderContext) relativeLinkTransformer(link *ast.Link) { 87 + dst := string(link.Destination) 88 + 89 + if len(dst) == 0 || dst[0] == '#' || 90 + bytes.Contains(link.Destination, []byte("://")) || 91 + bytes.HasPrefix(link.Destination, []byte("mailto:")) { 92 + return 93 + } 94 + 95 + newPath := path.Join("/", rctx.FullRepoName, "tree", rctx.Ref, dst) 96 + link.Destination = []byte(newPath) 97 + }
appview/pages/markup/readme.go appview/pages/markup/format.go