github.com/x-oss-byte/git-lfs@v2.5.2+incompatible/docs/man/mangen.go (about)

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