From: Russ Cox Date: Mon, 13 Apr 2009 22:25:50 +0000 (-0700) Subject: godoc: switch to go library template system X-Git-Tag: weekly.2009-11-06~1850 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=3d0e4741eb5d1c0ac219eeaea85397e6f8ee1f24;p=gostls13.git godoc: switch to go library template system R=gri DELTA=272 (38 added, 139 deleted, 95 changed) OCL=27372 CL=27390 --- diff --git a/usr/gri/pretty/Makefile b/usr/gri/pretty/Makefile index 0636289efd..836f0890ff 100644 --- a/usr/gri/pretty/Makefile +++ b/usr/gri/pretty/Makefile @@ -36,11 +36,11 @@ pretty.6: platform.6 astprinter.6 compilation.6 compilation.6: platform.6 typechecker.6 -symboltable.6: +symboltable.6: platform.6: utils.6 -astprinter.6: utils.6 symboltable.6 template.6 +astprinter.6: utils.6 symboltable.6 docprinter.6: astprinter.6 diff --git a/usr/gri/pretty/godoc.go b/usr/gri/pretty/godoc.go index f3903e8a9b..0a1ebd49b9 100644 --- a/usr/gri/pretty/godoc.go +++ b/usr/gri/pretty/godoc.go @@ -35,10 +35,12 @@ import ( "io"; "log"; "net"; + "once"; "os"; "parser"; pathutil "path"; "sort"; + "strings"; "tabwriter"; "template"; "time"; @@ -94,24 +96,13 @@ func init() { // ---------------------------------------------------------------------------- // Support -func hasPrefix(s, prefix string) bool { - return len(prefix) <= len(s) && s[0 : len(prefix)] == prefix; -} - - -func hasSuffix(s, suffix string) bool { - pos := len(s) - len(suffix); - return pos >= 0 && s[pos : len(s)] == suffix; -} - - func isGoFile(dir *os.Dir) bool { - return dir.IsRegular() && hasSuffix(dir.Name, ".go"); + return dir.IsRegular() && strings.HasSuffix(dir.Name, ".go"); } func isHTMLFile(dir *os.Dir) bool { - return dir.IsRegular() && hasSuffix(dir.Name, ".html"); + return dir.IsRegular() && strings.HasSuffix(dir.Name, ".html"); } @@ -127,7 +118,7 @@ func isFile(name string) bool { } -func printLink(c *http.Conn, dir, name string) { +func printLink(c io.Write, dir, name string) { fmt.Fprintf(c, "%s
\n", pathutil.Clean(filePrefix + dir + "/" + name), name); } @@ -208,26 +199,55 @@ func parse(path string, mode uint) (*ast.Program, errorList) { // Templates // html template -// TODO initialize only if needed (i.e. if run as a server) -var godoc_html = template.NewTemplateOrDie("godoc.html"); +var godoc_html string + +func readTemplate() { + name := "usr/gri/pretty/godoc.html"; + f, err := os.Open(name, os.O_RDONLY, 0); + if err != nil { + log.Exitf("open %s: %v", name, err); + } + var b io.ByteBuffer; + if n, err := io.Copy(f, &b); err != nil { + log.Exitf("copy %s: %v", name, err); + } + f.Close(); + godoc_html = string(b.Data()); +} + + +func servePage(c *http.Conn, title, content interface{}) { + once.Do(readTemplate); -func servePage(c *http.Conn, title string, contents func()) { c.SetHeader("content-type", "text/html; charset=utf-8"); - // TODO handle Apply errors - godoc_html.Apply(c, "" : func() { fmt.Fprint(c, title); }, - "HEADER-->" : func() { fmt.Fprint(c, title); }, - "TIMESTAMP-->" : func() { fmt.Fprint(c, time.UTC().String()); }, - "CONTENTS-->" : contents - }); + type Data struct { + title string; + header string; + timestamp string; + content string; + } + + // TODO(rsc): Once template system can handle []byte, + // remove this conversion. + if x, ok := title.([]byte); ok { + title = string(x); + } + if x, ok := content.([]byte); ok { + content = string(x); + } + + var d Data; + d.title = title.(string); + d.header = title.(string); + d.timestamp = time.UTC().String(); + d.content = content.(string); + template.Execute(godoc_html, &d, nil, c); } func serveError(c *http.Conn, err, arg string) { - servePage(c, "Error", func () { - fmt.Fprintf(c, "%v (%s)\n", err, arg); - }); + servePage(c, "Error", fmt.Sprintf("%v (%s)\n", err, arg)); } @@ -260,28 +280,29 @@ func serveDir(c *http.Conn, dirname string) { path := dirname + "/"; // Print contents in 3 sections: directories, go files, everything else - servePage(c, dirname + " - Contents", func () { - fmt.Fprintln(c, "

Directories

"); - for i, entry := range list { - if entry.IsDirectory() { - printLink(c, path, entry.Name); - } + var b io.ByteBuffer; + fmt.Fprintln(&b, "

Directories

"); + for i, entry := range list { + if entry.IsDirectory() { + printLink(&b, path, entry.Name); } + } - fmt.Fprintln(c, "

Go files

"); - for i, entry := range list { - if isGoFile(&entry) { - printLink(c, path, entry.Name); - } + fmt.Fprintln(&b, "

Go files

"); + for i, entry := range list { + if isGoFile(&entry) { + printLink(&b, path, entry.Name); } + } - fmt.Fprintln(c, "

Other files

"); - for i, entry := range list { - if !entry.IsDirectory() && !isGoFile(&entry) { - fmt.Fprintf(c, "%s
\n", entry.Name); - } + fmt.Fprintln(&b, "

Other files

"); + for i, entry := range list { + if !entry.IsDirectory() && !isGoFile(&entry) { + fmt.Fprintf(&b, "%s
\n", entry.Name); } - }); + } + + servePage(c, dirname + " - Contents", b.Data()); } @@ -307,35 +328,36 @@ func serveParseErrors(c *http.Conn, filename string, errors errorList) { } src := buf.Data(); - // TODO handle Apply errors - servePage(c, filename, func () { - // section title - fmt.Fprintf(c, "

Parse errors in %s

\n", filename); + // generate body + var b io.ByteBuffer; + // section title + fmt.Fprintf(&b, "

Parse errors in %s

\n", filename); - // handle read errors - if err1 != nil || err2 != nil { - fmt.Fprintf(c, "could not read file %s\n", filename); - return; - } + // handle read errors + if err1 != nil || err2 != nil { + fmt.Fprintf(&b, "could not read file %s\n", filename); + return; + } - // write source with error messages interspersed - fmt.Fprintln(c, "
");
-		offs := 0;
-		for i, e := range errors {
-			if 0 <= e.pos.Offset && e.pos.Offset <= len(src) {
-				// TODO handle Write errors
-				c.Write(src[offs : e.pos.Offset]);
-				// TODO this should be done using a .css file
-				fmt.Fprintf(c, "%s >>>", e.msg);
-				offs = e.pos.Offset;
-			} else {
-				log.Stdoutf("error position %d out of bounds (len = %d)", e.pos.Offset, len(src));
-			}
+	// write source with error messages interspersed
+	fmt.Fprintln(&b, "
");
+	offs := 0;
+	for i, e := range errors {
+		if 0 <= e.pos.Offset && e.pos.Offset <= len(src) {
+			// TODO handle Write errors
+			b.Write(src[offs : e.pos.Offset]);
+			// TODO this should be done using a .css file
+			fmt.Fprintf(&b, "%s >>>", e.msg);
+			offs = e.pos.Offset;
+		} else {
+			log.Stdoutf("error position %d out of bounds (len = %d)", e.pos.Offset, len(src));
 		}
-		// TODO handle Write errors
-		c.Write(src[offs : len(src)]);
-		fmt.Fprintln(c, "
"); - }); + } + // TODO handle Write errors + b.Write(src[offs : len(src)]); + fmt.Fprintln(&b, "
"); + + servePage(c, filename, b.Data()); } @@ -347,15 +369,16 @@ func serveGoSource(c *http.Conn, dirname string, filename string) { return; } - servePage(c, path + " - Go source", func () { - fmt.Fprintln(c, "
");
-		var p astPrinter.Printer;
-		writer := makeTabwriter(c);  // for nicely formatted output
-		p.Init(writer, nil, nil, true);
-		p.DoProgram(prog);
-		writer.Flush();  // ignore errors
-		fmt.Fprintln(c, "
"); - }); + var b io.ByteBuffer; + fmt.Fprintln(&b, "
");
+	var p astPrinter.Printer;
+	writer := makeTabwriter(&b);  // for nicely formatted output
+	p.Init(writer, nil, nil, true);
+	p.DoProgram(prog);
+	writer.Flush();  // ignore errors
+	fmt.Fprintln(&b, "
"); + + servePage(c, path + " - Go source", b.Data()); } @@ -410,7 +433,7 @@ func (p pakArray) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } func addFile(pmap map[string]*pakDesc, dirname string, filename string) { - if hasSuffix(filename, "_test.go") { + if strings.HasSuffix(filename, "_test.go") { // ignore package tests return; } @@ -507,23 +530,25 @@ func servePackage(c *http.Conn, p *pakDesc) { doc.AddProgram(prog); } - servePage(c, doc.PackageName() + " - Go package documentation", func () { - writer := makeTabwriter(c); // for nicely formatted output - doc.Print(writer); - writer.Flush(); // ignore errors - }); + var b io.ByteBuffer; + writer := makeTabwriter(&b); // for nicely formatted output + doc.Print(writer); + writer.Flush(); // ignore errors + + servePage(c, doc.PackageName() + " - Go package documentation", b.Data()); } func servePackageList(c *http.Conn, list pakArray) { - servePage(c, "Packages", func () { - for i := 0; i < len(list); i++ { - p := list[i]; - link := pathutil.Clean(p.dirname + "/" + p.pakname); - fmt.Fprintf(c, "%s (%s)
\n", - p.pakname, p.pakname, link); - } - }); + var b io.ByteBuffer; + for i := 0; i < len(list); i++ { + p := list[i]; + link := pathutil.Clean(p.dirname + "/" + p.pakname); + fmt.Fprintf(&b, "%s (%s)
\n", + p.pakname, p.pakname, link); + } + + servePage(c, "Packages", b.Data()); // TODO: show subdirectories } diff --git a/usr/gri/pretty/template.go b/usr/gri/pretty/template.go deleted file mode 100644 index 9aa83d4495..0000000000 --- a/usr/gri/pretty/template.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package template - -import ( - "os"; - "io"; -) - - -type Template struct { - template []byte; -} - - -func (T *Template) Init(filename string) *os.Error { - f, err0 := os.Open(filename, os.O_RDONLY, 0); - defer f.Close(); - if err0 != nil { - return err0; - } - - var buf io.ByteBuffer; - len, err1 := io.Copy(f, &buf); - if err1 == io.ErrEOF { - err1 = nil; - } - if err1 != nil { - return err1; - } - - T.template = buf.Data(); - - return nil; -} - - -// Returns true if buf starts with s, returns false otherwise. -// -func match(buf []byte, s string) bool { - if len(buf) < len(s) { - return false; - } - for i := 0; i < len(s); i++ { - if buf[i] != s[i] { - return false; - } - } - return true; -} - - -// Find the position of string s in buf, starting at i. -// Returns a value < 0 if not found. -// -func find(buf []byte, s string, i int) int { - if s == "" { - return i; - } -L: for ; i + len(s) <= len(buf); i++ { - for k := 0; k < len(s); k++ { - if buf[i+k] != s[k] { - continue L; - } - } - return i; - } - return -1 -} - - -type Substitution map [string] func() - -func (T *Template) Apply(w io.Write, prefix string, subs Substitution) *os.Error { - i0 := 0; // position from which to write from the template - i1 := 0; // position from which to look for the next prefix - - for { - // look for a prefix - i2 := find(T.template, prefix, i1); // position of prefix, if any - if i2 < 0 { - // no prefix found, we are done - break; - } - - // we have a prefix, look for a matching key - i1 = i2 + len(prefix); - for key, action := range subs { - if match(T.template[i1 : len(T.template)], key) { - // found a match - i1 += len(key); // next search starting pos - len, err := w.Write(T.template[i0 : i2]); // TODO handle errors - i0 = i1; // skip placeholder - action(); - break; - } - } - } - - // write the rest of the template - len, err := w.Write(T.template[i0 : len(T.template)]); // TODO handle errors - return err; -} - - -func NewTemplate(filename string) *Template { - t := new(Template); - if t.Init(filename) != nil { - return nil; - } - return t; -} - - -func NewTemplateOrDie(filename string) *Template { - t := NewTemplate(filename); - if t == nil { - panic("could not read template: " + filename); - } - return t; -}