template: change the signature of formatters for future development.

Make them more like Printf, with a ... final argument.  This breaks
code with existing formatters but not the templates that use them.

R=rsc, gri
CC=golang-dev
https://golang.org/cl/3378041
This commit is contained in:
Rob Pike 2010-12-01 13:33:49 -08:00
parent 009aebdba8
commit d1524217df
5 changed files with 54 additions and 44 deletions

View File

@ -563,7 +563,7 @@ func writeText(w io.Writer, text []byte, html bool) {
// Write anything to w; optionally html-escaped.
func writeAny(w io.Writer, x interface{}, html bool) {
func writeAny(w io.Writer, html bool, x interface{}) {
switch v := x.(type) {
case []byte:
writeText(w, v, html)
@ -584,23 +584,23 @@ func writeAny(w io.Writer, x interface{}, html bool) {
// Template formatter for "html" format.
func htmlFmt(w io.Writer, x interface{}, format string) {
writeAny(w, x, true)
func htmlFmt(w io.Writer, format string, x ...interface{}) {
writeAny(w, true, x[0])
}
// Template formatter for "html-esc" format.
func htmlEscFmt(w io.Writer, x interface{}, format string) {
func htmlEscFmt(w io.Writer, format string, x ...interface{}) {
var buf bytes.Buffer
writeAny(&buf, x, false)
writeAny(&buf, false, x[0])
template.HTMLEscape(w, buf.Bytes())
}
// Template formatter for "html-comment" format.
func htmlCommentFmt(w io.Writer, x interface{}, format string) {
func htmlCommentFmt(w io.Writer, format string, x ...interface{}) {
var buf bytes.Buffer
writeAny(&buf, x, false)
writeAny(&buf, false, x[0])
// TODO(gri) Provide list of words (e.g. function parameters)
// to be emphasized by ToHTML.
doc.ToHTML(w, buf.Bytes(), nil) // does html-escaping
@ -608,13 +608,13 @@ func htmlCommentFmt(w io.Writer, x interface{}, format string) {
// Template formatter for "" (default) format.
func textFmt(w io.Writer, x interface{}, format string) {
writeAny(w, x, false)
func textFmt(w io.Writer, format string, x ...interface{}) {
writeAny(w, false, x[0])
}
// Template formatter for the various "url-xxx" formats.
func urlFmt(w io.Writer, x interface{}, format string) {
func urlFmt(w io.Writer, format string, x ...interface{}) {
var path string
var line int
@ -622,7 +622,7 @@ func urlFmt(w io.Writer, x interface{}, format string) {
type positioner interface {
Pos() token.Position
}
switch t := x.(type) {
switch t := x[0].(type) {
case string:
path = t
case positioner:
@ -676,14 +676,14 @@ var infoKinds = [nKinds]string{
// Template formatter for "infoKind" format.
func infoKindFmt(w io.Writer, x interface{}, format string) {
fmt.Fprintf(w, infoKinds[x.(SpotKind)]) // infoKind entries are html-escaped
func infoKindFmt(w io.Writer, format string, x ...interface{}) {
fmt.Fprintf(w, infoKinds[x[0].(SpotKind)]) // infoKind entries are html-escaped
}
// Template formatter for "infoLine" format.
func infoLineFmt(w io.Writer, x interface{}, format string) {
info := x.(SpotInfo)
func infoLineFmt(w io.Writer, format string, x ...interface{}) {
info := x[0].(SpotInfo)
line := info.Lori()
if info.IsIndex() {
index, _ := searchIndex.get()
@ -702,8 +702,8 @@ func infoLineFmt(w io.Writer, x interface{}, format string) {
// Template formatter for "infoSnippet" format.
func infoSnippetFmt(w io.Writer, x interface{}, format string) {
info := x.(SpotInfo)
func infoSnippetFmt(w io.Writer, format string, x ...interface{}) {
info := x[0].(SpotInfo)
text := `<span class="alert">no snippet text available</span>`
if info.IsIndex() {
index, _ := searchIndex.get()
@ -716,30 +716,30 @@ func infoSnippetFmt(w io.Writer, x interface{}, format string) {
// Template formatter for "padding" format.
func paddingFmt(w io.Writer, x interface{}, format string) {
for i := x.(int); i > 0; i-- {
func paddingFmt(w io.Writer, format string, x ...interface{}) {
for i := x[0].(int); i > 0; i-- {
fmt.Fprint(w, `<td width="25"></td>`)
}
}
// Template formatter for "time" format.
func timeFmt(w io.Writer, x interface{}, format string) {
template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x.(int64)/1e9).String()))
func timeFmt(w io.Writer, format string, x ...interface{}) {
template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x[0].(int64)/1e9).String()))
}
// Template formatter for "dir/" format.
func dirslashFmt(w io.Writer, x interface{}, format string) {
if x.(*os.FileInfo).IsDirectory() {
func dirslashFmt(w io.Writer, format string, x ...interface{}) {
if x[0].(*os.FileInfo).IsDirectory() {
w.Write([]byte{'/'})
}
}
// Template formatter for "localname" format.
func localnameFmt(w io.Writer, x interface{}, format string) {
_, localname := pathutil.Split(x.(string))
func localnameFmt(w io.Writer, format string, x ...interface{}) {
_, localname := pathutil.Split(x[0].(string))
template.HTMLEscape(w, []byte(localname))
}

View File

@ -367,7 +367,7 @@ func main() {
if i > 0 {
fmt.Println()
}
writeAny(os.Stdout, d, *html)
writeAny(os.Stdout, *html, d)
fmt.Println()
}
return

View File

@ -16,12 +16,14 @@ import (
// It is stored under the name "str" and is the default formatter.
// You can override the default formatter by storing your default
// under the name "" in your custom formatter map.
func StringFormatter(w io.Writer, value interface{}, format string) {
if b, ok := value.([]byte); ok {
w.Write(b)
return
func StringFormatter(w io.Writer, format string, value ...interface{}) {
if len(value) == 1 {
if b, ok := value[0].([]byte); ok {
w.Write(b)
return
}
}
fmt.Fprint(w, value)
fmt.Fprint(w, value...)
}
var (
@ -60,11 +62,15 @@ func HTMLEscape(w io.Writer, s []byte) {
}
// HTMLFormatter formats arbitrary values for HTML
func HTMLFormatter(w io.Writer, value interface{}, format string) {
b, ok := value.([]byte)
func HTMLFormatter(w io.Writer, format string, value ...interface{}) {
ok := false
var b []byte
if len(value) == 1 {
b, ok = value[0].([]byte)
}
if !ok {
var buf bytes.Buffer
fmt.Fprint(&buf, value)
fmt.Fprint(&buf, value...)
b = buf.Bytes()
}
HTMLEscape(w, b)

View File

@ -55,9 +55,10 @@
map passed to the template set up routines or in the default
set ("html","str","") and is used to process the data for
output. The formatter function has signature
func(wr io.Writer, data interface{}, formatter string)
where wr is the destination for output, data is the field
value, and formatter is its name at the invocation site.
func(wr io.Writer, formatter string, data ...interface{})
where wr is the destination for output, data holds the field
values at the instantiation, and formatter is its name at
the invocation site.
*/
package template
@ -101,7 +102,7 @@ const (
// FormatterMap is the type describing the mapping from formatter
// names to the functions that implement them.
type FormatterMap map[string]func(io.Writer, interface{}, string)
type FormatterMap map[string]func(io.Writer, string, ...interface{})
// Built-in formatters.
var builtins = FormatterMap{
@ -690,13 +691,13 @@ func (t *Template) writeVariable(v *variableElement, st *state) {
// is it in user-supplied map?
if t.fmap != nil {
if fn, ok := t.fmap[formatter]; ok {
fn(st.wr, val, formatter)
fn(st.wr, formatter, val)
return
}
}
// is it in builtin map?
if fn, ok := builtins[formatter]; ok {
fn(st.wr, val, formatter)
fn(st.wr, formatter, val)
return
}
t.execError(st, v.linenum, "missing formatter %s for variable %s", formatter, v.name)

View File

@ -76,9 +76,12 @@ func plus1(v interface{}) string {
return fmt.Sprint(i + 1)
}
func writer(f func(interface{}) string) func(io.Writer, interface{}, string) {
return func(w io.Writer, v interface{}, format string) {
io.WriteString(w, f(v))
func writer(f func(interface{}) string) func(io.Writer, string, ...interface{}) {
return func(w io.Writer, format string, v ...interface{}) {
if len(v) != 1 {
panic("test writer expected one arg")
}
io.WriteString(w, f(v[0]))
}
}
@ -601,7 +604,7 @@ func TestHTMLFormatterWithByte(t *testing.T) {
s := "Test string."
b := []byte(s)
var buf bytes.Buffer
HTMLFormatter(&buf, b, "")
HTMLFormatter(&buf, "", b)
bs := buf.String()
if bs != s {
t.Errorf("munged []byte, expected: %s got: %s", s, bs)