template.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. package pongo2
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "strings"
  7. )
  8. type TemplateWriter interface {
  9. io.Writer
  10. WriteString(string) (int, error)
  11. }
  12. type templateWriter struct {
  13. w io.Writer
  14. }
  15. func (tw *templateWriter) WriteString(s string) (int, error) {
  16. return tw.w.Write([]byte(s))
  17. }
  18. func (tw *templateWriter) Write(b []byte) (int, error) {
  19. return tw.w.Write(b)
  20. }
  21. type Template struct {
  22. set *TemplateSet
  23. // Input
  24. isTplString bool
  25. name string
  26. tpl string
  27. size int
  28. // Calculation
  29. tokens []*Token
  30. parser *Parser
  31. // first come, first serve (it's important to not override existing entries in here)
  32. level int
  33. parent *Template
  34. child *Template
  35. blocks map[string]*NodeWrapper
  36. exportedMacros map[string]*tagMacroNode
  37. // Output
  38. root *nodeDocument
  39. // Options allow you to change the behavior of template-engine.
  40. // You can change the options before calling the Execute method.
  41. Options *Options
  42. }
  43. func newTemplateString(set *TemplateSet, tpl []byte) (*Template, error) {
  44. return newTemplate(set, "<string>", true, tpl)
  45. }
  46. func newTemplate(set *TemplateSet, name string, isTplString bool, tpl []byte) (*Template, error) {
  47. strTpl := string(tpl)
  48. // Create the template
  49. t := &Template{
  50. set: set,
  51. isTplString: isTplString,
  52. name: name,
  53. tpl: strTpl,
  54. size: len(strTpl),
  55. blocks: make(map[string]*NodeWrapper),
  56. exportedMacros: make(map[string]*tagMacroNode),
  57. Options: newOptions(),
  58. }
  59. // Copy all settings from another Options.
  60. t.Options.Update(set.Options)
  61. // Tokenize it
  62. tokens, err := lex(name, strTpl)
  63. if err != nil {
  64. return nil, err
  65. }
  66. t.tokens = tokens
  67. // For debugging purposes, show all tokens:
  68. /*for i, t := range tokens {
  69. fmt.Printf("%3d. %s\n", i, t)
  70. }*/
  71. // Parse it
  72. err = t.parse()
  73. if err != nil {
  74. return nil, err
  75. }
  76. return t, nil
  77. }
  78. func (tpl *Template) newContextForExecution(context Context) (*Template, *ExecutionContext, error) {
  79. if tpl.Options.TrimBlocks || tpl.Options.LStripBlocks {
  80. // Issue #94 https://github.com/flosch/pongo2/issues/94
  81. // If an application configures pongo2 template to trim_blocks,
  82. // the first newline after a template tag is removed automatically (like in PHP).
  83. prev := &Token{
  84. Typ: TokenHTML,
  85. Val: "\n",
  86. }
  87. for _, t := range tpl.tokens {
  88. if tpl.Options.LStripBlocks {
  89. if prev.Typ == TokenHTML && t.Typ != TokenHTML && t.Val == "{%" {
  90. prev.Val = strings.TrimRight(prev.Val, "\t ")
  91. }
  92. }
  93. if tpl.Options.TrimBlocks {
  94. if prev.Typ != TokenHTML && t.Typ == TokenHTML && prev.Val == "%}" {
  95. if len(t.Val) > 0 && t.Val[0] == '\n' {
  96. t.Val = t.Val[1:len(t.Val)]
  97. }
  98. }
  99. }
  100. prev = t
  101. }
  102. }
  103. // Determine the parent to be executed (for template inheritance)
  104. parent := tpl
  105. for parent.parent != nil {
  106. parent = parent.parent
  107. }
  108. // Create context if none is given
  109. newContext := make(Context)
  110. newContext.Update(tpl.set.Globals)
  111. if context != nil {
  112. newContext.Update(context)
  113. if len(newContext) > 0 {
  114. // Check for context name syntax
  115. err := newContext.checkForValidIdentifiers()
  116. if err != nil {
  117. return parent, nil, err
  118. }
  119. // Check for clashes with macro names
  120. for k := range newContext {
  121. _, has := tpl.exportedMacros[k]
  122. if has {
  123. return parent, nil, &Error{
  124. Filename: tpl.name,
  125. Sender: "execution",
  126. OrigError: fmt.Errorf("context key name '%s' clashes with macro '%s'", k, k),
  127. }
  128. }
  129. }
  130. }
  131. }
  132. // Create operational context
  133. ctx := newExecutionContext(parent, newContext)
  134. return parent, ctx, nil
  135. }
  136. func (tpl *Template) execute(context Context, writer TemplateWriter) error {
  137. parent, ctx, err := tpl.newContextForExecution(context)
  138. if err != nil {
  139. return err
  140. }
  141. // Run the selected document
  142. if err := parent.root.Execute(ctx, writer); err != nil {
  143. return err
  144. }
  145. return nil
  146. }
  147. func (tpl *Template) newTemplateWriterAndExecute(context Context, writer io.Writer) error {
  148. return tpl.execute(context, &templateWriter{w: writer})
  149. }
  150. func (tpl *Template) newBufferAndExecute(context Context) (*bytes.Buffer, error) {
  151. // Create output buffer
  152. // We assume that the rendered template will be 30% larger
  153. buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3)))
  154. if err := tpl.execute(context, buffer); err != nil {
  155. return nil, err
  156. }
  157. return buffer, nil
  158. }
  159. // Executes the template with the given context and writes to writer (io.Writer)
  160. // on success. Context can be nil. Nothing is written on error; instead the error
  161. // is being returned.
  162. func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error {
  163. buf, err := tpl.newBufferAndExecute(context)
  164. if err != nil {
  165. return err
  166. }
  167. _, err = buf.WriteTo(writer)
  168. if err != nil {
  169. return err
  170. }
  171. return nil
  172. }
  173. // Same as ExecuteWriter. The only difference between both functions is that
  174. // this function might already have written parts of the generated template in the
  175. // case of an execution error because there's no intermediate buffer involved for
  176. // performance reasons. This is handy if you need high performance template
  177. // generation or if you want to manage your own pool of buffers.
  178. func (tpl *Template) ExecuteWriterUnbuffered(context Context, writer io.Writer) error {
  179. return tpl.newTemplateWriterAndExecute(context, writer)
  180. }
  181. // Executes the template and returns the rendered template as a []byte
  182. func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
  183. // Execute template
  184. buffer, err := tpl.newBufferAndExecute(context)
  185. if err != nil {
  186. return nil, err
  187. }
  188. return buffer.Bytes(), nil
  189. }
  190. // Executes the template and returns the rendered template as a string
  191. func (tpl *Template) Execute(context Context) (string, error) {
  192. // Execute template
  193. buffer, err := tpl.newBufferAndExecute(context)
  194. if err != nil {
  195. return "", err
  196. }
  197. return buffer.String(), nil
  198. }
  199. func (tpl *Template) ExecuteBlocks(context Context, blocks []string) (map[string]string, error) {
  200. var parents []*Template
  201. result := make(map[string]string)
  202. parent := tpl
  203. for parent != nil {
  204. parents = append(parents, parent)
  205. parent = parent.parent
  206. }
  207. for _, t := range parents {
  208. buffer := bytes.NewBuffer(make([]byte, 0, int(float64(t.size)*1.3)))
  209. _, ctx, err := t.newContextForExecution(context)
  210. if err != nil {
  211. return nil, err
  212. }
  213. for _, blockName := range blocks {
  214. if _, ok := result[blockName]; ok {
  215. continue
  216. }
  217. if blockWrapper, ok := t.blocks[blockName]; ok {
  218. bErr := blockWrapper.Execute(ctx, buffer)
  219. if bErr != nil {
  220. return nil, bErr
  221. }
  222. result[blockName] = buffer.String()
  223. buffer.Reset()
  224. }
  225. }
  226. // We have found all blocks
  227. if len(blocks) == len(result) {
  228. break
  229. }
  230. }
  231. return result, nil
  232. }