mautrix-go/format/markdown.go

87 lines
2.4 KiB
Go

// Copyright (c) 2022 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package format
import (
"fmt"
"strings"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/renderer/html"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format/mdext"
)
const paragraphStart = "<p>"
const paragraphEnd = "</p>"
var Extensions = goldmark.WithExtensions(extension.Strikethrough, extension.Table, mdext.Spoiler)
var HTMLOptions = goldmark.WithRendererOptions(html.WithHardWraps(), html.WithUnsafe())
var withHTML = goldmark.New(Extensions, HTMLOptions)
var noHTML = goldmark.New(Extensions, HTMLOptions, goldmark.WithExtensions(mdext.EscapeHTML))
// UnwrapSingleParagraph removes paragraph tags surrounding a string if the string only contains a single paragraph.
func UnwrapSingleParagraph(html string) string {
html = strings.TrimRight(html, "\n")
if strings.HasPrefix(html, paragraphStart) && strings.HasSuffix(html, paragraphEnd) {
htmlBodyWithoutP := html[len(paragraphStart) : len(html)-len(paragraphEnd)]
if !strings.Contains(htmlBodyWithoutP, paragraphStart) {
return htmlBodyWithoutP
}
}
return html
}
func RenderMarkdownCustom(text string, renderer goldmark.Markdown) event.MessageEventContent {
var buf strings.Builder
err := renderer.Convert([]byte(text), &buf)
if err != nil {
panic(fmt.Errorf("markdown parser errored: %w", err))
}
htmlBody := UnwrapSingleParagraph(buf.String())
return HTMLToContent(htmlBody)
}
func HTMLToContent(html string) event.MessageEventContent {
text := HTMLToMarkdown(html)
if html != text {
return event.MessageEventContent{
FormattedBody: html,
Format: event.FormatHTML,
MsgType: event.MsgText,
Body: text,
}
}
return event.MessageEventContent{
MsgType: event.MsgText,
Body: text,
}
}
func RenderMarkdown(text string, allowMarkdown, allowHTML bool) event.MessageEventContent {
var htmlBody string
if allowMarkdown {
rndr := withHTML
if !allowHTML {
rndr = noHTML
}
return RenderMarkdownCustom(text, rndr)
} else if allowHTML {
htmlBody = strings.Replace(text, "\n", "<br>", -1)
return HTMLToContent(htmlBody)
} else {
return event.MessageEventContent{
MsgType: event.MsgText,
Body: text,
}
}
}