// Parse parses a string into a template. Nested template definitions will be // associated with the top-level template t. Parse may be called multiple times // to parse definitions of templates to associate with t. It is an error if a // resulting template is non-empty (contains content other than template // definitions) and would replace a non-empty template with the same name. // (In multiple calls to Parse with the same receiver template, only one call // can contain text other than space, comments, and template definitions.) func (t *Template) Parse(text string) (*Template, error) { t.init() trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) if err != nil { return nil, err } // Add the newly parsed trees, including the one for t, into our common structure. for name, tree := range trees { // If the name we parsed is the name of this template, overwrite this template. // The associate method checks it's not a redefinition. tmpl := t if name != t.name { tmpl = t.New(name) } // Even if t == tmpl, we need to install it in the common.tmpl map. if replace, err := t.associate(tmpl, tree); err != nil { return nil, err } else if replace { tmpl.Tree = tree } tmpl.leftDelim = t.leftDelim tmpl.rightDelim = t.rightDelim } return t, nil }
func TestAddParseTree(t *testing.T) { // Create some templates. root, err := New("root").Parse(cloneText1) if err != nil { t.Fatal(err) } _, err = root.Parse(cloneText2) if err != nil { t.Fatal(err) } // Add a new parse tree. tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins) if err != nil { t.Fatal(err) } added, err := root.AddParseTree("c", tree["c"]) // Execute. var b bytes.Buffer err = added.ExecuteTemplate(&b, "a", 0) if err != nil { t.Fatal(err) } if b.String() != "broot" { t.Errorf("expected %q got %q", "broot", b.String()) } }
func compileTemplate(name, src string) (string, error) { tset, err := parse.Parse(name, src, "{{", "}}", funcStubs) if err != nil { return "", err } return tset[name].Root.String(), nil }
// Issue 7032 func TestAddParseTreeToUnparsedTemplate(t *testing.T) { master := "{{define \"master\"}}{{end}}" tmpl := New("master") tree, err := parse.Parse("master", master, "", "", nil) if err != nil { t.Fatalf("unexpected parse err: %v", err) } masterTree := tree["master"] tmpl.AddParseTree("master", masterTree) // used to panic }
func compileTree(text string) *parse.Tree { funcs := template.FuncMap{ topAssetsFuncName: nop, bottomAssetsFuncName: nop, } treeMap, err := parse.Parse("", text, leftDelim, rightDelim, funcs) if err != nil { panic(err) } return treeMap[""] }
func (t *Template) Parse(s string) (*Template, error) { fs := map[string]interface{}{ "print1": print1, } m, err := parse.Parse("name", s, "{{", "}}", fs) if err != nil { return nil, err } t.Tree = m["name"] return t, nil }
/** parseText 需要知道第一个 tree 的 kind. 以便添加到 t. 可能会载入新的文件, 产生模板名称对应的模板尚未载入. 直到全部解析完才会完整载入. 参数 ns 是给 tree 的 uri 命名. */ func parseText(t *Template, names map[string]bool, kind Kind, ns, text string) error { var name string trees, err := parse.Parse(ns, text, t.leftDelim, t.rightDelim, placeholderFuncs, t.base.funcs) if err != nil { return err } rootdir := t.RootDir() dir := absPath(ns) for from, tree := range trees { name = from // define 内嵌模板不能有扩展名 if name != ns { if path.Ext(name) != "" { return fmt.Errorf( "template: extension are not supported on define %q", from) } name = relToURI(rootdir, dir, name) } if name == "" { return fmt.Errorf("template: is invalid on define %q", from) } // 需要再次检查模板是否被载入 if t.base.Lookup(name).IsValid() { return fmt.Errorf( "template: redefinition of %q", name) } tree.Name = name tree.ParseName = ns err = hackNode(t, names, name, tree.Root, -1, tree.Root) if err == nil { _, err = t.AddParseTree(kind, tree) } if err != nil { return err } } names[ns] = false return nil }
func main() { root := template.Must(template.New("root").Parse(`{{define "a"}} {{.}} {{template "b" }} {{.}} ">{{.}} </a>{{end}}`)) tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil) if err != nil { fmt.Println(err) } added := template.Must(root.AddParseTree("b", tree["b"])) err = added.ExecuteTemplate(os.Stdout, "a", "1>0") if err != nil { fmt.Println(err) } }
// Parse defines the template by parsing the text. Nested template definitions will be // associated with the top-level template t. Parse may be called multiple times // to parse definitions of templates to associate with t. It is an error if a // resulting template is non-empty (contains content other than template // definitions) and would replace a non-empty template with the same name. // (In multiple calls to Parse with the same receiver template, only one call // can contain text other than space, comments, and template definitions.) func (t *Template) Parse(text string) (*Template, error) { t.muFuncs.RLock() trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) t.muFuncs.RUnlock() if err != nil { return nil, err } // Add the newly parsed trees, including the one for t, into our common structure. for name, tree := range trees { if _, err := t.AddParseTree(name, tree); err != nil { return nil, err } } return t, nil }
func TestAddParseTree(t *testing.T) { root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`)) tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil) if err != nil { t.Fatal(err) } added := Must(root.AddParseTree("b", tree["b"])) b := new(bytes.Buffer) err = added.ExecuteTemplate(b, "a", "1>0") if err != nil { t.Fatal(err) } if got, want := b.String(), ` 1>0 <a href=" 1%3e0 "></a>`; got != want { t.Errorf("got %q want %q", got, want) } }
// Parse parses a string into a template. func (ve *ViewEngine) Parse(name, src string) (*ViewEngine, error) { name = normalizeName(name) ve.mu.Lock() defer ve.mu.Unlock() // Error if a template by this name has already been added. _, ok := ve.pages[name] if ok || ve.partials.Lookup(name) != nil { return ve, fmt.Errorf("viewengine: redefinition of template %q", name) } trees, err := parse.Parse(name, src, "", "", builtins) if err != nil { return ve, err } isPage := false // If any of the templates start with the contentSectionPrefix // then the template set is treated as a page. for _, v := range trees { if strings.HasPrefix(v.Name, contentSectionPrefix) { isPage = true break } } if isPage { log.Printf("viewengine: adding page %q\n", name) ve.pages[name] = trees } else { for _, v := range trees { log.Printf("viewengine: adding partial %q\n", v.Name) _, err := ve.partials.AddParseTree(v.Name, v) if err != nil { return ve, err } } } return ve, nil }
// Parse parses the given text as a set of template trees, adding placeholders // for undefined functions and variables. func Parse(name string, text string) (map[string]*parse.Tree, error) { text = ReplaceVariableShorthands(text, '@', "") funcs := make(map[string]interface{}) var treeSet map[string]*parse.Tree var err error for { treeSet, err = parse.Parse(name, text, leftDelim, rightDelim, funcs) if err != nil { if m := funcNotDefinedRe.FindStringSubmatch(err.Error()); m != nil { funcs[m[1]] = func() {} continue } if m := varNotDefinedRe.FindStringSubmatch(err.Error()); m != nil { prepend := fmt.Sprintf("{{ $%s := . }}\n", m[1]) text = prepend + defineRe.ReplaceAllString(text, "$0"+strings.Replace(prepend, "$", "$$", -1)) continue } return nil, err } break } return treeSet, nil }
func (t *Template) load(name string, included bool, from string) error { for _, v := range t.loaded { if v == name { return nil } } s, err := t.loadText(name) if err != nil { return err } matches := commentRe.FindStringSubmatch(s) comment := "" if matches != nil && len(matches) > 0 { comment = matches[1] } // Add it to the loaded list before calling parseComment, since // it's the path which can trigger additional loads. t.loaded = append(t.loaded, name) err = t.parseComment(name, comment, name, included) if err != nil { return err } if idx := strings.Index(s, "</head>"); idx >= 0 { s = s[:idx] + fmt.Sprintf("{{ template %q . }}", topBoilerplateName) + s[idx:] } if idx := strings.Index(s, "</body>"); idx >= 0 { s = s[:idx] + fmt.Sprintf("{{ template %q . }}", bottomBoilerplateName) + s[idx:] } // Replace {{ block }} with {{ template }} + {{ define }} s, err = replaceBlocks(name, s) if err != nil { return err } // The $Vars definition must be present at parse // time, because otherwise the parser will throw an // error when it finds a variable which wasn't // previously defined // Prepend to the template and to any define nodes found s = templatePrepend + defineRe.ReplaceAllString(s, "$0"+strings.Replace(templatePrepend, "$", "$$", -1)) // Replace @variable shorthands s = templateutil.ReplaceVariableShorthands(s, '@', varsKey) var fns map[string]interface{} if t.funcMap != nil { fns = t.funcMap.asTemplateFuncMap() } var treeMap map[string]*parse.Tree fixed := make(map[string]bool) for { treeMap, err = parse.Parse(name, s, leftDelim, rightDelim, templateFuncs.asTemplateFuncMap(), fns) if err == nil { break } if m := undefinedVariableRe.FindStringSubmatch(err.Error()); m != nil && !fixed[err.Error()] { fixed[err.Error()] = true // Look back to the previous define or beginning of the file line, _ := strconv.Atoi(m[1]) maxP := 0 // Find the position in s where the error line begins for ii := 0; ii < len(s) && line > 1; ii++ { maxP++ if s[ii] == '\n' { line-- } } varName := m[2] // Advance until the variable usage if idx := strings.Index(s[maxP:], "$"+varName); idx > 0 { maxP += idx } p := 0 // Check if we have any defines before the error line dm := defineRe.FindAllStringIndex(s, -1) for ii := len(dm) - 1; ii >= 0; ii-- { v := dm[ii] if v[1] < maxP { p = v[1] break } } s = s[:p] + fmt.Sprintf("{{ $%s := %s }}", varName, varNop) + s[p:] err = nil } if err != nil { return fmt.Errorf("error parsing %s", err) } } if err := t.replaceExtendTag(name, treeMap, from); err != nil { return err } var renames map[string]string for k, v := range treeMap { if _, contains := t.trees[k]; contains { log.Debugf("Template %s redefined", k) // Redefinition of a template, which is allowed // by gondola templates. Just rename this // template and change any template // nodes referring to it in the final sweep if renames == nil { renames = make(map[string]string) } fk := k for { k += "_" if len(renames[fk]) < len(k) { renames[fk] = k break } } } err := t.AddParseTree(k, v) if err != nil { return err } } if t.contentType == "" { mimeType := mime.TypeByExtension(path.Ext(t.name)) if mimeType == "" { mimeType = "text/html; charset=utf-8" } t.contentType = mimeType } if renames != nil { t.renameTemplates(renames) } return nil }
func main() { // TODO: MountDir() method takes a directory path // and does a ParseGlob() on it. // Templates loaded from the dir have their names // shortened to just the relative path. // Ex. MountDir("/path/to/dir") might add a template "blah.gohtml" // rather than "/path/to/dir/blah.gohtml". // TODO: NoCache = true; should cause ParseGlob, ParseFiles, and MountDir // to always reread the template from disk when executing it. // Basically, the calls to those parse methods cause // the file names to be cached and if NoCache == true then // Execute() reloads the template set from scratch. /* Goal is to load all view files into a single template set and transparently handle any master page content area naming collisions across pages. */ var err error var masterPageTemplates []string var tmpl = template.New("~") // Parse an empty template to avoid panics due // to an uninitialized internal parse tree. // https://groups.google.com/forum/#!topic/golang-nuts/C8jPINo8sHc _, _ = tmpl.Parse("") filenames, err := filepath.Glob("./test_views/views/simple/*.gohtml") if err != nil { panic(err) } for _, filename := range filenames { fmt.Printf("file: %s\n", filename) // Read file. srcBytes, err := ioutil.ReadFile(filename) if err != nil { panic(err) } src := string(srcBytes) tree, err := parse.Parse(filename, src, "", "", nil) if err != nil { panic(err) } // Iterate all templates defined in the file. for _, fileTemplate := range tree { // Preprocess page templates. // Do nothing to master page templates. if !strings.HasSuffix(filename, ".master.gohtml") { processPageTemplate(filename, fileTemplate) } else { // Keep track of which templates were from // master page template files. masterPageTemplates = append(masterPageTemplates, fileTemplate.Name) } // Add to our global template set. _, err = tmpl.AddParseTree(fileTemplate.Name, fileTemplate) if err != nil { panic(err) } } fmt.Println() } // Execute a template as a test. var templateName = "test_views/views/simple/page1.gohtml" // Clone the template set. execTmpl, err := tmpl.Clone() if err != nil { panic(err) } // Update the ~ {{template}} calls in all // master page templates to have the file name prefix. for _, masterPageTemplateName := range masterPageTemplates { var execTmplItem = execTmpl.Lookup(masterPageTemplateName) prefixTildeTemplates(templateName, execTmplItem.Root.Nodes) } // *Then* execute the template. // We can cache the template set at this point for future renders of this view. fmt.Println("*************************") var renderedOutput = &bytes.Buffer{} err = execTmpl.ExecuteTemplate(renderedOutput, templateName, nil) if err != nil { panic(err) } fmt.Print(renderedOutput.String()) fmt.Println() fmt.Println("*************************") }