diff options
Diffstat (limited to 'modules/base/markdown.go')
-rw-r--r-- | modules/base/markdown.go | 77 |
1 files changed, 72 insertions, 5 deletions
diff --git a/modules/base/markdown.go b/modules/base/markdown.go index 540ee58f..cf7a3193 100644 --- a/modules/base/markdown.go +++ b/modules/base/markdown.go @@ -14,6 +14,7 @@ import ( "regexp" "strings" + "github.com/Unknwon/com" "github.com/russross/blackfriday" "golang.org/x/net/html" @@ -99,12 +100,33 @@ func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, options.Renderer.Link(out, link, title, content) } +var ( + svgSuffix = []byte(".svg") + svgSuffixWithMark = []byte(".svg?") +) + func (options *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { - if len(link) > 0 && !isLink(link) { - link = []byte(path.Join(strings.Replace(options.urlPrefix, "/src/", "/raw/", 1), string(link))) + prefix := strings.Replace(options.urlPrefix, "/src/", "/raw/", 1) + if len(link) > 0 { + if isLink(link) { + // External link with .svg suffix usually means CI status. + if bytes.HasSuffix(link, svgSuffix) || bytes.Contains(link, svgSuffixWithMark) { + options.Renderer.Image(out, link, title, alt) + return + } + } else { + if link[0] != '/' { + prefix += "/" + } + link = []byte(prefix + string(link)) + } } + out.WriteString(`<a href="`) + out.Write(link) + out.WriteString(`">`) options.Renderer.Image(out, link, title, alt) + out.WriteString("</a>") } var ( @@ -159,7 +181,21 @@ func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte { return rawBytes } +func cutoutVerbosePrefix(prefix string) string { + count := 0 + for i := 0; i < len(prefix); i++ { + if prefix[i] == '/' { + count++ + } + if count >= 3 { + return prefix[:i] + } + } + return prefix +} + func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string) []byte { + urlPrefix = cutoutVerbosePrefix(urlPrefix) ms := issueIndexPattern.FindAll(rawBytes, -1) for _, m := range ms { var space string @@ -209,11 +245,21 @@ func RenderRawMarkdown(body []byte, urlPrefix string) []byte { return body } +var ( + leftAngleBracket = []byte("</") + rightAngleBracket = []byte(">") +) + +var noEndTags = []string{"img", "input", "br", "hr"} + // PostProcessMarkdown treats different types of HTML differently, // and only renders special links for plain text blocks. func PostProcessMarkdown(rawHtml []byte, urlPrefix string) []byte { + startTags := make([]string, 0, 5) var buf bytes.Buffer tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml)) + +OUTER_LOOP: for html.ErrorToken != tokenizer.Next() { token := tokenizer.Token() switch token.Type { @@ -225,17 +271,38 @@ func PostProcessMarkdown(rawHtml []byte, urlPrefix string) []byte { tagName := token.Data // If this is an excluded tag, we skip processing all output until a close tag is encountered. if strings.EqualFold("a", tagName) || strings.EqualFold("code", tagName) || strings.EqualFold("pre", tagName) { + stackNum := 1 for html.ErrorToken != tokenizer.Next() { token = tokenizer.Token() + // Copy the token to the output verbatim buf.WriteString(token.String()) - // If this is the close tag, we are done - if html.EndTagToken == token.Type && strings.EqualFold(tagName, token.Data) { - break + + if token.Type == html.StartTagToken { + stackNum++ + } + + // If this is the close tag to the outer-most, we are done + if token.Type == html.EndTagToken && strings.EqualFold(tagName, token.Data) { + stackNum-- + + if stackNum == 0 { + break + } } } + continue OUTER_LOOP + } + + if !com.IsSliceContainsStr(noEndTags, token.Data) { + startTags = append(startTags, token.Data) } + case html.EndTagToken: + buf.Write(leftAngleBracket) + buf.WriteString(startTags[len(startTags)-1]) + buf.Write(rightAngleBracket) + startTags = startTags[:len(startTags)-1] default: buf.WriteString(token.String()) } |