Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122// heavily inspired by: https://github.com/kaleocheng/goldmark-extensions
package markup
import ( "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")
// An AtNode struct represents an AtNodetype AtNode struct { Handle string ast.BaseInline}
var _ ast.Node = &AtNode{}
// Dump implements Node.Dump.func (n *AtNode) Dump(source []byte, level int) { ast.DumpHelper(n, source, level, nil, nil)}
// KindAt is a NodeKind of the At node.var KindAt = ast.NewNodeKind("At")
// Kind implements Node.Kind.func (n *AtNode) Kind() ast.NodeKind { return KindAt}
var atRegexp = regexp.MustCompile(`(^|\s|\()(@)([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\b)`)var markdownLinkRegexp = regexp.MustCompile(`(?ms)\[.*\]\(.*\)`)
type atParser struct{}
// NewAtParser return a new InlineParser that parses// at expressions.func NewAtParser() parser.InlineParser { return &atParser{}}
func (s *atParser) Trigger() []byte { return []byte{'@'}}
func (s *atParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { line, segment := block.PeekLine() m := atRegexp.FindSubmatchIndex(line) if m == nil { return nil }
// Check for all links in the markdown to see if the handle found is inside one linksIndexes := markdownLinkRegexp.FindAllIndex(block.Source(), -1) for _, linkMatch := range linksIndexes { if linkMatch[0] < segment.Start && segment.Start < linkMatch[1] { return nil } }
atSegment := text.NewSegment(segment.Start, segment.Start+m[1]) block.Advance(m[1]) node := &AtNode{} node.AppendChild(node, ast.NewTextSegment(atSegment)) node.Handle = string(atSegment.Value(block.Source())[1:]) return node}
// atHtmlRenderer is a renderer.NodeRenderer implementation that// renders At nodes.type atHtmlRenderer struct { html.Config}
// NewAtHTMLRenderer returns a new AtHTMLRenderer.func NewAtHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { r := &atHtmlRenderer{ Config: html.NewConfig(), } for _, opt := range opts { opt.SetHTMLOption(&r.Config) } return r}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.func (r *atHtmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { reg.Register(KindAt, r.renderAt)}
func (r *atHtmlRenderer) renderAt(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { if entering { w.WriteString(`<a href="/`) w.WriteString(n.(*AtNode).Handle) w.WriteString(`" class="mention">`) } else { w.WriteString("</a>") } return ast.WalkContinue, nil}
type atExt struct{}
// At is an extension that allow you to use at expression like '@user.bsky.social' .var AtExt = &atExt{}
func (e *atExt) Extend(m goldmark.Markdown) { m.Parser().AddOptions(parser.WithInlineParsers( util.Prioritized(NewAtParser(), 500), )) m.Renderer().AddOptions(renderer.WithNodeRenderers( util.Prioritized(NewAtHTMLRenderer(), 500), ))}