ccf651de49003ed8651318a7bb28bfb9f2459372
[scm/test.git] / docs / man / mangen.go
1 package main
2
3 import (
4         "bufio"
5         "fmt"
6         "io/ioutil"
7         "os"
8         "path/filepath"
9         "regexp"
10         "strings"
11 )
12
13 func readManDir() (string, []os.FileInfo) {
14         rootDirs := []string{
15                 "..",
16                 "/tmp/docker_run/git-lfs",
17         }
18
19         var err error
20         for _, rootDir := range rootDirs {
21                 fs, err := ioutil.ReadDir(filepath.Join(rootDir, "docs", "man"))
22                 if err == nil {
23                         return rootDir, fs
24                 }
25         }
26
27         fmt.Fprintf(os.Stderr, "Failed to open man dir: %v\n", err)
28         os.Exit(2)
29         return "", nil
30 }
31
32 // Reads all .ronn files & and converts them to string literals
33 // triggered by "go generate" comment
34 // Literals are inserted into a map using an init function, this means
35 // that there are no compilation errors if 'go generate' hasn't been run, just
36 // blank man files.
37 func main() {
38         fmt.Fprintf(os.Stderr, "Converting man pages into code...\n")
39         rootDir, fs := readManDir()
40         manDir := filepath.Join(rootDir, "docs", "man")
41         out, err := os.Create(filepath.Join(rootDir, "commands", "mancontent_gen.go"))
42         if err != nil {
43                 fmt.Fprintf(os.Stderr, "Failed to create go file: %v\n", err)
44                 os.Exit(2)
45         }
46         out.WriteString("package commands\n\nfunc init() {\n")
47         out.WriteString("// THIS FILE IS GENERATED, DO NOT EDIT\n")
48         out.WriteString("// Use 'go generate ./commands' to update\n")
49         fileregex := regexp.MustCompile(`git-lfs(?:-([A-Za-z\-]+))?.\d.ronn`)
50         headerregex := regexp.MustCompile(`^###?\s+([A-Za-z0-9 ]+)`)
51         // only pick up caps in links to avoid matching optional args
52         linkregex := regexp.MustCompile(`\[([A-Z\- ]+)\]`)
53         // man links
54         manlinkregex := regexp.MustCompile(`(git)(?:-(lfs))?-([a-z\-]+)\(\d\)`)
55         count := 0
56         for _, f := range fs {
57                 if match := fileregex.FindStringSubmatch(f.Name()); match != nil {
58                         fmt.Fprintf(os.Stderr, "%v\n", f.Name())
59                         cmd := match[1]
60                         if len(cmd) == 0 {
61                                 // This is git-lfs.1.ronn
62                                 cmd = "git-lfs"
63                         }
64                         out.WriteString("ManPages[\"" + cmd + "\"] = `")
65                         contentf, err := os.Open(filepath.Join(manDir, f.Name()))
66                         if err != nil {
67                                 fmt.Fprintf(os.Stderr, "Failed to open %v: %v\n", f.Name(), err)
68                                 os.Exit(2)
69                         }
70                         // Process the ronn to make it nicer as help text
71                         scanner := bufio.NewScanner(contentf)
72                         firstHeaderDone := false
73                         skipNextLineIfBlank := false
74                         lastLineWasBullet := false
75                 scanloop:
76                         for scanner.Scan() {
77                                 line := scanner.Text()
78                                 trimmedline := strings.TrimSpace(line)
79                                 if skipNextLineIfBlank && len(trimmedline) == 0 {
80                                         skipNextLineIfBlank = false
81                                         lastLineWasBullet = false
82                                         continue
83                                 }
84
85                                 // Special case headers
86                                 if hmatch := headerregex.FindStringSubmatch(line); hmatch != nil {
87                                         header := strings.ToLower(hmatch[1])
88                                         switch header {
89                                         case "synopsis":
90                                                 // Ignore this, just go direct to command
91
92                                         case "description":
93                                                 // Just skip the header & newline
94                                                 skipNextLineIfBlank = true
95                                         case "options":
96                                                 out.WriteString("Options:" + "\n")
97                                         case "see also":
98                                                 // don't include any content after this
99                                                 break scanloop
100                                         default:
101                                                 out.WriteString(strings.ToUpper(header[:1]) + header[1:] + "\n")
102                                                 out.WriteString(strings.Repeat("-", len(header)) + "\n")
103                                         }
104                                         firstHeaderDone = true
105                                         lastLineWasBullet = false
106                                         continue
107                                 }
108
109                                 if lmatches := linkregex.FindAllStringSubmatch(line, -1); lmatches != nil {
110                                         for _, lmatch := range lmatches {
111                                                 linktext := strings.ToLower(lmatch[1])
112                                                 line = strings.Replace(line, lmatch[0], `"`+strings.ToUpper(linktext[:1])+linktext[1:]+`"`, 1)
113                                         }
114                                 }
115                                 if manmatches := manlinkregex.FindAllStringSubmatch(line, -1); manmatches != nil {
116                                         for _, manmatch := range manmatches {
117                                                 line = strings.Replace(line, manmatch[0], strings.Join(manmatch[1:], " "), 1)
118                                         }
119                                 }
120
121                                 // Skip content until after first header
122                                 if !firstHeaderDone {
123                                         continue
124                                 }
125                                 // OK, content here
126
127                                 // remove characters that markdown would render invisible in a text env.
128                                 for _, invis := range []string{"`", "<br>"} {
129                                         line = strings.Replace(line, invis, "", -1)
130                                 }
131
132                                 // indent bullets
133                                 if strings.HasPrefix(line, "*") {
134                                         lastLineWasBullet = true
135                                 } else if lastLineWasBullet && !strings.HasPrefix(line, " ") {
136                                         // indent paragraphs under bullets if not already done
137                                         line = "  " + line
138                                 }
139
140                                 out.WriteString(line + "\n")
141                         }
142                         out.WriteString("`\n")
143                         contentf.Close()
144                         count++
145                 }
146         }
147         out.WriteString("}\n")
148         fmt.Fprintf(os.Stderr, "Successfully processed %d man pages.\n", count)
149
150 }