364 lines
13 KiB
Go
364 lines
13 KiB
Go
package pterm
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/mattn/go-runewidth"
|
|
|
|
"github.com/pterm/pterm/internal"
|
|
)
|
|
|
|
// BoxPrinter is able to render a box around printables.
|
|
type BoxPrinter struct {
|
|
Title string
|
|
TitleTopLeft bool
|
|
TitleTopRight bool
|
|
TitleTopCenter bool
|
|
TitleBottomLeft bool
|
|
TitleBottomRight bool
|
|
TitleBottomCenter bool
|
|
TextStyle *Style
|
|
VerticalString string
|
|
BoxStyle *Style
|
|
HorizontalString string
|
|
TopRightCornerString string
|
|
TopLeftCornerString string
|
|
BottomLeftCornerString string
|
|
BottomRightCornerString string
|
|
TopPadding int
|
|
BottomPadding int
|
|
RightPadding int
|
|
LeftPadding int
|
|
}
|
|
|
|
// DefaultBox is the default BoxPrinter.
|
|
var DefaultBox = BoxPrinter{
|
|
VerticalString: "|",
|
|
TopRightCornerString: "└",
|
|
TopLeftCornerString: "┘",
|
|
BottomLeftCornerString: "┐",
|
|
BottomRightCornerString: "┌",
|
|
HorizontalString: "─",
|
|
BoxStyle: &ThemeDefault.BoxStyle,
|
|
TextStyle: &ThemeDefault.BoxTextStyle,
|
|
RightPadding: 1,
|
|
LeftPadding: 1,
|
|
TopPadding: 0,
|
|
BottomPadding: 0,
|
|
TitleTopLeft: true,
|
|
}
|
|
|
|
// WithTitle returns a new box with a specific Title.
|
|
func (p BoxPrinter) WithTitle(str string) *BoxPrinter {
|
|
p.Title = str
|
|
return &p
|
|
}
|
|
|
|
// WithTitleTopLeft returns a new box with a specific Title alignment.
|
|
func (p BoxPrinter) WithTitleTopLeft(b ...bool) *BoxPrinter {
|
|
b2 := internal.WithBoolean(b)
|
|
p.TitleTopLeft = b2
|
|
p.TitleTopRight = false
|
|
p.TitleTopCenter = false
|
|
p.TitleBottomLeft = false
|
|
p.TitleBottomRight = false
|
|
p.TitleBottomCenter = false
|
|
return &p
|
|
}
|
|
|
|
// WithTitleTopRight returns a new box with a specific Title alignment.
|
|
func (p BoxPrinter) WithTitleTopRight(b ...bool) *BoxPrinter {
|
|
b2 := internal.WithBoolean(b)
|
|
p.TitleTopLeft = false
|
|
p.TitleTopRight = b2
|
|
p.TitleTopCenter = false
|
|
p.TitleBottomLeft = false
|
|
p.TitleBottomRight = false
|
|
p.TitleBottomCenter = false
|
|
return &p
|
|
}
|
|
|
|
// WithTitleTopCenter returns a new box with a specific Title alignment.
|
|
func (p BoxPrinter) WithTitleTopCenter(b ...bool) *BoxPrinter {
|
|
b2 := internal.WithBoolean(b)
|
|
p.TitleTopLeft = false
|
|
p.TitleTopRight = false
|
|
p.TitleTopCenter = b2
|
|
p.TitleBottomLeft = false
|
|
p.TitleBottomRight = false
|
|
p.TitleBottomCenter = false
|
|
return &p
|
|
}
|
|
|
|
// WithTitleBottomLeft returns a new box with a specific Title alignment.
|
|
func (p BoxPrinter) WithTitleBottomLeft(b ...bool) *BoxPrinter {
|
|
b2 := internal.WithBoolean(b)
|
|
p.TitleTopLeft = false
|
|
p.TitleTopRight = false
|
|
p.TitleTopCenter = false
|
|
p.TitleBottomLeft = b2
|
|
p.TitleBottomRight = false
|
|
p.TitleBottomCenter = false
|
|
return &p
|
|
}
|
|
|
|
// WithTitleBottomRight returns a new box with a specific Title alignment.
|
|
func (p BoxPrinter) WithTitleBottomRight(b ...bool) *BoxPrinter {
|
|
b2 := internal.WithBoolean(b)
|
|
p.TitleTopLeft = false
|
|
p.TitleTopRight = false
|
|
p.TitleTopCenter = false
|
|
p.TitleBottomLeft = false
|
|
p.TitleBottomRight = b2
|
|
p.TitleBottomCenter = false
|
|
return &p
|
|
}
|
|
|
|
// WithTitleBottomCenter returns a new box with a specific Title alignment.
|
|
func (p BoxPrinter) WithTitleBottomCenter(b ...bool) *BoxPrinter {
|
|
b2 := internal.WithBoolean(b)
|
|
p.TitleTopLeft = false
|
|
p.TitleTopRight = false
|
|
p.TitleTopCenter = false
|
|
p.TitleBottomLeft = false
|
|
p.TitleBottomRight = false
|
|
p.TitleBottomCenter = b2
|
|
return &p
|
|
}
|
|
|
|
// WithBoxStyle returns a new box with a specific box Style.
|
|
func (p BoxPrinter) WithBoxStyle(style *Style) *BoxPrinter {
|
|
p.BoxStyle = style
|
|
return &p
|
|
}
|
|
|
|
// WithTextStyle returns a new box with a specific text Style.
|
|
func (p BoxPrinter) WithTextStyle(style *Style) *BoxPrinter {
|
|
p.TextStyle = style
|
|
return &p
|
|
}
|
|
|
|
// WithTopRightCornerString returns a new box with a specific TopRightCornerString.
|
|
func (p BoxPrinter) WithTopRightCornerString(str string) *BoxPrinter {
|
|
p.TopRightCornerString = str
|
|
return &p
|
|
}
|
|
|
|
// WithTopLeftCornerString returns a new box with a specific TopLeftCornerString.
|
|
func (p BoxPrinter) WithTopLeftCornerString(str string) *BoxPrinter {
|
|
p.TopLeftCornerString = str
|
|
return &p
|
|
}
|
|
|
|
// WithBottomRightCornerString returns a new box with a specific BottomRightCornerString.
|
|
func (p BoxPrinter) WithBottomRightCornerString(str string) *BoxPrinter {
|
|
p.BottomRightCornerString = str
|
|
return &p
|
|
}
|
|
|
|
// WithBottomLeftCornerString returns a new box with a specific BottomLeftCornerString.
|
|
func (p BoxPrinter) WithBottomLeftCornerString(str string) *BoxPrinter {
|
|
p.BottomLeftCornerString = str
|
|
return &p
|
|
}
|
|
|
|
// WithVerticalString returns a new box with a specific VerticalString.
|
|
func (p BoxPrinter) WithVerticalString(str string) *BoxPrinter {
|
|
p.VerticalString = str
|
|
return &p
|
|
}
|
|
|
|
// WithHorizontalString returns a new box with a specific HorizontalString.
|
|
func (p BoxPrinter) WithHorizontalString(str string) *BoxPrinter {
|
|
p.HorizontalString = str
|
|
return &p
|
|
}
|
|
|
|
// WithTopPadding returns a new box with a specific TopPadding.
|
|
func (p BoxPrinter) WithTopPadding(padding int) *BoxPrinter {
|
|
if padding < 0 {
|
|
padding = 0
|
|
}
|
|
p.TopPadding = padding
|
|
return &p
|
|
}
|
|
|
|
// WithBottomPadding returns a new box with a specific BottomPadding.
|
|
func (p BoxPrinter) WithBottomPadding(padding int) *BoxPrinter {
|
|
if padding < 0 {
|
|
padding = 0
|
|
}
|
|
p.BottomPadding = padding
|
|
return &p
|
|
}
|
|
|
|
// WithRightPadding returns a new box with a specific RightPadding.
|
|
func (p BoxPrinter) WithRightPadding(padding int) *BoxPrinter {
|
|
if padding < 0 {
|
|
padding = 0
|
|
}
|
|
p.RightPadding = padding
|
|
return &p
|
|
}
|
|
|
|
// WithLeftPadding returns a new box with a specific LeftPadding.
|
|
func (p BoxPrinter) WithLeftPadding(padding int) *BoxPrinter {
|
|
if padding < 0 {
|
|
padding = 0
|
|
}
|
|
p.LeftPadding = padding
|
|
return &p
|
|
}
|
|
|
|
// Sprint formats using the default formats for its operands and returns the resulting string.
|
|
// Spaces are added between operands when neither is a string.
|
|
func (p BoxPrinter) Sprint(a ...interface{}) string {
|
|
if p.BoxStyle == nil {
|
|
p.BoxStyle = &ThemeDefault.BoxStyle
|
|
}
|
|
if p.TextStyle == nil {
|
|
p.TextStyle = &ThemeDefault.BoxTextStyle
|
|
}
|
|
maxWidth := internal.GetStringMaxWidth(Sprint(a...))
|
|
|
|
var topLine string
|
|
var bottomLine string
|
|
|
|
if p.Title == "" {
|
|
topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
|
|
bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
|
|
} else {
|
|
p.Title = strings.ReplaceAll(p.Title, "\n", " ")
|
|
if (maxWidth + p.RightPadding + p.LeftPadding - 4) < len(RemoveColorFromString(p.Title)) {
|
|
p.RightPadding = len(RemoveColorFromString(p.Title)) - (maxWidth + p.RightPadding + p.LeftPadding - 5)
|
|
}
|
|
if p.TitleTopLeft {
|
|
topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, true) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
|
|
bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
|
|
} else if p.TitleTopRight {
|
|
topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, false) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
|
|
bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
|
|
} else if p.TitleTopCenter {
|
|
topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + internal.AddTitleToLineCenter(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
|
|
bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
|
|
} else if p.TitleBottomLeft {
|
|
topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
|
|
bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, true) + p.BoxStyle.Sprint(p.TopLeftCornerString)
|
|
} else if p.TitleBottomRight {
|
|
topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
|
|
bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, false) + p.BoxStyle.Sprint(p.TopLeftCornerString)
|
|
} else if p.TitleBottomCenter {
|
|
topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
|
|
maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
|
|
bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + internal.AddTitleToLineCenter(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
|
|
}
|
|
}
|
|
|
|
boxString := strings.Repeat("\n", p.TopPadding) + Sprint(a...) + strings.Repeat("\n", p.BottomPadding)
|
|
|
|
ss := strings.Split(boxString, "\n")
|
|
for i, s2 := range ss {
|
|
if runewidth.StringWidth(RemoveColorFromString(s2)) < maxWidth {
|
|
ss[i] = p.BoxStyle.Sprint(p.VerticalString) + strings.Repeat(" ", p.LeftPadding) + p.TextStyle.Sprint(s2) +
|
|
strings.Repeat(" ", maxWidth-runewidth.StringWidth(RemoveColorFromString(s2))+p.RightPadding) +
|
|
p.BoxStyle.Sprint(p.VerticalString)
|
|
} else {
|
|
ss[i] = p.BoxStyle.Sprint(p.VerticalString) + strings.Repeat(" ", p.LeftPadding) + p.TextStyle.Sprint(s2) +
|
|
strings.Repeat(" ", p.RightPadding) + p.BoxStyle.Sprint(p.VerticalString)
|
|
}
|
|
}
|
|
return topLine + "\n" + strings.Join(ss, "\n") + "\n" + bottomLine
|
|
}
|
|
|
|
// Sprintln formats using the default formats for its operands and returns the resulting string.
|
|
// Spaces are always added between operands and a newline is appended.
|
|
func (p BoxPrinter) Sprintln(a ...interface{}) string {
|
|
return p.Sprint(strings.TrimSuffix(Sprintln(a...), "\n")) + "\n"
|
|
}
|
|
|
|
// Sprintf formats according to a format specifier and returns the resulting string.
|
|
func (p BoxPrinter) Sprintf(format string, a ...interface{}) string {
|
|
return p.Sprint(Sprintf(format, a...))
|
|
}
|
|
|
|
// Sprintfln formats according to a format specifier and returns the resulting string.
|
|
// Spaces are always added between operands and a newline is appended.
|
|
func (p BoxPrinter) Sprintfln(format string, a ...interface{}) string {
|
|
return p.Sprintf(format, a...) + "\n"
|
|
}
|
|
|
|
// Print formats using the default formats for its operands and writes to standard output.
|
|
// Spaces are added between operands when neither is a string.
|
|
// It returns the number of bytes written and any write error encountered.
|
|
func (p BoxPrinter) Print(a ...interface{}) *TextPrinter {
|
|
Print(p.Sprint(a...))
|
|
tp := TextPrinter(p)
|
|
return &tp
|
|
}
|
|
|
|
// Println formats using the default formats for its operands and writes to standard output.
|
|
// Spaces are always added between operands and a newline is appended.
|
|
// It returns the number of bytes written and any write error encountered.
|
|
func (p BoxPrinter) Println(a ...interface{}) *TextPrinter {
|
|
Print(p.Sprintln(a...))
|
|
tp := TextPrinter(p)
|
|
return &tp
|
|
}
|
|
|
|
// Printf formats according to a format specifier and writes to standard output.
|
|
// It returns the number of bytes written and any write error encountered.
|
|
func (p BoxPrinter) Printf(format string, a ...interface{}) *TextPrinter {
|
|
Print(p.Sprintf(format, a...))
|
|
tp := TextPrinter(p)
|
|
return &tp
|
|
}
|
|
|
|
// Printfln formats according to a format specifier and writes to standard output.
|
|
// Spaces are always added between operands and a newline is appended.
|
|
// It returns the number of bytes written and any write error encountered.
|
|
func (p BoxPrinter) Printfln(format string, a ...interface{}) *TextPrinter {
|
|
Print(p.Sprintfln(format, a...))
|
|
tp := TextPrinter(p)
|
|
return &tp
|
|
}
|
|
|
|
// PrintOnError prints every error which is not nil.
|
|
// If every error is nil, nothing will be printed.
|
|
// This can be used for simple error checking.
|
|
func (p BoxPrinter) PrintOnError(a ...interface{}) *TextPrinter {
|
|
for _, arg := range a {
|
|
if err, ok := arg.(error); ok {
|
|
if err != nil {
|
|
p.Println(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
tp := TextPrinter(p)
|
|
return &tp
|
|
}
|
|
|
|
// PrintOnErrorf wraps every error which is not nil and prints it.
|
|
// If every error is nil, nothing will be printed.
|
|
// This can be used for simple error checking.
|
|
func (p BoxPrinter) PrintOnErrorf(format string, a ...interface{}) *TextPrinter {
|
|
for _, arg := range a {
|
|
if err, ok := arg.(error); ok {
|
|
if err != nil {
|
|
p.Println(fmt.Errorf(format, err))
|
|
}
|
|
}
|
|
}
|
|
|
|
tp := TextPrinter(p)
|
|
return &tp
|
|
}
|