2021-03-02 02:29:00 +03:00
|
|
|
package lipgloss
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
2021-03-06 00:34:46 +03:00
|
|
|
"unicode"
|
2021-03-02 02:29:00 +03:00
|
|
|
|
|
|
|
"github.com/muesli/reflow/truncate"
|
|
|
|
"github.com/muesli/reflow/wordwrap"
|
|
|
|
"github.com/muesli/termenv"
|
|
|
|
)
|
|
|
|
|
2021-03-16 20:42:24 +03:00
|
|
|
// Property for a key.
|
2021-03-08 15:59:32 +03:00
|
|
|
type propKey int
|
|
|
|
|
2021-03-16 20:42:24 +03:00
|
|
|
// Available properties.
|
2021-03-08 15:59:32 +03:00
|
|
|
const (
|
|
|
|
boldKey propKey = iota
|
|
|
|
italicKey
|
|
|
|
underlineKey
|
|
|
|
strikethroughKey
|
|
|
|
reverseKey
|
|
|
|
blinkKey
|
|
|
|
faintKey
|
|
|
|
foregroundKey
|
|
|
|
backgroundKey
|
|
|
|
widthKey
|
2021-03-24 21:42:45 +03:00
|
|
|
heightKey
|
2021-03-08 15:59:32 +03:00
|
|
|
alignKey
|
2021-03-24 20:55:43 +03:00
|
|
|
paddingTopKey
|
|
|
|
paddingRightKey
|
|
|
|
paddingBottomKey
|
|
|
|
paddingLeftKey
|
2021-03-08 15:59:32 +03:00
|
|
|
colorWhitespaceKey
|
2021-03-24 20:55:43 +03:00
|
|
|
marginTopKey
|
|
|
|
marginRightKey
|
|
|
|
marginBottomKey
|
|
|
|
marginLeftKey
|
2021-03-26 03:43:40 +03:00
|
|
|
borderKey
|
|
|
|
borderStyleKey
|
|
|
|
borderTopKey
|
|
|
|
borderRightKey
|
|
|
|
borderBottomKey
|
|
|
|
borderLeftKey
|
|
|
|
borderTopFGColorKey
|
|
|
|
borderRightFGColorKey
|
|
|
|
borderBottomFGColorKey
|
|
|
|
borderLeftFGColorKey
|
|
|
|
borderTopBGColorKey
|
|
|
|
borderRightBGColorKey
|
|
|
|
borderBottomBGColorKey
|
|
|
|
borderLeftBGColorKey
|
2021-03-08 15:59:32 +03:00
|
|
|
inlineKey
|
|
|
|
maxWidthKey
|
2021-03-24 21:42:45 +03:00
|
|
|
maxHeightKey
|
2021-03-08 15:59:32 +03:00
|
|
|
drawClearTrailingSpacesKey
|
|
|
|
underlineWhitespaceKey
|
|
|
|
strikethroughWhitespaceKey
|
|
|
|
underlineSpacesKey
|
|
|
|
strikethroughSpacesKey
|
|
|
|
)
|
2021-03-04 20:54:40 +03:00
|
|
|
|
2021-03-16 20:42:24 +03:00
|
|
|
// A set of properties.
|
|
|
|
type rules map[propKey]interface{}
|
|
|
|
|
|
|
|
// NewStyle returns a new, empty Style. While it's syntactic sugar for the
|
2021-03-18 18:23:08 +03:00
|
|
|
// Style{} primitive, it's recommended to use this function for creating styles
|
2021-03-18 03:29:18 +03:00
|
|
|
// incase the underlying implementation changes.
|
2021-03-05 03:23:41 +03:00
|
|
|
func NewStyle() Style {
|
2021-03-16 20:42:24 +03:00
|
|
|
return Style{}
|
2021-03-05 03:23:41 +03:00
|
|
|
}
|
|
|
|
|
2021-03-18 03:29:18 +03:00
|
|
|
// Style contains a set of rules that comprise a style as a whole.
|
2021-03-16 20:42:24 +03:00
|
|
|
type Style struct {
|
|
|
|
rules map[propKey]interface{}
|
|
|
|
value string
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetString sets the underlying string value for this style. To render once
|
|
|
|
// the underlying string is set, use the Style.String. This method is
|
|
|
|
// a convenience for cases when having a stringer implementation is handy, such
|
|
|
|
// as when using fmt.Sprintf. You can also simply define a style and render out
|
|
|
|
// strings directly with Style.Render.
|
|
|
|
func (s Style) SetString(str string) Style {
|
|
|
|
s.value = str
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
// String implements stringer for a Style, returning the rendered result based
|
|
|
|
// on the rules in this style. An underlying string value must be set with
|
|
|
|
// Style.SetString prior to using this method.
|
|
|
|
func (s Style) String() string {
|
|
|
|
return s.Render(s.value)
|
|
|
|
}
|
2021-03-05 03:23:41 +03:00
|
|
|
|
2021-03-24 19:58:56 +03:00
|
|
|
// Copy returns a copy of this style, including any underlying string values.
|
2021-03-08 15:59:32 +03:00
|
|
|
func (s Style) Copy() Style {
|
2021-03-16 20:42:24 +03:00
|
|
|
o := NewStyle()
|
2021-03-16 23:35:28 +03:00
|
|
|
o.rules = make(rules)
|
2021-03-16 20:42:24 +03:00
|
|
|
for k, v := range s.rules {
|
|
|
|
o.rules[k] = v
|
2021-03-05 03:23:41 +03:00
|
|
|
}
|
2021-03-24 19:58:56 +03:00
|
|
|
o.value = s.value
|
2021-03-05 18:04:58 +03:00
|
|
|
return o
|
2021-03-05 03:23:41 +03:00
|
|
|
}
|
|
|
|
|
2021-03-08 15:59:32 +03:00
|
|
|
// Inherit takes values from the style in the argument applies them to this
|
|
|
|
// style, overwriting existing definitions. Only values explicitly set on the
|
|
|
|
// style in argument will be applied.
|
|
|
|
//
|
2021-03-18 20:29:48 +03:00
|
|
|
// Margins, padding, and underlying string values are not inherited.
|
2021-03-08 15:59:32 +03:00
|
|
|
func (o Style) Inherit(i Style) {
|
2021-03-16 20:42:24 +03:00
|
|
|
for k, v := range i.rules {
|
2021-03-08 15:59:32 +03:00
|
|
|
switch k {
|
2021-03-24 20:55:43 +03:00
|
|
|
case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey:
|
2021-03-08 15:59:32 +03:00
|
|
|
// Margins are not inherited
|
|
|
|
continue
|
2021-03-24 20:55:43 +03:00
|
|
|
case paddingTopKey, paddingRightKey, paddingBottomKey, paddingLeftKey:
|
2021-03-08 15:59:32 +03:00
|
|
|
// Padding is not inherited
|
|
|
|
continue
|
|
|
|
}
|
2021-03-06 03:25:58 +03:00
|
|
|
|
2021-03-16 20:42:24 +03:00
|
|
|
if _, exists := o.rules[k]; exists {
|
2021-03-08 15:59:32 +03:00
|
|
|
continue
|
|
|
|
}
|
2021-03-16 20:42:24 +03:00
|
|
|
o.rules[k] = v
|
2021-03-06 03:25:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 15:59:32 +03:00
|
|
|
// Render applies the defined style formatting to a given string.
|
2021-03-05 05:07:54 +03:00
|
|
|
func (s Style) Render(str string) string {
|
2021-03-02 02:29:00 +03:00
|
|
|
var (
|
2021-03-08 15:59:32 +03:00
|
|
|
te termenv.Style
|
|
|
|
teSpace termenv.Style
|
|
|
|
teWhitespace termenv.Style
|
|
|
|
|
|
|
|
bold = s.getAsBool(boldKey, false)
|
|
|
|
italic = s.getAsBool(italicKey, false)
|
|
|
|
underline = s.getAsBool(underlineKey, false)
|
|
|
|
strikethrough = s.getAsBool(strikethroughKey, false)
|
|
|
|
reverse = s.getAsBool(reverseKey, false)
|
|
|
|
blink = s.getAsBool(blinkKey, false)
|
|
|
|
faint = s.getAsBool(faintKey, false)
|
|
|
|
|
|
|
|
fg = s.getAsColor(foregroundKey)
|
|
|
|
bg = s.getAsColor(backgroundKey)
|
|
|
|
|
2021-03-24 21:42:45 +03:00
|
|
|
width = s.getAsInt(widthKey)
|
|
|
|
height = s.getAsInt(heightKey)
|
|
|
|
align = s.getAsAlign(alignKey)
|
2021-03-08 15:59:32 +03:00
|
|
|
|
2021-03-24 20:55:43 +03:00
|
|
|
topPadding = s.getAsInt(paddingTopKey)
|
|
|
|
rightPadding = s.getAsInt(paddingRightKey)
|
|
|
|
bottomPadding = s.getAsInt(paddingBottomKey)
|
|
|
|
leftPadding = s.getAsInt(paddingLeftKey)
|
2021-03-08 15:59:32 +03:00
|
|
|
|
2021-03-24 20:55:43 +03:00
|
|
|
topMargin = s.getAsInt(marginTopKey)
|
|
|
|
rightMargin = s.getAsInt(marginRightKey)
|
|
|
|
bottomMargin = s.getAsInt(marginBottomKey)
|
|
|
|
leftMargin = s.getAsInt(marginLeftKey)
|
2021-03-08 15:59:32 +03:00
|
|
|
|
|
|
|
colorWhitespace = s.getAsBool(colorWhitespaceKey, true)
|
|
|
|
inline = s.getAsBool(inlineKey, false)
|
|
|
|
maxWidth = s.getAsInt(maxWidthKey)
|
2021-03-24 21:42:45 +03:00
|
|
|
maxHeight = s.getAsInt(maxHeightKey)
|
2021-03-08 15:59:32 +03:00
|
|
|
|
|
|
|
drawClearTrailingSpaces = s.getAsBool(drawClearTrailingSpacesKey, true)
|
|
|
|
underlineWhitespace = s.getAsBool(underlineWhitespaceKey, false)
|
|
|
|
strikethroughWhitespace = s.getAsBool(strikethroughWhitespaceKey, false)
|
|
|
|
|
|
|
|
underlineSpaces = underline && s.getAsBool(underlineSpacesKey, true)
|
|
|
|
strikethroughSpaces = strikethrough && s.getAsBool(strikethroughSpacesKey, true)
|
|
|
|
|
|
|
|
// Do we need to style whitespace (padding and space outsode
|
|
|
|
// paragraphs) separately?
|
2021-03-24 20:45:40 +03:00
|
|
|
styleWhitespace = reverse || underlineWhitespace || strikethroughWhitespace
|
2021-03-08 15:59:32 +03:00
|
|
|
|
|
|
|
// Do we need to style spaces separately?
|
|
|
|
useSpaceStyler = underlineSpaces || strikethroughSpaces
|
2021-03-05 20:23:25 +03:00
|
|
|
)
|
2021-03-05 05:07:54 +03:00
|
|
|
|
2021-03-08 15:59:32 +03:00
|
|
|
if bold {
|
|
|
|
te = te.Bold()
|
2021-03-05 20:23:25 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if italic {
|
|
|
|
te = te.Italic()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if underline {
|
|
|
|
te = te.Underline()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if reverse {
|
2021-03-24 20:45:40 +03:00
|
|
|
if reverse {
|
|
|
|
teWhitespace = teWhitespace.Reverse()
|
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
te = te.Reverse()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if blink {
|
|
|
|
te = te.Blink()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if faint {
|
|
|
|
te = te.Faint()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
2021-03-18 21:34:39 +03:00
|
|
|
if fg != noColor {
|
2021-03-16 20:42:24 +03:00
|
|
|
fgc := color(fg.value())
|
2021-03-08 15:59:32 +03:00
|
|
|
te = te.Foreground(fgc)
|
|
|
|
te.Foreground(fgc)
|
|
|
|
if styleWhitespace {
|
|
|
|
teWhitespace = teWhitespace.Foreground(fgc)
|
|
|
|
}
|
|
|
|
if useSpaceStyler {
|
|
|
|
teSpace = teSpace.Foreground(fgc)
|
2021-03-04 20:54:40 +03:00
|
|
|
}
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
2021-03-18 21:34:39 +03:00
|
|
|
if bg != noColor {
|
2021-03-16 20:42:24 +03:00
|
|
|
bgc := color(bg.value())
|
2021-03-08 15:59:32 +03:00
|
|
|
te = te.Background(bgc)
|
|
|
|
if colorWhitespace {
|
|
|
|
teWhitespace = teWhitespace.Background(bgc)
|
|
|
|
}
|
|
|
|
if useSpaceStyler {
|
|
|
|
teSpace = teSpace.Background(bgc)
|
2021-03-04 20:54:40 +03:00
|
|
|
}
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
2021-03-06 00:34:46 +03:00
|
|
|
if underline {
|
2021-03-08 15:59:32 +03:00
|
|
|
te = te.Underline()
|
2021-03-06 00:34:46 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if strikethrough {
|
|
|
|
te = te.CrossOut()
|
2021-03-06 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2021-03-05 20:23:25 +03:00
|
|
|
if underlineWhitespace {
|
2021-03-08 15:59:32 +03:00
|
|
|
teWhitespace = teWhitespace.Underline()
|
2021-03-05 20:23:25 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if strikethroughWhitespace {
|
|
|
|
teWhitespace = teWhitespace.CrossOut()
|
2021-03-05 03:23:41 +03:00
|
|
|
}
|
2021-03-06 00:34:46 +03:00
|
|
|
if underlineSpaces {
|
2021-03-08 15:59:32 +03:00
|
|
|
teSpace = teSpace.Underline()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-06 00:34:46 +03:00
|
|
|
if strikethroughSpaces {
|
2021-03-08 15:59:32 +03:00
|
|
|
teSpace = teSpace.CrossOut()
|
2021-03-05 03:23:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Strip newlines in single line mode
|
2021-03-08 15:59:32 +03:00
|
|
|
if inline {
|
2021-03-02 02:29:00 +03:00
|
|
|
str = strings.Replace(str, "\n", "", -1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Word wrap
|
2021-03-08 15:59:32 +03:00
|
|
|
if !inline && width > 0 {
|
|
|
|
str = wordwrap.String(str, width-leftPadding-rightPadding)
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
2021-03-05 20:23:25 +03:00
|
|
|
// Render core text
|
|
|
|
{
|
|
|
|
var b strings.Builder
|
2021-03-06 00:34:46 +03:00
|
|
|
|
2021-03-05 20:23:25 +03:00
|
|
|
l := strings.Split(str, "\n")
|
|
|
|
for i := range l {
|
2021-03-06 00:34:46 +03:00
|
|
|
if useSpaceStyler {
|
|
|
|
// Look for spaces and apply a different styler
|
2021-03-18 19:39:09 +03:00
|
|
|
for _, r := range l[i] {
|
2021-03-06 00:34:46 +03:00
|
|
|
if unicode.IsSpace(r) {
|
2021-03-08 15:59:32 +03:00
|
|
|
b.WriteString(teSpace.Styled(string(r)))
|
2021-03-06 00:34:46 +03:00
|
|
|
continue
|
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
b.WriteString(te.Styled(string(r)))
|
2021-03-06 00:34:46 +03:00
|
|
|
}
|
|
|
|
} else {
|
2021-03-08 15:59:32 +03:00
|
|
|
b.WriteString(te.Styled(l[i]))
|
2021-03-06 00:34:46 +03:00
|
|
|
}
|
2021-03-05 20:23:25 +03:00
|
|
|
if i != len(l)-1 {
|
|
|
|
b.WriteRune('\n')
|
|
|
|
}
|
2021-03-04 20:54:40 +03:00
|
|
|
}
|
2021-03-05 20:23:25 +03:00
|
|
|
|
|
|
|
str = b.String()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Left/right padding
|
2021-03-08 15:59:32 +03:00
|
|
|
if leftPadding > 0 {
|
2021-03-05 20:23:25 +03:00
|
|
|
var st *termenv.Style
|
|
|
|
if colorWhitespace || styleWhitespace {
|
2021-03-08 15:59:32 +03:00
|
|
|
st = &teWhitespace
|
2021-03-04 20:54:40 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
str = padLeft(str, leftPadding, st)
|
2021-03-05 20:23:25 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if (colorWhitespace || drawClearTrailingSpaces) && rightPadding > 0 {
|
2021-03-05 20:23:25 +03:00
|
|
|
var st *termenv.Style
|
|
|
|
if colorWhitespace || styleWhitespace {
|
2021-03-08 15:59:32 +03:00
|
|
|
st = &teWhitespace
|
2021-03-04 20:54:40 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
str = padRight(str, rightPadding, st)
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Top/bottom padding
|
2021-03-08 15:59:32 +03:00
|
|
|
if topPadding > 0 && !inline {
|
|
|
|
str = strings.Repeat("\n", topPadding) + str
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if bottomPadding > 0 && !inline {
|
|
|
|
str += strings.Repeat("\n", bottomPadding)
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
2021-03-24 21:42:45 +03:00
|
|
|
// Height
|
|
|
|
if height > 0 {
|
|
|
|
h := strings.Count(str, "\n")
|
|
|
|
if height > h {
|
|
|
|
str += strings.Repeat("\n", height-h)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-02 02:29:00 +03:00
|
|
|
// Set alignment. This will also pad short lines with spaces so that all
|
2021-03-04 20:54:40 +03:00
|
|
|
// lines are the same length, so we run it under a few different conditions
|
|
|
|
// beyond alignment.
|
|
|
|
{
|
2021-03-05 20:23:25 +03:00
|
|
|
numLines := strings.Count(str, "\n")
|
|
|
|
|
2021-03-24 23:42:34 +03:00
|
|
|
if !(numLines == 0 && width == 0) && (drawClearTrailingSpaces || colorWhitespace) {
|
2021-03-05 20:23:25 +03:00
|
|
|
var st *termenv.Style
|
|
|
|
if colorWhitespace || styleWhitespace {
|
2021-03-08 15:59:32 +03:00
|
|
|
st = &teWhitespace
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-24 23:34:03 +03:00
|
|
|
str = alignText(str, align, width, st)
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 15:59:32 +03:00
|
|
|
// Add left and right margin
|
|
|
|
str = padLeft(str, leftMargin, nil)
|
|
|
|
str = padRight(str, rightMargin, nil)
|
2021-03-02 02:29:00 +03:00
|
|
|
|
|
|
|
// Top/bottom margin
|
2021-03-08 15:59:32 +03:00
|
|
|
if !inline {
|
2021-03-02 02:29:00 +03:00
|
|
|
var maybeSpaces string
|
|
|
|
|
2021-03-05 03:23:41 +03:00
|
|
|
if drawClearTrailingSpaces {
|
2021-03-02 02:29:00 +03:00
|
|
|
_, width := getLines(str)
|
|
|
|
maybeSpaces = strings.Repeat(" ", width)
|
|
|
|
}
|
|
|
|
|
2021-03-08 15:59:32 +03:00
|
|
|
if topMargin > 0 {
|
|
|
|
str = strings.Repeat(maybeSpaces+"\n", topMargin) + str
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-08 15:59:32 +03:00
|
|
|
if bottomMargin > 0 {
|
|
|
|
str += strings.Repeat("\n"+maybeSpaces, bottomMargin)
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 04:56:41 +03:00
|
|
|
str = s.applyBorder(str)
|
|
|
|
|
2021-03-10 18:21:21 +03:00
|
|
|
// Truncate according to MaxWidth
|
2021-03-08 15:59:32 +03:00
|
|
|
if maxWidth > 0 {
|
2021-03-02 02:29:00 +03:00
|
|
|
lines := strings.Split(str, "\n")
|
|
|
|
|
|
|
|
for i := range lines {
|
2021-03-08 15:59:32 +03:00
|
|
|
lines[i] = truncate.String(lines[i], uint(maxWidth))
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
str = strings.Join(lines, "\n")
|
|
|
|
}
|
|
|
|
|
2021-03-24 21:42:45 +03:00
|
|
|
// Truncate according to MaxHeight
|
|
|
|
if maxHeight > 0 {
|
|
|
|
lines := strings.Split(str, "\n")
|
|
|
|
str = strings.Join(lines[:min(maxHeight, len(lines))], "\n")
|
|
|
|
}
|
|
|
|
|
2021-03-02 02:29:00 +03:00
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply left padding.
|
2021-03-05 20:23:25 +03:00
|
|
|
func padLeft(str string, n int, style *termenv.Style) string {
|
|
|
|
if n == 0 {
|
2021-03-02 02:29:00 +03:00
|
|
|
return str
|
|
|
|
}
|
2021-03-05 20:23:25 +03:00
|
|
|
|
|
|
|
sp := strings.Repeat(" ", n)
|
|
|
|
if style != nil {
|
|
|
|
sp = style.Styled(sp)
|
|
|
|
}
|
|
|
|
|
|
|
|
b := strings.Builder{}
|
|
|
|
l := strings.Split(str, "\n")
|
|
|
|
|
|
|
|
for i := range l {
|
|
|
|
b.WriteString(sp)
|
|
|
|
b.WriteString(l[i])
|
|
|
|
if i != len(l)-1 {
|
|
|
|
b.WriteRune('\n')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.String()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply right right padding.
|
2021-03-05 20:23:25 +03:00
|
|
|
func padRight(str string, n int, style *termenv.Style) string {
|
2021-03-02 02:29:00 +03:00
|
|
|
if n == 0 || str == "" {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
2021-03-05 20:23:25 +03:00
|
|
|
sp := strings.Repeat(" ", n)
|
|
|
|
if style != nil {
|
|
|
|
sp = style.Styled(sp)
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
2021-03-05 20:23:25 +03:00
|
|
|
b := strings.Builder{}
|
|
|
|
l := strings.Split(str, "\n")
|
|
|
|
|
|
|
|
for i := range l {
|
|
|
|
b.WriteString(l[i])
|
|
|
|
b.WriteString(sp)
|
|
|
|
if i != len(l)-1 {
|
|
|
|
b.WriteRune('\n')
|
|
|
|
}
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
2021-03-05 20:23:25 +03:00
|
|
|
return b.String()
|
2021-03-02 02:29:00 +03:00
|
|
|
}
|
2021-03-24 21:42:45 +03:00
|
|
|
|
2021-03-24 23:34:03 +03:00
|
|
|
func max(a, b int) int {
|
|
|
|
if a > b {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2021-03-24 21:42:45 +03:00
|
|
|
func min(a, b int) int {
|
|
|
|
if a < b {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|