cchat-discord/internal/segments/blockquote/blockquote.go

81 lines
1.9 KiB
Go

package blockquote
import (
"github.com/diamondburned/cchat-discord/internal/segments/renderer"
"github.com/diamondburned/cchat/text"
"github.com/diamondburned/cchat/utils/empty"
"github.com/yuin/goldmark/ast"
)
func init() {
renderer.Register(ast.KindBlockquote, blockquote)
}
func blockquote(r *renderer.Text, node ast.Node, enter bool) ast.WalkStatus {
n := node.(*ast.Blockquote)
if enter {
// Block formatting.
r.EnsureBreak()
defer r.EnsureBreak()
// Create a segment.
var seg = Segment{Start: r.Buffer.Len()}
// A blockquote contains a paragraph each line. Because Discord.
for child := n.FirstChild(); child != nil; child = child.NextSibling() {
r.Buffer.WriteString("> ")
ast.Walk(child, func(node ast.Node, enter bool) (ast.WalkStatus, error) {
// We only call when entering, since we don't want to trigger a
// hard new line after each paragraph.
if enter {
return r.RenderNode(node, enter)
}
return ast.WalkContinue, nil
})
}
// Search until the last non-whitespace.
var i = r.Buffer.Len() - 1
for bytes := r.Buffer.Bytes(); i > 0 && isSpace(bytes[i]); i-- {
}
// The ending will have a trailing character that's not covered, so
// we'll need to do that ourselves.
// End the codeblock at that non-whitespace location.
seg.End = i + 1
r.Append(seg)
}
return ast.WalkSkipChildren
}
type Segment struct {
empty.TextSegment
Start, End int
}
var (
_ text.Segment = (*Segment)(nil)
_ text.Quoteblocker = (*Segment)(nil)
)
func (b Segment) Bounds() (start, end int) {
return b.Start, b.End
}
func (b Segment) AsBlockquoter() text.Quoteblocker {
return b
}
func (b Segment) QuotePrefix() string {
return "> "
}
// isSpace is a quick function that matches if the byte is a space, a new line
// or a return carriage.
func isSpace(b byte) bool {
return b == ' ' || b == '\n' || b == '\r'
}