2019-10-14 00:29:10 +02:00
|
|
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package mdstripper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-10-31 02:06:25 +01:00
|
|
|
"io"
|
2019-10-14 00:29:10 +02:00
|
|
|
|
2019-10-31 02:06:25 +01:00
|
|
|
"github.com/russross/blackfriday/v2"
|
2019-10-14 00:29:10 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// MarkdownStripper extends blackfriday.Renderer
|
|
|
|
type MarkdownStripper struct {
|
|
|
|
links []string
|
|
|
|
coallesce bool
|
2019-10-31 02:06:25 +01:00
|
|
|
empty bool
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
blackfridayExtensions = 0 |
|
2019-10-31 02:06:25 +01:00
|
|
|
blackfriday.NoIntraEmphasis |
|
|
|
|
blackfriday.Tables |
|
|
|
|
blackfriday.FencedCode |
|
|
|
|
blackfriday.Strikethrough |
|
|
|
|
blackfriday.NoEmptyLineBeforeBlock |
|
|
|
|
blackfriday.DefinitionLists |
|
|
|
|
blackfriday.Footnotes |
|
|
|
|
blackfriday.HeadingIDs |
|
|
|
|
blackfriday.AutoHeadingIDs |
|
2019-10-14 00:29:10 +02:00
|
|
|
// Not included in modules/markup/markdown/markdown.go;
|
|
|
|
// required here to process inline links
|
2019-10-31 02:06:25 +01:00
|
|
|
blackfriday.Autolink
|
2019-10-14 00:29:10 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// StripMarkdown parses markdown content by removing all markup and code blocks
|
|
|
|
// in order to extract links and other references
|
|
|
|
func StripMarkdown(rawBytes []byte) (string, []string) {
|
2019-10-31 02:06:25 +01:00
|
|
|
buf, links := StripMarkdownBytes(rawBytes)
|
|
|
|
return string(buf), links
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// StripMarkdownBytes parses markdown content by removing all markup and code blocks
|
|
|
|
// in order to extract links and other references
|
|
|
|
func StripMarkdownBytes(rawBytes []byte) ([]byte, []string) {
|
|
|
|
stripper := &MarkdownStripper{
|
|
|
|
links: make([]string, 0, 10),
|
2019-10-31 02:06:25 +01:00
|
|
|
empty: true,
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 02:06:25 +01:00
|
|
|
parser := blackfriday.New(blackfriday.WithRenderer(stripper), blackfriday.WithExtensions(blackfridayExtensions))
|
|
|
|
ast := parser.Parse(rawBytes)
|
|
|
|
var buf bytes.Buffer
|
|
|
|
stripper.RenderHeader(&buf, ast)
|
|
|
|
ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
|
|
|
return stripper.RenderNode(&buf, node, entering)
|
|
|
|
})
|
|
|
|
stripper.RenderFooter(&buf, ast)
|
|
|
|
return buf.Bytes(), stripper.GetLinks()
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenderNode is the main rendering method. It will be called once for
|
|
|
|
// every leaf node and twice for every non-leaf node (first with
|
|
|
|
// entering=true, then with entering=false). The method should write its
|
|
|
|
// rendition of the node to the supplied writer w.
|
|
|
|
func (r *MarkdownStripper) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
|
|
|
if !entering {
|
|
|
|
return blackfriday.GoToNext
|
|
|
|
}
|
|
|
|
switch node.Type {
|
|
|
|
case blackfriday.Text:
|
|
|
|
r.processString(w, node.Literal, node.Parent == nil)
|
|
|
|
return blackfriday.GoToNext
|
|
|
|
case blackfriday.Link:
|
|
|
|
r.processLink(w, node.LinkData.Destination)
|
|
|
|
r.coallesce = false
|
|
|
|
return blackfriday.SkipChildren
|
|
|
|
}
|
2019-10-14 00:29:10 +02:00
|
|
|
r.coallesce = false
|
2019-10-31 02:06:25 +01:00
|
|
|
return blackfriday.GoToNext
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 02:06:25 +01:00
|
|
|
// RenderHeader is a method that allows the renderer to produce some
|
|
|
|
// content preceding the main body of the output document.
|
|
|
|
func (r *MarkdownStripper) RenderHeader(w io.Writer, ast *blackfriday.Node) {
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 02:06:25 +01:00
|
|
|
// RenderFooter is a symmetric counterpart of RenderHeader.
|
|
|
|
func (r *MarkdownStripper) RenderFooter(w io.Writer, ast *blackfriday.Node) {
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 02:06:25 +01:00
|
|
|
func (r *MarkdownStripper) doubleSpace(w io.Writer) {
|
|
|
|
if !r.empty {
|
|
|
|
_, _ = w.Write([]byte{'\n'})
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-31 02:06:25 +01:00
|
|
|
func (r *MarkdownStripper) processString(w io.Writer, text []byte, coallesce bool) {
|
2019-10-14 00:29:10 +02:00
|
|
|
// Always break-up words
|
|
|
|
if !coallesce || !r.coallesce {
|
2019-10-31 02:06:25 +01:00
|
|
|
r.doubleSpace(w)
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
2019-10-31 02:06:25 +01:00
|
|
|
_, _ = w.Write(text)
|
2019-10-14 00:29:10 +02:00
|
|
|
r.coallesce = coallesce
|
2019-10-31 02:06:25 +01:00
|
|
|
r.empty = false
|
2019-10-14 00:29:10 +02:00
|
|
|
}
|
2019-10-31 02:06:25 +01:00
|
|
|
|
|
|
|
func (r *MarkdownStripper) processLink(w io.Writer, link []byte) {
|
2019-10-14 00:29:10 +02:00
|
|
|
// Links are processed out of band
|
|
|
|
r.links = append(r.links, string(link))
|
|
|
|
r.coallesce = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLinks returns the list of link data collected while parsing
|
|
|
|
func (r *MarkdownStripper) GetLinks() []string {
|
|
|
|
return r.links
|
|
|
|
}
|