mfer/vendor/github.com/pterm/pterm/box_printer.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
}