Skip to content

Commit 0c7b1a3

Browse files
committed
Fix live reload when editing inline partials
Fixes #13723
1 parent 970b887 commit 0c7b1a3

File tree

6 files changed

+226
-89
lines changed

6 files changed

+226
-89
lines changed

hugolib/rebuild_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,60 @@ MyTemplate: {{ partial "MyTemplate.html" . }}|
17661766
b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate Edited")
17671767
}
17681768

1769+
func TestRebuildEditInlinePartial13723(t *testing.T) {
1770+
t.Parallel()
1771+
1772+
files := `
1773+
-- hugo.toml --
1774+
baseURL = "https://example.com"
1775+
disableLiveReload = true
1776+
title = "Foo"
1777+
-- layouts/baseof.html --
1778+
{{ block "main" . }}Main.{{ end }}
1779+
{{ partial "myinlinepartialinbaseof.html" . }}|
1780+
{{- define "_partials/myinlinepartialinbaseof.html" }}
1781+
My inline partial in baseof.
1782+
{{ end }}
1783+
-- layouts/_partials/mypartial.html --
1784+
Mypartial.
1785+
{{ partial "myinlinepartial.html" . }}|
1786+
{{- define "_partials/myinlinepartial.html" }}
1787+
Mypartial Inline.|{{ .Title }}|
1788+
{{ end }}
1789+
-- layouts/_partials/myotherpartial.html --
1790+
Myotherpartial.
1791+
{{ partial "myotherinlinepartial.html" . }}|
1792+
{{- define "_partials/myotherinlinepartial.html" }}
1793+
Myotherpartial Inline.|{{ .Title }}|
1794+
{{ return "myotherinlinepartial" }}
1795+
{{ end }}
1796+
-- layouts/all.html --
1797+
{{ define "main" }}
1798+
{{ partial "mypartial.html" . }}|
1799+
{{ partial "myotherpartial.html" . }}|
1800+
{{ partial "myinlinepartialinall.html" . }}|
1801+
{{ end }}
1802+
{{- define "_partials/myinlinepartialinall.html" }}
1803+
My inline partial in all.
1804+
{{ end }}
1805+
1806+
`
1807+
b := TestRunning(t, files)
1808+
b.AssertFileContent("public/index.html", "Mypartial.", "Mypartial Inline.|Foo")
1809+
1810+
// Edit inline partial in partial.
1811+
b.EditFileReplaceAll("layouts/_partials/mypartial.html", "Mypartial Inline.", "Mypartial Inline Edited.").Build()
1812+
b.AssertFileContent("public/index.html", "Mypartial Inline Edited.|Foo")
1813+
1814+
// Edit inline partial in baseof.
1815+
b.EditFileReplaceAll("layouts/baseof.html", "My inline partial in baseof.", "My inline partial in baseof Edited.").Build()
1816+
b.AssertFileContent("public/index.html", "My inline partial in baseof Edited.")
1817+
1818+
// Edit inline partial in all.
1819+
b.EditFileReplaceAll("layouts/all.html", "My inline partial in all.", "My inline partial in all Edited.").Build()
1820+
b.AssertFileContent("public/index.html", "My inline partial in all Edited.")
1821+
}
1822+
17691823
func TestRebuildEditAsciidocContentFile(t *testing.T) {
17701824
if !asciidocext.Supports() {
17711825
t.Skip("skip asciidoc")

tpl/internal/go_templates/htmltemplate/hugo_template.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package template
1515

1616
import (
1717
"fmt"
18+
"iter"
1819

1920
"github.com/gohugoio/hugo/common/types"
2021
template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
@@ -38,6 +39,19 @@ func (t *Template) Prepare() (*template.Template, error) {
3839
return t.text, nil
3940
}
4041

42+
func (t *Template) All() iter.Seq[*Template] {
43+
return func(yield func(t *Template) bool) {
44+
ns := t.nameSpace
45+
ns.mu.Lock()
46+
defer ns.mu.Unlock()
47+
for _, v := range ns.set {
48+
if !yield(v) {
49+
return
50+
}
51+
}
52+
}
53+
}
54+
4155
// See https://github.com/golang/go/issues/5884
4256
func StripTags(html string) string {
4357
return stripTags(html)

tpl/internal/go_templates/texttemplate/hugo_template.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"context"
1818
"fmt"
1919
"io"
20+
"iter"
2021
"reflect"
2122

2223
"github.com/gohugoio/hugo/common/herrors"
@@ -433,3 +434,18 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
433434
func isTrue(val reflect.Value) (truth, ok bool) {
434435
return hreflect.IsTruthfulValue(val), true
435436
}
437+
438+
func (t *Template) All() iter.Seq[*Template] {
439+
return func(yield func(t *Template) bool) {
440+
if t.common == nil {
441+
return
442+
}
443+
t.muTmpl.RLock()
444+
defer t.muTmpl.RUnlock()
445+
for _, v := range t.tmpl {
446+
if !yield(v) {
447+
return
448+
}
449+
}
450+
}
451+
}

tpl/internal/go_templates/texttemplate/parse/parse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode,
533533
t.rangeDepth--
534534
}
535535
switch next.Type() {
536-
case nodeEnd: //done
536+
case nodeEnd: // done
537537
case nodeElse:
538538
// Special case for "else if" and "else with".
539539
// If the "else" is followed immediately by an "if" or "with",

tpl/tplimpl/templates.go

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tplimpl
22

33
import (
44
"io"
5+
"iter"
56
"regexp"
67
"strconv"
78
"strings"
@@ -44,16 +45,15 @@ var embeddedTemplatesAliases = map[string][]string{
4445
"_shortcodes/twitter.html": {"_shortcodes/tweet.html"},
4546
}
4647

47-
func (s *TemplateStore) parseTemplate(ti *TemplInfo) error {
48-
err := s.tns.doParseTemplate(ti)
48+
func (s *TemplateStore) parseTemplate(ti *TemplInfo, replace bool) error {
49+
err := s.tns.doParseTemplate(ti, replace)
4950
if err != nil {
5051
return s.addFileContext(ti, "parse of template failed", err)
5152
}
52-
5353
return err
5454
}
5555

56-
func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
56+
func (t *templateNamespace) doParseTemplate(ti *TemplInfo, replace bool) error {
5757
if !ti.noBaseOf || ti.category == CategoryBaseof {
5858
// Delay parsing until we have the base template.
5959
return nil
@@ -68,7 +68,7 @@ func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
6868

6969
if ti.D.IsPlainText {
7070
prototype := t.parseText
71-
if prototype.Lookup(name) != nil {
71+
if !replace && prototype.Lookup(name) != nil {
7272
name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10)
7373
}
7474
templ, err = prototype.New(name).Parse(ti.content)
@@ -77,7 +77,7 @@ func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
7777
}
7878
} else {
7979
prototype := t.parseHTML
80-
if prototype.Lookup(name) != nil {
80+
if !replace && prototype.Lookup(name) != nil {
8181
name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10)
8282
}
8383
templ, err = prototype.New(name).Parse(ti.content)
@@ -181,19 +181,24 @@ func (t *templateNamespace) applyBaseTemplate(overlay *TemplInfo, base keyTempla
181181
return nil
182182
}
183183

184-
func (t *templateNamespace) templatesIn(in tpl.Template) []tpl.Template {
185-
var templs []tpl.Template
186-
if textt, ok := in.(*texttemplate.Template); ok {
187-
for _, t := range textt.Templates() {
188-
templs = append(templs, t)
189-
}
190-
}
191-
if htmlt, ok := in.(*htmltemplate.Template); ok {
192-
for _, t := range htmlt.Templates() {
193-
templs = append(templs, t)
184+
func (t *templateNamespace) templatesIn(in tpl.Template) iter.Seq[tpl.Template] {
185+
return func(yield func(t tpl.Template) bool) {
186+
switch in := in.(type) {
187+
case *htmltemplate.Template:
188+
for t := range in.All() {
189+
if !yield(t) {
190+
return
191+
}
192+
}
193+
194+
case *texttemplate.Template:
195+
for t := range in.All() {
196+
if !yield(t) {
197+
return
198+
}
199+
}
194200
}
195201
}
196-
return templs
197202
}
198203

199204
/*
@@ -337,8 +342,6 @@ func (t *templateNamespace) createPrototypes(init bool) error {
337342
t.prototypeHTML = htmltemplate.Must(t.parseHTML.Clone())
338343
t.prototypeText = texttemplate.Must(t.parseText.Clone())
339344
}
340-
// t.execHTML = htmltemplate.Must(t.parseHTML.Clone())
341-
// t.execText = texttemplate.Must(t.parseText.Clone())
342345

343346
return nil
344347
}
@@ -350,3 +353,14 @@ func newTemplateNamespace(funcs map[string]any) *templateNamespace {
350353
standaloneText: texttemplate.New("").Funcs(funcs),
351354
}
352355
}
356+
357+
func isText(t tpl.Template) bool {
358+
switch t.(type) {
359+
case *texttemplate.Template:
360+
return true
361+
case *htmltemplate.Template:
362+
return false
363+
default:
364+
panic("unknown template type")
365+
}
366+
}

0 commit comments

Comments
 (0)