mirror of
https://github.com/golang/go
synced 2024-11-13 19:00:25 -07:00
godoc: moved package directory support code into separate file
- in prep. for some restructuring to be able to better deal with very large file systems - moved a utility function into index.go - no functionality changes, only code reorg. R=r, adg1 CC=golang-dev https://golang.org/cl/2098044
This commit is contained in:
parent
9e5bf27acb
commit
889377c572
@ -7,6 +7,7 @@ include ../../Make.inc
|
|||||||
TARG=godoc
|
TARG=godoc
|
||||||
GOFILES=\
|
GOFILES=\
|
||||||
codewalk.go\
|
codewalk.go\
|
||||||
|
dirtrees.go\
|
||||||
godoc.go\
|
godoc.go\
|
||||||
index.go\
|
index.go\
|
||||||
main.go\
|
main.go\
|
||||||
|
301
src/cmd/godoc/dirtrees.go
Normal file
301
src/cmd/godoc/dirtrees.go
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
// This file contains the code dealing with package directory trees.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/doc"
|
||||||
|
"go/parser"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
pathutil "path"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type Directory struct {
|
||||||
|
Depth int
|
||||||
|
Path string // includes Name
|
||||||
|
Name string
|
||||||
|
Text string // package documentation, if any
|
||||||
|
Dirs []*Directory // subdirectories
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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}
|
||||||
|
}
|
@ -25,7 +25,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"template"
|
"template"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
|
||||||
"utf8"
|
"utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -115,70 +114,7 @@ func registerPublicHandlers(mux *http.ServeMux) {
|
|||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Predicates and small utility functions
|
// Path mapping
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func absolutePath(path, defaultRoot string) string {
|
func absolutePath(path, defaultRoot string) string {
|
||||||
abspath := fsMap.ToAbsolute(path)
|
abspath := fsMap.ToAbsolute(path)
|
||||||
@ -205,239 +141,6 @@ func relativePath(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// 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
|
// HTML formatting support
|
||||||
|
|
||||||
|
@ -583,6 +583,15 @@ func (x *Indexer) VisitDir(path string, f *os.FileInfo) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func pkgName(filename string) string {
|
||||||
|
file, err := parser.ParseFile(filename, nil, parser.PackageClauseOnly)
|
||||||
|
if err != nil || file == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return file.Name.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (x *Indexer) VisitFile(path string, f *os.FileInfo) {
|
func (x *Indexer) VisitFile(path string, f *os.FileInfo) {
|
||||||
if !isGoFile(f) {
|
if !isGoFile(f) {
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user