github.com/ezbuy/gauge@v0.9.4-0.20171013092048-7ac5bd3931cd/build/man/man.go (about)

     1  // Copyright 2015 ThoughtWorks, Inc.
     2  
     3  // This file is part of Gauge.
     4  
     5  // Gauge is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  
    10  // Gauge is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  
    15  // You should have received a copy of the GNU General Public License
    16  // along with Gauge.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package main
    19  
    20  import (
    21  	"log"
    22  
    23  	"strings"
    24  
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	"math"
    30  
    31  	"html/template"
    32  
    33  	"github.com/getgauge/common"
    34  	"github.com/getgauge/gauge/cmd"
    35  	"github.com/russross/blackfriday"
    36  	"github.com/spf13/cobra"
    37  	"github.com/spf13/cobra/doc"
    38  )
    39  
    40  const (
    41  	maxDescLength = 46
    42  	maxLineLength = 77
    43  )
    44  
    45  type link struct {
    46  	Class string
    47  	Link  string
    48  	Name  string
    49  }
    50  
    51  type text struct {
    52  	name    string
    53  	content string
    54  }
    55  
    56  var indexTemplate, _ = template.New("test").Parse(`<ul>
    57  {{ range . }}
    58  	<li><a class="{{ .Class }}" href="{{ .Link }}">{{ .Name }}</a></li>
    59  {{ end }}
    60  </ul>`)
    61  
    62  type writer struct {
    63  	text string
    64  }
    65  
    66  func (w *writer) Write(b []byte) (int, error) {
    67  	w.text += string(b)
    68  	return 0, nil
    69  }
    70  
    71  func main() {
    72  	mdPath := filepath.Join("_man", "md")
    73  	htmlPath := filepath.Join("_man", "html")
    74  	createDir(mdPath)
    75  	createDir(htmlPath)
    76  	if err := genMarkdownManPages(mdPath); err != nil {
    77  		log.Fatal(err.Error())
    78  	}
    79  	texts := indentText(mdPath)
    80  	links := getLinks(texts)
    81  	for _, t := range texts {
    82  		name := strings.TrimSuffix(t.name, filepath.Ext(t.name)) + ".html"
    83  		var newLinks []link
    84  		for _, l := range links {
    85  			if l.Link == name {
    86  				newLinks = append(newLinks, link{Name: l.Name, Link: l.Link, Class: "active"})
    87  			} else {
    88  				newLinks = append(newLinks, l)
    89  			}
    90  		}
    91  		page := strings.Replace(html, "<!--NAV-->", prepareIndex(newLinks), -1)
    92  		output := strings.Replace(page, "<!--CONTENT-->", string(blackfriday.MarkdownCommon([]byte(t.content))), -1)
    93  		ioutil.WriteFile(filepath.Join(htmlPath, name), []byte(output), 0644)
    94  	}
    95  	log.Printf("HTML man pages are available in %s dir\n", htmlPath)
    96  }
    97  func createDir(p string) {
    98  	if err := os.MkdirAll(p, common.NewDirectoryPermissions); err != nil {
    99  		log.Fatal(err.Error())
   100  	}
   101  }
   102  
   103  func genMarkdownManPages(out string) error {
   104  	if err := doc.GenMarkdownTree(setupCmd(), out); err != nil {
   105  		return err
   106  	}
   107  	log.Printf("Added markdown man pages to `%s`\n", out)
   108  	return nil
   109  }
   110  
   111  func setupCmd() *cobra.Command {
   112  	cmd.GaugeCmd.Short = "A light-weight cross-platform test automation tool"
   113  	cmd.GaugeCmd.Long = "Gauge is a light-weight cross-platform test automation tool with the ability to author test cases in the business language."
   114  	return cmd.GaugeCmd
   115  }
   116  
   117  func getLinks(texts []text) (links []link) {
   118  	for _, t := range texts {
   119  		name := strings.TrimSuffix(t.name, filepath.Ext(t.name))
   120  		links = append(links, link{Class: "", Name: strings.Replace(name, "_", " ", -1), Link: name + ".html"})
   121  	}
   122  	return
   123  }
   124  
   125  func prepareIndex(links []link) string {
   126  	w := &writer{}
   127  	err := indexTemplate.Execute(w, links)
   128  	if err != nil {
   129  		log.Fatalf(err.Error())
   130  	}
   131  	return w.text
   132  }
   133  
   134  func indentText(p string) (texts []text) {
   135  	filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
   136  		if strings.HasSuffix(info.Name(), ".md") {
   137  			bytes, err := ioutil.ReadFile(path)
   138  			if err != nil {
   139  				return err
   140  			}
   141  			var lines []string
   142  			for _, l := range strings.Split(string(bytes), string("\n")) {
   143  				tLine := strings.TrimSpace(l)
   144  				if strings.HasPrefix(tLine, "-") && len(tLine) > maxLineLength {
   145  					lines = append(lines, indentFlag(l, tLine)...)
   146  				} else {
   147  					lines = append(lines, strings.Replace(l, ".md", ".html", -1))
   148  				}
   149  			}
   150  			texts = append(texts, text{name: info.Name(), content: strings.Join(lines, "\n")})
   151  		}
   152  		return nil
   153  	})
   154  	return
   155  }
   156  
   157  func indentFlag(line, tLine string) (lines []string) {
   158  	words := strings.Split(tLine, "  ")
   159  	desc := strings.TrimSpace(words[len(words)-1])
   160  	dWords := strings.Split(desc, " ")
   161  	times := math.Ceil(float64(len(desc)) / maxDescLength)
   162  	for i := 0; float64(i) < times; i++ {
   163  		till := 0
   164  		length := 0
   165  		for i, v := range dWords {
   166  			length += len(v)
   167  			if length > maxDescLength {
   168  				till = i - 1
   169  				break
   170  			}
   171  			if i == len(dWords)-1 {
   172  				till = len(dWords)
   173  			}
   174  		}
   175  		if len(dWords) == 0 {
   176  			continue
   177  		}
   178  		prefix := strings.Replace(line, desc, strings.Join(dWords[:till], " "), -1)
   179  		if i != 0 {
   180  			prefix = strings.Repeat(" ", strings.Index(line, desc)) + strings.Join(dWords[:till], " ")
   181  		}
   182  		lines = append(lines, prefix)
   183  		dWords = dWords[till:]
   184  	}
   185  	return
   186  }
   187  
   188  const html = `
   189  <!DOCTYPE html>
   190  <html>
   191  
   192  <head>
   193      <title>Gauge - Manual</title>
   194      <link href="https://getgauge.io/assets/images/favicons/favicon.ico" rel="shortcut icon" type="image/ico" />
   195      <style type='text/css' media='all'>
   196          body#manpage {
   197              margin: 0
   198          }
   199  
   200          .mp {
   201              max-width: 100ex;
   202              padding: 0 9ex 1ex 4ex;
   203              margin-top: 1.5%;
   204          }
   205  
   206          .mp p,
   207          .mp pre,
   208          .mp ul,
   209          .mp ol,
   210          .mp dl {
   211              margin: 0 0 20px 0;
   212          }
   213  
   214          .mp h2 {
   215              margin: 10px 0 0 0
   216          }
   217  
   218          .mp h3 {
   219              margin: 0 0 0 0;
   220          }
   221  
   222          .mp dt {
   223              margin: 0;
   224              clear: left
   225          }
   226  
   227          .mp dt.flush {
   228              float: left;
   229              width: 8ex
   230          }
   231  
   232          .mp dd {
   233              margin: 0 0 0 9ex
   234          }
   235  
   236          .mp h1,
   237          .mp h2,
   238          .mp h3,
   239          .mp h4 {
   240              clear: left
   241          }
   242  
   243          .mp pre {
   244              margin-bottom: 20px;
   245          }
   246  
   247          .mp pre+h2,
   248          .mp pre+h3 {
   249              margin-top: 22px
   250          }
   251  
   252          .mp h2+pre,
   253          .mp h3+pre {
   254              margin-top: 5px
   255          }
   256  
   257          .mp img {
   258              display: block;
   259              margin: auto
   260          }
   261  
   262          .mp h1.man-title {
   263              display: none
   264          }
   265  
   266          .mp,
   267          .mp code,
   268          .mp pre,
   269          .mp tt,
   270          .mp kbd,
   271          .mp samp,
   272          .mp h3,
   273          .mp h4 {
   274              font-family: monospace;
   275              font-size: 14px;
   276              line-height: 1.42857142857143
   277          }
   278  
   279          .mp h2 {
   280              font-size: 16px;
   281              line-height: 1.25
   282          }
   283  
   284          .mp h1 {
   285              font-size: 20px;
   286              line-height: 2
   287          }
   288  
   289          .mp {
   290              text-align: justify;
   291              background: #fff
   292          }
   293  
   294          .mp,
   295          .mp code,
   296          .mp pre,
   297          .mp pre code,
   298          .mp tt,
   299          .mp kbd,
   300          .mp samp {
   301              color: #131211
   302          }
   303  
   304          .mp h1,
   305          .mp h2,
   306          .mp h3,
   307          .mp h4 {
   308              color: #030201
   309          }
   310  
   311          .mp u {
   312              text-decoration: underline
   313          }
   314  
   315          .mp code,
   316          .mp strong,
   317          .mp b {
   318              font-weight: bold;
   319              color: #131211
   320          }
   321  
   322          .mp em,
   323          .mp var {
   324              font-style: italic;
   325              color: #232221;
   326              text-decoration: none
   327          }
   328  
   329          .mp a,
   330          .mp a:link,
   331          .mp a:hover,
   332          .mp a code,
   333          .mp a pre,
   334          .mp a tt,
   335          .mp a kbd,
   336          .mp a samp {
   337              color: #0000ff
   338          }
   339  
   340          .mp b.man-ref {
   341              font-weight: normal;
   342              color: #434241
   343          }
   344  
   345          .mp pre code {
   346              font-weight: normal;
   347              color: #434241
   348          }
   349  
   350          .mp h2+pre,
   351          h3+pre {
   352              padding-left: 0
   353          }
   354  
   355          ol.man-decor,
   356          ol.man-decor li {
   357              margin: 3px 0 10px 0;
   358              padding: 0;
   359              float: left;
   360              width: 33%;
   361              list-style-type: none;
   362              text-transform: uppercase;
   363              color: #999;
   364              letter-spacing: 1px;
   365          }
   366  
   367          ol.man-decor {
   368              width: 100%;
   369          }
   370  
   371          ol.man-decor li.tl {
   372              text-align: left;
   373          }
   374  
   375          ol.man-decor li.tc {
   376              text-align: center;
   377              letter-spacing: 4px;
   378          }
   379  
   380          ol.man-decor li.tr {
   381              text-align: right;
   382              float: right;
   383          }
   384  
   385          .man-navigation ul {
   386              font-size: 16px;
   387          }
   388      </style>
   389      <style type='text/css' media='all'>
   390          .man-navigation {
   391              display: block !important;
   392              position: fixed;
   393              top: 0;
   394              left: 113ex;
   395              height: 100%;
   396              width: 100%;
   397              padding: 48px 0 0 0;
   398              border-left: 1px solid #dbdbdb;
   399              background: #eee;
   400          }
   401  
   402          .man-navigation a,
   403          .man-navigation a:hover,
   404          .man-navigation a:link,
   405          .man-navigation a:visited {
   406              display: block;
   407              margin: 0;
   408              padding: 5px 2px 5px 0px;
   409              color: #999;
   410              text-decoration: none;
   411          }
   412  
   413          .man-navigation a:hover {
   414              color: #111;
   415              text-decoration: underline;
   416          }
   417  
   418          li {
   419              list-style: none;
   420          }
   421  
   422          .mp li {
   423              margin-left: -3ex;
   424          }
   425  
   426          a.active {
   427              font-weight: bolder;
   428              color: #717171 !important;
   429          }
   430      </style>
   431  </head>
   432  
   433  <body id='manpage'>
   434      <div class='mp' id='man'>
   435          <!--CONTENT-->
   436  		<div><b>Complete documentation is available <a href="https://docs.getgauge.io/">here</a>.</b></div>
   437          <nav id="menu" class='man-navigation' style='display:none'>
   438              <!--NAV-->
   439          </nav>
   440      </div>
   441  
   442  </body>
   443  
   444  </html>
   445  `