Skip to content

Commit 209f94c

Browse files
committed
cache pages hashtags to improve performance
1 parent d9f0944 commit 209f94c

File tree

1 file changed

+125
-91
lines changed

1 file changed

+125
-91
lines changed

extensions/hashtags/hashtags.go

Lines changed: 125 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,30 @@ import (
2525
var templates embed.FS
2626

2727
func 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

Comments
 (0)