back interdiff of round #9 and #8

appview/pages: markup: add @ user-mention parsing in markdown #736

merged
opened by boltless.me targeting master from feat/mentions
ERROR
appview/pages/markup/markdown.go

Failed to calculate interdiff for this file.

REVERTED
appview/pages/markup/markdown_at_extension.go
··· 1 - // heavily inspired by: https://github.com/kaleocheng/goldmark-extensions 2 - 3 - package markup 4 - 5 - import ( 6 - "regexp" 7 - 8 - "github.com/yuin/goldmark" 9 - "github.com/yuin/goldmark/ast" 10 - "github.com/yuin/goldmark/parser" 11 - "github.com/yuin/goldmark/renderer" 12 - "github.com/yuin/goldmark/renderer/html" 13 - "github.com/yuin/goldmark/text" 14 - "github.com/yuin/goldmark/util" 15 - ) 16 - 17 - // An AtNode struct represents an AtNode 18 - type AtNode struct { 19 - handle string 20 - ast.BaseInline 21 - } 22 - 23 - var _ ast.Node = &AtNode{} 24 - 25 - // Dump implements Node.Dump. 26 - func (n *AtNode) Dump(source []byte, level int) { 27 - ast.DumpHelper(n, source, level, nil, nil) 28 - } 29 - 30 - // KindAt is a NodeKind of the At node. 31 - var KindAt = ast.NewNodeKind("At") 32 - 33 - // Kind implements Node.Kind. 34 - func (n *AtNode) Kind() ast.NodeKind { 35 - return KindAt 36 - } 37 - 38 - var atRegexp = regexp.MustCompile(`(^|\s|\()(@)([a-zA-Z0-9.-]+)(\b)`) 39 - 40 - type atParser struct{} 41 - 42 - // NewAtParser return a new InlineParser that parses 43 - // at expressions. 44 - func NewAtParser() parser.InlineParser { 45 - return &atParser{} 46 - } 47 - 48 - func (s *atParser) Trigger() []byte { 49 - return []byte{'@'} 50 - } 51 - 52 - func (s *atParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { 53 - line, segment := block.PeekLine() 54 - m := atRegexp.FindSubmatchIndex(line) 55 - if m == nil { 56 - return nil 57 - } 58 - atSegment := text.NewSegment(segment.Start, segment.Start+m[1]) 59 - block.Advance(m[1]) 60 - node := &AtNode{} 61 - node.AppendChild(node, ast.NewTextSegment(atSegment)) 62 - node.handle = string(atSegment.Value(block.Source())[1:]) 63 - return node 64 - } 65 - 66 - // atHtmlRenderer is a renderer.NodeRenderer implementation that 67 - // renders At nodes. 68 - type atHtmlRenderer struct { 69 - html.Config 70 - } 71 - 72 - // NewAtHTMLRenderer returns a new AtHTMLRenderer. 73 - func NewAtHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { 74 - r := &atHtmlRenderer{ 75 - Config: html.NewConfig(), 76 - } 77 - for _, opt := range opts { 78 - opt.SetHTMLOption(&r.Config) 79 - } 80 - return r 81 - } 82 - 83 - // RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs. 84 - func (r *atHtmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { 85 - reg.Register(KindAt, r.renderAt) 86 - } 87 - 88 - func (r *atHtmlRenderer) renderAt(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { 89 - if entering { 90 - w.WriteString(`<a href="/@`) 91 - w.WriteString(n.(*AtNode).handle) 92 - w.WriteString(`" class="mention">`) 93 - } else { 94 - w.WriteString("</a>") 95 - } 96 - return ast.WalkContinue, nil 97 - } 98 - 99 - type atExt struct{} 100 - 101 - // At is an extension that allow you to use at expression like '@user.bsky.social' . 102 - var AtExt = &atExt{} 103 - 104 - func (e *atExt) Extend(m goldmark.Markdown) { 105 - m.Parser().AddOptions(parser.WithInlineParsers( 106 - util.Prioritized(NewAtParser(), 500), 107 - )) 108 - m.Renderer().AddOptions(renderer.WithNodeRenderers( 109 - util.Prioritized(NewAtHTMLRenderer(), 500), 110 - )) 111 - } 112 - 113 - // FindUserMentions returns Set of user handles from given markup soruce. 114 - // It doesn't guarntee unique DIDs 115 - func FindUserMentions(source string) []string { 116 - var ( 117 - mentions []string 118 - mentionsSet = make(map[string]struct{}) 119 - md = NewMarkdown() 120 - sourceBytes = []byte(source) 121 - root = md.Parser().Parse(text.NewReader(sourceBytes)) 122 - ) 123 - ast.Walk(root, func(n ast.Node, entering bool) (ast.WalkStatus, error) { 124 - if entering && n.Kind() == KindAt { 125 - handle := n.(*AtNode).handle 126 - mentionsSet[handle] = struct{}{} 127 - return ast.WalkSkipChildren, nil 128 - } 129 - return ast.WalkContinue, nil 130 - }) 131 - for handle := range mentionsSet { 132 - mentions = append(mentions, handle) 133 - } 134 - return mentions 135 - }
ERROR
appview/pages/markup/sanitizer.go

Failed to calculate interdiff for this file.

ERROR
input.css

Failed to calculate interdiff for this file.

NEW
appview/pages/markup/extension/atlink.go
··· 1 + // heavily inspired by: https://github.com/kaleocheng/goldmark-extensions 2 + 3 + package extension 4 + 5 + import ( 6 + "regexp" 7 + 8 + "github.com/yuin/goldmark" 9 + "github.com/yuin/goldmark/ast" 10 + "github.com/yuin/goldmark/parser" 11 + "github.com/yuin/goldmark/renderer" 12 + "github.com/yuin/goldmark/renderer/html" 13 + "github.com/yuin/goldmark/text" 14 + "github.com/yuin/goldmark/util" 15 + ) 16 + 17 + // An AtNode struct represents an AtNode 18 + type AtNode struct { 19 + handle string 20 + ast.BaseInline 21 + } 22 + 23 + var _ ast.Node = &AtNode{} 24 + 25 + // Dump implements Node.Dump. 26 + func (n *AtNode) Dump(source []byte, level int) { 27 + ast.DumpHelper(n, source, level, nil, nil) 28 + } 29 + 30 + // KindAt is a NodeKind of the At node. 31 + var KindAt = ast.NewNodeKind("At") 32 + 33 + // Kind implements Node.Kind. 34 + func (n *AtNode) Kind() ast.NodeKind { 35 + return KindAt 36 + } 37 + 38 + var atRegexp = regexp.MustCompile(`(^|\s|\()(@)([a-zA-Z0-9.-]+)(\b)`) 39 + 40 + type atParser struct{} 41 + 42 + // NewAtParser return a new InlineParser that parses 43 + // at expressions. 44 + func NewAtParser() parser.InlineParser { 45 + return &atParser{} 46 + } 47 + 48 + func (s *atParser) Trigger() []byte { 49 + return []byte{'@'} 50 + } 51 + 52 + func (s *atParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { 53 + line, segment := block.PeekLine() 54 + m := atRegexp.FindSubmatchIndex(line) 55 + if m == nil { 56 + return nil 57 + } 58 + atSegment := text.NewSegment(segment.Start, segment.Start+m[1]) 59 + block.Advance(m[1]) 60 + node := &AtNode{} 61 + node.AppendChild(node, ast.NewTextSegment(atSegment)) 62 + node.handle = string(atSegment.Value(block.Source())[1:]) 63 + return node 64 + } 65 + 66 + // atHtmlRenderer is a renderer.NodeRenderer implementation that 67 + // renders At nodes. 68 + type atHtmlRenderer struct { 69 + html.Config 70 + } 71 + 72 + // NewAtHTMLRenderer returns a new AtHTMLRenderer. 73 + func NewAtHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { 74 + r := &atHtmlRenderer{ 75 + Config: html.NewConfig(), 76 + } 77 + for _, opt := range opts { 78 + opt.SetHTMLOption(&r.Config) 79 + } 80 + return r 81 + } 82 + 83 + // RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs. 84 + func (r *atHtmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { 85 + reg.Register(KindAt, r.renderAt) 86 + } 87 + 88 + func (r *atHtmlRenderer) renderAt(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { 89 + if entering { 90 + w.WriteString(`<a href="/@`) 91 + w.WriteString(n.(*AtNode).handle) 92 + w.WriteString(`" class="mention">`) 93 + } else { 94 + w.WriteString("</a>") 95 + } 96 + return ast.WalkContinue, nil 97 + } 98 + 99 + type atExt struct{} 100 + 101 + // At is an extension that allow you to use at expression like '@user.bsky.social' . 102 + var AtExt = &atExt{} 103 + 104 + func (e *atExt) Extend(m goldmark.Markdown) { 105 + m.Parser().AddOptions(parser.WithInlineParsers( 106 + util.Prioritized(NewAtParser(), 500), 107 + )) 108 + m.Renderer().AddOptions(renderer.WithNodeRenderers( 109 + util.Prioritized(NewAtHTMLRenderer(), 500), 110 + )) 111 + }