cchat-discord/internal/segments/md.go

127 lines
3.3 KiB
Go

package segments
import (
"github.com/diamondburned/arikawa/v2/discord"
"github.com/diamondburned/arikawa/v2/state/store"
"github.com/diamondburned/cchat-discord/internal/segments/embed"
"github.com/diamondburned/cchat-discord/internal/segments/renderer"
"github.com/diamondburned/cchat/text"
"github.com/diamondburned/ningen/v2/md"
_ "github.com/diamondburned/cchat-discord/internal/segments/blockquote"
_ "github.com/diamondburned/cchat-discord/internal/segments/codeblock"
_ "github.com/diamondburned/cchat-discord/internal/segments/colored"
_ "github.com/diamondburned/cchat-discord/internal/segments/emoji"
_ "github.com/diamondburned/cchat-discord/internal/segments/inline"
_ "github.com/diamondburned/cchat-discord/internal/segments/link"
_ "github.com/diamondburned/cchat-discord/internal/segments/mention"
)
func ParseMessage(m *discord.Message, s store.Cabinet) text.Rich {
var rich text.Rich
ParseMessageRich(&rich, m, s)
return rich
}
func ParseMessageRich(rich *text.Rich, m *discord.Message, s store.Cabinet) {
var content = []byte(m.Content)
if len(content) == 0 {
return
}
var node = md.ParseWithMessage(content, s, m, true)
r := renderer.New(content)
r.Buffer.Grow(len(rich.Content))
r.Buffer.WriteString(rich.Content)
// Register the needed states for some renderers.
r.WithState(m, s)
// Render the main message body.
r.Walk(node)
// Render the extra bits.
embed.RenderAttachments(r, m.Attachments)
embed.RenderEmbeds(r, m.Embeds, m, s)
rich.Content = r.String()
rich.Segments = append(rich.Segments, r.Segments...)
}
func ParseWithMessage(b []byte, m *discord.Message, s store.Cabinet) text.Rich {
var rich text.Rich
ParseWithMessageRich(&rich, b, m, s)
return rich
}
func ParseWithMessageRich(rich *text.Rich, b []byte, m *discord.Message, s store.Cabinet) {
if len(b) == 0 {
return
}
node := md.ParseWithMessage(b, s, m, true)
r := renderer.New(b)
r.Buffer.Grow(len(rich.Content))
r.Buffer.WriteString(rich.Content)
r.WithState(m, s)
r.Walk(node)
rich.Content = r.String()
rich.Segments = append(rich.Segments, r.Segments...)
}
// Ellipsize caps the length of the rendered text segment to be not longer than
// the given length. The ellipsize will be appended if it is.
func Ellipsize(rich text.Rich, maxLen int) text.Rich {
if maxLen > len(rich.Content) {
maxLen = len(rich.Content) - 1
if maxLen <= 0 {
return text.Rich{}
}
rich.Content += "…"
}
return Substring(rich, 0, maxLen)
}
// Substring slices the given rich text.
func Substring(rich text.Rich, start, end int) text.Rich {
substring := text.Rich{
Content: rich.Content[start:end],
Segments: make([]text.Segment, 0, len(rich.Segments)),
}
for _, seg := range rich.Segments {
i, j := seg.Bounds()
// Bound-check: check if the starting point is within the range.
if start <= i && i <= end {
// If the current segment is cleanly within the bound, then we can
// directly insert it.
if j <= end {
substring.Segments = append(substring.Segments, seg)
continue
}
substring.Segments = append(substring.Segments, trimmedSegment{
Segment: seg,
start: i, // preserve the segment's starting point
end: end,
})
}
}
return substring
}
type trimmedSegment struct {
text.Segment
start, end int
}
func (seg trimmedSegment) Bounds() (int, int) {
return seg.start, seg.end
}