forked from smallstep/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
printer.go
126 lines (114 loc) · 3.12 KB
/
printer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package usage
import (
"bytes"
"fmt"
"io"
"regexp"
"strings"
"text/template"
md "github.com/smallstep/cli/pkg/blackfriday"
"github.com/urfave/cli"
)
var sectionRe = regexp.MustCompile(`(?m:^##)`)
//var sectionRe = regexp.MustCompile(`^## [^\n]*$`)
// HelpPrinter overwrites cli.HelpPrinter and prints the formatted help to the terminal.
func HelpPrinter(w io.Writer, templ string, data interface{}) {
b := helpPreprocessor(w, templ, data)
w.Write(Render(b))
}
func htmlHelpPrinter(w io.Writer, templ string, data interface{}) {
b := helpPreprocessor(w, templ, data)
w.Write([]byte(`<html><head><title>Step</title>`))
w.Write([]byte(`<link href="/style.css" rel="stylesheet" type="text/css">`))
w.Write([]byte(`</head><body class="markdown-body">`))
w.Write(md.Run(b))
w.Write([]byte(`<br></body></html>`))
}
func markdownHelpPrinter(w io.Writer, templ string, data interface{}) {
b := helpPreprocessor(w, templ, data)
var frontMatterTemplate = `---
layout: auto-doc
title: {{.HelpName}}
---
`
t, err := template.New("frontmatter").Parse(frontMatterTemplate)
if err != nil {
panic(err)
}
err = t.Execute(w, data)
if err != nil {
panic(err)
}
w.Write(b)
}
func helpPreprocessor(w io.Writer, templ string, data interface{}) []byte {
buf := new(bytes.Buffer)
cli.HelpPrinterCustom(buf, templ, data, nil)
//w.Write(buf.Bytes())
s := string(markdownify(buf.Bytes()))
// Move the OPTIONS section to the right place. urfave puts them at the end
// of the file, we want them to be after POSITIONAL ARGUMENTS, DESCRIPTION,
// USAGE, or NAME (in that order, depending on which sections exist).
optLoc := strings.Index(s, "## OPTIONS")
if optLoc != -1 {
optEnd := findSectionEnd("OPTIONS", s)
if optEnd != -1 {
options := s[optLoc:optEnd]
s = s[:optLoc] + s[optEnd:]
if newLoc := findSectionEnd("POSITIONAL ARGUMENTS", s); newLoc != -1 {
s = s[:newLoc] + options + s[newLoc:]
} else if newLoc := findSectionEnd("DESCRIPTION", s); newLoc != -1 {
s = s[:newLoc] + options + s[newLoc:]
} else if newLoc := findSectionEnd("USAGE", s); newLoc != -1 {
s = s[:newLoc] + options + s[newLoc:]
} else if newLoc := findSectionEnd("NAME", s); newLoc != -1 {
s = s[:newLoc] + options + s[newLoc:]
} else {
// Keep it at the end I guess :/.
s = s + options
}
}
}
return []byte(s)
}
func findSectionEnd(h, s string) int {
start := strings.Index(s, fmt.Sprintf("## %s", h))
if start == -1 {
return start
}
nextSection := sectionRe.FindStringIndex(s[start+2:])
if nextSection == nil {
return len(s)
}
return start + 2 + nextSection[0]
}
// Convert some stuff that we can't easily write in help files because
// backticks and raw strings don't mix:
// - "<foo>" to "`foo`"
// - "'''" to "```"
func markdownify(b []byte) []byte {
for i := 0; i < len(b); i++ {
switch b[i] {
case '<':
if b[i-1] != '\\' {
b[i] = '`'
} else {
copy(b[i-1:], b[i:])
}
case '>':
if b[i-1] != '\\' {
b[i] = '`'
} else {
copy(b[i-1:], b[i:])
}
case '\'':
if len(b) >= i+3 && string(b[i:i+3]) == "'''" {
b[i] = '`'
b[i+1] = '`'
b[i+2] = '`'
i += 2
}
}
}
return b
}