diff --git a/get.go b/get.go index 87906227..1ccac552 100644 --- a/get.go +++ b/get.go @@ -165,6 +165,11 @@ func (s Style) GetMarginLeft() int { return s.getAsInt(marginLeftKey) } +// GetMarginBackground returns the style's margin background color. +func (s Style) GetMarginBackground() TerminalColor { + return s.getAsColor(marginBackgroundKey) +} + // GetHorizontalMargins returns the style's left and right margins. Unset // values are measured as 0. func (s Style) GetHorizontalMargins() int { diff --git a/go.mod b/go.mod index 9e46b24a..54365343 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/charmbracelet/lipgloss go 1.15 require ( + github.com/knz/lipgloss-convert v0.1.1 github.com/mattn/go-runewidth v0.0.13 github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 diff --git a/go.sum b/go.sum index ba0a481e..ca30f0c2 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,14 @@ +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/knz/lipgloss-convert v0.1.1 h1:11Wfcdif9jyyI5H3ohANyqkmIpVPjiBoGZdCzPWz9fA= +github.com/knz/lipgloss-convert v0.1.1/go.mod h1:S14GmtoiW/VAHqB7xEzuZOt0/G6GQ2dfjJN0fHpm30Q= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= @@ -9,8 +20,14 @@ github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9Fn github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 h1:STjmj0uFfRryL9fzRA/OupNppeAID6QJYPMavTL7jtY= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/style_test.go b/style_test.go index c1ee043d..e625c798 100644 --- a/style_test.go +++ b/style_test.go @@ -1,15 +1,115 @@ -package lipgloss +package lipgloss_test import ( + "strings" "testing" + + "github.com/charmbracelet/lipgloss" + lipglossc "github.com/knz/lipgloss-convert" ) func BenchmarkStyleRender(b *testing.B) { - s := NewStyle(). + s := lipgloss.NewStyle(). Bold(true). - Foreground(Color("#ffffff")) + Foreground(lipgloss.Color("#ffffff")) for i := 0; i < b.N; i++ { s.Render("Hello world") } } + +type S = lipgloss.Style + +func TestStyle(t *testing.T) { + td := []struct { + changeStyle func(S) S + repr string + }{ + {func(s S) S { return s.Align(0.5) }, `align: 0.5;`}, + {func(s S) S { return s.Background(lipgloss.Color("#0f0")) }, `background: #0f0;`}, + {func(s S) S { return s.Blink(true) }, `blink: true;`}, + {func(s S) S { return s.Bold(true) }, `bold: true;`}, + {func(s S) S { return s.BorderBottom(true) }, `border-bottom: true;`}, + {func(s S) S { return s.BorderBottomBackground(lipgloss.Color("#0f0")) }, `border-bottom-background: #0f0;`}, + {func(s S) S { return s.BorderBottomForeground(lipgloss.Color("#0f0")) }, `border-bottom-foreground: #0f0;`}, + {func(s S) S { return s.BorderLeft(true) }, `border-left: true;`}, + {func(s S) S { return s.BorderLeftBackground(lipgloss.Color("#0f0")) }, `border-left-background: #0f0;`}, + {func(s S) S { return s.BorderLeftForeground(lipgloss.Color("#0f0")) }, `border-left-foreground: #0f0;`}, + {func(s S) S { return s.BorderRight(true) }, `border-right: true;`}, + {func(s S) S { return s.BorderRightBackground(lipgloss.Color("#0f0")) }, `border-right-background: #0f0;`}, + {func(s S) S { return s.BorderRightForeground(lipgloss.Color("#0f0")) }, `border-right-foreground: #0f0;`}, + {func(s S) S { + return s.BorderStyle(lipgloss.Border{"a", "b", "c", "d", "e", "f", "g", "h"}) + }, `border-style: border("a","b","c","d","e","f","g","h");`}, + {func(s S) S { return s.BorderTop(true) }, `border-top: true;`}, + {func(s S) S { return s.BorderTopBackground(lipgloss.Color("#0f0")) }, `border-top-background: #0f0;`}, + {func(s S) S { return s.BorderTopForeground(lipgloss.Color("#0f0")) }, `border-top-foreground: #0f0;`}, + {func(s S) S { + return s.Border(lipgloss.Border{"a", "b", "c", "d", "e", "f", "g", "h"}, true) + }, `border-bottom: true; border-left: true; border-right: true; ` + + `border-style: border("a","b","c","d","e","f","g","h"); ` + + `border-top: true;`}, + {func(s S) S { return s.BorderBackground(lipgloss.Color("#0f0")) }, `border-bottom-background: #0f0; border-left-background: #0f0; border-right-background: #0f0; border-top-background: #0f0;`}, + {func(s S) S { return s.BorderForeground(lipgloss.Color("#0f0")) }, `border-bottom-foreground: #0f0; border-left-foreground: #0f0; border-right-foreground: #0f0; border-top-foreground: #0f0;`}, + + {func(s S) S { return s.ColorWhitespace(true) }, `color-whitespace: true;`}, + {func(s S) S { return s.Faint(true) }, `faint: true;`}, + {func(s S) S { return s.Foreground(lipgloss.Color("#0f0")) }, `foreground: #0f0;`}, + {func(s S) S { return s.Height(3) }, `height: 3;`}, + {func(s S) S { return s.Inline(true) }, `inline: true;`}, + {func(s S) S { return s.Italic(true) }, `italic: true;`}, + {func(s S) S { return s.Margin(1, 2, 3, 4) }, `margin-bottom: 3; margin-left: 4; margin-right: 2; margin-top: 1;`}, + {func(s S) S { return s.MarginBottom(3) }, `margin-bottom: 3;`}, + {func(s S) S { return s.MarginLeft(3) }, `margin-left: 3;`}, + {func(s S) S { return s.MarginRight(3) }, `margin-right: 3;`}, + {func(s S) S { return s.MarginTop(3) }, `margin-top: 3;`}, + {func(s S) S { return s.MarginBackground(lipgloss.Color("#0f0")) }, `margin-background: #0f0;`}, + {func(s S) S { return s.MaxHeight(3) }, `max-height: 3;`}, + {func(s S) S { return s.MaxWidth(3) }, `max-width: 3;`}, + {func(s S) S { return s.Padding(1, 2, 3, 4) }, `padding-bottom: 3; padding-left: 4; padding-right: 2; padding-top: 1;`}, + {func(s S) S { return s.PaddingBottom(3) }, `padding-bottom: 3;`}, + {func(s S) S { return s.PaddingLeft(3) }, `padding-left: 3;`}, + {func(s S) S { return s.PaddingRight(3) }, `padding-right: 3;`}, + {func(s S) S { return s.PaddingTop(3) }, `padding-top: 3;`}, + {func(s S) S { return s.Reverse(true) }, `reverse: true;`}, + {func(s S) S { return s.Strikethrough(true) }, `strikethrough: true;`}, + {func(s S) S { return s.StrikethroughSpaces(true) }, `strikethrough-spaces: true;`}, + {func(s S) S { return s.Underline(true) }, `underline: true;`}, + {func(s S) S { return s.UnderlineSpaces(true) }, `underline-spaces: true;`}, + {func(s S) S { return s.Width(3) }, `width: 3;`}, + } + + for _, tc := range td { + // Apply the style change and compare to the reference. + s := lipgloss.NewStyle() + s = tc.changeStyle(s) + repr := lipglossc.Export(s) + if repr != tc.repr { + t.Errorf("expected %q, got %q", tc.repr, repr) + continue + } + + // Apply the unset function and assert the resulting style is + // empty. + r := strings.Split(tc.repr, ":") + if len(r) > 2 { + // Special case: border-background / border-foreground. + if strings.HasPrefix(r[0], "border-") && strings.HasSuffix(r[0], "ground") { + r[0] = "border-" + r[0][strings.LastIndexByte(r[0], '-')+1:] + } else { + // Special case: padding, margin, border etc. + r[0] = r[0][:strings.IndexByte(r[0], '-')] + } + } + unset := r[0] + ": unset" + u, err := lipglossc.Import(s, unset) + if err != nil { + t.Error(err) + continue + } + repr = lipglossc.Export(u) + if repr != "" { + t.Errorf("expected empty style, got %q", repr) + } + } +} diff --git a/unset.go b/unset.go index 0a03720d..3ab4f751 100644 --- a/unset.go +++ b/unset.go @@ -111,8 +111,8 @@ func (s Style) UnsetColorWhitespace() Style { return s } -// UnsetMargins removes all margin style rules. -func (s Style) UnsetMargins() Style { +// UnsetMargin removes all margin style rules. +func (s Style) UnsetMargin() Style { delete(s.rules, marginLeftKey) delete(s.rules, marginRightKey) delete(s.rules, marginTopKey) @@ -120,6 +120,12 @@ func (s Style) UnsetMargins() Style { return s } +// UnsetMargins is the old name for UnsetMargin. +// Preserved for compatibility. +func (s Style) UnsetMargins() Style { + return s.UnsetMargin() +} + // UnsetMarginLeft removes the left margin style rule, if set. func (s Style) UnsetMarginLeft() Style { delete(s.rules, marginLeftKey) @@ -152,6 +158,16 @@ func (s Style) UnsetMarginBackground() Style { return s } +// UnsetBorder removes the border properties, if set. +func (s Style) UnsetBorder() Style { + delete(s.rules, borderTopKey) + delete(s.rules, borderBottomKey) + delete(s.rules, borderLeftKey) + delete(s.rules, borderRightKey) + delete(s.rules, borderStyleKey) + return s +} + // UnsetBorderStyle removes the border style rule, if set. func (s Style) UnsetBorderStyle() Style { delete(s.rules, borderStyleKey) @@ -229,13 +245,19 @@ func (s Style) UnsetBorderBackground() Style { return s } -// UnsetBorderTopBackgroundColor removes the top border background color rule, +// UnsetBorderTopBackground removes the top border background color rule, // if set. -func (s Style) UnsetBorderTopBackgroundColor() Style { +func (s Style) UnsetBorderTopBackground() Style { delete(s.rules, borderTopBackgroundKey) return s } +// UnsetBorderTopBackgroundColor is an old name for UnsetBorderTopBackground. +// Preserved for compatibility. +func (s Style) UnsetBorderTopBackgroundColor() Style { + return s.UnsetBorderTopBackground() +} + // UnsetBorderRightBackground removes the right border background color // rule, if set. func (s Style) UnsetBorderRightBackground() Style {