@@ -25,20 +25,30 @@ import (
2525var templates embed.FS
2626
2727func init () {
28- RegisterExtension (Hashtags {})
28+ h := Hashtags {
29+ pages : make (map [Page ][]* HashTag ),
30+ }
31+
32+ RegisterExtension (& h )
2933}
3034
31- type Hashtags struct {}
35+ type Hashtags struct {
36+ pages map [Page ][]* HashTag
37+ mu sync.Mutex
38+ }
3239
33- func (Hashtags ) Name () string { return "hashtags" }
34- func (Hashtags ) Init () {
35- Get (`/+/tags` , tagsHandler )
36- Get (`/+/tag/{tag}` , tagHandler )
37- RegisterWidget (WidgetAfterView , 1 , relatedPages )
40+ func (* Hashtags ) Name () string { return "hashtags" }
41+ func (h * Hashtags ) Init () {
42+ Get (`/+/tags` , h . tagsHandler )
43+ Get (`/+/tag/{tag}` , h . tagHandler )
44+ RegisterWidget (WidgetAfterView , 1 , h . relatedPages )
3845 RegisterBuildPage ("/+/tags" , true )
3946 RegisterLink (links )
4047 RegisterTemplate (templates , "templates" )
41- shortcode .RegisterShortCode ("hashtag-pages" , shortcode.ShortCode {Render : hashtagPages })
48+ shortcode .RegisterShortCode ("hashtag-pages" , shortcode.ShortCode {Render : h .hashtagPages })
49+
50+ Listen (PageChanged , h .PageChanged )
51+ Listen (PageDeleted , h .PageDeleted )
4252
4353 MarkdownConverter ().Renderer ().AddOptions (renderer .WithNodeRenderers (
4454 util .Prioritized (& HashTag {}, 0 ),
@@ -48,92 +58,32 @@ func (Hashtags) Init() {
4858 ))
4959}
5060
51- func links (Page ) []Command {
52- return []Command {link {}}
53- }
54-
55- type link struct {}
61+ func (h * Hashtags ) PageChanged (p Page ) error {
62+ delete (h .pages , p )
5663
57- func (l link ) Icon () string { return "fa-solid fa-tags" }
58- func (l link ) Name () string { return "Hashtags" }
59- func (l link ) Attrs () map [template.HTMLAttr ]any {
60- return map [template.HTMLAttr ]any {
61- "href" : "/+/tags" ,
62- }
64+ return nil
6365}
6466
65- type HashTag struct {
66- ast.BaseInline
67- value []byte
68- unique unique.Handle [string ]
67+ func (h * Hashtags ) PageDeleted (p Page ) error {
68+ return h .PageChanged (p )
6969}
7070
71- func (h * HashTag ) RegisterFuncs (reg renderer.NodeRendererFuncRegisterer ) {
72- reg .Register (KindHashTag , renderHashtag )
73- }
74-
75- func (h * HashTag ) Trigger () []byte {
76- return []byte {'#' }
77- }
78-
79- func (h * HashTag ) Parse (parent ast.Node , block text.Reader , pc parser.Context ) ast.Node {
80- l , _ := block .PeekLine ()
81- if len (l ) < 1 {
82- return nil
83- }
84-
85- var line string = string (l )
86-
87- var i int
88- for ui , c := range line {
89- if ui == 0 {
90- i += utf8 .RuneLen (c )
91- continue
92- }
93-
94- if ! (unicode .In (c , unicode .Letter , unicode .Number , unicode .Dash ) || c == '_' ) || unicode .IsSpace (c ) {
95- break
96- }
71+ func (h * Hashtags ) hashtagsFor (p Page ) []* HashTag {
72+ h .mu .Lock ()
73+ defer h .mu .Unlock ()
9774
98- i += utf8 .RuneLen (c )
75+ if tags , ok := h .pages [p ]; ok {
76+ return tags
9977 }
100- if i > len (line ) || i == 1 {
101- return nil
102- }
103- block .Advance (i )
104- tag := line [1 :i ]
105- return & HashTag {
106- value : []byte (tag ),
107- unique : unique .Make (strings .ToLower (tag )),
108- }
109- }
11078
111- func (h * HashTag ) Dump (source []byte , level int ) {
112- m := map [string ]string {
113- "value" : fmt .Sprintf ("%#v" , h .value ),
114- }
115- ast .DumpHelper (h , source , level , m , nil )
116- }
117-
118- var KindHashTag = ast .NewNodeKind ("Hashtag" )
119-
120- func (h * HashTag ) Kind () ast.NodeKind {
121- return KindHashTag
122- }
123-
124- func renderHashtag (writer util.BufWriter , source []byte , n ast.Node , entering bool ) (ast.WalkStatus , error ) {
125- if ! entering || n .Kind () != KindHashTag {
126- return ast .WalkContinue , nil
127- }
79+ _ , tree := p .AST ()
80+ tags := FindAllInAST [* HashTag ](tree )
81+ h .pages [p ] = tags
12882
129- tag := n .(* HashTag )
130- fmt .Fprintf (writer , `<a href="/+/tag/%s" class="tag">%s</a>` , tag .value , tag .value )
131- RegisterBuildPage (fmt .Sprintf ("/+/tag/%s" , tag .value ), true )
132- RegisterBuildPage (fmt .Sprintf ("/+/tag/%s" , strings .ToLower (string (tag .value ))), true )
133- return ast .WalkContinue , nil
83+ return tags
13484}
13585
136- func tagsHandler (r Request ) Output {
86+ func ( h * Hashtags ) tagsHandler (r Request ) Output {
13787 tags := map [string ][]Page {}
13888 var lck sync.Mutex
13989
@@ -167,25 +117,24 @@ func tagsHandler(r Request) Output {
167117 })
168118}
169119
170- func tagHandler (r Request ) Output {
120+ func ( h * Hashtags ) tagHandler (r Request ) Output {
171121 tag := r .PathValue ("tag" )
172122
173123 return Render ("tag" , Locals {
174124 "page" : DynamicPage {NameVal : "#" + tag },
175- "pages" : tagPages (r .Context (), tag ),
125+ "pages" : h . tagPages (r .Context (), tag ),
176126 })
177127}
178128
179- func tagPages (ctx context.Context , hashtag string ) []Page {
129+ func ( h * Hashtags ) tagPages (ctx context.Context , hashtag string ) []Page {
180130 uniqHandle := unique .Make (strings .ToLower (hashtag ))
181131
182132 return MapPage (ctx , func (p Page ) Page {
183133 if p .Name () == Config .Index {
184134 return nil
185135 }
186136
187- _ , tree := p .AST ()
188- tags := FindAllInAST [* HashTag ](tree )
137+ tags := h .hashtagsFor (p )
189138 for _ , t := range tags {
190139 if uniqHandle == t .unique {
191140 return p
@@ -196,7 +145,7 @@ func tagPages(ctx context.Context, hashtag string) []Page {
196145 })
197146}
198147
199- func relatedPages (p Page ) template.HTML {
148+ func ( h * Hashtags ) relatedPages (p Page ) template.HTML {
200149 if p .Name () == Config .Index {
201150 return ""
202151 }
@@ -229,9 +178,9 @@ func relatedPages(p Page) template.HTML {
229178 })
230179}
231180
232- func hashtagPages (hashtag Markdown ) template.HTML {
181+ func ( h * Hashtags ) hashtagPages (hashtag Markdown ) template.HTML {
233182 hashtag_value := strings .Trim (string (hashtag ), "# \n " )
234- pages := tagPages (context .Background (), hashtag_value )
183+ pages := h . tagPages (context .Background (), hashtag_value )
235184
236185 slices .SortFunc (pages , func (a , b Page ) int {
237186 if modtime := b .ModTime ().Compare (a .ModTime ()); modtime != 0 {
@@ -244,3 +193,88 @@ func hashtagPages(hashtag Markdown) template.HTML {
244193 output := Partial ("hashtag-pages" , Locals {"pages" : pages })
245194 return template .HTML (output )
246195}
196+
197+ func links (Page ) []Command {
198+ return []Command {link {}}
199+ }
200+
201+ type link struct {}
202+
203+ func (l link ) Icon () string { return "fa-solid fa-tags" }
204+ func (l link ) Name () string { return "Hashtags" }
205+ func (l link ) Attrs () map [template.HTMLAttr ]any {
206+ return map [template.HTMLAttr ]any {
207+ "href" : "/+/tags" ,
208+ }
209+ }
210+
211+ type HashTag struct {
212+ ast.BaseInline
213+ value []byte
214+ unique unique.Handle [string ]
215+ }
216+
217+ func (h * HashTag ) RegisterFuncs (reg renderer.NodeRendererFuncRegisterer ) {
218+ reg .Register (KindHashTag , renderHashtag )
219+ }
220+
221+ func (h * HashTag ) Trigger () []byte {
222+ return []byte {'#' }
223+ }
224+
225+ func (h * HashTag ) Parse (parent ast.Node , block text.Reader , pc parser.Context ) ast.Node {
226+ l , _ := block .PeekLine ()
227+ if len (l ) < 1 {
228+ return nil
229+ }
230+
231+ var line string = string (l )
232+
233+ var i int
234+ for ui , c := range line {
235+ if ui == 0 {
236+ i += utf8 .RuneLen (c )
237+ continue
238+ }
239+
240+ if ! (unicode .In (c , unicode .Letter , unicode .Number , unicode .Dash ) || c == '_' ) || unicode .IsSpace (c ) {
241+ break
242+ }
243+
244+ i += utf8 .RuneLen (c )
245+ }
246+ if i > len (line ) || i == 1 {
247+ return nil
248+ }
249+ block .Advance (i )
250+ tag := line [1 :i ]
251+ return & HashTag {
252+ value : []byte (tag ),
253+ unique : unique .Make (strings .ToLower (tag )),
254+ }
255+ }
256+
257+ func (h * HashTag ) Dump (source []byte , level int ) {
258+ m := map [string ]string {
259+ "value" : fmt .Sprintf ("%#v" , h .value ),
260+ }
261+ ast .DumpHelper (h , source , level , m , nil )
262+ }
263+
264+ var KindHashTag = ast .NewNodeKind ("Hashtag" )
265+
266+ func (h * HashTag ) Kind () ast.NodeKind {
267+ return KindHashTag
268+ }
269+
270+ func renderHashtag (writer util.BufWriter , source []byte , n ast.Node , entering bool ) (ast.WalkStatus , error ) {
271+ if ! entering || n .Kind () != KindHashTag {
272+ return ast .WalkContinue , nil
273+ }
274+
275+ tag := n .(* HashTag )
276+ fmt .Fprintf (writer , `<a href="/+/tag/%s" class="tag">%s</a>` , tag .value , tag .value )
277+ RegisterBuildPage (fmt .Sprintf ("/+/tag/%s" , tag .value ), true )
278+ RegisterBuildPage (fmt .Sprintf ("/+/tag/%s" , strings .ToLower (string (tag .value ))), true )
279+ return ast .WalkContinue , nil
280+ }
0 commit comments