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
|
// Package text is a set of utility functions for text processing and outputting to the terminal.
package text
import (
"fmt"
"regexp"
"strings"
"time"
"github.com/muesli/reflow/ansi"
"github.com/muesli/reflow/truncate"
)
const (
ellipsis = "..."
minWidthForEllipsis = len(ellipsis) + 2
)
var indentRE = regexp.MustCompile(`(?m)^`)
// Indent returns a copy of the string s with indent prefixed to it, will apply indent
// to each line of the string.
func Indent(s, indent string) string {
if len(strings.TrimSpace(s)) == 0 {
return s
}
return indentRE.ReplaceAllLiteralString(s, indent)
}
// DisplayWidth calculates what the rendered width of string s will be.
func DisplayWidth(s string) int {
return ansi.PrintableRuneWidth(s)
}
// Truncate returns a copy of the string s that has been shortened to fit the maximum display width.
func Truncate(maxWidth int, s string) string {
w := DisplayWidth(s)
if w <= maxWidth {
return s
}
tail := ""
if maxWidth >= minWidthForEllipsis {
tail = ellipsis
}
r := truncate.StringWithTail(s, uint(maxWidth), tail)
if DisplayWidth(r) < maxWidth {
r += " "
}
return r
}
// Pluralize returns a concatenated string with num and the plural form of thing if necessary.
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))
}
// RelativeTimeAgo returns a human readable string of the time duration between a and b that is estimated
// to the nearest unit of time.
func RelativeTimeAgo(a, b time.Time) string {
ago := a.Sub(b)
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")
}
|