@@ -7,13 +7,14 @@ package markdown
7
7
8
8
import (
9
9
"bytes"
10
+ "io"
10
11
"strings"
11
12
12
13
"code.gitea.io/gitea/modules/markup"
13
14
"code.gitea.io/gitea/modules/setting"
14
15
"code.gitea.io/gitea/modules/util"
15
16
16
- "github.com/russross/blackfriday"
17
+ "github.com/russross/blackfriday/v2 "
17
18
)
18
19
19
20
// Renderer is a extended version of underlying render object.
@@ -25,134 +26,138 @@ type Renderer struct {
25
26
26
27
var byteMailto = []byte ("mailto:" )
27
28
28
- // Link defines how formal links should be processed to produce corresponding HTML elements.
29
- func (r * Renderer ) Link (out * bytes.Buffer , link []byte , title []byte , content []byte ) {
30
- // special case: this is not a link, a hash link or a mailto:, so it's a
31
- // relative URL
32
- if len (link ) > 0 && ! markup .IsLink (link ) &&
33
- link [0 ] != '#' && ! bytes .HasPrefix (link , byteMailto ) {
34
- lnk := string (link )
35
- if r .IsWiki {
36
- lnk = util .URLJoin ("wiki" , lnk )
37
- }
38
- mLink := util .URLJoin (r .URLPrefix , lnk )
39
- link = []byte (mLink )
40
- }
41
-
42
- if len (content ) > 10 && string (content [0 :9 ]) == "<a href=\" " && bytes .Contains (content [9 :], []byte ("<img" )) {
43
- // Image with link case: markdown `[![]()]()`
44
- // If the content is an image, then we change the original href around it
45
- // which points to itself to a new address "link"
46
- rightQuote := bytes .Index (content [9 :], []byte ("\" " ))
47
- content = bytes .Replace (content , content [9 :9 + rightQuote ], link , 1 )
48
- out .Write (content )
49
- } else {
50
- r .Renderer .Link (out , link , title , content )
51
- }
29
+ var htmlEscaper = [256 ][]byte {
30
+ '&' : []byte ("&" ),
31
+ '<' : []byte ("<" ),
32
+ '>' : []byte (">" ),
33
+ '"' : []byte (""" ),
52
34
}
53
35
54
- // List renders markdown bullet or digit lists to HTML
55
- func (r * Renderer ) List (out * bytes.Buffer , text func () bool , flags int ) {
56
- marker := out .Len ()
57
- if out .Len () > 0 {
58
- out .WriteByte ('\n' )
59
- }
60
-
61
- if flags & blackfriday .LIST_TYPE_DEFINITION != 0 {
62
- out .WriteString ("<dl>" )
63
- } else if flags & blackfriday .LIST_TYPE_ORDERED != 0 {
64
- out .WriteString ("<ol class='ui list'>" )
65
- } else {
66
- out .WriteString ("<ul class='ui list'>" )
67
- }
68
- if ! text () {
69
- out .Truncate (marker )
70
- return
36
+ func escapeHTML (w io.Writer , s []byte ) {
37
+ var start , end int
38
+ for end < len (s ) {
39
+ escSeq := htmlEscaper [s [end ]]
40
+ if escSeq != nil {
41
+ _ , _ = w .Write (s [start :end ])
42
+ _ , _ = w .Write (escSeq )
43
+ start = end + 1
44
+ }
45
+ end ++
71
46
}
72
- if flags & blackfriday .LIST_TYPE_DEFINITION != 0 {
73
- out .WriteString ("</dl>\n " )
74
- } else if flags & blackfriday .LIST_TYPE_ORDERED != 0 {
75
- out .WriteString ("</ol>\n " )
76
- } else {
77
- out .WriteString ("</ul>\n " )
47
+ if start < len (s ) && end <= len (s ) {
48
+ _ , _ = w .Write (s [start :end ])
78
49
}
79
50
}
80
51
81
- // ListItem defines how list items should be processed to produce corresponding HTML elements.
82
- func (r * Renderer ) ListItem (out * bytes.Buffer , text []byte , flags int ) {
83
- // Detect procedures to draw checkboxes.
84
- prefix := ""
85
- if bytes .HasPrefix (text , []byte ("<p>" )) {
86
- prefix = "<p>"
87
- }
88
- switch {
89
- case bytes .HasPrefix (text , []byte (prefix + "[ ] " )):
90
- text = append ([]byte (`<span class="ui fitted disabled checkbox"><input type="checkbox" disabled="disabled" /><label /></span>` ), text [3 + len (prefix ):]... )
91
- if prefix != "" {
92
- text = bytes .Replace (text , []byte (prefix ), []byte {}, 1 )
52
+ // RenderNode is a default renderer of a single node of a syntax tree. For
53
+ // block nodes it will be called twice: first time with entering=true, second
54
+ // time with entering=false, so that it could know when it's working on an open
55
+ // tag and when on close. It writes the result to w.
56
+ //
57
+ // The return value is a way to tell the calling walker to adjust its walk
58
+ // pattern: e.g. it can terminate the traversal by returning Terminate. Or it
59
+ // can ask the walker to skip a subtree of this node by returning SkipChildren.
60
+ // The typical behavior is to return GoToNext, which asks for the usual
61
+ // traversal to the next node.
62
+ func (r * Renderer ) RenderNode (w io.Writer , node * blackfriday.Node , entering bool ) blackfriday.WalkStatus {
63
+ switch node .Type {
64
+ case blackfriday .Image :
65
+ prefix := r .URLPrefix
66
+ if r .IsWiki {
67
+ prefix = util .URLJoin (prefix , "wiki" , "raw" )
93
68
}
94
- case bytes .HasPrefix (text , []byte (prefix + "[x] " )):
95
- text = append ([]byte (`<span class="ui checked fitted disabled checkbox"><input type="checkbox" checked="" disabled="disabled" /><label /></span>` ), text [3 + len (prefix ):]... )
96
- if prefix != "" {
97
- text = bytes .Replace (text , []byte (prefix ), []byte {}, 1 )
69
+ prefix = strings .Replace (prefix , "/src/" , "/media/" , 1 )
70
+ link := node .LinkData .Destination
71
+ if len (link ) > 0 && ! markup .IsLink (link ) {
72
+ lnk := string (link )
73
+ lnk = util .URLJoin (prefix , lnk )
74
+ lnk = strings .Replace (lnk , " " , "+" , - 1 )
75
+ link = []byte (lnk )
76
+ }
77
+ node .LinkData .Destination = link
78
+ // Render link around image only if parent is not link already
79
+ if node .Parent != nil && node .Parent .Type != blackfriday .Link {
80
+ if entering {
81
+ _ , _ = w .Write ([]byte (`<a href="` ))
82
+ escapeHTML (w , link )
83
+ _ , _ = w .Write ([]byte (`">` ))
84
+ return r .Renderer .RenderNode (w , node , entering )
85
+ }
86
+ s := r .Renderer .RenderNode (w , node , entering )
87
+ _ , _ = w .Write ([]byte (`</a>` ))
88
+ return s
89
+ }
90
+ return r .Renderer .RenderNode (w , node , entering )
91
+ case blackfriday .Link :
92
+ // special case: this is not a link, a hash link or a mailto:, so it's a
93
+ // relative URL
94
+ link := node .LinkData .Destination
95
+ if len (link ) > 0 && ! markup .IsLink (link ) &&
96
+ link [0 ] != '#' && ! bytes .HasPrefix (link , byteMailto ) &&
97
+ node .LinkData .Footnote == nil {
98
+ lnk := string (link )
99
+ if r .IsWiki {
100
+ lnk = util .URLJoin ("wiki" , lnk )
101
+ }
102
+ link = []byte (util .URLJoin (r .URLPrefix , lnk ))
103
+ }
104
+ node .LinkData .Destination = link
105
+ return r .Renderer .RenderNode (w , node , entering )
106
+ case blackfriday .Text :
107
+ isListItem := false
108
+ for n := node .Parent ; n != nil ; n = n .Parent {
109
+ if n .Type == blackfriday .Item {
110
+ isListItem = true
111
+ break
112
+ }
113
+ }
114
+ if isListItem {
115
+ text := node .Literal
116
+ switch {
117
+ case bytes .HasPrefix (text , []byte ("[ ] " )):
118
+ _ , _ = w .Write ([]byte (`<span class="ui fitted disabled checkbox"><input type="checkbox" disabled="disabled" /><label /></span>` ))
119
+ text = text [3 :]
120
+ case bytes .HasPrefix (text , []byte ("[x] " )):
121
+ _ , _ = w .Write ([]byte (`<span class="ui checked fitted disabled checkbox"><input type="checkbox" checked="" disabled="disabled" /><label /></span>` ))
122
+ text = text [3 :]
123
+ }
124
+ node .Literal = text
98
125
}
99
126
}
100
- r .Renderer .ListItem (out , text , flags )
101
- }
102
-
103
- // Image defines how images should be processed to produce corresponding HTML elements.
104
- func (r * Renderer ) Image (out * bytes.Buffer , link []byte , title []byte , alt []byte ) {
105
- prefix := r .URLPrefix
106
- if r .IsWiki {
107
- prefix = util .URLJoin (prefix , "wiki" , "raw" )
108
- }
109
- prefix = strings .Replace (prefix , "/src/" , "/media/" , 1 )
110
- if len (link ) > 0 && ! markup .IsLink (link ) {
111
- lnk := string (link )
112
- lnk = util .URLJoin (prefix , lnk )
113
- lnk = strings .Replace (lnk , " " , "+" , - 1 )
114
- link = []byte (lnk )
115
- }
116
-
117
- // Put a link around it pointing to itself by default
118
- out .WriteString (`<a href="` )
119
- out .Write (link )
120
- out .WriteString (`">` )
121
- r .Renderer .Image (out , link , title , alt )
122
- out .WriteString ("</a>" )
127
+ return r .Renderer .RenderNode (w , node , entering )
123
128
}
124
129
125
130
const (
126
131
blackfridayExtensions = 0 |
127
- blackfriday .EXTENSION_NO_INTRA_EMPHASIS |
128
- blackfriday .EXTENSION_TABLES |
129
- blackfriday .EXTENSION_FENCED_CODE |
130
- blackfriday .EXTENSION_STRIKETHROUGH |
131
- blackfriday .EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK |
132
- blackfriday .EXTENSION_DEFINITION_LISTS |
133
- blackfriday .EXTENSION_FOOTNOTES |
134
- blackfriday .EXTENSION_HEADER_IDS |
135
- blackfriday .EXTENSION_AUTO_HEADER_IDS
132
+ blackfriday .NoIntraEmphasis |
133
+ blackfriday .Tables |
134
+ blackfriday .FencedCode |
135
+ blackfriday .Strikethrough |
136
+ blackfriday .NoEmptyLineBeforeBlock |
137
+ blackfriday .DefinitionLists |
138
+ blackfriday .Footnotes |
139
+ blackfriday .HeadingIDs |
140
+ blackfriday .AutoHeadingIDs
136
141
blackfridayHTMLFlags = 0 |
137
- blackfriday .HTML_SKIP_STYLE |
138
- blackfriday .HTML_OMIT_CONTENTS |
139
- blackfriday .HTML_USE_SMARTYPANTS
142
+ blackfriday .Smartypants
140
143
)
141
144
142
145
// RenderRaw renders Markdown to HTML without handling special links.
143
146
func RenderRaw (body []byte , urlPrefix string , wikiMarkdown bool ) []byte {
144
147
renderer := & Renderer {
145
- Renderer : blackfriday .HtmlRenderer (blackfridayHTMLFlags , "" , "" ),
148
+ Renderer : blackfriday .NewHTMLRenderer (blackfriday.HTMLRendererParameters {
149
+ Flags : blackfridayHTMLFlags ,
150
+ }),
146
151
URLPrefix : urlPrefix ,
147
152
IsWiki : wikiMarkdown ,
148
153
}
149
154
150
155
exts := blackfridayExtensions
151
156
if setting .Markdown .EnableHardLineBreak {
152
- exts |= blackfriday .EXTENSION_HARD_LINE_BREAK
157
+ exts |= blackfriday .HardLineBreak
153
158
}
154
159
155
- body = blackfriday .Markdown (body , renderer , exts )
160
+ body = blackfriday .Run (body , blackfriday . WithRenderer ( renderer ), blackfriday . WithExtensions ( exts ) )
156
161
return markup .SanitizeBytes (body )
157
162
}
158
163
0 commit comments