From 8eba71c643b573bf3892c9255b46cf934943e6c6 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Thu, 01 May 2025 16:48:23 +0000 Subject: [PATCH] appview: pages/markup: render markdown with transformations --- appview/pages/markup/format.go | 26 ++++++++++++++++++++++++++ appview/pages/markup/markdown.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- appview/pages/markup/readme.go | 26 -------------------------- 3 file(s) changed, 100 insertion(s)(+), 27 deletion(s)(-) diff --git a/appview/pages/markup/format.go b/appview/pages/markup/format.go new file mode 100644 --- /dev/null +++ b/appview/pages/markup/format.go @@ -0,0 +1,26 @@ +package markup + +import "strings" + +type Format string + +const ( + FormatMarkdown Format = "markdown" + FormatText Format = "text" +) + +var FileTypes map[Format][]string = map[Format][]string{ + FormatMarkdown: []string{".md", ".markdown", ".mdown", ".mkdn", ".mkd"}, +} + +func GetFormat(filename string) Format { + for format, extensions := range FileTypes { + for _, extension := range extensions { + if strings.HasSuffix(filename, extension) { + return format + } + } + } + // default format + return FormatText +} diff --git a/appview/pages/markup/markdown.go b/appview/pages/markup/markdown.go --- a/appview/pages/markup/markdown.go +++ b/appview/pages/markup/markdown.go @@ -3,22 +3,95 @@ import ( "bytes" + "path" "github.com/yuin/goldmark" + "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" ) -func RenderMarkdown(source string) string { +// RendererType defines the type of renderer to use based on context +type RendererType int + +const ( + // RendererTypeRepoMarkdown is for repository documentation markdown files + RendererTypeRepoMarkdown RendererType = iota + // RendererTypeIssueComment is for issue comments + RendererTypeIssueComment + // RendererTypePullComment is for pull request comments + RendererTypePullComment + // RendererTypeDefault is the default renderer with minimal transformations + RendererTypeDefault +) + +// RenderContext holds the contextual data for rendering markdown. +// It can be initialized empty, and that'll skip any transformations +// and use the default renderer (RendererTypeDefault). +type RenderContext struct { + Ref string + FullRepoName string + RendererType RendererType +} + +func (rctx *RenderContext) RenderMarkdown(source string) string { md := goldmark.New( goldmark.WithExtensions(extension.GFM), goldmark.WithParserOptions( parser.WithAutoHeadingID(), ), ) + + if rctx != nil { + var transformers []util.PrioritizedValue + + transformers = append(transformers, util.Prioritized(&MarkdownTransformer{rctx: rctx}, 10000)) + + md.Parser().AddOptions( + parser.WithASTTransformers(transformers...), + ) + } + var buf bytes.Buffer if err := md.Convert([]byte(source), &buf); err != nil { return source } return buf.String() +} + +type MarkdownTransformer struct { + rctx *RenderContext +} + +func (a *MarkdownTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { + _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { + if !entering { + return ast.WalkContinue, nil + } + + switch a.rctx.RendererType { + case RendererTypeRepoMarkdown: + a.rctx.relativeLinkTransformer(n.(*ast.Link)) + case RendererTypeDefault: + a.rctx.relativeLinkTransformer(n.(*ast.Link)) + // more types here like RendererTypeIssue/Pull etc. + } + + return ast.WalkContinue, nil + }) +} + +func (rctx *RenderContext) relativeLinkTransformer(link *ast.Link) { + dst := string(link.Destination) + + if len(dst) == 0 || dst[0] == '#' || + bytes.Contains(link.Destination, []byte("://")) || + bytes.HasPrefix(link.Destination, []byte("mailto:")) { + return + } + + newPath := path.Join("/", rctx.FullRepoName, "tree", rctx.Ref, dst) + link.Destination = []byte(newPath) } diff --git a/appview/pages/markup/readme.go b/appview/pages/markup/readme.go deleted file mode 100644 --- a/appview/pages/markup/readme.go +++ /dev/null @@ -1,26 +0,0 @@ -package markup - -import "strings" - -type Format string - -const ( - FormatMarkdown Format = "markdown" - FormatText Format = "text" -) - -var FileTypes map[Format][]string = map[Format][]string{ - FormatMarkdown: []string{".md", ".markdown", ".mdown", ".mkdn", ".mkd"}, -} - -func GetFormat(filename string) Format { - for format, extensions := range FileTypes { - for _, extension := range extensions { - if strings.HasSuffix(filename, extension) { - return format - } - } - } - // default format - return FormatText -} -- tangled.sh