add vendoring

This commit is contained in:
Aine
2022-11-16 12:08:51 +02:00
parent 14751cbf3a
commit c1d33fe3cb
1104 changed files with 759066 additions and 0 deletions

5
vendor/maunium.net/go/mautrix/format/doc.go generated vendored Normal file
View File

@@ -0,0 +1,5 @@
// Package format contains utilities for working with Matrix HTML, specifically
// methods to parse Markdown into HTML and to parse Matrix HTML into text or markdown.
//
// https://spec.matrix.org/v1.2/client-server-api/#mroommessage-msgtypes
package format

365
vendor/maunium.net/go/mautrix/format/htmlparser.go generated vendored Normal file
View File

@@ -0,0 +1,365 @@
// Copyright (c) 2020 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"
"math"
"strconv"
"strings"
"golang.org/x/net/html"
"maunium.net/go/mautrix/id"
)
type Context map[string]interface{}
type TextConverter func(string, Context) string
type SpoilerConverter func(text, reason string, ctx Context) string
type LinkConverter func(text, href string, ctx Context) string
type ColorConverter func(text, fg, bg string, ctx Context) string
type CodeBlockConverter func(code, language string, ctx Context) string
type PillConverter func(displayname, mxid, eventID string, ctx Context) string
func DefaultPillConverter(displayname, mxid, eventID string, _ Context) string {
switch {
case len(mxid) == 0, mxid[0] == '@':
// User link, always just show the displayname
return displayname
case len(eventID) > 0:
// Event ID link, always just show the link
return fmt.Sprintf("https://matrix.to/#/%s/%s", mxid, eventID)
case mxid[0] == '!' && displayname == mxid:
// Room ID link with no separate display text, just show the link
return fmt.Sprintf("https://matrix.to/#/%s", mxid)
case mxid[0] == '#':
// Room alias link, just show the alias
return mxid
default:
// Other link (e.g. room ID link with display text), show text and link
return fmt.Sprintf("%s (https://matrix.to/#/%s)", displayname, mxid)
}
}
// HTMLParser is a somewhat customizable Matrix HTML parser.
type HTMLParser struct {
PillConverter PillConverter
TabsToSpaces int
Newline string
HorizontalLine string
BoldConverter TextConverter
ItalicConverter TextConverter
StrikethroughConverter TextConverter
UnderlineConverter TextConverter
LinkConverter LinkConverter
SpoilerConverter SpoilerConverter
ColorConverter ColorConverter
MonospaceBlockConverter CodeBlockConverter
MonospaceConverter TextConverter
TextConverter TextConverter
}
// TaggedString is a string that also contains a HTML tag.
type TaggedString struct {
string
tag string
}
func (parser *HTMLParser) maybeGetAttribute(node *html.Node, attribute string) (string, bool) {
for _, attr := range node.Attr {
if attr.Key == attribute {
return attr.Val, true
}
}
return "", false
}
func (parser *HTMLParser) getAttribute(node *html.Node, attribute string) string {
val, _ := parser.maybeGetAttribute(node, attribute)
return val
}
// Digits counts the number of digits in a non-negative integer.
func Digits(num int) int {
return int(math.Floor(math.Log10(float64(num))) + 1)
}
func (parser *HTMLParser) listToString(node *html.Node, stripLinebreak bool, ctx Context) string {
ordered := node.Data == "ol"
taggedChildren := parser.nodeToTaggedStrings(node.FirstChild, stripLinebreak, ctx)
counter := 1
indentLength := 0
if ordered {
start := parser.getAttribute(node, "start")
if len(start) > 0 {
counter, _ = strconv.Atoi(start)
}
longestIndex := (counter - 1) + len(taggedChildren)
indentLength = Digits(longestIndex)
}
indent := strings.Repeat(" ", indentLength+2)
var children []string
for _, child := range taggedChildren {
if child.tag != "li" {
continue
}
var prefix string
// TODO make bullets and numbering configurable
if ordered {
indexPadding := indentLength - Digits(counter)
prefix = fmt.Sprintf("%d. %s", counter, strings.Repeat(" ", indexPadding))
} else {
prefix = "* "
}
str := prefix + child.string
counter++
parts := strings.Split(str, "\n")
for i, part := range parts[1:] {
parts[i+1] = indent + part
}
str = strings.Join(parts, "\n")
children = append(children, str)
}
return strings.Join(children, "\n")
}
func (parser *HTMLParser) basicFormatToString(node *html.Node, stripLinebreak bool, ctx Context) string {
str := parser.nodeToTagAwareString(node.FirstChild, stripLinebreak, ctx)
switch node.Data {
case "b", "strong":
if parser.BoldConverter != nil {
return parser.BoldConverter(str, ctx)
}
return fmt.Sprintf("**%s**", str)
case "i", "em":
if parser.ItalicConverter != nil {
return parser.ItalicConverter(str, ctx)
}
return fmt.Sprintf("_%s_", str)
case "s", "del", "strike":
if parser.StrikethroughConverter != nil {
return parser.StrikethroughConverter(str, ctx)
}
return fmt.Sprintf("~~%s~~", str)
case "u", "ins":
if parser.UnderlineConverter != nil {
return parser.UnderlineConverter(str, ctx)
}
case "tt", "code":
if parser.MonospaceConverter != nil {
return parser.MonospaceConverter(str, ctx)
}
return fmt.Sprintf("`%s`", str)
}
return str
}
func (parser *HTMLParser) spanToString(node *html.Node, stripLinebreak bool, ctx Context) string {
str := parser.nodeToTagAwareString(node.FirstChild, stripLinebreak, ctx)
if node.Data == "span" {
reason, isSpoiler := parser.maybeGetAttribute(node, "data-mx-spoiler")
if isSpoiler {
if parser.SpoilerConverter != nil {
str = parser.SpoilerConverter(str, reason, ctx)
} else if len(reason) > 0 {
str = fmt.Sprintf("||%s|%s||", reason, str)
} else {
str = fmt.Sprintf("||%s||", str)
}
}
}
if parser.ColorConverter != nil {
fg := parser.getAttribute(node, "data-mx-color")
if len(fg) == 0 && node.Data == "font" {
fg = parser.getAttribute(node, "color")
}
bg := parser.getAttribute(node, "data-mx-bg-color")
if len(bg) > 0 || len(fg) > 0 {
str = parser.ColorConverter(str, fg, bg, ctx)
}
}
return str
}
func (parser *HTMLParser) headerToString(node *html.Node, stripLinebreak bool, ctx Context) string {
children := parser.nodeToStrings(node.FirstChild, stripLinebreak, ctx)
length := int(node.Data[1] - '0')
prefix := strings.Repeat("#", length) + " "
return prefix + strings.Join(children, "")
}
func (parser *HTMLParser) blockquoteToString(node *html.Node, stripLinebreak bool, ctx Context) string {
str := parser.nodeToTagAwareString(node.FirstChild, stripLinebreak, ctx)
childrenArr := strings.Split(strings.TrimSpace(str), "\n")
// TODO make blockquote prefix configurable
for index, child := range childrenArr {
childrenArr[index] = "> " + child
}
return strings.Join(childrenArr, "\n")
}
func (parser *HTMLParser) linkToString(node *html.Node, stripLinebreak bool, ctx Context) string {
str := parser.nodeToTagAwareString(node.FirstChild, stripLinebreak, ctx)
href := parser.getAttribute(node, "href")
if len(href) == 0 {
return str
}
if parser.PillConverter != nil {
parsedMatrix, err := id.ParseMatrixURIOrMatrixToURL(href)
if err == nil && parsedMatrix != nil {
return parser.PillConverter(str, parsedMatrix.PrimaryIdentifier(), parsedMatrix.SecondaryIdentifier(), ctx)
}
}
if parser.LinkConverter != nil {
return parser.LinkConverter(str, href, ctx)
} else if str == href {
return str
}
return fmt.Sprintf("%s (%s)", str, href)
}
func (parser *HTMLParser) tagToString(node *html.Node, stripLinebreak bool, ctx Context) string {
switch node.Data {
case "blockquote":
return parser.blockquoteToString(node, stripLinebreak, ctx)
case "ol", "ul":
return parser.listToString(node, stripLinebreak, ctx)
case "h1", "h2", "h3", "h4", "h5", "h6":
return parser.headerToString(node, stripLinebreak, ctx)
case "br":
return parser.Newline
case "b", "strong", "i", "em", "s", "strike", "del", "u", "ins", "tt", "code":
return parser.basicFormatToString(node, stripLinebreak, ctx)
case "span", "font":
return parser.spanToString(node, stripLinebreak, ctx)
case "a":
return parser.linkToString(node, stripLinebreak, ctx)
case "p":
return parser.nodeToTagAwareString(node.FirstChild, stripLinebreak, ctx)
case "hr":
return parser.HorizontalLine
case "pre":
var preStr, language string
if node.FirstChild != nil && node.FirstChild.Type == html.ElementNode && node.FirstChild.Data == "code" {
class := parser.getAttribute(node.FirstChild, "class")
if strings.HasPrefix(class, "language-") {
language = class[len("language-"):]
}
preStr = parser.nodeToString(node.FirstChild.FirstChild, false, ctx)
} else {
preStr = parser.nodeToString(node.FirstChild, false, ctx)
}
if parser.MonospaceBlockConverter != nil {
return parser.MonospaceBlockConverter(preStr, language, ctx)
}
if len(preStr) == 0 || preStr[len(preStr)-1] != '\n' {
preStr += "\n"
}
return fmt.Sprintf("```%s\n%s```", language, preStr)
default:
return parser.nodeToTagAwareString(node.FirstChild, stripLinebreak, ctx)
}
}
func (parser *HTMLParser) singleNodeToString(node *html.Node, stripLinebreak bool, ctx Context) TaggedString {
switch node.Type {
case html.TextNode:
if stripLinebreak {
node.Data = strings.Replace(node.Data, "\n", "", -1)
}
if parser.TextConverter != nil {
node.Data = parser.TextConverter(node.Data, ctx)
}
return TaggedString{node.Data, "text"}
case html.ElementNode:
return TaggedString{parser.tagToString(node, stripLinebreak, ctx), node.Data}
case html.DocumentNode:
return TaggedString{parser.nodeToTagAwareString(node.FirstChild, stripLinebreak, ctx), "html"}
default:
return TaggedString{"", "unknown"}
}
}
func (parser *HTMLParser) nodeToTaggedStrings(node *html.Node, stripLinebreak bool, ctx Context) (strs []TaggedString) {
for ; node != nil; node = node.NextSibling {
strs = append(strs, parser.singleNodeToString(node, stripLinebreak, ctx))
}
return
}
var BlockTags = []string{"p", "h1", "h2", "h3", "h4", "h5", "h6", "ol", "ul", "pre", "blockquote", "div", "hr", "table"}
func (parser *HTMLParser) isBlockTag(tag string) bool {
for _, blockTag := range BlockTags {
if tag == blockTag {
return true
}
}
return false
}
func (parser *HTMLParser) nodeToTagAwareString(node *html.Node, stripLinebreak bool, ctx Context) string {
strs := parser.nodeToTaggedStrings(node, stripLinebreak, ctx)
var output strings.Builder
for _, str := range strs {
tstr := str.string
if parser.isBlockTag(str.tag) {
tstr = fmt.Sprintf("\n%s\n", tstr)
}
output.WriteString(tstr)
}
return strings.TrimSpace(output.String())
}
func (parser *HTMLParser) nodeToStrings(node *html.Node, stripLinebreak bool, ctx Context) (strs []string) {
for ; node != nil; node = node.NextSibling {
strs = append(strs, parser.singleNodeToString(node, stripLinebreak, ctx).string)
}
return
}
func (parser *HTMLParser) nodeToString(node *html.Node, stripLinebreak bool, ctx Context) string {
return strings.Join(parser.nodeToStrings(node, stripLinebreak, ctx), "")
}
// Parse converts Matrix HTML into text using the settings in this parser.
func (parser *HTMLParser) Parse(htmlData string, ctx Context) string {
if parser.TabsToSpaces >= 0 {
htmlData = strings.Replace(htmlData, "\t", strings.Repeat(" ", parser.TabsToSpaces), -1)
}
node, _ := html.Parse(strings.NewReader(htmlData))
return parser.nodeToTagAwareString(node, true, ctx)
}
// HTMLToText converts Matrix HTML into text with the default settings.
func HTMLToText(html string) string {
return (&HTMLParser{
TabsToSpaces: 4,
Newline: "\n",
HorizontalLine: "\n---\n",
PillConverter: DefaultPillConverter,
}).Parse(html, make(Context))
}
// HTMLToMarkdown converts Matrix HTML into markdown with the default settings.
//
// Currently, the only difference to HTMLToText is how links are formatted.
func HTMLToMarkdown(html string) string {
return (&HTMLParser{
TabsToSpaces: 4,
Newline: "\n",
HorizontalLine: "\n---\n",
PillConverter: DefaultPillConverter,
LinkConverter: func(text, href string, ctx Context) string {
if text == href {
return text
}
return fmt.Sprintf("[%s](%s)", text, href)
},
}).Parse(html, make(Context))
}

86
vendor/maunium.net/go/mautrix/format/markdown.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
// 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,
}
}
}

View File

@@ -0,0 +1,137 @@
// 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 mdext
import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
type astDiscordUnderline struct {
ast.BaseInline
}
func (n *astDiscordUnderline) Dump(source []byte, level int) {
ast.DumpHelper(n, source, level, nil, nil)
}
var astKindDiscordUnderline = ast.NewNodeKind("DiscordUnderline")
func (n *astDiscordUnderline) Kind() ast.NodeKind {
return astKindDiscordUnderline
}
type discordUnderlineDelimiterProcessor struct{}
func (p *discordUnderlineDelimiterProcessor) IsDelimiter(b byte) bool {
return b == '_'
}
func (p *discordUnderlineDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool {
return opener.Char == closer.Char
}
func (p *discordUnderlineDelimiterProcessor) OnMatch(consumes int) ast.Node {
if consumes == 1 {
// Slightly hacky hack: if the delimiter parser tries to give us text wrapped with a single underline,
// send it over to the emphasis area instead of returning an underline node.
return ast.NewEmphasis(consumes)
}
return &astDiscordUnderline{}
}
var defaultDiscordUnderlineDelimiterProcessor = &discordUnderlineDelimiterProcessor{}
type discordUnderlineParser struct{}
var defaultDiscordUnderlineParser = &discordUnderlineParser{}
// NewDiscordUnderlineParser return a new InlineParser that parses
// Discord underline expressions.
func NewDiscordUnderlineParser() parser.InlineParser {
return defaultDiscordUnderlineParser
}
func (s *discordUnderlineParser) Trigger() []byte {
return []byte{'_'}
}
func (s *discordUnderlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
before := block.PrecendingCharacter()
line, segment := block.PeekLine()
node := parser.ScanDelimiter(line, before, 2, defaultDiscordUnderlineDelimiterProcessor)
if node == nil {
return nil
}
node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
block.Advance(node.OriginalLength)
pc.PushDelimiter(node)
return node
}
func (s *discordUnderlineParser) CloseBlock(parent ast.Node, pc parser.Context) {
// nothing to do
}
// discordUnderlineHTMLRenderer is a renderer.NodeRenderer implementation that
// renders discord underline nodes.
type discordUnderlineHTMLRenderer struct {
html.Config
}
// NewDiscordUnderlineHTMLRenderer returns a new discordUnderlineHTMLRenderer.
func NewDiscordUnderlineHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &discordUnderlineHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
func (r *discordUnderlineHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(astKindDiscordUnderline, r.renderDiscordUnderline)
}
var DiscordUnderlineAttributeFilter = html.GlobalAttributeFilter
func (r *discordUnderlineHTMLRenderer) renderDiscordUnderline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("<u")
html.RenderAttributes(w, n, DiscordUnderlineAttributeFilter)
_ = w.WriteByte('>')
} else {
_, _ = w.WriteString("<u>")
}
} else {
_, _ = w.WriteString("</u>")
}
return ast.WalkContinue, nil
}
type discordUnderline struct{}
// DiscordUnderline is an extension that allow you to use underline expression like '__text__' .
var DiscordUnderline = &discordUnderline{}
func (e *discordUnderline) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
// This must be a higher priority (= lower priority number) than the emphasis parser
// in https://github.com/yuin/goldmark/blob/v1.4.12/parser/parser.go#L601
util.Prioritized(NewDiscordUnderlineParser(), 450),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewDiscordUnderlineHTMLRenderer(), 500),
))
}

61
vendor/maunium.net/go/mautrix/format/mdext/nohtml.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
// 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 mdext
import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/util"
)
type extEscapeHTML struct{}
type escapingHTMLRenderer struct{}
// EscapeHTML is an extension that escapes HTML in the input markdown instead of passing it through as-is.
var EscapeHTML = &extEscapeHTML{}
var defaultEHR = &escapingHTMLRenderer{}
func (eeh *extEscapeHTML) Extend(m goldmark.Markdown) {
m.Renderer().AddOptions(renderer.WithNodeRenderers(util.Prioritized(defaultEHR, 0)))
}
func (ehr *escapingHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindHTMLBlock, ehr.renderHTMLBlock)
reg.Register(ast.KindRawHTML, ehr.renderRawHTML)
}
func (ehr *escapingHTMLRenderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkSkipChildren, nil
}
n := node.(*ast.RawHTML)
l := n.Segments.Len()
for i := 0; i < l; i++ {
segment := n.Segments.At(i)
html.DefaultWriter.RawWrite(w, segment.Value(source))
}
return ast.WalkSkipChildren, nil
}
func (ehr *escapingHTMLRenderer) renderHTMLBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.HTMLBlock)
if entering {
l := n.Lines().Len()
for i := 0; i < l; i++ {
line := n.Lines().At(i)
html.DefaultWriter.RawWrite(w, line.Value(source))
}
} else {
if n.HasClosure() {
closure := n.ClosureLine
html.DefaultWriter.RawWrite(w, closure.Value(source))
}
}
return ast.WalkContinue, nil
}

View File

@@ -0,0 +1,62 @@
// 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 mdext
import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
type simpleSpoilerParser struct{}
var defaultSimpleSpoilerParser = &simpleSpoilerParser{}
func NewSimpleSpoilerParser() parser.InlineParser {
return defaultSimpleSpoilerParser
}
func (s *simpleSpoilerParser) Trigger() []byte {
return []byte{'|'}
}
func (s *simpleSpoilerParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
// This is basically copied from https://github.com/yuin/goldmark/blob/master/extension/strikethrough.go
before := block.PrecendingCharacter()
line, segment := block.PeekLine()
node := parser.ScanDelimiter(line, before, 2, defaultSpoilerDelimiterProcessor)
if node == nil {
return nil
}
node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
block.Advance(node.OriginalLength)
pc.PushDelimiter(node)
return node
}
func (s *simpleSpoilerParser) CloseBlock(parent ast.Node, pc parser.Context) {
// nothing to do
}
type simpleSpoiler struct{}
// SimpleSpoiler is an extension that allow you to use simple spoiler expression like '||text||' .
//
// For spoilers with reasons ('||reason|text||'), use the Spoiler extension.
var SimpleSpoiler = &simpleSpoiler{}
func (e *simpleSpoiler) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewSimpleSpoilerParser(), 500),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewSpoilerHTMLRenderer(), 500),
))
}

168
vendor/maunium.net/go/mautrix/format/mdext/spoiler.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
// 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 mdext
import (
"bytes"
"fmt"
stdhtml "html"
"regexp"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
var astKindSpoiler = ast.NewNodeKind("Spoiler")
type astSpoiler struct {
ast.BaseInline
reason string
}
func (n *astSpoiler) Dump(source []byte, level int) {
ast.DumpHelper(n, source, level, nil, nil)
}
func (n *astSpoiler) Kind() ast.NodeKind {
return astKindSpoiler
}
type spoilerDelimiterProcessor struct{}
var defaultSpoilerDelimiterProcessor = &spoilerDelimiterProcessor{}
func (p *spoilerDelimiterProcessor) IsDelimiter(b byte) bool {
return b == '|'
}
func (p *spoilerDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool {
return opener.Char == closer.Char
}
func (p *spoilerDelimiterProcessor) OnMatch(consumes int) ast.Node {
return &astSpoiler{}
}
type spoilerParser struct{}
var defaultSpoilerParser = &spoilerParser{}
func NewSpoilerParser() parser.InlineParser {
return defaultSpoilerParser
}
func (s *spoilerParser) Trigger() []byte {
return []byte{'|'}
}
var spoilerRegex = regexp.MustCompile(`^\|\|(?:([^|]+?)\|[^|])?`)
var spoilerContextKey = parser.NewContextKey()
type spoilerContext struct {
reason string
segment text.Segment
bottom *parser.Delimiter
}
func (s *spoilerParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
line, segment := block.PeekLine()
if spoiler, ok := pc.Get(spoilerContextKey).(spoilerContext); ok {
if !bytes.HasPrefix(line, []byte("||")) {
return nil
}
block.Advance(2)
pc.Set(spoilerContextKey, nil)
n := &astSpoiler{
BaseInline: ast.BaseInline{},
reason: spoiler.reason,
}
parser.ProcessDelimiters(spoiler.bottom, pc)
var c ast.Node = spoiler.bottom
for c != nil {
next := c.NextSibling()
parent.RemoveChild(parent, c)
n.AppendChild(n, c)
c = next
}
return n
}
match := spoilerRegex.FindSubmatch(line)
if match == nil {
return nil
}
length := 2
reason := string(match[1])
if len(reason) > 0 {
length += len(match[1]) + 1
}
block.Advance(length)
delim := parser.NewDelimiter(true, false, length, '|', defaultSpoilerDelimiterProcessor)
pc.Set(spoilerContextKey, spoilerContext{
reason: reason,
segment: segment,
bottom: delim,
})
return delim
}
func (s *spoilerParser) CloseBlock(parent ast.Node, pc parser.Context) {
// nothing to do
}
type spoilerHTMLRenderer struct {
html.Config
}
func NewSpoilerHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &spoilerHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
func (r *spoilerHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(astKindSpoiler, r.renderSpoiler)
}
func (r *spoilerHTMLRenderer) renderSpoiler(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
node := n.(*astSpoiler)
if len(node.reason) == 0 {
_, _ = w.WriteString("<span data-mx-spoiler>")
} else {
_, _ = fmt.Fprintf(w, `<span data-mx-spoiler="%s">`, stdhtml.EscapeString(node.reason))
}
} else {
_, _ = w.WriteString("</span>")
}
return ast.WalkContinue, nil
}
type extSpoiler struct{}
// Spoiler is an extension that allow you to use spoiler expression like '||text||' or ||reason|text|| .
//
// There are some types of nested formatting that aren't supported with advanced spoilers.
// The SimpleSpoiler extension that doesn't support reasons can be used to work around those.
var Spoiler = &extSpoiler{}
func (e *extSpoiler) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewSpoilerParser(), 500),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewSpoilerHTMLRenderer(), 500),
))
}