Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122package markup
import ( "maps" "net/url" "path" "slices" "strconv" "strings"
"github.com/bluesky-social/indigo/atproto/syntax" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/text" "tangled.org/core/appview/models" textension "tangled.org/core/appview/pages/markup/extension")
// FindReferences collects all links referencing tangled-related objects// like issues, PRs, comments or even @-mentions// This function doesn't actually check for the existence of records in the DB// or the PDS; it merely returns a list of what are presumed to be references.func FindReferences(host string, source string) ([]string, []models.ReferenceLink) { var ( refLinkSet = make(map[models.ReferenceLink]struct{}) mentionsSet = make(map[string]struct{}) md = NewMarkdown(host) sourceBytes = []byte(source) root = md.Parser().Parse(text.NewReader(sourceBytes)) )
ast.Walk(root, func(n ast.Node, entering bool) (ast.WalkStatus, error) { if !entering { return ast.WalkContinue, nil } switch n.Kind() { case textension.KindAt: handle := n.(*textension.AtNode).Handle mentionsSet[handle] = struct{}{} return ast.WalkSkipChildren, nil case ast.KindLink: dest := string(n.(*ast.Link).Destination) ref := parseTangledLink(host, dest) if ref != nil { refLinkSet[*ref] = struct{}{} } return ast.WalkSkipChildren, nil case ast.KindAutoLink: an := n.(*ast.AutoLink) if an.AutoLinkType == ast.AutoLinkURL { dest := string(an.URL(sourceBytes)) ref := parseTangledLink(host, dest) if ref != nil { refLinkSet[*ref] = struct{}{} } } return ast.WalkSkipChildren, nil } return ast.WalkContinue, nil }) mentions := slices.Collect(maps.Keys(mentionsSet)) references := slices.Collect(maps.Keys(refLinkSet)) return mentions, references}
func parseTangledLink(baseHost string, urlStr string) *models.ReferenceLink { u, err := url.Parse(urlStr) if err != nil { return nil }
if u.Host != "" && !strings.EqualFold(u.Host, baseHost) { return nil }
p := path.Clean(u.Path) parts := strings.FieldsFunc(p, func(r rune) bool { return r == '/' }) if len(parts) < 4 { // need at least: handle / repo / kind / id return nil }
var ( handle = parts[0] repo = parts[1] kindSeg = parts[2] subjectSeg = parts[3] )
handle = strings.TrimPrefix(handle, "@")
var kind models.RefKind switch kindSeg { case "issues": kind = models.RefKindIssue case "pulls": kind = models.RefKindPull default: return nil }
subjectId, err := strconv.Atoi(subjectSeg) if err != nil { return nil } var commentRkey *syntax.RecordKey if u.Fragment != "" { if strings.HasPrefix(u.Fragment, "comment-") { if rkey, err := syntax.ParseRecordKey(u.Fragment[len("comment-"):]); err != nil { commentRkey = &rkey } } }
return &models.ReferenceLink{ Handle: handle, Repo: repo, Kind: kind, SubjectId: subjectId, CommentRkey: commentRkey, }}