Skip to content

template formatter chaining #676

@adg

Description

@adg
A recent issue with the HTMLFormatter mishandling []byte values  has
prompted a discussion about restructuring the way template formatters work.  

See the thread here:

http://groups.google.com/group/golang-dev/t/9e19c6b2f578c5ad

And relevant notes from rsc:

--

The most interesting formatters, at least in the template code
I've written or seen, are the ones that take a particular
rich data type and do something type-specific.  For example,
godoc has formatters that format a syntax tree as HTML or
as text.  The directory listings have a tiny little formatter
called dir/ that takes a *os.Dir and emits a "/" if it is a
directory, and "" otherwise, which puts the / on adler32/ on
the http://golang.org/src/pkg/hash/ page.  There are probably
more that I'm forgetting, but it's important not to make those
harder.

One possibility would be to change the signature of the
formatters to allow

func Formatter(w io.Writer, value T, format string)

for any type T, and have Execute treat T = string or T = []byte
as meaning "if it's not that type already, convert to a string
representation and then pass that type".  So you'd have

func HTMLFormatter(w io.Writer, value []byte, format string)

and Execute would then do the conversions, but

func DirSlash(w io.Writer, value *os.Dir, format string)
func HTMLAst(w io.Writer, value ast.Expr, format string)

would still work too (and Execute could give a good error
when the value isn't the expected type).

That opens the door to the possibility of using formatters
that accept []byte in pseudo-pipelines, like

{foo|ast|html}

as long as the ast formatter can handle foo and then the
html formatter can handle []byte.  Execute would run "ast"
writing to a bytes.Buffer and then run "html" passing
buf.Bytes() writing to wherever it usually writes to.

The reflect package didn't have Call when we wrote template,
so this wasn't an option we could have even considered.
Json-template does pipelines now but there it's easier to
just keep returning new values and taking old values, because
there are no static types in the program.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions