Index: src/cmd/godoc/godoc.go |
=================================================================== |
--- a/src/cmd/godoc/godoc.go |
+++ b/src/cmd/godoc/godoc.go |
@@ -25,7 +25,6 @@ |
"sync" |
"template" |
"time" |
- "unicode" |
"utf8" |
) |
@@ -115,70 +114,7 @@ |
// ---------------------------------------------------------------------------- |
-// Predicates and small utility functions |
- |
-func isGoFile(f *os.FileInfo) bool { |
- return f.IsRegular() && |
- !strings.HasPrefix(f.Name, ".") && // ignore .files |
- pathutil.Ext(f.Name) == ".go" |
-} |
- |
- |
-func isPkgFile(f *os.FileInfo) bool { |
- return isGoFile(f) && |
- !strings.HasSuffix(f.Name, "_test.go") // ignore test files |
-} |
- |
- |
-func isPkgDir(f *os.FileInfo) bool { |
- return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_' |
-} |
- |
- |
-func pkgName(filename string) string { |
- file, err := parser.ParseFile(filename, nil, parser.PackageClauseOnly) |
- if err != nil || file == nil { |
- return "" |
- } |
- return file.Name.Name |
-} |
- |
- |
-func firstSentence(s string) string { |
- i := -1 // index+1 of first terminator (punctuation ending a sentence) |
- j := -1 // index+1 of first terminator followed by white space |
- prev := 'A' |
- for k, ch := range s { |
- k1 := k + 1 |
- if ch == '.' || ch == '!' || ch == '?' { |
- if i < 0 { |
- i = k1 // first terminator |
- } |
- if k1 < len(s) && s[k1] <= ' ' { |
- if j < 0 { |
- j = k1 // first terminator followed by white space |
- } |
- if !unicode.IsUpper(prev) { |
- j = k1 |
- break |
- } |
- } |
- } |
- prev = ch |
- } |
- |
- if j < 0 { |
- // use the next best terminator |
- j = i |
- if j < 0 { |
- // no terminator at all, use the entire string |
- j = len(s) |
- } |
- } |
- |
- return s[0:j] |
-} |
- |
+// Path mapping |
func absolutePath(path, defaultRoot string) string { |
abspath := fsMap.ToAbsolute(path) |
@@ -206,239 +142,6 @@ |
// ---------------------------------------------------------------------------- |
-// Package directories |
- |
-type Directory struct { |
- Depth int |
- Path string // includes Name |
- Name string |
- Text string // package documentation, if any |
- Dirs []*Directory // subdirectories |
-} |
- |
- |
-func newDirTree(path, name string, depth, maxDepth int) *Directory { |
- if depth >= maxDepth { |
- // return a dummy directory so that the parent directory |
- // doesn't get discarded just because we reached the max |
- // directory depth |
- return &Directory{depth, path, name, "", nil} |
- } |
- |
- list, _ := ioutil.ReadDir(path) // ignore errors |
- |
- // determine number of subdirectories and package files |
- ndirs := 0 |
- nfiles := 0 |
- var synopses [4]string // prioritized package documentation (0 == highest priority) |
- for _, d := range list { |
- switch { |
- case isPkgDir(d): |
- ndirs++ |
- case isPkgFile(d): |
- nfiles++ |
- if synopses[0] == "" { |
- // no "optimal" package synopsis yet; continue to collect synopses |
- file, err := parser.ParseFile(pathutil.Join(path, d.Name), nil, |
- parser.ParseComments|parser.PackageClauseOnly) |
- if err == nil && file.Doc != nil { |
- // prioritize documentation |
- i := -1 |
- switch file.Name.Name { |
- case name: |
- i = 0 // normal case: directory name matches package name |
- case fakePkgName: |
- i = 1 // synopses for commands |
- case "main": |
- i = 2 // directory contains a main package |
- default: |
- i = 3 // none of the above |
- } |
- if 0 <= i && i < len(synopses) && synopses[i] == "" { |
- synopses[i] = firstSentence(doc.CommentText(file.Doc)) |
- } |
- } |
- } |
- } |
- } |
- |
- // create subdirectory tree |
- var dirs []*Directory |
- if ndirs > 0 { |
- dirs = make([]*Directory, ndirs) |
- i := 0 |
- for _, d := range list { |
- if isPkgDir(d) { |
- dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth+1, maxDepth) |
- if dd != nil { |
- dirs[i] = dd |
- i++ |
- } |
- } |
- } |
- dirs = dirs[0:i] |
- } |
- |
- // if there are no package files and no subdirectories |
- // (with package files), ignore the directory |
- if nfiles == 0 && len(dirs) == 0 { |
- return nil |
- } |
- |
- // select the highest-priority synopsis for the directory entry, if any |
- synopsis := "" |
- for _, synopsis = range synopses { |
- if synopsis != "" { |
- break |
- } |
- } |
- |
- return &Directory{depth, path, name, synopsis, dirs} |
-} |
- |
- |
-// newDirectory creates a new package directory tree with at most maxDepth |
-// levels, anchored at root. The result tree is pruned such that it only |
-// contains directories that contain package files or that contain |
-// subdirectories containing package files (transitively). If maxDepth is |
-// too shallow, the leaf nodes are assumed to contain package files even if |
-// their contents are not known (i.e., in this case the tree may contain |
-// directories w/o any package files). |
-// |
-func newDirectory(root string, maxDepth int) *Directory { |
- d, err := os.Lstat(root) |
- if err != nil || !isPkgDir(d) { |
- return nil |
- } |
- return newDirTree(root, d.Name, 0, maxDepth) |
-} |
- |
- |
-func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { |
- if dir != nil { |
- if !skipRoot { |
- c <- dir |
- } |
- for _, d := range dir.Dirs { |
- d.walk(c, false) |
- } |
- } |
-} |
- |
- |
-func (dir *Directory) iter(skipRoot bool) <-chan *Directory { |
- c := make(chan *Directory) |
- go func() { |
- dir.walk(c, skipRoot) |
- close(c) |
- }() |
- return c |
-} |
- |
- |
-func (dir *Directory) lookupLocal(name string) *Directory { |
- for _, d := range dir.Dirs { |
- if d.Name == name { |
- return d |
- } |
- } |
- return nil |
-} |
- |
- |
-// lookup looks for the *Directory for a given path, relative to dir. |
-func (dir *Directory) lookup(path string) *Directory { |
- d := strings.Split(dir.Path, "/", -1) |
- p := strings.Split(path, "/", -1) |
- i := 0 |
- for i < len(d) { |
- if i >= len(p) || d[i] != p[i] { |
- return nil |
- } |
- i++ |
- } |
- for dir != nil && i < len(p) { |
- dir = dir.lookupLocal(p[i]) |
- i++ |
- } |
- return dir |
-} |
- |
- |
-// DirEntry describes a directory entry. The Depth and Height values |
-// are useful for presenting an entry in an indented fashion. |
-// |
-type DirEntry struct { |
- Depth int // >= 0 |
- Height int // = DirList.MaxHeight - Depth, > 0 |
- Path string // includes Name, relative to DirList root |
- Name string |
- Synopsis string |
-} |
- |
- |
-type DirList struct { |
- MaxHeight int // directory tree height, > 0 |
- List []DirEntry |
-} |
- |
- |
-// listing creates a (linear) directory listing from a directory tree. |
-// If skipRoot is set, the root directory itself is excluded from the list. |
-// |
-func (root *Directory) listing(skipRoot bool) *DirList { |
- if root == nil { |
- return nil |
- } |
- |
- // determine number of entries n and maximum height |
- n := 0 |
- minDepth := 1 << 30 // infinity |
- maxDepth := 0 |
- for d := range root.iter(skipRoot) { |
- n++ |
- if minDepth > d.Depth { |
- minDepth = d.Depth |
- } |
- if maxDepth < d.Depth { |
- maxDepth = d.Depth |
- } |
- } |
- maxHeight := maxDepth - minDepth + 1 |
- |
- if n == 0 { |
- return nil |
- } |
- |
- // create list |
- list := make([]DirEntry, n) |
- i := 0 |
- for d := range root.iter(skipRoot) { |
- p := &list[i] |
- p.Depth = d.Depth - minDepth |
- p.Height = maxHeight - p.Depth |
- // the path is relative to root.Path - remove the root.Path |
- // prefix (the prefix should always be present but avoid |
- // crashes and check) |
- path := d.Path |
- if strings.HasPrefix(d.Path, root.Path) { |
- path = d.Path[len(root.Path):] |
- } |
- // remove trailing '/' if any - path must be relative |
- if len(path) > 0 && path[0] == '/' { |
- path = path[1:] |
- } |
- p.Path = path |
- p.Name = d.Name |
- p.Synopsis = d.Text |
- i++ |
- } |
- |
- return &DirList{maxHeight, list} |
-} |
- |
- |
-// ---------------------------------------------------------------------------- |
// HTML formatting support |
// aposescaper implements an io.Writer that escapes single quotes: |