"io";
"log";
"net";
+ "once";
"os";
"parser";
pathutil "path";
"sort";
+ "strings";
"tabwriter";
"template";
"time";
// ----------------------------------------------------------------------------
// 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");
}
}
-func printLink(c *http.Conn, dir, name string) {
+func printLink(c io.Write, dir, name string) {
fmt.Fprintf(c, "<a href=\"%s\">%s</a><br />\n", pathutil.Clean(filePrefix + dir + "/" + name), name);
}
// 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, "<!--", template.Substitution {
- "TITLE-->" : 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));
}
path := dirname + "/";
// Print contents in 3 sections: directories, go files, everything else
- servePage(c, dirname + " - Contents", func () {
- fmt.Fprintln(c, "<h2>Directories</h2>");
- for i, entry := range list {
- if entry.IsDirectory() {
- printLink(c, path, entry.Name);
- }
+ var b io.ByteBuffer;
+ fmt.Fprintln(&b, "<h2>Directories</h2>");
+ for i, entry := range list {
+ if entry.IsDirectory() {
+ printLink(&b, path, entry.Name);
}
+ }
- fmt.Fprintln(c, "<h2>Go files</h2>");
- for i, entry := range list {
- if isGoFile(&entry) {
- printLink(c, path, entry.Name);
- }
+ fmt.Fprintln(&b, "<h2>Go files</h2>");
+ for i, entry := range list {
+ if isGoFile(&entry) {
+ printLink(&b, path, entry.Name);
}
+ }
- fmt.Fprintln(c, "<h2>Other files</h2>");
- for i, entry := range list {
- if !entry.IsDirectory() && !isGoFile(&entry) {
- fmt.Fprintf(c, "%s<br />\n", entry.Name);
- }
+ fmt.Fprintln(&b, "<h2>Other files</h2>");
+ for i, entry := range list {
+ if !entry.IsDirectory() && !isGoFile(&entry) {
+ fmt.Fprintf(&b, "%s<br />\n", entry.Name);
}
- });
+ }
+
+ servePage(c, dirname + " - Contents", b.Data());
}
}
src := buf.Data();
- // TODO handle Apply errors
- servePage(c, filename, func () {
- // section title
- fmt.Fprintf(c, "<h1>Parse errors in %s</h1>\n", filename);
+ // generate body
+ var b io.ByteBuffer;
+ // section title
+ fmt.Fprintf(&b, "<h1>Parse errors in %s</h1>\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, "<pre>");
- 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, "<b><font color=red>%s >>></font></b>", 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, "<pre>");
+ 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, "<b><font color=red>%s >>></font></b>", 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, "</pre>");
- });
+ }
+ // TODO handle Write errors
+ b.Write(src[offs : len(src)]);
+ fmt.Fprintln(&b, "</pre>");
+
+ servePage(c, filename, b.Data());
}
return;
}
- servePage(c, path + " - Go source", func () {
- fmt.Fprintln(c, "<pre>");
- 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, "</pre>");
- });
+ var b io.ByteBuffer;
+ fmt.Fprintln(&b, "<pre>");
+ 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, "</pre>");
+
+ servePage(c, path + " - Go source", b.Data());
}
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;
}
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, "<a href=\"%s\">%s</a> <font color=grey>(%s)</font><br />\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, "<a href=\"%s\">%s</a> <font color=grey>(%s)</font><br />\n",
+ p.pakname, p.pakname, link);
+ }
+
+ servePage(c, "Packages", b.Data());
// TODO: show subdirectories
}
+++ /dev/null
-// 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;
-}