mirror of
https://github.com/charmbracelet/lipgloss.git
synced 2024-08-17 18:00:46 +03:00
Generally run-of-the-mill directives to disable magic number and exhaustive switches. Reasonings should be self explanatory while reading the code and comments, where applicable.
155 lines
4.3 KiB
Go
155 lines
4.3 KiB
Go
package lipgloss
|
|
|
|
import (
|
|
"math"
|
|
"strings"
|
|
|
|
"github.com/muesli/reflow/ansi"
|
|
)
|
|
|
|
// Position represents a position along a horizontal or vertical axis. It's in
|
|
// situations where an axis is involved, like alignment, joining, placement and
|
|
// so on.
|
|
//
|
|
// A value of 0 represents the start (the left or top) and 1 represents the end
|
|
// (the right or bottom). 0.5 represents the center.
|
|
//
|
|
// There are constants Top, Bottom, Center, Left and Right in this package that
|
|
// can be used to aid readability.
|
|
type Position float64
|
|
|
|
func (p Position) value() float64 {
|
|
return math.Min(1, math.Max(0, float64(p)))
|
|
}
|
|
|
|
// Position aliases.
|
|
const (
|
|
Top Position = 0.0
|
|
Bottom Position = 1.0
|
|
Center Position = 0.5
|
|
Left Position = 0.0
|
|
Right Position = 1.0
|
|
)
|
|
|
|
// Place places a string or text block vertically in an unstyled box of a given
|
|
// width or height.
|
|
func Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
|
|
return renderer.Place(width, height, hPos, vPos, str, opts...)
|
|
}
|
|
|
|
// Place places a string or text block vertically in an unstyled box of a given
|
|
// width or height.
|
|
func (r *Renderer) Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
|
|
return r.PlaceVertical(height, vPos, r.PlaceHorizontal(width, hPos, str, opts...), opts...)
|
|
}
|
|
|
|
// PlaceHorizontal places a string or text block horizontally in an unstyled
|
|
// block of a given width. If the given width is shorter than the max width of
|
|
// the string (measured by its longest line) this will be a noop.
|
|
func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
|
|
return renderer.PlaceHorizontal(width, pos, str, opts...)
|
|
}
|
|
|
|
// PlaceHorizontal places a string or text block horizontally in an unstyled
|
|
// block of a given width. If the given width is shorter than the max width of
|
|
// the string (measured by its longest line) this will be a noöp.
|
|
func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
|
|
lines, contentWidth := getLines(str)
|
|
gap := width - contentWidth
|
|
|
|
if gap <= 0 {
|
|
return str
|
|
}
|
|
|
|
ws := newWhitespace(r, opts...)
|
|
|
|
var b strings.Builder
|
|
for i, l := range lines {
|
|
// Is this line shorter than the longest line?
|
|
short := max(0, contentWidth-ansi.PrintableRuneWidth(l))
|
|
|
|
switch pos { //nolint:exhaustive
|
|
case Left:
|
|
b.WriteString(l)
|
|
b.WriteString(ws.render(gap + short))
|
|
|
|
case Right:
|
|
b.WriteString(ws.render(gap + short))
|
|
b.WriteString(l)
|
|
|
|
default: // somewhere in the middle
|
|
totalGap := gap + short
|
|
|
|
split := int(math.Round(float64(totalGap) * pos.value()))
|
|
left := totalGap - split
|
|
right := totalGap - left
|
|
|
|
b.WriteString(ws.render(left))
|
|
b.WriteString(l)
|
|
b.WriteString(ws.render(right))
|
|
}
|
|
|
|
if i < len(lines)-1 {
|
|
b.WriteRune('\n')
|
|
}
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// PlaceVertical places a string or text block vertically in an unstyled block
|
|
// of a given height. If the given height is shorter than the height of the
|
|
// string (measured by its newlines) then this will be a noop.
|
|
func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
|
|
return renderer.PlaceVertical(height, pos, str, opts...)
|
|
}
|
|
|
|
// PlaceVertical places a string or text block vertically in an unstyled block
|
|
// of a given height. If the given height is shorter than the height of the
|
|
// string (measured by its newlines) then this will be a noöp.
|
|
func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
|
|
contentHeight := strings.Count(str, "\n") + 1
|
|
gap := height - contentHeight
|
|
|
|
if gap <= 0 {
|
|
return str
|
|
}
|
|
|
|
ws := newWhitespace(r, opts...)
|
|
|
|
_, width := getLines(str)
|
|
emptyLine := ws.render(width)
|
|
b := strings.Builder{}
|
|
|
|
switch pos { //nolint:exhaustive
|
|
case Top:
|
|
b.WriteString(str)
|
|
b.WriteRune('\n')
|
|
for i := 0; i < gap; i++ {
|
|
b.WriteString(emptyLine)
|
|
if i < gap-1 {
|
|
b.WriteRune('\n')
|
|
}
|
|
}
|
|
|
|
case Bottom:
|
|
b.WriteString(strings.Repeat(emptyLine+"\n", gap))
|
|
b.WriteString(str)
|
|
|
|
default: // Somewhere in the middle
|
|
split := int(math.Round(float64(gap) * pos.value()))
|
|
top := gap - split
|
|
bottom := gap - top
|
|
|
|
b.WriteString(strings.Repeat(emptyLine+"\n", top))
|
|
b.WriteString(str)
|
|
|
|
for i := 0; i < bottom; i++ {
|
|
b.WriteRune('\n')
|
|
b.WriteString(emptyLine)
|
|
}
|
|
}
|
|
|
|
return b.String()
|
|
}
|