diff --git a/doc/all.css b/doc/all.css index 94d4774dd99..23611c6db87 100644 --- a/doc/all.css +++ b/doc/all.css @@ -29,6 +29,9 @@ pre { background: #F0F0F0; padding: 0.5em 1em; } +h3 { + font-size: 100%; +} /* Top bar */ #container { diff --git a/src/pkg/go/doc/Makefile b/src/pkg/go/doc/Makefile index 04c9fe74f45..0330757661f 100644 --- a/src/pkg/go/doc/Makefile +++ b/src/pkg/go/doc/Makefile @@ -11,3 +11,9 @@ GOFILES=\ example.go\ include ../../../Make.pkg + +# Script to test heading detection heuristic +CLEANFILES+=headscan +headscan: headscan.go + $(GC) headscan.go + $(LD) -o headscan headscan.$(O) diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go index 19216f85b96..44a047588d1 100644 --- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -7,11 +7,14 @@ package doc import ( + "bytes" "go/ast" "io" "regexp" "strings" "text/template" // for HTMLEscape + "unicode" + "unicode/utf8" ) func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' } @@ -168,6 +171,8 @@ var ( html_endp = []byte("
\n") html_pre = []byte("") html_endpre = []byte("\n") + html_h = []byte("
. // Turn each run of indented lines into a
block without indent. +// Enclose headings with header tags. // // URLs in the comment text are converted into links; if the URL also appears // in the words map, the link is taken from the map (if the corresponding map @@ -286,6 +338,8 @@ func unindent(block [][]byte) { // into a link. func ToHTML(w io.Writer, s []byte, words map[string]string) { inpara := false + lastWasBlank := false + lastNonblankWasHeading := false close := func() { if inpara { @@ -308,6 +362,7 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) { // close paragraph close() i++ + lastWasBlank = true continue } if indentLen(line) > 0 { @@ -336,8 +391,27 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) { w.Write(html_endpre) continue } + + if lastWasBlank && !lastNonblankWasHeading && i+2 < len(lines) && + isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 { + // current line is non-blank, sourounded by blank lines + // and the next non-blank line is not indented: this + // might be a heading. + if head := heading(line); head != nil { + close() + w.Write(html_h) + template.HTMLEscape(w, head) + w.Write(html_endh) + i += 2 + lastNonblankWasHeading = true + continue + } + } + // open paragraph open() + lastWasBlank = false + lastNonblankWasHeading = false emphasize(w, lines[i], words, true) // nice text formatting i++ } diff --git a/src/pkg/go/doc/comment_test.go b/src/pkg/go/doc/comment_test.go new file mode 100644 index 00000000000..9e77ae2cdea --- /dev/null +++ b/src/pkg/go/doc/comment_test.go @@ -0,0 +1,39 @@ +// Copyright 2011 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 doc + +import ( + "testing" +) + +var headingTests = []struct { + line string + ok bool +}{ + {"Section", true}, + {"A typical usage", true}, + {"ΔΛΞ is Greek", true}, + {"Foo 42", true}, + {"", false}, + {"section", false}, + {"A typical usage:", true}, + {"δ is Greek", false}, // TODO: consider allowing this + {"Foo §", false}, + {"Fermat's Last Sentence", true}, + {"Fermat's", true}, + {"'sX", false}, + {"Ted 'Too' Bar", false}, + {"Use n+m", false}, + {"Scanning:", true}, + {"N:M", false}, +} + +func TestIsHeading(t *testing.T) { + for _, tt := range headingTests { + if h := heading([]byte(tt.line)); (h != nil) != tt.ok { + t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok) + } + } +} diff --git a/src/pkg/go/doc/headscan.go b/src/pkg/go/doc/headscan.go new file mode 100644 index 00000000000..95953b3bdcb --- /dev/null +++ b/src/pkg/go/doc/headscan.go @@ -0,0 +1,53 @@ +package main + +import ( + "bytes" + "flag" + "go/doc" + "go/parser" + "go/token" + "log" + "os" + "path/filepath" + "strings" +) + +func isGoFile(fi os.FileInfo) bool { + return strings.HasSuffix(fi.Name(), ".go") && + !strings.HasSuffix(fi.Name(), "_test.go") +} + +func main() { + fset := token.NewFileSet() + rootDir := flag.String("root", "./", "root of filesystem tree to scan") + flag.Parse() + err := filepath.Walk(*rootDir, func(path string, fi os.FileInfo, err error) error { + if !fi.IsDir() { + return nil + } + pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments) + if err != nil { + log.Println(path, err) + return nil + } + for _, pkg := range pkgs { + d := doc.NewPackageDoc(pkg, path) + buf := new(bytes.Buffer) + doc.ToHTML(buf, []byte(d.Doc), nil) + b := buf.Bytes() + for { + i := bytes.Index(b, []byte("")) + if i == -1 { + break + } + line := bytes.SplitN(b[i:], []byte("\n"), 2)[0] + log.Printf("%s: %s", path, line) + b = b[i+len(line):] + } + } + return nil + }) + if err != nil { + log.Fatal(err) + } +}