+74
-1
appview/pages/markup/markdown.go
+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
appview/pages/markup/readme.go
appview/pages/markup/format.go