Пример #1
0
// 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
}
Пример #2
0
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())
	}
}
Пример #3
0
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
}
Пример #4
0
// 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
}
Пример #5
0
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[""]
}
Пример #6
0
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
}
Пример #7
0
/**
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
}
Пример #8
0
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)
	}

}
Пример #9
0
// 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
}
Пример #10
0
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&gt;0 <a href=" 1%3e0 "></a>`; got != want {
		t.Errorf("got %q want %q", got, want)
	}
}
Пример #11
0
// 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
}
Пример #12
0
// 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
}
Пример #13
0
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
}
Пример #14
0
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("*************************")
}