Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(394)

Delta Between Two Patch Sets: src/pkg/text/template/template.go

Issue 5415060: code review 5415060: text/template: new, simpler API (Closed)
Left Patch Set: diff -r fd80a4497037 https://go.googlecode.com/hg/ Created 13 years, 3 months ago
Right Patch Set: diff -r 75348d679279 https://go.googlecode.com/hg/ Created 13 years, 3 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/text/template/parse/parse_test.go ('k') | src/pkg/text/template/testdata/tmpl1.tmpl » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package template 5 package template
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "fmt" 9 "fmt"
10 "reflect" 10 "reflect"
(...skipping 26 matching lines...) Expand all
37 return &Template{ 37 return &Template{
38 name: name, 38 name: name,
39 } 39 }
40 } 40 }
41 41
42 // Name returns the name of the template. 42 // Name returns the name of the template.
43 func (t *Template) Name() string { 43 func (t *Template) Name() string {
44 return t.name 44 return t.name
45 } 45 }
46 46
47 // newTemplate allocates a new template associated with the given one and with t he same 47 // New allocates a new template associated with the given one and with the same
48 // delimiters. The association, which is transitive, allows one template to 48 // delimiters. The association, which is transitive, allows one template to
49 // invoke another with a {{template}} action. 49 // invoke another with a {{template}} action.
50 func (t *Template) newTemplate(name string) *Template { 50 func (t *Template) New(name string) *Template {
51 t.init() 51 t.init()
52 return &Template{ 52 return &Template{
53 name: name, 53 name: name,
54 common: t.common, 54 common: t.common,
55 leftDelim: t.leftDelim, 55 leftDelim: t.leftDelim,
56 rightDelim: t.rightDelim, 56 rightDelim: t.rightDelim,
57 } 57 }
58 } 58 }
59 59
60 func (t *Template) init() { 60 func (t *Template) init() {
61 if t.common == nil { 61 if t.common == nil {
62 t.common = new(common) 62 t.common = new(common)
63 t.tmpl = make(map[string]*Template) 63 t.tmpl = make(map[string]*Template)
64 t.parseFuncs = make(FuncMap) 64 t.parseFuncs = make(FuncMap)
65 t.execFuncs = make(map[string]reflect.Value) 65 t.execFuncs = make(map[string]reflect.Value)
66 } 66 }
67 } 67 }
68 68
69 // Template returns a slice of the templates associated with t, including t itse lf. 69 // Clone returns a duplicate of the template, including all associated
70 // templates. The actual representation is not copied, but the name space of
71 // associated templates is, so further calls to Parse in the copy will add
72 // templates to the copy but not to the original. Clone can be used to prepare
73 // common templates and use them with variant definitions for other templates by
74 // adding the variants after the clone is made.
75 func (t *Template) Clone() *Template {
76 » nt := t.copy()
77 » nt.init()
78 » for k, v := range t.tmpl {
79 » » // The associated templates share nt's common structure.
80 » » tmpl := v.copy()
81 » » tmpl.common = nt.common
82 » » nt.tmpl[k] = tmpl
83 » }
84 » for k, v := range t.parseFuncs {
85 » » nt.parseFuncs[k] = v
86 » }
87 » for k, v := range t.execFuncs {
88 » » nt.execFuncs[k] = v
89 » }
90 » return nt
91 }
92
93 // copy returns a shallow copy of t, with common set to nil.
94 func (t *Template) copy() *Template {
95 » nt := New(t.name)
96 » nt.Tree = t.Tree
97 » nt.leftDelim = t.leftDelim
98 » nt.rightDelim = t.rightDelim
99 » return nt
100 }
101
102 // Templates returns a slice of the templates associated with t, including t
103 // itself.
70 func (t *Template) Templates() []*Template { 104 func (t *Template) Templates() []*Template {
71 // Return a slice so we don't expose the map. 105 // Return a slice so we don't expose the map.
72 m := make([]*Template, 0, len(t.tmpl)) 106 m := make([]*Template, 0, len(t.tmpl))
73 for _, v := range t.tmpl { 107 for _, v := range t.tmpl {
74 m = append(m, v) 108 m = append(m, v)
75 } 109 }
76 return m 110 return m
77 } 111 }
78 112
79 // Delims sets the action delimiters to the specified strings, to be used in 113 // Delims sets the action delimiters to the specified strings, to be used in
(...skipping 20 matching lines...) Expand all
100 134
101 // Template returns the template with the given name that is associated with t, 135 // Template returns the template with the given name that is associated with t,
102 // or nil if there is no such template. 136 // or nil if there is no such template.
103 func (t *Template) Template(name string) *Template { 137 func (t *Template) Template(name string) *Template {
104 return t.tmpl[name] 138 return t.tmpl[name]
105 } 139 }
106 140
107 // Parse parses a string into a template. Nested template definitions will be 141 // Parse parses a string into a template. Nested template definitions will be
108 // associated with the top-level template t. Parse may be called multiple times 142 // associated with the top-level template t. Parse may be called multiple times
109 // to parse definitions of templates to associate with t. It is an error if a 143 // to parse definitions of templates to associate with t. It is an error if a
110 // resulting template is non-trivial (contains content other than template 144 // resulting template is non-empty (contains content other than template
111 // definitions) and would replace a non-trivial template with the same name. 145 // definitions) and would replace a non-empty template with the same name.
146 // (In multiple calls to Parse with the same receiver template, only one call
147 // can contain text other than space, comments, and template definitions.)
112 func (t *Template) Parse(text string) (*Template, error) { 148 func (t *Template) Parse(text string) (*Template, error) {
113 t.init() 149 t.init()
114 trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.par seFuncs, builtins) 150 trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.par seFuncs, builtins)
115 if err != nil { 151 if err != nil {
116 return nil, err 152 return nil, err
117 } 153 }
118 // Add the newly parsed trees, including the one for t, into our common structure. 154 // Add the newly parsed trees, including the one for t, into our common structure.
119 for name, tree := range trees { 155 for name, tree := range trees {
120 » » // The receiver template should rewritten not recreated. 156 » » // If the name we parsed is the name of this template, overwrite this template.
157 » » // The associate method checks it's not a redefinition.
121 tmpl := t 158 tmpl := t
122 if name != t.name { 159 if name != t.name {
123 » » » tmpl = New(name) 160 » » » tmpl = t.New(name)
124 » » » tmpl.common = t.common 161 » » }
125 » » } 162 » » // Even if t == tmpl, we need to install it in the common.tmpl m ap.
126 if err := t.associate(tmpl); err != nil { 163 if err := t.associate(tmpl); err != nil {
127 return nil, err 164 return nil, err
128 } 165 }
129 tmpl.Tree = tree 166 tmpl.Tree = tree
130 tmpl.leftDelim = t.leftDelim 167 tmpl.leftDelim = t.leftDelim
131 tmpl.rightDelim = t.rightDelim 168 tmpl.rightDelim = t.rightDelim
132 } 169 }
133 return t, nil 170 return t, nil
134 } 171 }
135 172
136 // associate installs the new template into the group of templates associated 173 // associate installs the new template into the group of templates associated
137 // with t. It is an error to reuse a name except to overwrite an empty 174 // with t. It is an error to reuse a name except to overwrite an empty
138 // template. 175 // template. The two are already known to share the common structure.
139 func (t *Template) associate(new *Template) error { 176 func (t *Template) associate(new *Template) error {
177 if new.common != t.common {
178 panic("internal error: associate not common")
179 }
140 name := new.name 180 name := new.name
141 if old := t.tmpl[name]; old != nil { 181 if old := t.tmpl[name]; old != nil {
142 oldIsEmpty := isEmpty(old.Root) 182 oldIsEmpty := isEmpty(old.Root)
143 newIsEmpty := isEmpty(new.Root) 183 newIsEmpty := isEmpty(new.Root)
144 if !oldIsEmpty && !newIsEmpty { 184 if !oldIsEmpty && !newIsEmpty {
145 return fmt.Errorf("template: redefinition of template %q ", name) 185 return fmt.Errorf("template: redefinition of template %q ", name)
146 } 186 }
147 if newIsEmpty { 187 if newIsEmpty {
148 // Whether old is empty or not, new is empty; no reason to replace old. 188 // Whether old is empty or not, new is empty; no reason to replace old.
149 return nil 189 return nil
150 } 190 }
151 } 191 }
152 t.tmpl[name] = new 192 t.tmpl[name] = new
153 new.common = t.common
154 return nil 193 return nil
155 } 194 }
156 195
157 // isEmpty reports whether this tree (node) is empty of everything but space. 196 // isEmpty reports whether this tree (node) is empty of everything but space.
158 func isEmpty(n parse.Node) bool { 197 func isEmpty(n parse.Node) bool {
159 switch n := n.(type) { 198 switch n := n.(type) {
160 case *parse.ActionNode: 199 case *parse.ActionNode:
161 case *parse.IfNode: 200 case *parse.IfNode:
162 case *parse.ListNode: 201 case *parse.ListNode:
163 for _, node := range n.Nodes { 202 for _, node := range n.Nodes {
164 if !isEmpty(node) { 203 if !isEmpty(node) {
165 return false 204 return false
166 } 205 }
167 } 206 }
168 return true 207 return true
169 case *parse.RangeNode: 208 case *parse.RangeNode:
170 case *parse.TemplateNode: 209 case *parse.TemplateNode:
171 case *parse.TextNode: 210 case *parse.TextNode:
172 return len(bytes.TrimSpace(n.Text)) == 0 211 return len(bytes.TrimSpace(n.Text)) == 0
173 case *parse.WithNode: 212 case *parse.WithNode:
174 default: 213 default:
175 panic("unknown node: " + n.String()) 214 panic("unknown node: " + n.String())
176 } 215 }
177 return false 216 return false
178 } 217 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b