github.com/mattermost/mattermost-server/v5@v5.39.3/utils/markdown.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package utils 5 6 import ( 7 "strings" 8 9 "github.com/yuin/goldmark" 10 "github.com/yuin/goldmark/ast" 11 "github.com/yuin/goldmark/extension" 12 astExt "github.com/yuin/goldmark/extension/ast" 13 "github.com/yuin/goldmark/renderer" 14 "github.com/yuin/goldmark/util" 15 ) 16 17 // StripMarkdown remove some markdown syntax 18 func StripMarkdown(markdown string) (string, error) { 19 md := goldmark.New( 20 goldmark.WithExtensions(extension.Strikethrough), 21 goldmark.WithRenderer( 22 renderer.NewRenderer(renderer.WithNodeRenderers( 23 util.Prioritized(newNotificationRenderer(), 500), 24 )), 25 ), 26 ) 27 28 var buf strings.Builder 29 if err := md.Convert([]byte(markdown), &buf); err != nil { 30 return "", err 31 } 32 33 return strings.TrimSpace(buf.String()), nil 34 } 35 36 type notificationRenderer struct { 37 } 38 39 func newNotificationRenderer() *notificationRenderer { 40 return ¬ificationRenderer{} 41 } 42 43 func (r *notificationRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { 44 // block 45 reg.Register(ast.KindDocument, r.renderDefault) 46 reg.Register(ast.KindHeading, r.renderItem) 47 reg.Register(ast.KindBlockquote, r.renderDefault) 48 reg.Register(ast.KindCodeBlock, r.renderCodeBlock) 49 reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock) 50 reg.Register(ast.KindHTMLBlock, r.renderDefault) 51 reg.Register(ast.KindList, r.renderDefault) 52 reg.Register(ast.KindListItem, r.renderItem) 53 reg.Register(ast.KindParagraph, r.renderItem) 54 reg.Register(ast.KindTextBlock, r.renderTextBlock) 55 reg.Register(ast.KindThematicBreak, r.renderDefault) 56 57 // inlines 58 reg.Register(ast.KindAutoLink, r.renderDefault) 59 reg.Register(ast.KindCodeSpan, r.renderDefault) 60 reg.Register(ast.KindEmphasis, r.renderDefault) 61 reg.Register(ast.KindImage, r.renderDefault) 62 reg.Register(ast.KindLink, r.renderDefault) 63 reg.Register(ast.KindRawHTML, r.renderDefault) 64 reg.Register(ast.KindText, r.renderText) 65 reg.Register(ast.KindString, r.renderString) 66 67 // strikethrough 68 reg.Register(astExt.KindStrikethrough, r.renderDefault) 69 } 70 71 // renderDefault renderer function to renderDefault without changes 72 func (r *notificationRenderer) renderDefault(_ util.BufWriter, _ []byte, _ ast.Node, _ bool) (ast.WalkStatus, error) { 73 return ast.WalkContinue, nil 74 } 75 76 func (r *notificationRenderer) renderItem(w util.BufWriter, _ []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { 77 if !entering { 78 if node.NextSibling() != nil { 79 _ = w.WriteByte(' ') 80 } 81 } 82 return ast.WalkContinue, nil 83 } 84 85 func (r *notificationRenderer) renderCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { 86 n := node.(*ast.CodeBlock) 87 if entering { 88 r.writeLines(w, source, n) 89 } 90 91 return ast.WalkContinue, nil 92 } 93 94 func (r *notificationRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { 95 n := node.(*ast.FencedCodeBlock) 96 if entering { 97 r.writeLines(w, source, n) 98 } 99 100 return ast.WalkContinue, nil 101 } 102 103 func (r *notificationRenderer) renderText(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { 104 if !entering { 105 return ast.WalkContinue, nil 106 } 107 n := node.(*ast.Text) 108 segment := n.Segment 109 _, _ = w.Write(segment.Value(source)) 110 if !n.IsRaw() { 111 if n.HardLineBreak() || n.SoftLineBreak() { 112 _ = w.WriteByte('\n') 113 } 114 } 115 return ast.WalkContinue, nil 116 } 117 118 func (r *notificationRenderer) renderTextBlock(w util.BufWriter, _ []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { 119 if !entering { 120 if _, ok := node.NextSibling().(ast.Node); ok && node.FirstChild() != nil { 121 _ = w.WriteByte(' ') 122 } 123 } 124 return ast.WalkContinue, nil 125 } 126 127 func (r *notificationRenderer) renderString(w util.BufWriter, _ []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { 128 if !entering { 129 return ast.WalkContinue, nil 130 } 131 n := node.(*ast.String) 132 _, _ = w.Write(n.Value) 133 return ast.WalkContinue, nil 134 } 135 136 func (r *notificationRenderer) writeLines(w util.BufWriter, source []byte, n ast.Node) { 137 for i := 0; i < n.Lines().Len(); i++ { 138 line := n.Lines().At(i) 139 value := line.Value(source) 140 _, _ = w.Write(value) 141 } 142 }