Speed up template compilation by not building the same parse trees multiple times.

Compile panel_debug.
Add the ability to print int8, int16, int32, uint, uint8, uint16, uint32 and uint64 in compiled templates.
Add the ability to pass string nodes to subtemplates in the template compiler.
Fix bunit in the template compiler.

Shorten some things.
This commit is contained in:
Azareal 2019-10-13 08:49:08 +10:00
parent f76af39a11
commit df6e268a06
5 changed files with 120 additions and 61 deletions

View File

@ -6,14 +6,16 @@ import (
"io"
"log"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/Azareal/Gosora/common/alerts"
"github.com/Azareal/Gosora/common/phrases"
p "github.com/Azareal/Gosora/common/phrases"
"github.com/Azareal/Gosora/common/templates"
"github.com/Azareal/Gosora/query_gen"
)
var Ctemplates []string // TODO: Use this to filter out top level templates we don't need
@ -184,8 +186,7 @@ func CompileTemplates() error {
log.Print("Compiling the default templates")
var wg sync.WaitGroup
err := compileTemplates(&wg, c, "")
if err != nil {
if err := compileTemplates(&wg, c, ""); err != nil {
return err
}
oroots := c.GetOverridenRoots()
@ -198,7 +199,7 @@ func CompileTemplates() error {
c.SetPerThemeTmpls(tmpls)
log.Print("theme: ", theme)
log.Printf("perThemeTmpls: %+v\n", tmpls)
err = compileTemplates(&wg, c, theme)
err := compileTemplates(&wg, c, theme)
if err != nil {
return err
}
@ -207,30 +208,30 @@ func CompileTemplates() error {
return nil
}
func compileCommons(c *tmpl.CTemplateSet, header *Header, header2 *Header, forumList []Forum, out TItemHold) error {
func compileCommons(c *tmpl.CTemplateSet, head *Header, head2 *Header, forumList []Forum, o TItemHold) error {
// TODO: Add support for interface{}s
_, user2, user3 := tmplInitUsers()
now := time.Now()
// Convienience function to save a line here and there
htitle := func(name string) *Header {
header.Title = name
return header
head.Title = name
return head
}
/*htitle2 := func(name string) *Header {
header2.Title = name
return header2
head2.Title = name
return head2
}*/
var topicsList []*TopicsRow
topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "127.0.0.1", 1, 0, 1, 1, 0, "classname", 0, "", &user2, "", 0, &user3, "General", "/forum/general.2", nil})
topicListPage := TopicListPage{htitle("Topic List"), topicsList, forumList, Config.DefaultForum, TopicListSort{"lastupdated", false}, Paginator{[]int{1}, 1, 1}}
out.Add("topics", "c.TopicListPage", topicListPage)
o.Add("topics", "c.TopicListPage", topicListPage)
forumItem := BlankForum(1, "general-forum.1", "General Forum", "Where the general stuff happens", true, "all", 0, "", 0)
forumPage := ForumPage{htitle("General Forum"), topicsList, forumItem, Paginator{[]int{1}, 1, 1}}
out.Add("forum", "c.ForumPage", forumPage)
out.Add("forums", "c.ForumsPage", ForumsPage{htitle("Forum List"), forumList})
o.Add("forum", "c.ForumPage", forumPage)
o.Add("forums", "c.ForumsPage", ForumsPage{htitle("Forum List"), forumList})
poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{
PollOption{0, "Nothing"},
@ -247,8 +248,8 @@ func compileCommons(c *tmpl.CTemplateSet, header *Header, header2 *Header, forum
replyList = append(replyList, ru)
tpage := TopicPage{htitle("Topic Name"), replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, Paginator{[]int{1}, 1, 1}}
tpage.Forum.Link = BuildForumURL(NameToSlug(tpage.Forum.Name), tpage.Forum.ID)
out.Add("topic", "c.TopicPage", tpage)
out.Add("topic_alt", "c.TopicPage", tpage)
o.Add("topic", "c.TopicPage", tpage)
o.Add("topic_alt", "c.TopicPage", tpage)
return nil
}
@ -362,6 +363,16 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
t.AddStd("panel", "c.Panel", Panel{basePage, "panel_dashboard_right", "", "panel_dashboard", inter})
ges := []GridElement{GridElement{"","", "", 1, "grid_istat", "", "", ""}}
t.AddStd("panel_dashboard", "c.DashGrids", DashGrids{ges,ges})
goVersion := runtime.Version()
dbVersion := qgen.Builder.DbVersion()
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
debugCache := DebugPageCache{1, 1, 1, 2, 2, 2, true}
debugDatabase := DebugPageDatabase{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
debugDisk := DebugPageDisk{1,1,1,1,1,1}
dpage := PanelDebugPage{basePage, goVersion, dbVersion, "0s", 1, qgen.Builder.GetAdapter().GetName(), 1, 1, memStats, debugCache, debugDatabase, debugDisk}
t.AddStd("panel_debug", "c.PanelDebugPage", dpage)
//t.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter})
writeTemplate := func(name string, content interface{}) {
@ -732,7 +743,7 @@ func initDefaultTmplFuncMap() {
panic("phraseNameInt is not a string")
}
// TODO: Log non-existent phrases?
return template.HTML(phrases.GetTmplPhrase(phraseName))
return template.HTML(p.GetTmplPhrase(phraseName))
}
// TODO: Implement this in the template generator too
@ -743,7 +754,7 @@ func initDefaultTmplFuncMap() {
}
// TODO: Log non-existent phrases?
// TODO: Optimise TmplPhrasef so we don't use slow Sprintf there
return template.HTML(phrases.GetTmplPhrasef(phraseName, args...))
return template.HTML(p.GetTmplPhrasef(phraseName, args...))
}
fmap["level"] = func(levelInt interface{}) interface{} {
@ -751,7 +762,7 @@ func initDefaultTmplFuncMap() {
if !ok {
panic("levelInt is not an integer")
}
return template.HTML(phrases.GetLevelPhrase(level))
return template.HTML(p.GetLevelPhrase(level))
}
fmap["bunit"] = func(byteInt interface{}) interface{} {

View File

@ -218,18 +218,15 @@ import "errors"
if !c.config.SkipInitBlock {
stub += "// nolint\nfunc init() {\n"
if !c.config.SkipHandles && c.themeName == "" {
stub += "\tc.Template_" + fname + "_handle = Template_" + fname + "\n"
stub += "\tc.Ctemplates = append(c.Ctemplates,\"" + fname + "\")\n"
}
if !c.config.SkipTmplPtrMap {
stub += "tmpl := Template_" + fname + "\n"
stub += "\tc.TmplPtrMap[\"" + fname + "\"] = &tmpl\n"
stub += "\tc.TmplPtrMap[\"o_" + fname + "\"] = tmpl\n"
}
stub += "}\n\n"
}
@ -287,8 +284,7 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
if r := recover(); r != nil {
fmt.Println(r)
debug.PrintStack()
err := c.loggerf.Sync()
if err != nil {
if err := c.loggerf.Sync(); err != nil {
fmt.Println(err)
}
log.Fatal("")
@ -311,13 +307,14 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
c.localDispStructIndex = 0
c.stats = make(map[string]int)
tree := parse.New(name, c.funcMap)
treeSet := make(map[string]*parse.Tree)
tree, err = tree.Parse(content, "{{", "}}", treeSet, c.funcMap)
//tree := parse.New(name, c.funcMap)
//treeSet := make(map[string]*parse.Tree)
treeSet, err := parse.Parse(name, content, "{{", "}}", c.funcMap)
if err != nil {
return "", err
}
c.detail(name)
c.detailf("treeSet: %+v\n", treeSet)
fname := strings.TrimSuffix(name, filepath.Ext(name))
if c.themeName != "" {
@ -374,8 +371,21 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
TemplateName: fname,
OutBuf: &outBuf,
}
c.templateList = map[string]*parse.Tree{fname: tree}
c.detail(c.templateList)
c.templateList = map[string]*parse.Tree{}
for nname, tree := range treeSet {
if name == nname {
c.templateList[fname] = tree
} else {
if !strings.HasPrefix(nname, ".html") {
c.templateList[nname] = tree
} else {
c.templateList[strings.TrimSuffix(nname, ".html")] = tree
}
}
}
c.detailf("c.templateList: %+v\n", c.templateList)
c.localVars = make(map[string]map[string]VarItemReflect)
c.localVars[fname] = make(map[string]VarItemReflect)
c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect}
@ -392,8 +402,14 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
//}
//c.detailf("c: %+v\n", c)
c.detailf("name: %+v\n", name)
c.detailf("fname: %+v\n", fname)
startIndex := con.StartTemplate("")
c.rootIterate(c.templateList[fname], con)
ttree := c.templateList[fname]
if ttree == nil {
panic("ttree is nil")
}
c.rootIterate(ttree, con)
con.EndTemplate("")
c.afterTemplate(con, startIndex)
//c.templateFragmentCount[fname] = c.fragmentCursor[fname] + 1
@ -574,6 +590,10 @@ w.Write([]byte(`, " + ", -1)
func (c *CTemplateSet) rootIterate(tree *parse.Tree, con CContext) {
c.dumpCall("rootIterate", tree, con)
if tree.Root == nil {
c.detailf("tree: %+v\n", tree)
panic("tree root node is empty")
}
c.detail(tree.Root)
for _, node := range tree.Root.Nodes {
c.detail("Node:", node.String())
@ -1195,8 +1215,9 @@ ArgLoop:
}
leftParam, _ := c.compileIfVarSub(con, leftOperand)
out = "{\nbyteFloat, unit := c.ConvertByteUnit(float64(" + leftParam + "))\n"
out += "w.Write(fmt.Sprintf(\"%.1f\", byteFloat) + unit)\n"
out += "w.Write(StringToBytes(fmt.Sprintf(\"%.1f\", byteFloat) + unit))\n}\n"
literal = true
c.importMap["fmt"] = "fmt"
break ArgLoop
case "abstime":
// TODO: Implement level literals
@ -1601,9 +1622,18 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
if c.guestOnly && base == "StringToBytes("+con.RootHolder+".CurrentUser.Session))" {
return
}
case reflect.Int8, reflect.Int16, reflect.Int32:
c.importMap["strconv"] = "strconv"
base = "StringToBytes(strconv.FormatInt(int64(" + varname + "), 10))"
case reflect.Int64:
c.importMap["strconv"] = "strconv"
base = "StringToBytes(strconv.FormatInt(" + varname + ", 10))"
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
c.importMap["strconv"] = "strconv"
base = "StringToBytes(strconv.FormatUint(uint64(" + varname + "), 10))"
case reflect.Uint64:
c.importMap["strconv"] = "strconv"
base = "StringToBytes(strconv.FormatUint(" + varname + ", 10))"
case reflect.Struct:
// TODO: Avoid clashing with other packages which have structs named Time
if val.Type().Name() == "Time" {
@ -1638,19 +1668,6 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
defer c.retCall("compileSubTemplate")
c.detail("Template Node: ", node.Name)
// TODO: Cascade errors back up the tree to the caller?
content, err := c.loadTemplate(c.fileDir, node.Name)
if err != nil {
c.logger.Fatal(err)
}
tree := parse.New(node.Name, c.funcMap)
var treeSet = make(map[string]*parse.Tree)
tree, err = tree.Parse(content, "{{", "}}", treeSet, c.funcMap)
if err != nil {
c.logger.Fatal(err)
}
fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name))
if c.themeName != "" {
_, ok := c.perThemeTmpls[fname]
@ -1666,6 +1683,36 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
fname += "_member"
}
_, ok := c.templateList[fname]
if !ok {
// TODO: Cascade errors back up the tree to the caller?
content, err := c.loadTemplate(c.fileDir, node.Name)
if err != nil {
c.logger.Fatal(err)
}
//tree := parse.New(node.Name, c.funcMap)
//treeSet := make(map[string]*parse.Tree)
treeSet, err := parse.Parse(node.Name, content, "{{", "}}", c.funcMap)
if err != nil {
c.logger.Fatal(err)
}
c.detailf("treeSet: %+v\n", treeSet)
for nname, tree := range treeSet {
if node.Name == nname {
c.templateList[fname] = tree
} else {
if !strings.HasPrefix(nname, ".html") {
c.templateList[nname] = tree
} else {
c.templateList[strings.TrimSuffix(nname, ".html")] = tree
}
}
}
c.detailf("c.templateList: %+v\n", c.templateList)
}
con := pcon
con.VarHolder = "tmpl_" + fname + "_vars"
con.TemplateName = fname
@ -1675,7 +1722,6 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
case *parse.FieldNode:
// TODO: Incomplete but it should cover the basics
cur := pcon.HoldReflect
var varBit string
if cur.Kind() == reflect.Interface {
cur = cur.Elem()
@ -1707,6 +1753,11 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
}
con.VarHolder = pcon.VarHolder + varBit
con.HoldReflect = cur
case *parse.StringNode:
//con.VarHolder = pcon.VarHolder
//con.HoldReflect = pcon.HoldReflect
con.VarHolder = p.Quoted
con.HoldReflect = reflect.ValueOf(p.Quoted)
case *parse.DotNode:
con.VarHolder = pcon.VarHolder
con.HoldReflect = pcon.HoldReflect
@ -1730,7 +1781,7 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
}
}
c.templateList[fname] = tree
//c.templateList[fname] = tree
subtree := c.templateList[fname]
c.detail("subtree.Root", subtree.Root)
c.localVars[fname] = make(map[string]VarItemReflect)
@ -1746,9 +1797,7 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
c.rootIterate(subtree, con)
con.EndTemplate(endBit)
//c.templateFragmentCount[fname] = c.fragmentCursor[fname] + 1
_, ok := c.fragOnce[fname]
if !ok {
if _, ok := c.fragOnce[fname]; !ok {
c.fragOnce[fname] = true
}
@ -1785,11 +1834,11 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
func (c *CTemplateSet) loadTemplate(fileDir string, name string) (content string, err error) {
c.dumpCall("loadTemplate", fileDir, name)
c.detail("c.themeName: ", c.themeName)
if c.themeName != "" {
c.detail("per-theme override: ", "./themes/"+c.themeName+"/overrides/"+name)
res, err := ioutil.ReadFile("./themes/" + c.themeName + "/overrides/" + name)
t := "./themes/" + c.themeName + "/overrides/" + name
c.detail("per-theme override: ", true)
res, err := ioutil.ReadFile(t)
if err == nil {
content = string(res)
if c.config.Minify {
@ -1820,11 +1869,10 @@ func (c *CTemplateSet) afterTemplate(con CContext, startIndex int) {
c.dumpCall("afterTemplate", con, startIndex)
defer c.retCall("afterTemplate")
var loopDepth = 0
loopDepth := 0
var outBuf = *con.OutBuf
var varcounts = make(map[string]int)
var loopStart = startIndex
varcounts := make(map[string]int)
loopStart := startIndex
if outBuf[startIndex].Type == "startloop" && (len(outBuf) > startIndex+1) {
loopStart++
}
@ -1852,7 +1900,7 @@ func (c *CTemplateSet) afterTemplate(con CContext, startIndex int) {
var varstr string
var i int
var varmap = make(map[string]int)
varmap := make(map[string]int)
for name, count := range varcounts {
if count > 1 {
varstr += "var cached_var_" + strconv.Itoa(i) + " = " + name + "\n"

View File

@ -18,10 +18,10 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
goVersion := runtime.Version()
dbVersion := qgen.Builder.DbVersion()
var uptime string
upDuration := time.Since(c.StartTime)
hours := int(upDuration.Hours())
minutes := int(upDuration.Minutes())
var uptime string
if hours > 24 {
days := hours / 24
hours -= days * 24
@ -63,7 +63,7 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
debugCache := c.DebugPageCache{tlen, ulen, rlen, tcap, ucap, rcap, topicListThawed}
var fErr error
var count = func(tbl string) int {
count := func(tbl string) int {
if fErr != nil {
return 0
}
@ -96,7 +96,7 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
debugDatabase := c.DebugPageDatabase{c.Topics.Count(),c.Users.Count(),c.Rstore.Count(),c.Prstore.Count(),c.Activity.Count(),c.Likes.Count(),attachs,polls,loginLogs,regLogs,modLogs,adminLogs,views,viewsAgents,viewsForums,viewsLangs,viewsReferrers,viewsSystems,postChunks,topicChunks}
var dirSize = func(path string) int {
dirSize := func(path string) int {
if fErr != nil {
return 0
}

View File

@ -19,7 +19,7 @@
</div>
<div class="formrow">
<div class="formitem formlabel"><a>{{lang "panel_user_name"}}</a></div>
<div class="formitem"><input form="user_form" name="user-name" type="text" value="{{.Something.Name}}" placeholder="{{lang "panel_user_name_placeholder"}}" /></div>
<div class="formitem"><input form="user_form" name="user-name" type="text" value="{{.Something.Name}}" placeholder="{{lang "panel_user_name_placeholder"}}" autocomplete="off" /></div>
</div>
{{if .CurrentUser.Perms.EditUserPassword}}<div class="formrow">
<div class="formitem formlabel"><a>{{lang "panel_user_password"}}</a></div>
@ -34,7 +34,7 @@
<div class="formitem formlabel"><a>{{lang "panel_user_group"}}</a></div>
<div class="formitem">
<select form="user_form" name="user-group">
{{range .ItemList}}<option {{if eq .ID $.Something.Group}}selected {{end}}value="{{.ID}}">{{.Name}}</option>{{end}}
{{range .ItemList}}<option{{if eq .ID $.Something.Group}} selected{{end}} value="{{.ID}}">{{.Name}}</option>{{end}}
</select>
</div>
</div>{{end}}

View File

@ -94,4 +94,4 @@
{{if .CurrentUser.IsAdmin}}<div class="rowitem passive">
<a href="/panel/debug/">{{lang "panel_menu_debug"}}</a>
</div>{{end}}
</div>
</div>