-
Notifications
You must be signed in to change notification settings - Fork 0
/
extract.go
93 lines (72 loc) · 2.06 KB
/
extract.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package markdown
import (
"io"
"github.com/yuin/goldmark"
goldmarkAst "github.com/yuin/goldmark/ast"
goldmarkText "github.com/yuin/goldmark/text"
)
type EmbeddedLink struct {
URL string
OnlyLinkInBlock bool
}
type parsedText struct {
s []byte
node goldmarkAst.Node
parser goldmark.Markdown
}
func Parse(s string, mediaReplacer replacer) *parsedText {
r := goldmarkText.NewReader([]byte(s))
parser := NewParser(mediaReplacer)
ast := parser.Parser().Parse(r)
return &parsedText{
s: []byte(s),
node: ast,
parser: parser,
}
}
func (t *parsedText) Render(writer io.Writer) error {
return t.parser.Renderer().Render(writer, t.s, t.node)
}
type replacer func(inURL string) (bool, string)
func (t *parsedText) ExtractLinks() []*EmbeddedLink {
out := []*EmbeddedLink{}
walkNode([]byte(t.s), t.node, 0, func(ch goldmarkAst.Node, onlySecondLevelChildElement bool) {
if l, ok := ch.(*goldmarkAst.AutoLink); ok {
out = append(out, &EmbeddedLink{
URL: string(l.URL(t.s)),
OnlyLinkInBlock: onlySecondLevelChildElement,
})
} else if l, ok := ch.(*goldmarkAst.Link); ok {
out = append(out, &EmbeddedLink{
URL: string(l.Destination),
OnlyLinkInBlock: onlySecondLevelChildElement,
})
}
})
return out
}
func (t *parsedText) ExtractImageUrls() []*EmbeddedLink {
out := []*EmbeddedLink{}
walkNode([]byte(t.s), t.node, 0, func(ch goldmarkAst.Node, onlySecondLevelChildElement bool) {
if l, ok := ch.(*goldmarkAst.Image); ok {
out = append(out, &EmbeddedLink{
URL: string(l.Destination),
})
}
})
return out
}
type visiter func(n goldmarkAst.Node, onlySecondLevelChildElement bool)
func walkNode(source []byte, n goldmarkAst.Node, level int, visitNode visiter) {
// level 0 - root document
// level 1 - top level block element
onlySecondLevelChildElement := n.ChildCount() == 1 && level == 1
if n.ChildCount() > 0 {
ch := n.FirstChild()
for ch != nil {
visitNode(ch, onlySecondLevelChildElement)
walkNode(source, ch, level+1, visitNode)
ch = ch.NextSibling()
}
}
}