forked from cli/cli
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathutils.go
More file actions
128 lines (107 loc) · 2.76 KB
/
utils.go
File metadata and controls
128 lines (107 loc) · 2.76 KB
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
127
128
package utils
import (
"fmt"
"io"
"net/url"
"strings"
"time"
"github.com/briandowns/spinner"
"github.com/charmbracelet/glamour"
"github.com/cli/cli/internal/run"
"github.com/cli/cli/pkg/browser"
)
// OpenInBrowser opens the url in a web browser based on OS and $BROWSER environment variable
func OpenInBrowser(url string) error {
browseCmd, err := browser.Command(url)
if err != nil {
return err
}
return run.PrepareCmd(browseCmd).Run()
}
func RenderMarkdown(text string) (string, error) {
// Glamour rendering preserves carriage return characters in code blocks, but
// we need to ensure that no such characters are present in the output.
text = strings.ReplaceAll(text, "\r\n", "\n")
renderStyle := glamour.WithStandardStyle("notty")
// TODO: make color an input parameter
if isColorEnabled() {
renderStyle = glamour.WithEnvironmentConfig()
}
tr, err := glamour.NewTermRenderer(
renderStyle,
// glamour.WithBaseURL(""), // TODO: make configurable
// glamour.WithWordWrap(80), // TODO: make configurable
)
if err != nil {
return "", err
}
return tr.Render(text)
}
func Pluralize(num int, thing string) string {
if num == 1 {
return fmt.Sprintf("%d %s", num, thing)
}
return fmt.Sprintf("%d %ss", num, thing)
}
func fmtDuration(amount int, unit string) string {
return fmt.Sprintf("about %s ago", Pluralize(amount, unit))
}
func FuzzyAgo(ago time.Duration) string {
if ago < time.Minute {
return "less than a minute ago"
}
if ago < time.Hour {
return fmtDuration(int(ago.Minutes()), "minute")
}
if ago < 24*time.Hour {
return fmtDuration(int(ago.Hours()), "hour")
}
if ago < 30*24*time.Hour {
return fmtDuration(int(ago.Hours())/24, "day")
}
if ago < 365*24*time.Hour {
return fmtDuration(int(ago.Hours())/24/30, "month")
}
return fmtDuration(int(ago.Hours()/24/365), "year")
}
func Humanize(s string) string {
// Replaces - and _ with spaces.
replace := "_-"
h := func(r rune) rune {
if strings.ContainsRune(replace, r) {
return ' '
}
return r
}
return strings.Map(h, s)
}
// We do this so we can stub out the spinner in tests -- it made things really flakey. this is not
// an elegant solution.
var StartSpinner = func(s *spinner.Spinner) {
s.Start()
}
var StopSpinner = func(s *spinner.Spinner) {
s.Stop()
}
func Spinner(w io.Writer) *spinner.Spinner {
return spinner.New(spinner.CharSets[11], 400*time.Millisecond, spinner.WithWriter(w))
}
func IsURL(s string) bool {
return strings.HasPrefix(s, "http:/") || strings.HasPrefix(s, "https:/")
}
func DisplayURL(urlStr string) string {
u, err := url.Parse(urlStr)
if err != nil {
return urlStr
}
return u.Hostname() + u.Path
}
func GreenCheck() string {
return Green("✓")
}
func YellowDash() string {
return Yellow("-")
}
func RedX() string {
return Red("X")
}