summaryrefslogtreecommitdiffstats
path: root/src/util/doc.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/doc.go')
-rw-r--r--src/util/doc.go149
1 files changed, 104 insertions, 45 deletions
diff --git a/src/util/doc.go b/src/util/doc.go
index 540d6ca..7d15d9b 100644
--- a/src/util/doc.go
+++ b/src/util/doc.go
@@ -42,15 +42,16 @@ type HeaderFile struct {
// is a separate paragraph.
Preamble []string
Sections []HeaderSection
+ // AllDecls maps all decls to their URL fragments.
+ AllDecls map[string]string
}
type HeaderSection struct {
// Preamble contains a comment for a group of functions.
Preamble []string
Decls []HeaderDecl
- // Num is just the index of the section. It's included in order to help
- // text/template generate anchors.
- Num int
+ // Anchor, if non-empty, is the URL fragment to use in anchor tags.
+ Anchor string
// IsPrivate is true if the section contains private functions (as
// indicated by its name).
IsPrivate bool
@@ -65,10 +66,8 @@ type HeaderDecl struct {
Name string
// Decl contains the preformatted C declaration itself.
Decl string
- // Num is an index for the declaration, but the value is unique for all
- // declarations in a HeaderFile. It's included in order to help
- // text/template generate anchors.
- Num int
+ // Anchor, if non-empty, is the URL fragment to use in anchor tags.
+ Anchor string
}
const (
@@ -192,14 +191,6 @@ func extractDecl(lines []string, lineNo int) (decl string, rest []string, restLi
return
}
-func skipPast(s, skip string) string {
- i := strings.Index(s, skip)
- if i > 0 {
- return s[i:]
- }
- return s
-}
-
func skipLine(s string) string {
i := strings.Index(s, "\n")
if i > 0 {
@@ -227,8 +218,9 @@ func getNameFromDecl(decl string) (string, bool) {
}
return decl[:i], true
}
- decl = skipPast(decl, "STACK_OF(")
- decl = skipPast(decl, "LHASH_OF(")
+ decl = strings.TrimPrefix(decl, "OPENSSL_EXPORT ")
+ decl = strings.TrimPrefix(decl, "STACK_OF(")
+ decl = strings.TrimPrefix(decl, "LHASH_OF(")
i := strings.Index(decl, "(")
if i < 0 {
return "", false
@@ -243,6 +235,14 @@ func getNameFromDecl(decl string) (string, bool) {
return decl[j+1 : i], true
}
+func sanitizeAnchor(name string) string {
+ return strings.Replace(name, " ", "-", -1)
+}
+
+func isPrivateSection(name string) bool {
+ return strings.HasPrefix(name, "Private functions") || strings.HasPrefix(name, "Private structures") || strings.Contains(name, "(hidden)")
+}
+
func (config *Config) parseHeader(path string) (*HeaderFile, error) {
headerPath := filepath.Join(config.BaseDirectory, path)
@@ -284,7 +284,8 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) {
lines = lines[2:]
header := &HeaderFile{
- Name: filepath.Base(path),
+ Name: filepath.Base(path),
+ AllDecls: make(map[string]string),
}
for i, line := range lines {
@@ -314,7 +315,7 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) {
}
}
- var sectionNumber, declNumber int
+ allAnchors := make(map[string]struct{})
for {
// Start of a section.
@@ -330,10 +331,7 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) {
return nil, fmt.Errorf("blank line at start of section on line %d", lineNo)
}
- section := HeaderSection{
- Num: sectionNumber,
- }
- sectionNumber++
+ var section HeaderSection
if strings.HasPrefix(line, commentStart) {
comment, rest, restLineNo, err := extractComment(lines, lineNo)
@@ -341,8 +339,17 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) {
return nil, err
}
if len(rest) > 0 && len(rest[0]) == 0 {
+ anchor := sanitizeAnchor(firstSentence(comment))
+ if len(anchor) > 0 {
+ if _, ok := allAnchors[anchor]; ok {
+ return nil, fmt.Errorf("duplicate anchor: %s", anchor)
+ }
+ allAnchors[anchor] = struct{}{}
+ }
+
section.Preamble = comment
- section.IsPrivate = len(comment) > 0 && strings.HasPrefix(comment[0], "Private functions")
+ section.IsPrivate = len(comment) > 0 && isPrivateSection(comment[0])
+ section.Anchor = anchor
lines = rest[1:]
lineNo = restLineNo + 1
}
@@ -381,13 +388,33 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) {
if last := len(section.Decls) - 1; len(name) == 0 && len(comment) == 0 && last >= 0 {
section.Decls[last].Decl += "\n" + decl
} else {
+ // As a matter of style, comments should start
+ // with the name of the thing that they are
+ // commenting on. We make an exception here for
+ // #defines (because we often have blocks of
+ // them) and collective comments, which are
+ // detected by starting with “The” or “These”.
+ if len(comment) > 0 &&
+ !strings.HasPrefix(comment[0], name) &&
+ !strings.HasPrefix(decl, "#define ") &&
+ !strings.HasPrefix(comment[0], "The ") &&
+ !strings.HasPrefix(comment[0], "These ") {
+ return nil, fmt.Errorf("Comment for %q doesn't seem to match just above %s:%d\n", name, path, lineNo)
+ }
+ anchor := sanitizeAnchor(name)
+ // TODO(davidben): Enforce uniqueness. This is
+ // skipped because #ifdefs currently result in
+ // duplicate table-of-contents entries.
+ allAnchors[anchor] = struct{}{}
+
+ header.AllDecls[name] = anchor
+
section.Decls = append(section.Decls, HeaderDecl{
Comment: comment,
Name: name,
Decl: decl,
- Num: declNumber,
+ Anchor: anchor,
})
- declNumber++
}
if len(lines) > 0 && len(lines[0]) == 0 {
@@ -417,7 +444,7 @@ func firstSentence(paragraphs []string) string {
return s
}
-func markupPipeWords(s string) template.HTML {
+func markupPipeWords(allDecls map[string]string, s string) template.HTML {
ret := ""
for {
@@ -433,7 +460,14 @@ func markupPipeWords(s string) template.HTML {
j := strings.Index(s, " ")
if i > 0 && (j == -1 || j > i) {
ret += "<tt>"
+ anchor, isLink := allDecls[s[:i]]
+ if isLink {
+ ret += fmt.Sprintf("<a href=\"%s\">", template.HTMLEscapeString(anchor))
+ }
ret += s[:i]
+ if isLink {
+ ret += "</a>"
+ }
ret += "</tt>"
s = s[i+1:]
} else {
@@ -451,7 +485,12 @@ again:
if end > 0 {
end += start
w := strings.ToLower(string(s[start:end]))
- if w == "a" || w == "an" || w == "deprecated:" {
+ // The first word was already marked up as an HTML tag. Don't
+ // mark it up further.
+ if strings.ContainsRune(w, '<') {
+ return s
+ }
+ if w == "a" || w == "an" {
start = end + 1
goto again
}
@@ -471,10 +510,12 @@ func newlinesToBR(html template.HTML) template.HTML {
}
func generate(outPath string, config *Config) (map[string]string, error) {
+ allDecls := make(map[string]string)
+
headerTmpl := template.New("headerTmpl")
headerTmpl.Funcs(template.FuncMap{
"firstSentence": firstSentence,
- "markupPipeWords": markupPipeWords,
+ "markupPipeWords": func(s string) template.HTML { return markupPipeWords(allDecls, s) },
"markupFirstWord": markupFirstWord,
"newlinesToBR": newlinesToBR,
})
@@ -495,9 +536,9 @@ func generate(outPath string, config *Config) (map[string]string, error) {
<ol>
{{range .Sections}}
{{if not .IsPrivate}}
- {{if .Preamble}}<li class="header"><a href="#section-{{.Num}}">{{.Preamble | firstSentence | html | markupPipeWords}}</a></li>{{end}}
+ {{if .Anchor}}<li class="header"><a href="#{{.Anchor}}">{{.Preamble | firstSentence | html | markupPipeWords}}</a></li>{{end}}
{{range .Decls}}
- {{if .Name}}<li><a href="#decl-{{.Num}}"><tt>{{.Name}}</tt></a></li>{{end}}
+ {{if .Anchor}}<li><a href="#{{.Anchor}}"><tt>{{.Name}}</tt></a></li>{{end}}
{{end}}
{{end}}
{{end}}
@@ -505,23 +546,19 @@ func generate(outPath string, config *Config) (map[string]string, error) {
{{range .Sections}}
{{if not .IsPrivate}}
- <div class="section">
+ <div class="section" {{if .Anchor}}id="{{.Anchor}}"{{end}}>
{{if .Preamble}}
<div class="sectionpreamble">
- <a name="section-{{.Num}}">
{{range .Preamble}}<p>{{. | html | markupPipeWords}}</p>{{end}}
- </a>
</div>
{{end}}
{{range .Decls}}
- <div class="decl">
- <a name="decl-{{.Num}}">
+ <div class="decl" {{if .Anchor}}id="{{.Anchor}}"{{end}}>
{{range .Comment}}
<p>{{. | html | markupPipeWords | newlinesToBR | markupFirstWord}}</p>
{{end}}
<pre>{{.Decl}}</pre>
- </a>
</div>
{{end}}
</div>
@@ -535,6 +572,7 @@ func generate(outPath string, config *Config) (map[string]string, error) {
}
headerDescriptions := make(map[string]string)
+ var headers []*HeaderFile
for _, section := range config.Sections {
for _, headerPath := range section.Headers {
@@ -543,18 +581,26 @@ func generate(outPath string, config *Config) (map[string]string, error) {
return nil, errors.New("while parsing " + headerPath + ": " + err.Error())
}
headerDescriptions[header.Name] = firstSentence(header.Preamble)
- filename := filepath.Join(outPath, header.Name+".html")
- file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
- if err != nil {
- panic(err)
- }
- defer file.Close()
- if err := headerTmpl.Execute(file, header); err != nil {
- return nil, err
+ headers = append(headers, header)
+
+ for name, anchor := range header.AllDecls {
+ allDecls[name] = fmt.Sprintf("%s#%s", header.Name+".html", anchor)
}
}
}
+ for _, header := range headers {
+ filename := filepath.Join(outPath, header.Name+".html")
+ file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ panic(err)
+ }
+ defer file.Close()
+ if err := headerTmpl.Execute(file, header); err != nil {
+ return nil, err
+ }
+ }
+
return headerDescriptions, nil
}
@@ -605,6 +651,14 @@ func generateIndex(outPath string, config *Config, headerDescriptions map[string
return nil
}
+func copyFile(outPath string, inFilePath string) error {
+ bytes, err := ioutil.ReadFile(inFilePath)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(filepath.Join(outPath, filepath.Base(inFilePath)), bytes, 0666)
+}
+
func main() {
var (
configFlag *string = flag.String("config", "doc.config", "Location of config file")
@@ -645,4 +699,9 @@ func main() {
fmt.Printf("Failed to generate index: %s\n", err)
os.Exit(1)
}
+
+ if err := copyFile(*outputDir, "doc.css"); err != nil {
+ fmt.Printf("Failed to copy static file: %s\n", err)
+ os.Exit(1)
+ }
}