277 lines
7.1 KiB
Go
277 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/PuerkitoBio/goquery"
|
|
"golang.org/x/net/html"
|
|
"golang.org/x/net/html/atom"
|
|
)
|
|
|
|
const additionalCSS = `
|
|
details { margin-top: 20px; }
|
|
summary { margin-left: 20px; cursor: pointer; }
|
|
#footer > p, #footer > li { max-width: none; word-wrap: normal; }
|
|
`
|
|
|
|
const footerText = `Generated by <a href="https://godoc.org/golang.org/x/tools/godoc" target="_blank">godoc</a> + <a href="https://code.rocket9labs.com/tslocum/godoc-static" target="_blank">godoc-static</a>`
|
|
|
|
func topBar(basePath string, siteName string) string {
|
|
var index string
|
|
if linkIndex {
|
|
index = "index.html"
|
|
}
|
|
|
|
return `<div class="container">
|
|
<div class="top-heading" id="heading-wide"><a href="` + basePath + index + `">` + siteName + `</a></div>
|
|
<div class="top-heading" id="heading-narrow"><a href="` + basePath + index + `">` + siteName + `</a></div>
|
|
<!--<a href="#" id="menu-button"><span id="menu-button-arrow">▽</span></a>-->
|
|
<div id="menu">
|
|
<a href="` + basePath + index + `" style="margin-right: 10px;">Package Index</a>
|
|
</div>
|
|
</div>`
|
|
}
|
|
|
|
func siteFooterText(basePath string) string {
|
|
footer := siteFooter
|
|
addP := footer != ""
|
|
|
|
if addP {
|
|
footer += "<p>"
|
|
}
|
|
|
|
if siteZip != "" {
|
|
footer += `<a href="` + basePath + siteZip + `">Download ` + siteZip + `</a> to browse offline - `
|
|
}
|
|
footer += footerText
|
|
|
|
if addP {
|
|
footer += "</p>"
|
|
}
|
|
return footer
|
|
}
|
|
|
|
func updatePage(doc *goquery.Document, basePath string, siteName string) {
|
|
doc.Find("link").Remove()
|
|
doc.Find("script").Remove()
|
|
|
|
linkTag := &html.Node{
|
|
Type: html.ElementNode,
|
|
DataAtom: atom.Link,
|
|
Data: "link",
|
|
Attr: []html.Attribute{
|
|
{Key: "type", Val: "text/css"},
|
|
{Key: "rel", Val: "stylesheet"},
|
|
{Key: "href", Val: basePath + "lib/style.css"},
|
|
},
|
|
}
|
|
|
|
doc.Find("head").AppendNodes(linkTag)
|
|
|
|
doc.Find("#topbar").First().SetHtml(topBar(basePath, siteName))
|
|
|
|
importPathDisplay := doc.Find("#short-nav").First().Find("code").First()
|
|
if importPathDisplay.Length() > 0 {
|
|
importPathDisplayText := importPathDisplay.Text()
|
|
if strings.ContainsRune(importPathDisplayText, '.') && strings.HasPrefix(importPathDisplayText, `import "`) && strings.HasSuffix(importPathDisplayText, `"`) {
|
|
importPath := importPathDisplayText[8 : len(importPathDisplayText)-1]
|
|
|
|
browseImportPath := importPath
|
|
var browseInsert string
|
|
if strings.HasPrefix(importPath, "gitlab.com/") {
|
|
browseInsert = "/-/tree/master"
|
|
} else if strings.HasPrefix(importPath, "github.com/") || strings.HasPrefix(importPath, "git.sr.ht/") {
|
|
browseInsert = "/tree/master"
|
|
} else if strings.HasPrefix(importPath, "bitbucket.org/") || strings.HasPrefix(importPath, "code.rocket9labs.com/") {
|
|
browseInsert = "/src/master"
|
|
}
|
|
if browseInsert != "" {
|
|
var insertPos int
|
|
var found int
|
|
for i, c := range importPath {
|
|
if c == '/' {
|
|
found++
|
|
if found == 3 {
|
|
insertPos = i
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if insertPos > 0 {
|
|
browseImportPath = importPath[0:insertPos] + browseInsert + importPath[insertPos:]
|
|
}
|
|
}
|
|
|
|
importPathDisplay.SetHtml(fmt.Sprintf(`import "<a href="https://` + browseImportPath + `" target="_blank">` + importPath + `</a>"`))
|
|
}
|
|
}
|
|
|
|
doc.Find("a").Each(func(_ int, selection *goquery.Selection) {
|
|
href := selection.AttrOr("href", "")
|
|
|
|
var suffix string
|
|
if (strings.HasPrefix(href, "/src/") || strings.HasPrefix(href, "/pkg/")) && path.Ext(href) != "" {
|
|
suffix = ".html"
|
|
} else if linkIndex && !(strings.HasPrefix(href, "#") || strings.HasPrefix(href, "?")) && path.Ext(href) == "" {
|
|
suffix = "/index.html"
|
|
}
|
|
if suffix != "" {
|
|
pos := strings.IndexAny(href, "?#")
|
|
if pos >= 0 {
|
|
href = strings.TrimRight(href[0:pos], "/") + suffix + href[pos:]
|
|
} else {
|
|
href = strings.TrimRight(href, "/") + suffix
|
|
}
|
|
}
|
|
|
|
if strings.HasPrefix(href, "/src/") || strings.HasPrefix(href, "/pkg/") {
|
|
if strings.HasPrefix(href, "/pkg/") {
|
|
href = href[4:]
|
|
}
|
|
selection.SetAttr("href", basePath+href[1:])
|
|
} else if suffix != ""{
|
|
selection.SetAttr("href", href)
|
|
}
|
|
})
|
|
|
|
doc.Find("div").Each(func(_ int, selection *goquery.Selection) {
|
|
if selection.HasClass("toggle") {
|
|
var summary string
|
|
var err error
|
|
selection.Find("div").Each(func(_ int, subSelection *goquery.Selection) {
|
|
if subSelection.HasClass("collapsed") {
|
|
summary, err = subSelection.Find("span.text").First().Html()
|
|
if err != nil {
|
|
summary = "Summary not available"
|
|
}
|
|
|
|
subSelection.Remove()
|
|
}
|
|
})
|
|
|
|
selection.Find(".toggleButton").Remove()
|
|
|
|
selection.PrependHtml(fmt.Sprintf("<summary>%s</summary>", summary))
|
|
|
|
selection.RemoveClass("toggle")
|
|
|
|
selection.Nodes[0].Data = "details"
|
|
selection.Nodes[0].DataAtom = atom.Details
|
|
}
|
|
})
|
|
|
|
doc.Find("#footer").Last().SetHtml(siteFooterText(basePath))
|
|
}
|
|
|
|
func writeIndex(buf *bytes.Buffer, pkgs []string, filterPkgs []string) error {
|
|
var index string
|
|
if linkIndex {
|
|
index = "/index.html"
|
|
}
|
|
|
|
buf.Reset()
|
|
buf.WriteString(`<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="theme-color" content="#375EAB">
|
|
<title>` + siteName + `</title>
|
|
<link type="text/css" rel="stylesheet" href="lib/style.css">
|
|
</head>
|
|
<body>
|
|
|
|
<div id="lowframe" style="position: fixed; bottom: 0; left: 0; height: 0; width: 100%; border-top: thin solid grey; background-color: white; overflow: auto;">
|
|
...
|
|
</div><!-- #lowframe -->
|
|
|
|
<div id="topbar" class="wide">` + topBar("", siteName) + `</div>
|
|
<div id="page" class="wide">
|
|
<div class="container">
|
|
`)
|
|
|
|
if siteDescription != "" {
|
|
buf.WriteString(siteDescription)
|
|
}
|
|
|
|
buf.WriteString(`
|
|
<h1>
|
|
Packages
|
|
</h1>
|
|
<div class="pkg-dir">
|
|
<table>
|
|
<tr>
|
|
<th class="pkg-name">Name</th>
|
|
<th class="pkg-synopsis">Synopsis</th>
|
|
</tr>
|
|
`)
|
|
|
|
var padding int
|
|
var lastPkg string
|
|
var pkgBuf bytes.Buffer
|
|
for _, pkg := range pkgs {
|
|
pkgBuf.Reset()
|
|
cmd := exec.Command("go", "list", "-find", "-f", `{{ .Doc }}`, pkg)
|
|
cmd.Env = godocEnv
|
|
cmd.Dir = os.TempDir()
|
|
cmd.Stdout = &pkgBuf
|
|
setDeathSignal(cmd)
|
|
|
|
cmd.Run() // Ignore error
|
|
|
|
pkgLabel := pkg
|
|
if lastPkg != "" {
|
|
lastPkgSplit := strings.Split(lastPkg, "/")
|
|
pkgSplit := strings.Split(pkg, "/")
|
|
shared := 0
|
|
for i := range pkgSplit {
|
|
if i < len(lastPkgSplit) && strings.ToLower(lastPkgSplit[i]) == strings.ToLower(pkgSplit[i]) {
|
|
shared++
|
|
}
|
|
}
|
|
|
|
padding = shared * 20
|
|
pkgLabel = strings.Join(pkgSplit[shared:], "/")
|
|
}
|
|
lastPkg = pkg
|
|
|
|
var linkPackage bool
|
|
for _, filterPkg := range filterPkgs {
|
|
if pkg == filterPkg {
|
|
linkPackage = true
|
|
break
|
|
}
|
|
}
|
|
buf.WriteString(`
|
|
<tr>
|
|
<td class="pkg-name" style="padding-left: ` + strconv.Itoa(padding) + `px;">`)
|
|
if !linkPackage {
|
|
buf.WriteString(pkgLabel)
|
|
} else {
|
|
buf.WriteString(`<a href="` + pkg + index + `">` + pkgLabel + `</a>`)
|
|
}
|
|
buf.WriteString(`</td>
|
|
<td class="pkg-synopsis">
|
|
` + pkgBuf.String() + `
|
|
</td>
|
|
</tr>
|
|
`)
|
|
}
|
|
buf.WriteString(`
|
|
</table>
|
|
</div>
|
|
<div id="footer">` + siteFooterText("") + `</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`)
|
|
|
|
return writeFile(buf, "", "index.html")
|
|
}
|