Commit bc9ce6a1 authored by Robert Griesemer's avatar Robert Griesemer

go/doc: better headscan

- scan all comments not just the package documentation
- declutter output so that false positives are more easily spotted
- count the number of headings to quickly see differences
- minor tweaks

R=golang-dev, r, r
CC=golang-dev
https://golang.org/cl/5450061
parent 473de603
...@@ -303,9 +303,8 @@ func heading(line []byte) []byte { ...@@ -303,9 +303,8 @@ func heading(line []byte) []byte {
return nil return nil
} }
// allow ' for possessive 's only // allow "'" for possessive "'s" only
b := line for b := line; ; {
for {
i := bytes.IndexRune(b, '\'') i := bytes.IndexRune(b, '\'')
if i < 0 { if i < 0 {
break break
...@@ -339,7 +338,7 @@ func heading(line []byte) []byte { ...@@ -339,7 +338,7 @@ func heading(line []byte) []byte {
func ToHTML(w io.Writer, s []byte, words map[string]string) { func ToHTML(w io.Writer, s []byte, words map[string]string) {
inpara := false inpara := false
lastWasBlank := false lastWasBlank := false
lastNonblankWasHeading := false lastWasHeading := false
close := func() { close := func() {
if inpara { if inpara {
...@@ -389,10 +388,11 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) { ...@@ -389,10 +388,11 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) {
emphasize(w, line, nil, false) // no nice text formatting emphasize(w, line, nil, false) // no nice text formatting
} }
w.Write(html_endpre) w.Write(html_endpre)
lastWasHeading = false
continue continue
} }
if lastWasBlank && !lastNonblankWasHeading && i+2 < len(lines) && if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 { isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
// current line is non-blank, sourounded by blank lines // current line is non-blank, sourounded by blank lines
// and the next non-blank line is not indented: this // and the next non-blank line is not indented: this
...@@ -403,7 +403,7 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) { ...@@ -403,7 +403,7 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) {
template.HTMLEscape(w, head) template.HTMLEscape(w, head)
w.Write(html_endh) w.Write(html_endh)
i += 2 i += 2
lastNonblankWasHeading = true lastWasHeading = true
continue continue
} }
} }
...@@ -411,7 +411,7 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) { ...@@ -411,7 +411,7 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) {
// open paragraph // open paragraph
open() open()
lastWasBlank = false lastWasBlank = false
lastNonblankWasHeading = false lastWasHeading = false
emphasize(w, lines[i], words, true) // nice text formatting emphasize(w, lines[i], words, true) // nice text formatting
i++ i++
} }
......
...@@ -19,7 +19,7 @@ var headingTests = []struct { ...@@ -19,7 +19,7 @@ var headingTests = []struct {
{"", false}, {"", false},
{"section", false}, {"section", false},
{"A typical usage:", true}, {"A typical usage:", true},
{"δ is Greek", false}, // TODO: consider allowing this {"δ is Greek", false},
{"Foo §", false}, {"Foo §", false},
{"Fermat's Last Sentence", true}, {"Fermat's Last Sentence", true},
{"Fermat's", true}, {"Fermat's", true},
......
// 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.
/*
The headscan command extracts comment headings from package files;
it is used to detect false positives which may require an adjustment
to the comment formatting heuristics in comment.go.
Usage: headscan [-root root_directory]
By default, the $GOROOT/src directory is scanned.
*/
package main package main
import ( import (
"bytes" "bytes"
"flag" "flag"
"fmt"
"go/doc" "go/doc"
"go/parser" "go/parser"
"go/token" "go/token"
"log"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
) )
var (
root = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan")
verbose = flag.Bool("v", false, "verbose mode")
)
const (
html_h = "<h3>"
html_endh = "</h3>\n"
)
func isGoFile(fi os.FileInfo) bool { func isGoFile(fi os.FileInfo) bool {
return strings.HasSuffix(fi.Name(), ".go") && return strings.HasSuffix(fi.Name(), ".go") &&
!strings.HasSuffix(fi.Name(), "_test.go") !strings.HasSuffix(fi.Name(), "_test.go")
} }
func appendHeadings(list []string, comment string) []string {
var buf bytes.Buffer
doc.ToHTML(&buf, []byte(comment), nil)
for s := buf.String(); ; {
i := strings.Index(s, html_h)
if i < 0 {
break
}
i += len(html_h)
j := strings.Index(s, html_endh)
if j < 0 {
list = append(list, s[i:]) // incorrect HTML
break
}
list = append(list, s[i:j])
s = s[j+len(html_endh):]
}
return list
}
func main() { func main() {
fset := token.NewFileSet()
rootDir := flag.String("root", "./", "root of filesystem tree to scan")
flag.Parse() flag.Parse()
err := filepath.Walk(*rootDir, func(path string, fi os.FileInfo, err error) error { fset := token.NewFileSet()
nheadings := 0
err := filepath.Walk(*root, func(path string, fi os.FileInfo, err error) error {
if !fi.IsDir() { if !fi.IsDir() {
return nil return nil
} }
pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments) pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments)
if err != nil { if err != nil {
log.Println(path, err) if *verbose {
fmt.Fprintln(os.Stderr, err)
}
return nil return nil
} }
for _, pkg := range pkgs { for _, pkg := range pkgs {
d := doc.NewPackageDoc(pkg, path) d := doc.NewPackageDoc(pkg, path)
buf := new(bytes.Buffer) list := appendHeadings(nil, d.Doc)
doc.ToHTML(buf, []byte(d.Doc), nil) for _, d := range d.Consts {
b := buf.Bytes() list = appendHeadings(list, d.Doc)
for { }
i := bytes.Index(b, []byte("<h3>")) for _, d := range d.Types {
if i == -1 { list = appendHeadings(list, d.Doc)
break }
for _, d := range d.Vars {
list = appendHeadings(list, d.Doc)
}
for _, d := range d.Funcs {
list = appendHeadings(list, d.Doc)
}
if len(list) > 0 {
// directories may contain multiple packages;
// print path and package name
fmt.Printf("%s (package %s)\n", path, pkg.Name)
for _, h := range list {
fmt.Printf("\t%s\n", h)
} }
line := bytes.SplitN(b[i:], []byte("\n"), 2)[0] nheadings += len(list)
log.Printf("%s: %s", path, line)
b = b[i+len(line):]
} }
} }
return nil return nil
}) })
if err != nil { if err != nil {
log.Fatal(err) fmt.Fprintln(os.Stderr, err)
os.Exit(1)
} }
fmt.Println(nheadings, "headings found")
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment