Skip to content

feat: custom template delimiter #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions classfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ func (p *App) Static__2(pattern string, fs http.FileSystem, allowRedirect ...boo
p.StaticHttp(pattern, fs, allowRedirect...)
}

// custom delimiter,
// example:{{ }} => ${ }$
func (p *App) SetDelims(left, right string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we should support custom delimiter?

Copy link
Contributor Author

@LiusCraft LiusCraft Feb 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scene

It conflicts with yap
vue:

<script setup>
import { ref } from 'vue'
const message = ref('Hello World!')
</script>
<template>
  <h1>{{ message }}</h1>
</template>

to prevent conflicts with the front-end framework, developers can decide to change delimiter of the template
example vue:
goplus/community#63 (comment)

Additionally, most go web frameworks support modifying delimiters.

gin: https://github.com/gin-gonic/gin/blob/v1.9.1/gin.go#L239
beego: https://pkg.go.dev/github.com/astaxie/beego#WebConfig.TemplateLeft

@xushiwei PTAL

p.Engine.SetDelims(left, right)
}

// AppType represents an abstract of YAP applications.
type AppType interface {
InitYap(fs ...fs.FS)
Expand Down
8 changes: 8 additions & 0 deletions demo/classfile_delimiter/blog_yap.gox
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
setDelims "${","}$"
get "/p/:id", ctx => {
ctx.yap "article", {
"id": ctx.param("id"),
}
}

run ":8080"
25 changes: 25 additions & 0 deletions demo/classfile_delimiter/gop_autogen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import "github.com/goplus/yap"

const _ = true

type blog struct {
yap.App
}
//line demo/classfile_delimiter/blog_yap.gox:1
func (this *blog) MainEntry() {
//line demo/classfile_delimiter/blog_yap.gox:1:1
this.SetDelims("${", "}$")
//line demo/classfile_delimiter/blog_yap.gox:2:1
this.Get("/p/:id", func(ctx *yap.Context) {
//line demo/classfile_delimiter/blog_yap.gox:3:1
ctx.Yap__1("article", map[string]string{"id": ctx.Param("id")})
})
//line demo/classfile_delimiter/blog_yap.gox:8:1
this.Run(":8080")
}
func main() {
//line demo/classfile_delimiter/blog_yap.gox:8:1
yap.Gopt_App_Main(new(blog))
}
11 changes: 11 additions & 0 deletions demo/classfile_delimiter/yap/article_yap.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
article: ${ .id }$
</body>
</html>
8 changes: 4 additions & 4 deletions internal/templ/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ import (
"strings"
)

func Translate(text string) string {
func Translate(text, left, right string) string {
offs := make([]int, 0, 16)
base := 0
for {
pos := strings.Index(text[base:], "{{")
pos := strings.Index(text[base:], left)
if pos < 0 {
break
}
begin := base + pos + 2 // script begin
n := strings.Index(text[begin:], "}}")
n := strings.Index(text[begin:], right)
if n < 0 {
n = len(text) - begin // script length
}
Expand Down Expand Up @@ -59,7 +59,7 @@ func Translate(text string) string {
for i := 0; i < n; i++ {
off := offs[i]
b.WriteString(text[base:off])
b.WriteString("}}{{")
b.WriteString(right + left)
base = off
}
b.WriteString(text[base:])
Expand Down
11 changes: 8 additions & 3 deletions internal/templ/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,17 @@ end
</html>
`

var (
Left = "{{"
Right = "}}"
)

func TestTranslate(t *testing.T) {
if ret := Translate(yapScriptIn); ret != yapScriptOut {
if ret := Translate(yapScriptIn, Left, Right); ret != yapScriptOut {
t.Fatal("TestTranslate:", len(ret), len(yapScriptOut), len(yapScriptIn)+11*4, ret)
}
noScript := "abc"
if Translate(noScript) != noScript {
if Translate(noScript, Left, Right) != noScript {
t.Fatal("translate(noScript)")
}
noScriptEnd := `{{abc
Expand All @@ -92,7 +97,7 @@ efg
noScriptEndOut := `{{abc}}{{
efg
`
if ret := Translate(noScriptEnd); ret != noScriptEndOut {
if ret := Translate(noScriptEnd, Left, Right); ret != noScriptEndOut {
t.Fatal("translate(noScriptEnd):", ret)
}
}
18 changes: 14 additions & 4 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,38 @@ import (
"github.com/goplus/yap/internal/templ"
)

// template delimiter, default is {{ }}
type Delims struct {
Left string
Right string
}

// Template is the representation of a parsed template. The *parse.Tree
// field is exported only for use by html/template and should be treated
// as unexported by all other clients.
type Template struct {
*template.Template
delims Delims
}

// NewTemplate allocates a new, undefined template with the given name.
func NewTemplate(name string) Template {
return Template{template.New(name)}
return Template{template.New(name), Delims{"{{", "}}"}}
}

func (t Template) Parse(text string) (ret Template, err error) {
ret.Template, err = t.Template.Parse(templ.Translate(text))
t.Template.Delims(t.delims.Left, t.delims.Right)
ret.Template, err = t.Template.Parse(templ.Translate(text, t.delims.Left, t.delims.Right))
return
}

func ParseFSFile(f fs.FS, file string) (t Template, err error) {
func ParseFSFile(f fs.FS, file string, delims Delims) (t Template, err error) {
b, err := fs.ReadFile(f, file)
if err != nil {
return
}
name := filepath.Base(file)
return NewTemplate(name).Parse(string(b))
t = NewTemplate(name)
t.delims = delims
return t.Parse(string(b))
}
17 changes: 13 additions & 4 deletions yap.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ type Engine struct {
router
Mux *http.ServeMux

tpls map[string]Template
fs fs.FS
las func(addr string, handler http.Handler) error
tpls map[string]Template
fs fs.FS
las func(addr string, handler http.Handler) error
delims Delims
}

// New creates a YAP engine.
func New(fs ...fs.FS) *Engine {
e := new(Engine)
e.InitYap(fs...)
e.SetDelims("{{", "}}")
return e
}

Expand Down Expand Up @@ -68,6 +70,13 @@ func (p *Engine) initYapFS(fsys fs.FS) {
p.tpls = make(map[string]Template)
}

func (p *Engine) SetDelims(left, right string) {
if !(len(left) == 2 && len(right) == 2) {
log.Panicln("The length of the delimiter must be two")
}
p.delims = Delims{left, right}
}

func (p *Engine) yapFS() fs.FS {
if p.fs == nil {
p.initYapFS(os.DirFS("."))
Expand Down Expand Up @@ -156,7 +165,7 @@ func (p *Engine) templ(path string) (t Template, err error) {
}
t, ok := p.tpls[path]
if !ok {
t, err = ParseFSFile(fsys, path+"_yap.html")
t, err = ParseFSFile(fsys, path+"_yap.html", p.delims)
if err != nil {
return
}
Expand Down