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 `