func renderDoc(this *HomeRouter, pdoc *hv.Package, q, tag, docPath string) bool { this.Data["PkgFullIntro"] = pdoc.Doc var buf bytes.Buffer links := make([]*utils.Link, 0, len(pdoc.Types)+len(pdoc.Imports)+len(pdoc.TestImports)+ len(pdoc.Funcs)+10) // Get all types, functions and import packages for _, t := range pdoc.Types { links = append(links, &utils.Link{ Name: t.Name, Comment: template.HTMLEscapeString(t.Doc), }) buf.WriteString("'" + t.Name + "',") } for _, f := range pdoc.Funcs { links = append(links, &utils.Link{ Name: f.Name, Comment: template.HTMLEscapeString(f.Doc), }) buf.WriteString("'" + f.Name + "',") } for _, t := range pdoc.Types { for _, f := range t.Funcs { links = append(links, &utils.Link{ Name: f.Name, Comment: template.HTMLEscapeString(f.Doc), }) buf.WriteString("'" + f.Name + "',") } for _, m := range t.Methods { buf.WriteString("'" + t.Name + "." + m.Name + "',") } } // Ignore C. for _, v := range append(pdoc.Imports, pdoc.TestImports...) { if v != "C" { links = append(links, &utils.Link{ Name: path.Base(v) + ".", Path: v, }) } } // Set exported objects type-ahead. exportDataSrc := buf.String() if len(exportDataSrc) > 0 { pdoc.IsHasExport = true this.Data["IsHasExports"] = true exportDataSrc = exportDataSrc[:len(exportDataSrc)-1] this.Data["ExportDataSrc"] = "<script>$('.search-export').typeahead({local: [" + exportDataSrc + "],limit: 10});</script>" } pdoc.UserExamples = getUserExamples(pdoc.ImportPath) pdoc.IsHasConst = len(pdoc.Consts) > 0 pdoc.IsHasVar = len(pdoc.Vars) > 0 if len(pdoc.Examples)+len(pdoc.UserExamples) > 0 { pdoc.IsHasExample = true this.Data["IsHasExample"] = pdoc.IsHasExample this.Data["Examples"] = append(pdoc.Examples, pdoc.UserExamples...) } // Commented and total objects number. var comNum, totalNum int // Constants. this.Data["IsHasConst"] = pdoc.IsHasConst this.Data["Consts"] = pdoc.Consts for i, v := range pdoc.Consts { if len(v.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, v.Doc, nil) v.Doc = buf.String() } buf.Reset() v.Decl = template.HTMLEscapeString(v.Decl) v.Decl = strings.Replace(v.Decl, """, "\"", -1) utils.FormatCode(&buf, &v.Decl, links) v.FmtDecl = buf.String() pdoc.Consts[i] = v } // Variables. this.Data["IsHasVar"] = pdoc.IsHasVar this.Data["Vars"] = pdoc.Vars for i, v := range pdoc.Vars { if len(v.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, v.Doc, nil) v.Doc = buf.String() } buf.Reset() utils.FormatCode(&buf, &v.Decl, links) v.FmtDecl = buf.String() pdoc.Vars[i] = v } // Dirs. pinfos := models.GetSubPkgs(pdoc.ImportPath, tag, pdoc.Dirs) if len(pinfos) > 0 { pdoc.IsHasSubdir = true this.Data["IsHasSubdirs"] = pdoc.IsHasSubdir this.Data["Subdirs"] = pinfos this.Data["ViewDirPath"] = pdoc.ViewDirPath } // Files. if len(pdoc.Files) > 0 { pdoc.IsHasFile = true this.Data["IsHasFiles"] = pdoc.IsHasFile this.Data["Files"] = pdoc.Files var query string if i := strings.Index(pdoc.Files[0].BrowseUrl, "?"); i > -1 { query = pdoc.Files[0].BrowseUrl[i:] } this.Data["ViewFilePath"] = path.Dir(pdoc.Files[0].BrowseUrl) + "/" + query } var err error pfuncs := doc.RenderFuncs(pdoc) this.Data["Funcs"] = pdoc.Funcs for i, f := range pdoc.Funcs { if len(f.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, f.Doc, nil) f.Doc = buf.String() comNum++ } buf.Reset() utils.FormatCode(&buf, &f.Decl, links) f.FmtDecl = buf.String() + " {" if exs := getExamples(pdoc, "", f.Name); len(exs) > 0 { f.Examples = exs } totalNum++ pdoc.Funcs[i] = f } this.Data["Types"] = pdoc.Types for i, t := range pdoc.Types { for j, v := range t.Consts { if len(v.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, v.Doc, nil) v.Doc = buf.String() } buf.Reset() v.Decl = template.HTMLEscapeString(v.Decl) v.Decl = strings.Replace(v.Decl, """, "\"", -1) utils.FormatCode(&buf, &v.Decl, links) v.FmtDecl = buf.String() t.Consts[j] = v } for j, v := range t.Vars { if len(v.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, v.Doc, nil) v.Doc = buf.String() } buf.Reset() utils.FormatCode(&buf, &v.Decl, links) v.FmtDecl = buf.String() t.Vars[j] = v } for j, f := range t.Funcs { if len(f.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, f.Doc, nil) f.Doc = buf.String() comNum++ } buf.Reset() utils.FormatCode(&buf, &f.Decl, links) f.FmtDecl = buf.String() + " {" if exs := getExamples(pdoc, "", f.Name); len(exs) > 0 { f.Examples = exs } totalNum++ t.Funcs[j] = f } for j, m := range t.Methods { if len(m.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, m.Doc, nil) m.Doc = buf.String() comNum++ } buf.Reset() utils.FormatCode(&buf, &m.Decl, links) m.FmtDecl = buf.String() + " {" if exs := getExamples(pdoc, t.Name, m.Name); len(exs) > 0 { m.Examples = exs } totalNum++ t.Methods[j] = m } if len(t.Doc) > 0 { buf.Reset() godoc.ToHTML(&buf, t.Doc, nil) t.Doc = buf.String() comNum++ } buf.Reset() utils.FormatCode(&buf, &t.Decl, links) t.FmtDecl = buf.String() if exs := getExamples(pdoc, "", t.Name); len(exs) > 0 { t.Examples = exs } totalNum++ pdoc.Types[i] = t } if !pdoc.IsCmd { // Calculate documentation complete %. this.Data["DocCPLabel"], this.Data["DocCP"] = calDocCP(comNum, totalNum) } else { this.Data["IsCmd"] = true } // Examples. links = append(links, &utils.Link{ Name: path.Base(pdoc.ImportPath) + ".", }) for _, e := range pdoc.Examples { buf.Reset() utils.FormatCode(&buf, &e.Code, links) e.Code = buf.String() } for _, e := range pdoc.UserExamples { buf.Reset() utils.FormatCode(&buf, &e.Code, links) e.Code = buf.String() } this.Data["ImportPath"] = pdoc.ImportPath if len(tag) == 0 && (pdoc.IsCmd || pdoc.IsGoRepo || pdoc.IsGoSubrepo) { this.Data["IsHasHv"] = true } // GitHub redirects non-HTTPS link and Safari loses "#XXX". if strings.HasPrefix(pdoc.ImportPath, "github") { this.Data["Secure"] = "s" } this.TplNames = "tpl/docs.tpl" data, err := this.RenderBytes() if err != nil { beego.Error("generatePage(", pdoc.ImportPath, ") -> RenderBytes:", err) return false } n := utils.SaveDocPage(docPath, com.Html2JS(data)) if n == -1 { return false } pdoc.JsNum = n pdoc.Id, err = doc.SaveProject(pdoc, pfuncs) if err != nil { beego.Error("generatePage(", pdoc.ImportPath, ") -> SaveProject:", err) return false } models.SavePkgDoc(pdoc.ImportPath, pdoc.Readme) this.Data["UtcTime"] = pdoc.Created this.Data["TimeSince"] = calTimeSince(pdoc.Created) return true }
// 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 }