Exemple #1
0
func (p *Package) Examples() []*Example {
	if p.examples != nil || p.bpkg == nil {
		return p.examples
	}
	fset := token.NewFileSet()
	var files []*ast.File
	for _, val := range [][]string{p.bpkg.TestGoFiles, p.bpkg.XTestGoFiles} {
		for _, v := range val {
			f, err := parser.ParseFile(fset, p.env.Join(p.Dir(), v), nil, parser.ParseComments)
			if err == nil {
				files = append(files, f)
			}
		}
	}
	for _, v := range doc.Examples(files...) {
		p.examples = append(p.examples, &Example{
			fset:    fset,
			pkg:     p,
			example: v,
		})
	}
	p.examplesByKey = make(map[string][]*Example)
	for _, v := range p.examples {
		k := v.Key()
		p.examplesByKey[k] = append(p.examplesByKey[k], v)
	}
	if p.examples == nil {
		p.examples = make([]*Example, 0, 1)
	}
	return p.examples
}
Exemple #2
0
// parseExamples gets examples for packages in pkgs from *_test.go files in dir.
func parseExamples(fset *token.FileSet, pkgs map[string]*ast.Package, dir string) ([]*doc.Example, error) {
	var examples []*doc.Example
	filter := func(d os.FileInfo) bool {
		return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
	}
	testpkgs, err := parseDir(fset, dir, filter)
	if err != nil {
		return nil, err
	}
	globals := globalNames(pkgs)
	for _, testpkg := range testpkgs {
		var files []*ast.File
		for _, f := range testpkg.Files {
			files = append(files, f)
		}
		for _, e := range doc.Examples(files...) {
			name := stripExampleSuffix(e.Name)
			if name == "" || globals[name] {
				examples = append(examples, e)
			} else {
				log.Printf("skipping example Example%s: refers to unknown function or type", e.Name)
			}
		}
	}
	return examples, nil
}
Exemple #3
0
func (t *testFuncs) load(filename, pkg string, seen *bool) error {
	f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
	if err != nil {
		return expandScanner(err)
	}
	for _, d := range f.Decls {
		n, ok := d.(*ast.FuncDecl)
		if !ok {
			continue
		}
		if n.Recv != nil {
			continue
		}
		name := n.Name.String()
		switch {
		case isTest(name, "Test"):
			t.Tests = append(t.Tests, testFunc{pkg, name, ""})
			*seen = true
		case isTest(name, "Benchmark"):
			t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
			*seen = true
		}
	}
	for _, e := range doc.Examples(f) {
		if e.Output == "" {
			// Don't run examples with no output.
			continue
		}
		t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output})
		*seen = true
	}
	return nil
}
Exemple #4
0
func TestExamples(t *testing.T) {
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
	if err != nil {
		t.Fatal(err)
	}
	for i, e := range doc.Examples(file) {
		c := exampleTestCases[i]
		if e.Name != c.Name {
			t.Errorf("got Name == %q, want %q", e.Name, c.Name)
		}
		if w := c.Play; w != "" {
			var g string // hah
			if e.Play == nil {
				g = "<nil>"
			} else {
				var buf bytes.Buffer
				if err := format.Node(&buf, fset, e.Play); err != nil {
					t.Fatal(err)
				}
				g = buf.String()
			}
			if g != w {
				t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
			}
		}
		if g, w := e.Output, c.Output; g != w {
			t.Errorf("%s: got Output == %q, want %q", c.Name, g, w)
		}
	}
}
Exemple #5
0
func (ctx *Context) loadPackage(importPath string, flags int) (*Package, error) {
	bpkg, err := build.Import(importPath, ctx.cwd, 0)
	if _, ok := err.(*build.NoGoError); ok {
		return &Package{bpkg: bpkg}, nil
	}
	if err != nil {
		return nil, err
	}

	pkg := &Package{
		fset: token.NewFileSet(),
		bpkg: bpkg,
	}

	files := make(map[string]*ast.File)
	for _, name := range append(pkg.bpkg.GoFiles, pkg.bpkg.CgoFiles...) {
		file, err := pkg.parseFile(name)
		if err != nil {
			pkg.errors = append(pkg.errors, err)
			continue
		}
		files[name] = file
	}

	pkg.apkg, _ = ast.NewPackage(pkg.fset, files, simpleImporter, nil)

	if flags&loadDoc != 0 {
		mode := doc.Mode(0)
		if pkg.bpkg.ImportPath == "builtin" || flags&loadUnexported != 0 {
			mode |= doc.AllDecls
		}
		pkg.dpkg = doc.New(pkg.apkg, pkg.bpkg.ImportPath, mode)
		if pkg.bpkg.ImportPath == "builtin" {
			for _, t := range pkg.dpkg.Types {
				pkg.dpkg.Funcs = append(pkg.dpkg.Funcs, t.Funcs...)
				t.Funcs = nil
			}
			sort.Sort(byFuncName(pkg.dpkg.Funcs))
		}
	}

	if flags&loadExamples != 0 {
		for _, name := range append(pkg.bpkg.TestGoFiles, pkg.bpkg.XTestGoFiles...) {
			file, err := pkg.parseFile(name)
			if err != nil {
				pkg.errors = append(pkg.errors, err)
				continue
			}
			pkg.examples = append(pkg.examples, doc.Examples(file)...)
		}
	}

	return pkg, nil
}
Exemple #6
0
func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
	f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
	if err != nil {
		return expandScanner(err)
	}
	for _, d := range f.Decls {
		n, ok := d.(*ast.FuncDecl)
		if !ok {
			continue
		}
		if n.Recv != nil {
			continue
		}
		name := n.Name.String()
		switch {
		case name == "TestMain" && isTestFunc(n, "M"):
			if t.TestMain != nil {
				return errors.New("multiple definitions of TestMain")
			}
			t.TestMain = &testFunc{pkg, name, "", false}
			*doImport, *seen = true, true
		case isTest(name, "Test"):
			err := checkTestFunc(n, "T")
			if err != nil {
				return err
			}
			t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
			*doImport, *seen = true, true
		case isTest(name, "Benchmark"):
			err := checkTestFunc(n, "B")
			if err != nil {
				return err
			}
			t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
			*doImport, *seen = true, true
		}
	}
	ex := doc.Examples(f)
	sort.Sort(byOrder(ex))
	for _, e := range ex {
		*doImport = true // import test file whether executed or not
		if e.Output == "" && !e.EmptyOutput {
			// Don't run examples with no output.
			continue
		}
		t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
		*seen = true
	}
	return nil
}
Exemple #7
0
// collectExamples collects examples for pkg from testfiles.
func collectExamples(c *Corpus, pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example {
	var files []*ast.File
	for _, f := range testfiles {
		files = append(files, f)
	}

	var examples []*doc.Example
	globals := globalNames(pkg)
	for _, e := range doc.Examples(files...) {
		name := stripExampleSuffix(e.Name)
		if name == "" || globals[name] {
			examples = append(examples, e)
		} else if c.Verbose {
			log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name)
		}
	}

	return examples
}
Exemple #8
0
func buildDoc(importPath string, files []string) (*Package, error) {

	b := &builder{
		fset:        token.NewFileSet(),
		importPaths: make(map[string]map[string]string),
	}

	pkgs := make(map[string]*ast.Package)
	for _, f := range files {
		if strings.HasSuffix(f, "_test.go") {
			continue
		}
		if src, err := parser.ParseFile(b.fset, f, nil, parser.ParseComments); err == nil {
			name := src.Name.Name
			pkg, found := pkgs[name]
			if !found {
				pkg = &ast.Package{Name: name, Files: make(map[string]*ast.File)}
				pkgs[name] = pkg
			}
			pkg.Files[f] = src
		}
	}
	score := 0
	for _, pkg := range pkgs {
		switch {
		case score < 3 && strings.HasSuffix(importPath, pkg.Name):
			b.pkg = pkg
			score = 3
		case score < 2 && pkg.Name != "main":
			b.pkg = pkg
			score = 2
		case score < 1:
			b.pkg = pkg
			score = 1
		}
	}

	if b.pkg == nil {
		return nil, fmt.Errorf("Package %s not found", importPath)
	}

	ast.PackageExports(b.pkg)
	pdoc := doc.New(b.pkg, importPath, 0)

	pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r")

	// Collect examples.
	for _, f := range files {
		if !strings.HasSuffix(f, "_test.go") {
			continue
		}
		src, err := parser.ParseFile(b.fset, f, nil, parser.ParseComments)
		if err != nil {
			continue
		}
		if src.Name.Name != pdoc.Name && src.Name.Name != pdoc.Name+"_test" {
			continue
		}
		b.examples = append(b.examples, doc.Examples(src)...)
	}

	return &Package{
		Consts:     b.values(pdoc.Consts),
		Doc:        pdoc.Doc,
		Examples:   b.getExamples(""),
		Files:      b.files(pdoc.Filenames),
		Funcs:      b.funcs(pdoc.Funcs),
		ImportPath: pdoc.ImportPath,
		Name:       pdoc.Name,
		Types:      b.types(pdoc.Types),
		Updated:    time.Now(),
		Vars:       b.values(pdoc.Vars),
	}, nil
}
Exemple #9
0
// 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 *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo {
	var pkgFiles []string

	// If we're showing the default package, restrict to the ones
	// that would be used when building the package on this
	// system.  This makes sure that if there are separate
	// implementations for, say, Windows vs Unix, we don't
	// jumble them all together.
	if pkgname == "" {
		// Note: Uses current binary's GOOS/GOARCH.
		// To use different pair, such as if we allowed the user
		// to choose, set ctxt.GOOS and ctxt.GOARCH before
		// calling ctxt.ScanDir.
		ctxt := build.Default
		ctxt.IsAbsPath = pathpkg.IsAbs
		ctxt.ReadDir = fsReadDir
		ctxt.OpenFile = fsOpenFile
		dir, err := ctxt.ImportDir(abspath, 0)
		if err == nil {
			pkgFiles = append(dir.GoFiles, dir.CgoFiles...)
		}
	}

	// filter function to select the desired .go files
	filter := func(d os.FileInfo) bool {
		// Only Go files.
		if !isPkgFile(d) {
			return false
		}
		// If we are looking at cmd documentation, only accept
		// the special fakePkgFile containing the documentation.
		if !h.isPkg {
			return d.Name() == fakePkgFile
		}
		// Also restrict file list to pkgFiles.
		return pkgFiles == nil || inList(d.Name(), pkgFiles)
	}

	// 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 := pathpkg.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 = pathpkg.Split(pathpkg.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]
		sort.Strings(plist)
	}

	// get examples from *_test.go files
	var examples []*doc.Example
	filter = func(d os.FileInfo) bool {
		return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
	}
	if testpkgs, err := parseDir(fset, abspath, filter); err != nil {
		log.Println("parsing test files:", err)
	} else {
		for _, testpkg := range testpkgs {
			var files []*ast.File
			for _, f := range testpkg.Files {
				files = append(files, f)
			}
			examples = append(examples, doc.Examples(files...)...)
		}
	}

	// compute package documentation
	var past *ast.File
	var pdoc *doc.Package
	if pkg != nil {
		if mode&showSource == 0 {
			// show extracted documentation
			var m doc.Mode
			if mode&noFiltering != 0 {
				m = doc.AllDecls
			}
			if mode&allMethods != 0 {
				m |= doc.AllMethods
			}
			pdoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath
		} else {
			// show source code
			// TODO(gri) Consider eliminating export filtering in this mode,
			//           or perhaps eliminating the mode altogether.
			if mode&noFiltering == 0 {
				ast.PackageExports(pkg)
			}
			past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)
		}
	}

	// get directory information
	var dir *Directory
	var timestamp time.Time
	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 {
		// 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, 1)
		timestamp = time.Now()
	}

	return PageInfo{
		Dirname:  abspath,
		PList:    plist,
		FSet:     fset,
		PAst:     past,
		PDoc:     pdoc,
		Examples: examples,
		Dirs:     dir.listing(true),
		DirTime:  timestamp,
		DirFlat:  mode&flatDir != 0,
		IsPkg:    h.isPkg,
		Err:      nil,
	}
}
Exemple #10
0
func (b *builder) build(srcs []*source) (*Package, error) {

	b.pdoc.Updated = time.Now().UTC()

	references := make(map[string]bool)
	b.srcs = make(map[string]*source)
	for _, src := range srcs {
		if strings.HasSuffix(src.name, ".go") {
			b.srcs[src.name] = src
		} else {
			addReferences(references, src.data)

			fn := strings.ToLower(src.name)
			if fn == "readme" || strings.HasPrefix(fn, "readme.") {
				if b.pdoc.ReadmeFiles == nil {
					b.pdoc.ReadmeFiles = make(map[string][]byte)
				}

				b.pdoc.ReadmeFiles[src.name] = src.data
			}
		}
	}

	for r := range references {
		b.pdoc.References = append(b.pdoc.References, r)
	}

	if len(b.srcs) == 0 {
		return b.pdoc, nil
	}

	b.fset = token.NewFileSet()

	// Find the package and associated files.

	ctxt := build.Context{
		GOOS:          "linux",
		GOARCH:        "amd64",
		CgoEnabled:    true,
		ReleaseTags:   build.Default.ReleaseTags,
		JoinPath:      path.Join,
		IsAbsPath:     path.IsAbs,
		SplitPathList: func(list string) []string { return strings.Split(list, ":") },
		IsDir:         func(path string) bool { panic("unexpected") },
		HasSubdir:     func(root, dir string) (rel string, ok bool) { panic("unexpected") },
		ReadDir:       func(dir string) (fi []os.FileInfo, err error) { return b.readDir(dir) },
		OpenFile:      func(path string) (r io.ReadCloser, err error) { return b.openFile(path) },
		Compiler:      "gc",
	}

	var err error
	var bpkg *build.Package

	for _, env := range goEnvs {
		ctxt.GOOS = env.GOOS
		ctxt.GOARCH = env.GOARCH
		bpkg, err = ctxt.ImportDir("/", 0)
		if _, ok := err.(*build.NoGoError); !ok {
			break
		}
	}
	if err != nil {
		if _, ok := err.(*build.NoGoError); !ok {
			b.pdoc.Errors = append(b.pdoc.Errors, err.Error())
		}
		return b.pdoc, nil
	}

	// Parse the Go files

	files := make(map[string]*ast.File)
	names := append(bpkg.GoFiles, bpkg.CgoFiles...)
	sort.Strings(names)
	b.pdoc.Files = make([]*File, len(names))
	for i, name := range names {
		file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
		if err != nil {
			b.pdoc.Errors = append(b.pdoc.Errors, err.Error())
			continue
		}
		src := b.srcs[name]
		src.index = i
		b.pdoc.Files[i] = &File{Name: name, URL: src.browseURL}
		b.pdoc.SourceSize += len(src.data)
		files[name] = file
	}

	apkg, _ := ast.NewPackage(b.fset, files, simpleImporter, nil)

	// Find examples in the test files.

	names = append(bpkg.TestGoFiles, bpkg.XTestGoFiles...)
	sort.Strings(names)
	b.pdoc.TestFiles = make([]*File, len(names))
	for i, name := range names {
		file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
		if err != nil {
			b.pdoc.Errors = append(b.pdoc.Errors, err.Error())
			continue
		}
		b.pdoc.TestFiles[i] = &File{Name: name, URL: b.srcs[name].browseURL}
		b.pdoc.TestSourceSize += len(b.srcs[name].data)
		b.examples = append(b.examples, doc.Examples(file)...)
	}

	b.vetPackage(apkg)

	mode := doc.Mode(0)
	if b.pdoc.ImportPath == "builtin" {
		mode |= doc.AllDecls
	}

	dpkg := doc.New(apkg, b.pdoc.ImportPath, mode)

	b.pdoc.Name = dpkg.Name
	b.pdoc.Doc = strings.TrimRight(dpkg.Doc, " \t\n\r")
	b.pdoc.Synopsis = synopsis(b.pdoc.Doc)

	b.pdoc.Examples = b.getExamples("")
	b.pdoc.IsCmd = bpkg.IsCommand()
	b.pdoc.GOOS = ctxt.GOOS
	b.pdoc.GOARCH = ctxt.GOARCH

	b.pdoc.Consts = b.values(dpkg.Consts)
	b.pdoc.Funcs = b.funcs(dpkg.Funcs)
	b.pdoc.Types = b.types(dpkg.Types)
	b.pdoc.Vars = b.values(dpkg.Vars)
	b.pdoc.Notes = b.notes(dpkg.Notes)

	b.pdoc.Imports = bpkg.Imports
	b.pdoc.TestImports = bpkg.TestImports
	b.pdoc.XTestImports = bpkg.XTestImports

	return b.pdoc, nil
}
Exemple #11
0
// 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 *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) PageInfo {
	var pkgFiles []string

	// Restrict to the package files
	// that would be used when building the package on this
	// system.  This makes sure that if there are separate
	// implementations for, say, Windows vs Unix, we don't
	// jumble them all together.
	// Note: Uses current binary's GOOS/GOARCH.
	// To use different pair, such as if we allowed the user
	// to choose, set ctxt.GOOS and ctxt.GOARCH before
	// calling ctxt.ScanDir.
	ctxt := build.Default
	ctxt.IsAbsPath = pathpkg.IsAbs
	ctxt.ReadDir = fsReadDir
	ctxt.OpenFile = fsOpenFile
	if dir, err := ctxt.ImportDir(abspath, 0); err == nil {
		pkgFiles = append(dir.GoFiles, dir.CgoFiles...)
	}

	// filter function to select the desired .go files
	filter := func(d os.FileInfo) bool {
		// Only Go files.
		if !isPkgFile(d) {
			return false
		}
		// If we are looking at cmd documentation, only accept
		// the special fakePkgFile containing the documentation.
		if !h.isPkg {
			return d.Name() == fakePkgFile
		}
		// Also restrict file list to pkgFiles.
		return pkgFiles == nil || inList(d.Name(), pkgFiles)
	}

	// get package ASTs
	fset := token.NewFileSet()
	pkgs, err := parseDir(fset, abspath, filter)
	if err != nil {
		return PageInfo{Dirname: abspath, Err: err}
	}

	// select package
	var pkg *ast.Package // selected package
	if len(pkgs) == 1 {
		// Exactly one package - select it.
		for _, p := range pkgs {
			pkg = p
		}

	} else if len(pkgs) > 1 {
		// More than one package - report an error.
		var buf bytes.Buffer
		for _, p := range pkgs {
			if buf.Len() > 0 {
				fmt.Fprintf(&buf, ", ")
			}
			fmt.Fprintf(&buf, p.Name)
		}
		return PageInfo{
			Dirname: abspath,
			Err:     fmt.Errorf("%s contains more than one package: %s", abspath, buf.Bytes()),
		}
	}

	// get examples from *_test.go files
	var examples []*doc.Example
	filter = func(d os.FileInfo) bool {
		return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
	}
	if testpkgs, err := parseDir(fset, abspath, filter); err != nil {
		log.Println("parsing test files:", err)
	} else {
		for _, testpkg := range testpkgs {
			var files []*ast.File
			for _, f := range testpkg.Files {
				files = append(files, f)
			}
			examples = append(examples, doc.Examples(files...)...)
		}
	}

	// compute package documentation
	var past *ast.File
	var pdoc *doc.Package
	if pkg != nil {
		if mode&showSource == 0 {
			// show extracted documentation
			var m doc.Mode
			if mode&noFiltering != 0 {
				m = doc.AllDecls
			}
			if mode&allMethods != 0 {
				m |= doc.AllMethods
			}
			pdoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath
		} else {
			// show source code
			// TODO(gri) Consider eliminating export filtering in this mode,
			//           or perhaps eliminating the mode altogether.
			if mode&noFiltering == 0 {
				packageExports(fset, pkg)
			}
			past = ast.MergePackageFiles(pkg, 0)
		}
	}

	// get directory information
	var dir *Directory
	var timestamp time.Time
	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 {
		// 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, 1)
		timestamp = time.Now()
	}

	return PageInfo{
		Dirname:  abspath,
		FSet:     fset,
		PAst:     past,
		PDoc:     pdoc,
		Examples: examples,
		Dirs:     dir.listing(true),
		DirTime:  timestamp,
		DirFlat:  mode&flatDir != 0,
		IsPkg:    h.isPkg,
		Err:      nil,
	}
}
Exemple #12
0
// loadPackage returns details about the Go package named by the import
// path, interpreting local import paths relative to the srcDir directory.
func loadPackage(ctx *build.Context, importPath string, srcDir string, flags int) (*pkg, error) {
	bpkg, err := ctx.Import(importPath, srcDir, build.ImportComment)
	if _, ok := err.(*build.NoGoError); ok {
		return &pkg{Build: bpkg}, nil
	}
	if err != nil {
		return nil, err
	}

	pkg := &pkg{
		FSet:  token.NewFileSet(),
		Build: bpkg,
	}

	files := make(map[string]*ast.File)
	for _, name := range append(pkg.Build.GoFiles, pkg.Build.CgoFiles...) {
		file, err := pkg.parseFile(ctx, name)
		if err != nil {
			pkg.Errors = append(pkg.Errors, err)
			continue
		}
		files[name] = file
	}

	vendor := make(map[string]string)
	pkg.AST, _ = ast.NewPackage(pkg.FSet, files, importer(ctx, bpkg.Dir, vendor), nil)

	if flags&loadPackageFixVendor != 0 {
		for _, f := range pkg.AST.Files {
			for _, i := range f.Imports {
				if lit := i.Path; lit != nil {
					if s, err := strconv.Unquote(lit.Value); err != nil {
						if p, ok := vendor[s]; ok {
							lit.Value = strconv.Quote(p)
						}
					}
				}
			}
		}
	}

	if flags&loadPackageDoc != 0 {
		mode := godoc.Mode(0)
		if pkg.Build.ImportPath == "builtin" || flags&loadPackageUnexported != 0 {
			mode |= godoc.AllDecls
		}
		pkg.GoDoc = godoc.New(pkg.AST, pkg.Build.ImportPath, mode)
		if pkg.Build.ImportPath == "builtin" {
			for _, t := range pkg.GoDoc.Types {
				pkg.GoDoc.Funcs = append(pkg.GoDoc.Funcs, t.Funcs...)
				t.Funcs = nil
			}
			sort.Sort(byFuncName(pkg.GoDoc.Funcs))
		}
	}

	if flags&loadPackageExamples != 0 {
		for _, name := range append(pkg.Build.TestGoFiles, pkg.Build.XTestGoFiles...) {
			file, err := pkg.parseFile(ctx, name)
			if err != nil {
				pkg.Errors = append(pkg.Errors, err)
				continue
			}
			pkg.Examples = append(pkg.Examples, godoc.Examples(file)...)
		}
	}

	return pkg, nil
}
Exemple #13
0
func (b *builder) build(srcs []*source) (*Package, error) {

	b.pkg.Updated = time.Now().UTC()

	references := make(map[string]bool)
	b.srcs = make(map[string]*source)
	for _, src := range srcs {
		if strings.HasSuffix(src.name, ".go") {
			b.srcs[src.name] = src
		} else {
			addReferences(references, src.data)
		}
	}

	for r := range references {
		b.pkg.References = append(b.pkg.References, r)
	}

	if len(b.srcs) == 0 {
		return b.pkg, nil
	}

	b.fset = token.NewFileSet()
	b.importPaths = make(map[string]map[string]string)

	// Find the package and associated files.

	ctxt := build.Context{
		GOOS:          "linux",
		GOARCH:        "amd64",
		CgoEnabled:    true,
		JoinPath:      path.Join,
		IsAbsPath:     path.IsAbs,
		SplitPathList: func(list string) []string { return strings.Split(list, ":") },
		IsDir:         func(path string) bool { panic("unexpected") },
		HasSubdir:     func(root, dir string) (rel string, ok bool) { panic("unexpected") },
		ReadDir:       func(dir string) (fi []os.FileInfo, err error) { return b.readDir(dir) },
		OpenFile:      func(path string) (r io.ReadCloser, err error) { return b.openFile(path) },
		Compiler:      "gc",
	}
	pkg, err := ctxt.ImportDir(b.pkg.ImportPath, 0)
	if _, ok := err.(*build.NoGoError); ok {
		return b.pkg, nil
	} else if err != nil {
		b.pkg.Errors = append(b.pkg.Errors, err.Error())
		return b.pkg, nil
	}

	// Parse the Go files

	b.ast = &ast.Package{Name: pkg.Name, Files: make(map[string]*ast.File)}
	if pkg.IsCommand() && b.srcs["doc.go"] != nil {
		file, err := parser.ParseFile(b.fset, "doc.go", b.srcs["doc.go"].data, parser.ParseComments)
		if err == nil && file.Name.Name == "documentation" {
			b.ast.Files["doc.go"] = file
		}
	}
	if len(b.ast.Files) == 0 {
		for _, name := range append(pkg.GoFiles, pkg.CgoFiles...) {
			file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
			if err != nil {
				b.pkg.Errors = append(b.pkg.Errors, err.Error())
				continue
			}
			b.pkg.Files = append(b.pkg.Files, &File{Name: name, URL: b.srcs[name].browseURL})
			b.pkg.SourceSize += len(b.srcs[name].data)
			b.ast.Files[name] = file
		}
	}

	// Find examples in the test files.

	for _, name := range append(pkg.TestGoFiles, pkg.XTestGoFiles...) {
		file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
		if err != nil {
			b.pkg.Errors = append(b.pkg.Errors, err.Error())
			continue
		}
		b.pkg.TestFiles = append(b.pkg.TestFiles, &File{Name: name, URL: b.srcs[name].browseURL})
		b.pkg.TestSourceSize += len(b.srcs[name].data)
		b.examples = append(b.examples, doc.Examples(file)...)
	}

	b.vetPackage()

	mode := doc.Mode(0)
	if b.pkg.ImportPath == "builtin" {
		mode |= doc.AllDecls
	}

	pdoc := doc.New(b.ast, b.pkg.ImportPath, mode)

	b.pkg.Name = pdoc.Name
	b.pkg.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r")
	b.pkg.Synopsis = synopsis(b.pkg.Doc)

	b.pkg.Examples = b.getExamples("")
	b.pkg.IsCmd = pkg.IsCommand()

	b.pkg.Consts = b.values(pdoc.Consts)
	b.pkg.Funcs = b.funcs(pdoc.Funcs)
	b.pkg.Types = b.types(pdoc.Types)
	b.pkg.Vars = b.values(pdoc.Vars)

	b.pkg.Bugs = pdoc.Bugs

	b.pkg.Imports = pkg.Imports
	b.pkg.TestImports = pkg.TestImports
	b.pkg.XTestImports = pkg.XTestImports

	return b.pkg, nil
}
Exemple #14
0
func newPackage(dir *gosrc.Directory) (*Package, error) {

	pkg := &Package{
		Updated:        time.Now().UTC(),
		LineFmt:        dir.LineFmt,
		ImportPath:     dir.ImportPath,
		ProjectRoot:    dir.ProjectRoot,
		ProjectName:    dir.ProjectName,
		ProjectURL:     dir.ProjectURL,
		BrowseURL:      dir.BrowseURL,
		Etag:           PackageVersion + "-" + dir.Etag,
		VCS:            dir.VCS,
		DeadEndFork:    dir.DeadEndFork,
		Subdirectories: dir.Subdirectories,
	}

	var b builder
	b.srcs = make(map[string]*source)
	references := make(map[string]bool)
	for _, file := range dir.Files {
		if strings.HasSuffix(file.Name, ".go") {
			gosrc.OverwriteLineComments(file.Data)
			b.srcs[file.Name] = &source{name: file.Name, browseURL: file.BrowseURL, data: file.Data}
		} else {
			addReferences(references, file.Data)
		}
	}

	for r := range references {
		pkg.References = append(pkg.References, r)
	}

	if len(b.srcs) == 0 {
		return pkg, nil
	}

	b.fset = token.NewFileSet()

	// Find the package and associated files.

	ctxt := build.Context{
		GOOS:        "linux",
		GOARCH:      "amd64",
		CgoEnabled:  true,
		ReleaseTags: build.Default.ReleaseTags,
		BuildTags:   build.Default.BuildTags,
		Compiler:    "gc",
	}

	var err error
	var bpkg *build.Package

	for _, env := range goEnvs {
		ctxt.GOOS = env.GOOS
		ctxt.GOARCH = env.GOARCH
		// TODO(garyburd): Change second argument to build.ImportComment when
		// gddo is upgraded to Go 1.4.
		bpkg, err = dir.Import(&ctxt, 0 /* build.ImportComment */)
		if _, ok := err.(*build.NoGoError); !ok {
			break
		}
	}
	if err != nil {
		if _, ok := err.(*build.NoGoError); !ok {
			pkg.Errors = append(pkg.Errors, err.Error())
		}
		return pkg, nil
	}

	/*
		        TODO(garyburd): This block of code uses the import comment feature
		        added in Go 1.4. Uncomment this block when gddo upgraded to Go 1.4.
		        Also, change the second argument to dir.Import above from 0 to
		        build.ImportComment.

				if bpkg.ImportComment != "" && bpkg.ImportComment != dir.ImportPath {
					return nil, gosrc.NotFoundError{
						Message:  "not at canonical import path",
						Redirect: bpkg.ImportComment,
					}
				}
	*/

	// Parse the Go files

	files := make(map[string]*ast.File)
	names := append(bpkg.GoFiles, bpkg.CgoFiles...)
	sort.Strings(names)
	pkg.Files = make([]*File, len(names))
	for i, name := range names {
		file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
		if err != nil {
			pkg.Errors = append(pkg.Errors, err.Error())
		} else {
			files[name] = file
		}
		src := b.srcs[name]
		src.index = i
		pkg.Files[i] = &File{Name: name, URL: src.browseURL}
		pkg.SourceSize += len(src.data)
	}

	apkg, _ := ast.NewPackage(b.fset, files, simpleImporter, nil)

	// Find examples in the test files.

	names = append(bpkg.TestGoFiles, bpkg.XTestGoFiles...)
	sort.Strings(names)
	pkg.TestFiles = make([]*File, len(names))
	for i, name := range names {
		file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
		if err != nil {
			pkg.Errors = append(pkg.Errors, err.Error())
		} else {
			b.examples = append(b.examples, doc.Examples(file)...)
		}
		pkg.TestFiles[i] = &File{Name: name, URL: b.srcs[name].browseURL}
		pkg.TestSourceSize += len(b.srcs[name].data)
	}

	b.vetPackage(pkg, apkg)

	mode := doc.Mode(0)
	if pkg.ImportPath == "builtin" {
		mode |= doc.AllDecls
	}

	dpkg := doc.New(apkg, pkg.ImportPath, mode)

	if pkg.ImportPath == "builtin" {
		removeAssociations(dpkg)
	}

	pkg.Name = dpkg.Name
	pkg.Doc = strings.TrimRight(dpkg.Doc, " \t\n\r")
	pkg.Synopsis = synopsis(pkg.Doc)

	pkg.Examples = b.getExamples("")
	pkg.IsCmd = bpkg.IsCommand()
	pkg.GOOS = ctxt.GOOS
	pkg.GOARCH = ctxt.GOARCH

	pkg.Consts = b.values(dpkg.Consts)
	pkg.Funcs = b.funcs(dpkg.Funcs)
	pkg.Types = b.types(dpkg.Types)
	pkg.Vars = b.values(dpkg.Vars)
	pkg.Notes = b.notes(dpkg.Notes)

	pkg.Imports = bpkg.Imports
	pkg.TestImports = bpkg.TestImports
	pkg.XTestImports = bpkg.XTestImports

	return pkg, nil
}
Exemple #15
0
// build generates data from source files.
func (w *walker) build(srcs []*source) (*Package, error) {
	// Set created time.
	w.pdoc.Created = time.Now().UTC()

	// Add source files to walker, I skipped references here.
	w.srcs = make(map[string]*source)
	for _, src := range srcs {
		srcName := strings.ToLower(src.name) // For readme comparation.
		switch {
		case strings.HasSuffix(src.name, ".go"):
			w.srcs[src.name] = src
		case len(w.pdoc.Tag) > 0:
			continue // Only save latest readme.
		case strings.HasPrefix(srcName, "readme_zh") || strings.HasPrefix(srcName, "readme_cn"):
			models.SavePkgDoc(w.pdoc.ImportPath, "zh", src.data)
		case strings.HasPrefix(srcName, "readme"):
			models.SavePkgDoc(w.pdoc.ImportPath, "en", src.data)
		}
	}

	w.fset = token.NewFileSet()

	// Find the package and associated files.
	ctxt := build.Context{
		GOOS:          runtime.GOOS,
		GOARCH:        runtime.GOARCH,
		CgoEnabled:    true,
		JoinPath:      path.Join,
		IsAbsPath:     path.IsAbs,
		SplitPathList: func(list string) []string { return strings.Split(list, ":") },
		IsDir:         func(path string) bool { panic("unexpected") },
		HasSubdir:     func(root, dir string) (rel string, ok bool) { panic("unexpected") },
		ReadDir:       func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) },
		OpenFile:      func(path string) (r io.ReadCloser, err error) { return w.openFile(path) },
		Compiler:      "gc",
	}

	bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0)
	// Continue if there are no Go source files; we still want the directory info.
	_, nogo := err.(*build.NoGoError)
	if err != nil {
		if nogo {
			err = nil
			beego.Info("doc.walker.build -> No Go Source file")
		} else {
			return w.pdoc, errors.New("doc.walker.build -> " + err.Error())
		}
	}

	// Parse the Go files
	files := make(map[string]*ast.File)
	for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) {
		file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments)
		if err != nil {
			beego.Error("doc.walker.build -> parse go files:", err)
			continue
		}
		w.pdoc.Files = append(w.pdoc.Files, name)
		//w.pdoc.SourceSize += len(w.srcs[name].data)
		files[name] = file
	}

	apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil)

	// Find examples in the test files.
	for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) {
		file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments)
		if err != nil {
			beego.Error("doc.walker.build -> find examples:", err)
			continue
		}
		//w.pdoc.TestFiles = append(w.pdoc.TestFiles, &File{Name: name, URL: w.srcs[name].browseURL})
		//w.pdoc.TestSourceSize += len(w.srcs[name].data)
		w.examples = append(w.examples, doc.Examples(file)...)
	}

	//w.vetPackage(apkg)

	mode := doc.Mode(0)
	if w.pdoc.ImportPath == "builtin" {
		mode |= doc.AllDecls
	}

	pdoc := doc.New(apkg, w.pdoc.ImportPath, mode)

	w.pdoc.Synopsis = utils.Synopsis(pdoc.Doc)
	pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r")
	var buf bytes.Buffer
	doc.ToHTML(&buf, pdoc.Doc, nil)
	w.pdoc.Doc = w.pdoc.Doc + "<br />" + buf.String()
	w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "<p>", "<p><b>", 1)
	w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "</p>", "</b></p>", 1)
	w.pdoc.Doc = base32.StdEncoding.EncodeToString([]byte(w.pdoc.Doc))

	w.pdoc.Examples = w.getExamples("")
	w.pdoc.IsCmd = bpkg.IsCommand()
	w.srcLines = make(map[string][]string)
	w.pdoc.Consts = w.values(pdoc.Consts)
	w.pdoc.Funcs = w.funcs(pdoc.Funcs)
	w.pdoc.Types = w.types(pdoc.Types)
	w.pdoc.Vars = w.values(pdoc.Vars)
	//w.pdoc.Notes = w.notes(pdoc.Notes)

	w.pdoc.Imports = bpkg.Imports
	w.pdoc.TestImports = bpkg.TestImports
	//w.pdoc.XTestImports = bpkg.XTestImports

	beego.Info("doc.walker.build(", pdoc.ImportPath, "), Goroutine #", runtime.NumGoroutine())
	return w.pdoc, err
}
Exemple #16
0
func buildDoc(importPath, projectRoot, projectName, projectURL, etag string, lineFmt string, srcs []*source) (*Package, error) {

	b := &builder{
		lineFmt:     lineFmt,
		fset:        token.NewFileSet(),
		importPaths: make(map[string]map[string]string),
		srcs:        make(map[string]*source),
		pkg: &Package{
			ImportPath:  importPath,
			ProjectName: projectName,
			ProjectRoot: projectRoot,
			ProjectURL:  projectURL,
			Etag:        etag,
			Updated:     time.Now(),
		},
	}

	if len(srcs) == 0 {
		return b.pkg, nil
	}

	for _, src := range srcs {
		b.srcs[src.name] = src
	}

	// Find the package and associated files.

	ctxt := build.Context{
		GOOS:          "linux",
		GOARCH:        "amd64",
		CgoEnabled:    true,
		JoinPath:      path.Join,
		IsAbsPath:     path.IsAbs,
		SplitPathList: func(list string) []string { return strings.Split(list, ":") },
		IsDir:         func(path string) bool { panic("unexpected") },
		HasSubdir:     func(root, dir string) (rel string, ok bool) { panic("unexpected") },
		ReadDir:       func(dir string) (fi []os.FileInfo, err error) { return b.readDir(dir) },
		OpenFile:      func(path string) (r io.ReadCloser, err error) { return b.openFile(path) },
		Compiler:      "gc",
	}
	pkg, err := ctxt.ImportDir(b.pkg.ImportPath, 0)
	if err != nil {
		b.pkg.Errors = append(b.pkg.Errors, err.Error())
		return b.pkg, nil
	}

	// Parse the Go files

	b.ast = &ast.Package{Name: pkg.Name, Files: make(map[string]*ast.File)}
	if pkg.IsCommand() && b.srcs["doc.go"] != nil {
		file, err := parser.ParseFile(b.fset, "doc.go", b.srcs["doc.go"].data, parser.ParseComments)
		if err == nil && file.Name.Name == "documentation" {
			b.ast.Files["doc.go"] = file
		}
	}
	if len(b.ast.Files) == 0 {
		for _, name := range append(pkg.GoFiles, pkg.CgoFiles...) {
			file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
			if err != nil {
				b.pkg.Errors = append(b.pkg.Errors, err.Error())
				continue
			}
			b.pkg.Files = append(b.pkg.Files, &File{Name: name, URL: b.srcs[name].browseURL})
			b.pkg.SourceSize += len(b.srcs[name].data)
			b.ast.Files[name] = file
		}
	}

	// Find examples in the test files.

	for _, name := range append(pkg.TestGoFiles, pkg.XTestGoFiles...) {
		file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments)
		if err != nil {
			b.pkg.Errors = append(b.pkg.Errors, err.Error())
			continue
		}
		b.pkg.TestFiles = append(b.pkg.TestFiles, &File{Name: name, URL: b.srcs[name].browseURL})
		b.pkg.TestSourceSize += len(b.srcs[name].data)
		b.examples = append(b.examples, doc.Examples(file)...)
	}

	b.vetPackage()

	pdoc := doc.New(b.ast, b.pkg.ImportPath, 0)

	b.pkg.Name = pdoc.Name
	b.pkg.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r")
	b.pkg.Synopsis = synopsis(b.pkg.Doc)

	b.pkg.Examples = b.getExamples("")
	b.pkg.IsCmd = pkg.IsCommand()

	b.pkg.Consts = b.values(pdoc.Consts)
	b.pkg.Funcs = b.funcs(pdoc.Funcs)
	b.pkg.Types = b.types(pdoc.Types)
	b.pkg.Vars = b.values(pdoc.Vars)

	b.pkg.Imports = pkg.Imports
	b.pkg.TestImports = pkg.TestImports

	return b.pkg, nil
}
Exemple #17
0
// Build generates documentation from given source files through 'WalkType'.
func (w *Walker) Build(wr *WalkRes) (*Package, error) {
	ctxt := build.Context{
		CgoEnabled:  true,
		ReleaseTags: build.Default.ReleaseTags,
		BuildTags:   build.Default.BuildTags,
		Compiler:    "gc",
	}

	if w.Pdoc.PkgDecl == nil {
		w.Pdoc.PkgDecl = &PkgDecl{}
	}

	// Check 'WalkType'.
	switch wr.WalkType {
	case WT_Local:
		// Check root path.
		if len(wr.RootPath) == 0 {
			return nil, errors.New("WT_Local: empty root path")
		} else if !com.IsDir(wr.RootPath) {
			return nil, errors.New("WT_Local: cannot find specific directory or it's a file")
		}

		w.setLocalContext(&ctxt)
		return nil, errors.New("Hasn't supported yet!")
	case WT_Memory:
		// Convert source files.
		w.SrcFiles = make(map[string]*Source)
		w.Pdoc.Readme = make(map[string][]byte)
		for _, src := range wr.Srcs {
			srcName := strings.ToLower(src.Name()) // For readme comparation.
			switch {
			case strings.HasSuffix(src.Name(), ".go"):
				w.SrcFiles[src.Name()] = src
			case len(w.Pdoc.Tag) > 0 || (wr.WalkMode&WM_NoReadme != 0):
				// This means we are not on the latest version of the code,
				// so we do not collect the README files.
				continue
			case strings.HasPrefix(srcName, "readme_zh") || strings.HasPrefix(srcName, "readme_cn"):
				w.Pdoc.Readme["zh"] = src.Data()
			case strings.HasPrefix(srcName, "readme"):
				w.Pdoc.Readme["en"] = src.Data()
			}
		}

		// Check source files.
		if w.SrcFiles == nil {
			return nil, errors.New("WT_Memory: no Go source file")
		}

		w.setMemoryContext(&ctxt)

	default:
		return nil, errors.New("Hasn't supported yet!")
	}

	var err error
	var bpkg *build.Package

	for _, env := range goEnvs {
		ctxt.GOOS = env.GOOS
		ctxt.GOARCH = env.GOARCH

		bpkg, err = ctxt.ImportDir(w.Pdoc.ImportPath, 0)
		// Continue if there are no Go source files; we still want the directory info.
		_, nogo := err.(*build.NoGoError)
		if err != nil {
			if nogo {
				err = nil
			} else {
				return nil, errors.New("hv.Walker.Build -> ImportDir: " + err.Error())
			}
		}
	}

	w.Pdoc.IsCmd = bpkg.IsCommand()
	w.Pdoc.Synopsis = synopsis(bpkg.Doc)

	w.Pdoc.Imports = bpkg.Imports
	w.Pdoc.IsCgo = w.isCgo()
	w.Pdoc.TestImports = bpkg.TestImports

	// Check depth.
	if wr.WalkDepth <= WD_Imports {
		return w.Pdoc, nil
	}

	w.Fset = token.NewFileSet()
	// Parse the Go files
	files := make(map[string]*ast.File)
	for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) {
		file, err := parser.ParseFile(w.Fset, name, w.SrcFiles[name].Data(), parser.ParseComments)
		if err != nil {
			return nil, errors.New("hv.Walker.Build -> parse Go files: " + err.Error())
			continue
		}
		w.Pdoc.Files = append(w.Pdoc.Files, w.SrcFiles[name])
		w.Pdoc.SourceSize += int64(len(w.SrcFiles[name].Data()))
		files[name] = file
	}

	w.apkg, _ = ast.NewPackage(w.Fset, files, poorMansImporter, nil)

	// Find examples in the test files.
	for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) {
		file, err := parser.ParseFile(w.Fset, name, w.SrcFiles[name].Data(), parser.ParseComments)
		if err != nil {
			return nil, errors.New("hv.Walker.Build -> find examples: " + err.Error())
			continue
		}
		w.Pdoc.TestFiles = append(w.Pdoc.TestFiles, w.SrcFiles[name])
		//w.pdoc.TestSourceSize += len(w.srcs[name].data)

		if wr.WalkMode&WM_NoExample != 0 {
			continue
		}
		w.Examples = append(w.Examples, doc.Examples(file)...)
	}

	mode := doc.Mode(0)
	if w.Pdoc.ImportPath == "builtin" || wr.BuildAll {
		mode |= doc.AllDecls
	}
	pdoc := doc.New(w.apkg, w.Pdoc.ImportPath, mode)

	// Get doc.
	pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r")
	var buf bytes.Buffer
	doc.ToHTML(&buf, pdoc.Doc, nil)
	w.Pdoc.Doc = buf.String()
	// Highlight first sentence.
	w.Pdoc.Doc = strings.Replace(w.Pdoc.Doc, "<p>", "<p><b>", 1)
	w.Pdoc.Doc = strings.Replace(w.Pdoc.Doc, "</p>", "</b></p>", 1)

	if wr.WalkMode&WM_NoExample == 0 {
		w.getExamples()
	}

	w.SrcLines = make(map[string][]string)
	w.Pdoc.Consts = w.values(pdoc.Consts)
	w.Pdoc.Funcs, w.Pdoc.Ifuncs = w.funcs(pdoc.Funcs)
	w.Pdoc.Types, w.Pdoc.Itypes = w.types(pdoc.Types)
	w.Pdoc.Vars = w.values(pdoc.Vars)
	//w.Pdoc.Notes = w.notes(pdoc.Notes)

	return w.Pdoc, nil
}