diff options
| -rw-r--r-- | internal/text/markdown.go | 49 | ||||
| -rw-r--r-- | internal/text/markdown_test.go | 18 | 
2 files changed, 60 insertions, 7 deletions
diff --git a/internal/text/markdown.go b/internal/text/markdown.go index 13fb93378..1382cbf61 100644 --- a/internal/text/markdown.go +++ b/internal/text/markdown.go @@ -19,7 +19,9 @@  package text  import ( +	"bytes"  	"context" +	"io"  	"github.com/russross/blackfriday/v2"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -33,18 +35,51 @@ var (  	m            *minify.M  ) +type renderer struct { +	f        *formatter +	ctx      context.Context +	mentions []*gtsmodel.Mention +	tags     []*gtsmodel.Tag +	blackfriday.HTMLRenderer +} + +func (r *renderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { +	if node.Type == blackfriday.Text { +		// call RenderNode to do the html escaping +		var buff bytes.Buffer +		status := r.HTMLRenderer.RenderNode(&buff, node, entering) + +		html := buff.String() +		html = r.f.ReplaceTags(r.ctx, html, r.tags) +		html = r.f.ReplaceMentions(r.ctx, html, r.mentions) + +		// we don't have much recourse if this fails +		_, err := io.WriteString(w, html) +		if err != nil { +			log.Errorf("error outputting markdown text: %s", err) +		} +		return status +	} +	return r.HTMLRenderer.RenderNode(w, node, entering) +} +  func (f *formatter) FromMarkdown(ctx context.Context, md string, mentions []*gtsmodel.Mention, tags []*gtsmodel.Tag) string { -	// format tags nicely -	content := f.ReplaceTags(ctx, md, tags) -	// format mentions nicely -	content = f.ReplaceMentions(ctx, content, mentions) +	renderer := &renderer{ +		f:        f, +		ctx:      ctx, +		mentions: mentions, +		tags:     tags, +		HTMLRenderer: *blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{ +			Flags: blackfriday.CommonHTMLFlags, +		}), +	} -	// parse markdown -	contentBytes := blackfriday.Run([]byte(content), blackfriday.WithExtensions(bfExtensions)) +	// parse markdown, use custom renderer to add hashtag/mention links +	contentBytes := blackfriday.Run([]byte(md), blackfriday.WithExtensions(bfExtensions), blackfriday.WithRenderer(renderer))  	// clean anything dangerous out of it -	content = SanitizeHTML(string(contentBytes)) +	content := SanitizeHTML(string(contentBytes))  	if m == nil {  		m = minify.New() diff --git a/internal/text/markdown_test.go b/internal/text/markdown_test.go index 3e156f43e..4c9483c7e 100644 --- a/internal/text/markdown_test.go +++ b/internal/text/markdown_test.go @@ -65,6 +65,10 @@ const (  	mdWithFootnoteExpected          = "<p>fox mulder,fbi.<sup id=\"fnref:1\"><a href=\"#fn:1\" rel=\"nofollow noreferrer\">1</a></sup></p><div><hr><ol><li id=\"fn:1\">federated bureau of investigation<br></li></ol></div>"  	mdWithBlockQuote                = "get ready, there's a block quote coming:\n\n>line1\n>line2\n>\n>line3\n\n"  	mdWithBlockQuoteExpected        = "<p>get ready, there’s a block quote coming:</p><blockquote><p>line1<br>line2</p><p>line3</p></blockquote>" +	mdHashtagAndCodeBlock           = "#Hashtag\n\n```\n#Hashtag\n```" +	mdHashtagAndCodeBlockExpected   = "<p><a href=\"http://localhost:8080/tags/Hashtag\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>Hashtag</span></a></p><pre><code>#Hashtag\n</code></pre>" +	mdMentionAndCodeBlock           = "@the_mighty_zork\n\n```\n@the_mighty_zork\n```" +	mdMentionAndCodeBlockExpected   = "<p><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span></p><pre><code>@the_mighty_zork\n</code></pre>"  )  type MarkdownTestSuite struct { @@ -133,6 +137,20 @@ func (suite *MarkdownTestSuite) TestParseWithBlockquote() {  	suite.Equal(mdWithBlockQuoteExpected, s)  } +func (suite *MarkdownTestSuite) TestParseHashtagWithCodeBlock() { +	s := suite.formatter.FromMarkdown(context.Background(), mdHashtagAndCodeBlock, nil, []*gtsmodel.Tag{ +		suite.testTags["Hashtag"], +	}) +	suite.Equal(mdHashtagAndCodeBlockExpected, s) +} + +func (suite *MarkdownTestSuite) TestParseMentionWithCodeBlock() { +	s := suite.formatter.FromMarkdown(context.Background(), mdMentionAndCodeBlock, []*gtsmodel.Mention{ +		suite.testMentions["local_user_2_mention_zork"], +	}, nil) +	suite.Equal(mdMentionAndCodeBlockExpected, s) +} +  func TestMarkdownTestSuite(t *testing.T) {  	suite.Run(t, new(MarkdownTestSuite))  }  | 
