Support TokenEscapeChar, add tests

This commit is contained in:
Andinus 2024-11-16 11:48:50 +05:30
parent c9c7dd1b22
commit decc220bfc
Signed by: andinus
GPG Key ID: B67D55D482A799FD
4 changed files with 121 additions and 0 deletions

@ -22,6 +22,7 @@ type Option struct {
ShowLabels bool // Prepend & Append a string to every template, helpful in debugging ShowLabels bool // Prepend & Append a string to every template, helpful in debugging
CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '<!--', '-->' CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '<!--', '-->'
FixedIndent bool // Intended to improve readability when inspecting nested templates FixedIndent bool // Intended to improve readability when inspecting nested templates
TokenEscapeChar string // Escapes a token delimiter, i.e. if set to '\' then variables that have '\' prefix won't be replaced
} }
type TemplateNest struct { type TemplateNest struct {
@ -131,6 +132,7 @@ func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) {
delimiterStart := nest.option.Delimiters[0] delimiterStart := nest.option.Delimiters[0]
delimiterEnd := nest.option.Delimiters[1] delimiterEnd := nest.option.Delimiters[1]
escapeChar := nest.option.TokenEscapeChar
re := regexp.MustCompile(fmt.Sprintf( re := regexp.MustCompile(fmt.Sprintf(
"%s\\s*(.+?)\\s*%s", regexp.QuoteMeta(delimiterStart), regexp.QuoteMeta(delimiterEnd), "%s\\s*(.+?)\\s*%s", regexp.QuoteMeta(delimiterStart), regexp.QuoteMeta(delimiterEnd),
@ -151,6 +153,22 @@ func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) {
varName := string(contents[nameStartIdx:nameEndIdx]) varName := string(contents[nameStartIdx:nameEndIdx])
variableNames[varName] = struct{}{} variableNames[varName] = struct{}{}
// If token escape char is set then look behind for it and if we
// find the escape char then we're only going to remove the escape
// char and not remove this variable.
if escapeChar != "" && startIdx >= len(escapeChar) {
escapeStartIdx := startIdx - len(escapeChar)
if contentsStr[escapeStartIdx:startIdx] == escapeChar {
variables = append(variables, TemplateFileVariable{
Name: "",
StartPosition: uint(escapeStartIdx),
EndPosition: uint(escapeStartIdx + len(escapeChar)),
EscapedToken: true,
})
continue
}
}
// If fixed indent is enabled then record the indent level for this // If fixed indent is enabled then record the indent level for this
// variable. To get the indent level we look at each character in // variable. To get the indent level we look at each character in
// reverse from the start position of the variable until we find a // reverse from the start position of the variable until we find a
@ -276,6 +294,10 @@ func (nest *TemplateNest) Render(toRender interface{}) (string, error) {
rendered := tIndex.Contents rendered := tIndex.Contents
for i := len(tIndex.Variables) - 1; i >= 0; i-- { for i := len(tIndex.Variables) - 1; i >= 0; i-- {
variable := tIndex.Variables[i] variable := tIndex.Variables[i]
if variable.EscapedToken {
rendered = rendered[:variable.StartPosition] + rendered[variable.EndPosition:]
continue
}
// If the variable doesn't exist in template hash then replace it // If the variable doesn't exist in template hash then replace it
// with an empty string. // with an empty string.

@ -0,0 +1,97 @@
package tests
import (
"git.virtual.blue/tomgracey/template-nest-go"
"github.com/stretchr/testify/assert"
"io/ioutil"
"strings"
"testing"
)
func TestRenderWithEscapedVariable(t *testing.T) {
nest, err := templatenest.New(templatenest.Option{
TemplateDir: "templates",
TokenEscapeChar: "\\",
})
if err != nil {
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
}
page := templatenest.Hash{
"TEMPLATE": "00-simple-page",
"variable": "Simple Variable",
"simple_component": []templatenest.Hash{
templatenest.Hash{
"TEMPLATE": "01-simple-component-token-escape",
},
},
}
outputPath := "templates/output/09-simple-page-token-escape.html"
outputContents, err := ioutil.ReadFile(outputPath)
if err != nil {
t.Fatalf("error reading file (`%s`): %+v", outputPath, err)
}
assert.Equal(
t,
strings.TrimSpace(string(outputContents)),
nest.MustRender(page),
"Rendered output does not match expected output",
)
}
func TestRenderWithEscapedVariableAtStart(t *testing.T) {
nest, err := templatenest.New(templatenest.Option{
TemplateDir: "templates",
TokenEscapeChar: "\\",
})
if err != nil {
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
}
page := templatenest.Hash{
"TEMPLATE": "03-var-at-begin",
"variable": "Simple Variable",
}
outputPath := "templates/output/10-var-at-begin.html"
outputContents, err := ioutil.ReadFile(outputPath)
if err != nil {
t.Fatalf("error reading file (`%s`): %+v", outputPath, err)
}
assert.Equal(
t,
strings.TrimSpace(string(outputContents)),
nest.MustRender(page),
"Rendered output does not match expected output",
)
}
func TestRenderWithEscapedVariableAtStart01(t *testing.T) {
nest, err := templatenest.New(templatenest.Option{
TemplateDir: "templates",
TokenEscapeChar: "\\",
})
if err != nil {
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
}
page := templatenest.Hash{
"TEMPLATE": "03-var-at-begin-with-space",
}
outputPath := "templates/output/10-var-at-begin-with-space.html"
outputContents, err := ioutil.ReadFile(outputPath)
if err != nil {
t.Fatalf("error reading file (`%s`): %+v", outputPath, err)
}
assert.Equal(
t,
strings.TrimSpace(string(outputContents)),
nest.MustRender(page),
"Rendered output does not match expected output",
)
}

@ -0,0 +1 @@
\<!--% variable %-->

@ -0,0 +1 @@
<!--% variable %-->