diff --git a/formatter.go b/formatter.go
index 406806e..f040173 100644
--- a/formatter.go
+++ b/formatter.go
@@ -33,9 +33,16 @@ import (
"maunium.net/go/mautrix/util/variationselector"
)
-var discordExtensions = goldmark.WithExtensions(extension.Strikethrough, mdext.SimpleSpoiler, mdext.DiscordUnderline, &DiscordEveryone{})
+// escapeFixer is a hacky partial fix for the difference in escaping markdown, used with escapeReplacement
+//
+// Discord allows escaping with just one backslash, e.g. \__a__,
+// but standard markdown requires both to be escaped (\_\_a__)
var escapeFixer = regexp.MustCompile(`\\(__[^_]|\*\*[^*])`)
+func escapeReplacement(s string) string {
+ return s[:2] + `\` + s[2:]
+}
+
// indentableParagraphParser is the default paragraph parser with CanAcceptIndentedLine.
// Used when disabling CodeBlockParser (as disabling it without a replacement will make indented blocks disappear).
type indentableParagraphParser struct {
@@ -48,24 +55,24 @@ func (b *indentableParagraphParser) CanAcceptIndentedLine() bool {
return true
}
-func (portal *Portal) renderDiscordMarkdownOnlyHTML(text string) string {
- text = escapeFixer.ReplaceAllStringFunc(text, func(s string) string {
- return s[:2] + `\` + s[2:]
- })
+var discordRenderer = goldmark.New(
+ goldmark.WithParser(mdext.ParserWithoutFeatures(
+ parser.NewListParser(), parser.NewListItemParser(), parser.NewHTMLBlockParser(), parser.NewRawHTMLParser(),
+ parser.NewSetextHeadingParser(), parser.NewATXHeadingParser(), parser.NewThematicBreakParser(),
+ parser.NewLinkParser(), parser.NewCodeBlockParser(),
+ )),
+ goldmark.WithParserOptions(parser.WithBlockParsers(util.Prioritized(defaultIndentableParagraphParser, 500))),
+ format.HTMLOptions,
+ goldmark.WithExtensions(extension.Strikethrough, mdext.SimpleSpoiler, mdext.DiscordUnderline, ExtDiscordEveryone, ExtDiscordTag),
+)
- mdRenderer := goldmark.New(
- goldmark.WithParser(mdext.ParserWithoutFeatures(
- parser.NewListParser(), parser.NewListItemParser(), parser.NewHTMLBlockParser(), parser.NewRawHTMLParser(),
- parser.NewSetextHeadingParser(), parser.NewATXHeadingParser(), parser.NewThematicBreakParser(),
- parser.NewLinkParser(), parser.NewCodeBlockParser(),
- )),
- goldmark.WithParserOptions(parser.WithBlockParsers(util.Prioritized(defaultIndentableParagraphParser, 500))),
- format.HTMLOptions, discordExtensions,
- goldmark.WithExtensions(&DiscordTag{portal}),
- )
+func (portal *Portal) renderDiscordMarkdownOnlyHTML(text string) string {
+ text = escapeFixer.ReplaceAllStringFunc(text, escapeReplacement)
var buf strings.Builder
- err := mdRenderer.Convert([]byte(text), &buf)
+ ctx := parser.NewContext()
+ ctx.Set(parserContextPortal, portal)
+ err := discordRenderer.Convert([]byte(text), &buf, parser.WithContext(ctx))
if err != nil {
panic(fmt.Errorf("markdown parser errored: %w", err))
}
diff --git a/formatter_everyone.go b/formatter_everyone.go
index a3a5cf8..b1aed5a 100644
--- a/formatter_everyone.go
+++ b/formatter_everyone.go
@@ -96,9 +96,11 @@ func (r *discordEveryoneHTMLRenderer) renderDiscordEveryone(w util.BufWriter, so
return
}
-type DiscordEveryone struct{}
+type discordEveryone struct{}
-func (e *DiscordEveryone) Extend(m goldmark.Markdown) {
+var ExtDiscordEveryone = &discordEveryone{}
+
+func (e *discordEveryone) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(defaultDiscordEveryoneParser, 600),
))
diff --git a/formatter_tag.go b/formatter_tag.go
index 0326d5f..46701e5 100644
--- a/formatter_tag.go
+++ b/formatter_tag.go
@@ -37,7 +37,8 @@ import (
type astDiscordTag struct {
ast.BaseInline
- id int64
+ portal *Portal
+ id int64
}
var _ ast.Node = (*astDiscordTag)(nil)
@@ -143,7 +144,10 @@ func (s *discordTagParser) Trigger() []byte {
return []byte{'<'}
}
+var parserContextPortal = parser.NewContextKey()
+
func (s *discordTagParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
+ portal := pc.Get(parserContextPortal).(*Portal)
//before := block.PrecendingCharacter()
line, _ := block.PeekLine()
match := discordTagRegex.FindSubmatch(line)
@@ -157,7 +161,7 @@ func (s *discordTagParser) Parse(parent ast.Node, block text.Reader, pc parser.C
if err != nil {
return nil
}
- tag := astDiscordTag{id: id}
+ tag := astDiscordTag{id: id, portal: portal}
tagName := string(match[1])
switch {
case tagName == "@":
@@ -199,9 +203,9 @@ func (s *discordTagParser) CloseBlock(parent ast.Node, pc parser.Context) {
// nothing to do
}
-type discordTagHTMLRenderer struct {
- portal *Portal
-}
+type discordTagHTMLRenderer struct{}
+
+var defaultDiscordTagHTMLRenderer = &discordTagHTMLRenderer{}
func (r *discordTagHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(astKindDiscordTag, r.renderDiscordMention)
@@ -259,17 +263,17 @@ func (r *discordTagHTMLRenderer) renderDiscordMention(w util.BufWriter, source [
}
switch node := n.(type) {
case *astDiscordUserMention:
- puppet := r.portal.bridge.GetPuppetByID(strconv.FormatInt(node.id, 10))
+ puppet := node.portal.bridge.GetPuppetByID(strconv.FormatInt(node.id, 10))
_, _ = fmt.Fprintf(w, `%s`, puppet.MXID, puppet.Name)
return
case *astDiscordRoleMention:
- role := r.portal.bridge.DB.Role.GetByID(r.portal.GuildID, strconv.FormatInt(node.id, 10))
+ role := node.portal.bridge.DB.Role.GetByID(node.portal.GuildID, strconv.FormatInt(node.id, 10))
if role != nil {
_, _ = fmt.Fprintf(w, `@%s`, role.Color, role.Name)
return
}
case *astDiscordChannelMention:
- portal := r.portal.bridge.GetExistingPortalByID(database.PortalKey{
+ portal := node.portal.bridge.GetExistingPortalByID(database.PortalKey{
ChannelID: strconv.FormatInt(node.id, 10),
Receiver: "",
})
@@ -282,7 +286,7 @@ func (r *discordTagHTMLRenderer) renderDiscordMention(w util.BufWriter, source [
return
}
case *astDiscordCustomEmoji:
- reactionMXC := r.portal.getEmojiMXCByDiscordID(strconv.FormatInt(node.id, 10), node.name, node.animated)
+ reactionMXC := node.portal.getEmojiMXCByDiscordID(strconv.FormatInt(node.id, 10), node.name, node.animated)
if !reactionMXC.IsEmpty() {
_, _ = fmt.Fprintf(w, `
`, reactionMXC.String(), node.name)
return
@@ -310,15 +314,15 @@ func (r *discordTagHTMLRenderer) renderDiscordMention(w util.BufWriter, source [
return
}
-type DiscordTag struct {
- Portal *Portal
-}
+type discordTag struct{}
-func (e *DiscordTag) Extend(m goldmark.Markdown) {
+var ExtDiscordTag = &discordTag{}
+
+func (e *discordTag) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(defaultDiscordTagParser, 600),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
- util.Prioritized(&discordTagHTMLRenderer{e.Portal}, 600),
+ util.Prioritized(defaultDiscordTagHTMLRenderer, 600),
))
}