-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtemplate.go
More file actions
114 lines (102 loc) · 3.52 KB
/
template.go
File metadata and controls
114 lines (102 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package template
import (
"bytes"
"io"
)
// Template represents a compiled template ready for execution.
// A Template is immutable after compilation.
//
// Templates parsed without a loader-backed engine have no resolved name or
// engine reference. Templates loaded via Engine.Load carry an engine
// reference and a resolved name for caching, dependency tracking, and
// multi-file rendering.
type Template struct {
root []Statement
// name is the loader-resolved name (including any loader prefix);
// "" for templates parsed from an unnamed source string.
name string
// engine is the owning Engine; nil for templates parsed without an engine.
engine *Engine
// parent is the template this one extends, nil otherwise.
parent *Template
// blocks is this template's own block definitions, keyed by block name.
// Populated only when the template contains {% block %} tags.
blocks map[string]*BlockNode
}
// BlockNode is a forward declaration stub filled in when {% block %} is
// implemented. Defined here so Template.blocks can reference it without
// import cycles.
type BlockNode struct {
Name string
Body []Node
Line int
Col int
}
// NewTemplate creates a new Template from parsed AST nodes.
//
// Most callers should use [Engine.ParseString] or [Engine.Load], which handle
// lexing and parsing automatically.
func NewTemplate(root []Statement) *Template {
return &Template{root: root}
}
// Execute writes the template output to w using the given render context.
//
// For most use cases, [Template.Render] is simpler. Use Execute when you need
// control over the output destination or render context.
//
// When the template extends a parent (via {% extends %}), Execute walks up
// to the root of the extends chain and runs that template's body. The
// current template is recorded as ctx.currentLeaf so BlockNode.Execute
// can resolve overrides across the chain. For templates without a parent
// the loop is a no-op and the template runs its own body.
func (t *Template) Execute(ctx *RenderContext, w io.Writer) error {
root := t
for root.parent != nil {
root = root.parent
}
prevEngine := ctx.engine
prevAutoescape := ctx.autoescape
if ctx.engine == nil && t.engine != nil {
ctx.engine = t.engine
ctx.autoescape = t.engine.autoescape()
defer func() {
ctx.engine = prevEngine
ctx.autoescape = prevAutoescape
}()
}
// Preserve any outer currentLeaf (for nested include+extends
// scenarios) and restore on return.
prevLeaf := ctx.currentLeaf
ctx.currentLeaf = t
defer func() { ctx.currentLeaf = prevLeaf }()
return root.executeRoot(ctx, w)
}
// executeRoot runs this template's own top-level statements without
// walking the extends chain.
func (t *Template) executeRoot(ctx *RenderContext, w io.Writer) error {
for _, stmt := range t.root {
if err := stmt.Execute(ctx, w); err != nil {
return err
}
}
return nil
}
// Render executes the template with data and returns the output as a string.
//
// Render is a convenience wrapper around [Template.RenderTo] for the common
// case where a string result is needed.
func (t *Template) Render(data Data) (string, error) {
var buf bytes.Buffer
if err := t.RenderTo(&buf, data); err != nil {
return "", err
}
return buf.String(), nil
}
// RenderTo writes the template output to w using plain render data.
//
// Use RenderTo for the common writer-based path. Reach for [Template.Execute]
// only when you need direct control over [RenderContext].
func (t *Template) RenderTo(w io.Writer, data Data) error {
ctx := NewRenderContext(data)
return t.Execute(ctx, w)
}