@@ -7,13 +7,14 @@ package markdown
77
88import (
99 "bytes"
10+ "io"
1011 "strings"
1112
1213 "code.gitea.io/gitea/modules/markup"
1314 "code.gitea.io/gitea/modules/setting"
1415 "code.gitea.io/gitea/modules/util"
1516
16- "github.com/russross/blackfriday"
17+ "github.com/russross/blackfriday/v2 "
1718)
1819
1920// Renderer is a extended version of underlying render object.
@@ -25,134 +26,138 @@ type Renderer struct {
2526
2627var byteMailto = []byte ("mailto:" )
2728
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 (""" ),
5234}
5335
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 ++
7146 }
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 ])
7849 }
7950}
8051
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" )
9368 }
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
98125 }
99126 }
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 )
123128}
124129
125130const (
126131 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
136141 blackfridayHTMLFlags = 0 |
137- blackfriday .HTML_SKIP_STYLE |
138- blackfriday .HTML_OMIT_CONTENTS |
139- blackfriday .HTML_USE_SMARTYPANTS
142+ blackfriday .Smartypants
140143)
141144
142145// RenderRaw renders Markdown to HTML without handling special links.
143146func RenderRaw (body []byte , urlPrefix string , wikiMarkdown bool ) []byte {
144147 renderer := & Renderer {
145- Renderer : blackfriday .HtmlRenderer (blackfridayHTMLFlags , "" , "" ),
148+ Renderer : blackfriday .NewHTMLRenderer (blackfriday.HTMLRendererParameters {
149+ Flags : blackfridayHTMLFlags ,
150+ }),
146151 URLPrefix : urlPrefix ,
147152 IsWiki : wikiMarkdown ,
148153 }
149154
150155 exts := blackfridayExtensions
151156 if setting .Markdown .EnableHardLineBreak {
152- exts |= blackfriday .EXTENSION_HARD_LINE_BREAK
157+ exts |= blackfriday .HardLineBreak
153158 }
154159
155- body = blackfriday .Markdown (body , renderer , exts )
160+ body = blackfriday .Run (body , blackfriday . WithRenderer ( renderer ), blackfriday . WithExtensions ( exts ) )
156161 return markup .SanitizeBytes (body )
157162}
158163
0 commit comments