github.com/wolfi-dev/wolfictl@v0.16.11/pkg/cli/internal/wrapped/wrapped.go (about) 1 package wrapped 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "github.com/charmbracelet/lipgloss" 9 "github.com/muesli/reflow/wordwrap" 10 "golang.org/x/term" 11 ) 12 13 var ( 14 // LineLength is the maximum length allowed of a printed line of text. This is 15 // the primary control for this package. It defaults to the greater of the 16 // current terminal width (detected at init time) and MaxLineLength. 17 LineLength = initialLineLength 18 19 // MaxLineLength sets the upper bound for how long a line can be. This is a 20 // secondary control that adjusts how LineLength is calculated. It defaults to 21 // 120 (for readability). 22 MaxLineLength = 120 23 ) 24 25 // Println wraps the given message using LineLength and prints it to stdout with 26 // a trailing newline. 27 func Println(msg string) { 28 fmt.Println(wrapToLineLength(msg)) 29 } 30 31 // Sprint wraps the given message using LineLength and returns it as a string. 32 func Sprint(msg string) string { 33 return wrapToLineLength(msg) 34 } 35 36 // Fatal wraps the given message using LineLength and prints it to stderr with a 37 // trailing newline, then exits with a non-zero status code. 38 func Fatal(msg string) { 39 fmt.Fprintln(os.Stderr, wrapToLineLength(msg)) 40 os.Exit(1) 41 } 42 43 // Repeat repeats the given message string until the LineLength is reached, such 44 // that the returned string is exactly LineLength long. If the given string is 45 // already longer than LineLength, it is truncated to LineLength. 46 func Repeat(s string) string { 47 if length := lipgloss.Width(s); length > LineLength { 48 // Repeating is not necessary. 49 return truncate(s) 50 } 51 52 sb := new(strings.Builder) 53 54 // We're using lipgloss to measure the rendered length of the string (we don't 55 // want to measure the raw length of the string — some characters are rendered 56 // wider than others, while others are not rendered at all). 57 currentLength := lipgloss.Width(sb.String()) 58 for currentLength < LineLength { 59 sb.WriteString(s) 60 61 currentLength = lipgloss.Width(sb.String()) 62 } 63 64 // Truncate the string to the line length. 65 line := truncate(sb.String()) 66 67 return line 68 } 69 70 const ( 71 // This is only used if we can't detect the terminal width. 72 fallbackLineLength = 80 73 ) 74 75 var ( 76 initialLineLength = func() int { 77 // Defer to smaller-width terminal screens, but don't go bigger than 78 // MaxLineLength. 79 if terminalWidth < MaxLineLength { 80 return terminalWidth 81 } 82 83 return MaxLineLength 84 }() 85 86 terminalWidth = getTerminalWidth() 87 ) 88 89 func getTerminalWidth() int { 90 w, _, err := term.GetSize(int(os.Stdout.Fd())) 91 if err != nil { 92 return fallbackLineLength 93 } 94 return w 95 } 96 97 func wrapToLineLength(msg string) string { 98 return wordwrap.String(msg, LineLength) 99 } 100 101 var styleTruncated = lipgloss.NewStyle().Width(LineLength) 102 103 func truncate(msg string) string { 104 if length := lipgloss.Width(msg); length > LineLength { 105 return styleTruncated.Render(msg) 106 } 107 108 return msg 109 }