// getPageInfo returns the PageInfo for a package directory path. If the // parameter try is true, no errors are logged if getPageInfo fails. // If there is no corresponding package in the directory, // PageInfo.PDoc is nil. If there are no subdirectories, // PageInfo.Dirs is nil. // func (h *httpHandler) getPageInfo(path string, try bool) PageInfo { // the path is relative to h.fsroot dirname := pathutil.Join(h.fsRoot, path) // the package name is the directory name within its parent // (use dirname instead of path because dirname is clean; i.e. has no trailing '/') _, pkgname := pathutil.Split(dirname) // filter function to select the desired .go files filter := func(d *os.Dir) bool { if isPkgFile(d) { // Some directories contain main packages: Only accept // files that belong to the expected package so that // parser.ParsePackage doesn't return "multiple packages // found" errors. // Additionally, accept the special package name // fakePkgName if we are looking at cmd documentation. name := pkgName(dirname + "/" + d.Name) return name == pkgname || h.fsRoot == *cmdroot && name == fakePkgName } return false } // get package AST pkgs, err := parser.ParseDir(dirname, filter, parser.ParseComments) if err != nil && !try { // TODO: errors should be shown instead of an empty directory log.Stderrf("parser.parseDir: %s", err) } if len(pkgs) != 1 && !try { // TODO: should handle multiple packages log.Stderrf("parser.parseDir: found %d packages", len(pkgs)) } var pkg *ast.Package for _, pkg = range pkgs { break // take the first package found } // compute package documentation var pdoc *doc.PackageDoc if pkg != nil { ast.PackageExports(pkg) pdoc = doc.NewPackageDoc(pkg, pathutil.Clean(path)) // no trailing '/' in importpath } // get directory information var dir *Directory if tree, _ := fsTree.get(); tree != nil { // directory tree is present; lookup respective directory // (may still fail if the file system was updated and the // new directory tree has not yet beet computed) dir = tree.(*Directory).lookup(dirname) } else { // no directory tree present (either early after startup // or command-line mode); compute one level for this page dir = newDirectory(dirname, 1) } return PageInfo{pdoc, dir.listing(true), h.isPkg} }
func packageComment(pkg, pkgpath string) (info string, err os.Error) { fset := token.NewFileSet() pkgs, err := parser.ParseDir(fset, pkgpath, isGoFile, parser.PackageClauseOnly|parser.ParseComments) if err != nil { return } for name := range pkgs { if name == "main" { continue } if info != "" { return "", os.NewError("multiple non-main package docs") } pdoc := doc.NewPackageDoc(pkgs[name], pkg) info = pdoc.Doc } // grab only first paragraph if parts := strings.SplitN(info, "\n\n", 2); len(parts) > 1 { info = parts[0] } // replace newlines with spaces info = strings.Replace(info, "\n", " ", -1) // truncate if len(info) > MaxCommentLength { info = info[:MaxCommentLength] } return }
func packageComment(pkg, pkgpath string) (info string, err os.Error) { fset := token.NewFileSet() pkgs, err := parser.ParseDir(fset, pkgpath, nil, parser.PackageClauseOnly|parser.ParseComments) if err != nil { return } for name := range pkgs { if name == "main" { continue } if info != "" { return "", os.NewError("multiple non-main package docs") } pdoc := doc.NewPackageDoc(pkgs[name], pkg) info = pdoc.Doc } return }
// getPageInfo returns the PageInfo for a package directory abspath. If the // parameter genAST is set, an AST containing only the package exports is // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) // is extracted from the AST. If there is no corresponding package in the // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- // directories, PageInfo.Dirs is nil. If a directory read error occurred, // PageInfo.Err is set to the respective error but the error is not logged. // func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo { // filter function to select the desired .go files filter := func(d FileInfo) bool { // If we are looking at cmd documentation, only accept // the special fakePkgFile containing the documentation. return isPkgFile(d) && (h.isPkg || d.Name() == fakePkgFile) } // get package ASTs fset := token.NewFileSet() pkgs, err := parseDir(fset, abspath, filter) if err != nil && pkgs == nil { // only report directory read errors, ignore parse errors // (may be able to extract partial package information) return PageInfo{Dirname: abspath, Err: err} } // select package var pkg *ast.Package // selected package var plist []string // list of other package (names), if any if len(pkgs) == 1 { // Exactly one package - select it. for _, p := range pkgs { pkg = p } } else if len(pkgs) > 1 { // Multiple packages - select the best matching package: The // 1st choice is the package with pkgname, the 2nd choice is // the package with dirname, and the 3rd choice is a package // that is not called "main" if there is exactly one such // package. Otherwise, don't select a package. dirpath, dirname := filepath.Split(abspath) // If the dirname is "go" we might be in a sub-directory for // .go files - use the outer directory name instead for better // results. if dirname == "go" { _, dirname = filepath.Split(filepath.Clean(dirpath)) } var choice3 *ast.Package loop: for _, p := range pkgs { switch { case p.Name == pkgname: pkg = p break loop // 1st choice; we are done case p.Name == dirname: pkg = p // 2nd choice case p.Name != "main": choice3 = p } } if pkg == nil && len(pkgs) == 2 { pkg = choice3 } // Compute the list of other packages // (excluding the selected package, if any). plist = make([]string, len(pkgs)) i := 0 for name := range pkgs { if pkg == nil || name != pkg.Name { plist[i] = name i++ } } plist = plist[0:i] } // compute package documentation var past *ast.File var pdoc *doc.PackageDoc if pkg != nil { if mode&exportsOnly != 0 { ast.PackageExports(pkg) } if mode&genDoc != 0 { pdoc = doc.NewPackageDoc(pkg, path.Clean(relpath)) // no trailing '/' in importpath } else { past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments) } } // get directory information var dir *Directory var timestamp int64 if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil { // directory tree is present; lookup respective directory // (may still fail if the file system was updated and the // new directory tree has not yet been computed) dir = tree.(*Directory).lookup(abspath) timestamp = ts } if dir == nil { // the path may refer to a user-specified file system mapped // via fsMap; lookup that mapping and corresponding RWValue // if any var v *RWValue fsMap.Iterate(func(path string, value *RWValue) bool { if isParentOf(path, abspath) { // mapping found v = value return false } return true }) if v != nil { // found a RWValue associated with a user-specified file // system; a non-nil RWValue stores a (possibly out-of-date) // directory tree for that file system if tree, ts := v.get(); tree != nil && tree.(*Directory) != nil { dir = tree.(*Directory).lookup(abspath) timestamp = ts } } } if dir == nil { // no directory tree present (too early after startup or // command-line mode); compute one level for this page // note: cannot use path filter here because in general // it doesn't contain the fsTree path dir = newDirectory(abspath, nil, 1) timestamp = time.Seconds() } return PageInfo{abspath, plist, fset, past, pdoc, dir.listing(true), timestamp, h.isPkg, nil} }
// getPageInfo returns the PageInfo for a package directory abspath. If the // parameter genAST is set, an AST containing only the package exports is // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) // is extracted from the AST. If the parameter try is set, no errors are // logged if getPageInfo fails. If there is no corresponding package in the // directory, PageInfo.PDoc and PageInfo.PExp are nil. If there are no sub- // directories, PageInfo.Dirs is nil. // func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, genAST, try bool) PageInfo { // filter function to select the desired .go files filter := func(d *os.Dir) bool { // If we are looking at cmd documentation, only accept // the special fakePkgFile containing the documentation. return isPkgFile(d) && (h.isPkg || d.Name == fakePkgFile) } // get package ASTs pkgs, err := parser.ParseDir(abspath, filter, parser.ParseComments) if err != nil && !try { // TODO: errors should be shown instead of an empty directory log.Stderrf("parser.parseDir: %s", err) } // select package var pkg *ast.Package // selected package var plist []string // list of other package (names), if any if len(pkgs) == 1 { // Exactly one package - select it. for _, p := range pkgs { pkg = p } } else if len(pkgs) > 1 { // Multiple packages - select the best matching package: The // 1st choice is the package with pkgname, the 2nd choice is // the package with dirname, and the 3rd choice is a package // that is not called "main" if there is exactly one such // package. Otherwise, don't select a package. dirpath, dirname := pathutil.Split(abspath) // If the dirname is "go" we might be in a sub-directory for // .go files - use the outer directory name instead for better // results. if dirname == "go" { _, dirname = pathutil.Split(pathutil.Clean(dirpath)) } var choice3 *ast.Package loop: for _, p := range pkgs { switch { case p.Name == pkgname: pkg = p break loop // 1st choice; we are done case p.Name == dirname: pkg = p // 2nd choice case p.Name != "main": choice3 = p } } if pkg == nil && len(pkgs) == 2 { pkg = choice3 } // Compute the list of other packages // (excluding the selected package, if any). plist = make([]string, len(pkgs)) i := 0 for name, _ := range pkgs { if pkg == nil || name != pkg.Name { plist[i] = name i++ } } plist = plist[0:i] } // compute package documentation var past *ast.File var pdoc *doc.PackageDoc if pkg != nil { ast.PackageExports(pkg) if genAST { past = ast.MergePackageFiles(pkg, false) } else { pdoc = doc.NewPackageDoc(pkg, pathutil.Clean(relpath)) // no trailing '/' in importpath } } // get directory information var dir *Directory if tree, _ := fsTree.get(); tree != nil && tree.(*Directory) != nil { // directory tree is present; lookup respective directory // (may still fail if the file system was updated and the // new directory tree has not yet been computed) // TODO(gri) Need to build directory tree for fsMap entries dir = tree.(*Directory).lookup(abspath) } if dir == nil { // no directory tree present (either early after startup // or command-line mode, or we don't build a tree for the // directory; e.g. google3); compute one level for this page dir = newDirectory(abspath, 1) } return PageInfo{abspath, plist, past, pdoc, dir.listing(true), h.isPkg} }