From 387f2d73e8b61dd1970d79a334497b875a3319e8 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Thu, 28 Mar 2024 16:41:30 -0400 Subject: [PATCH 01/12] refactor: replace props map with struct fields Use an int to store property existence and style struct fields to store the actual values for each non-bool property. Fixes: https://github.com/charmbracelet/lipgloss/pull/139 Fixes: https://github.com/charmbracelet/lipgloss/pull/141 --- get.go | 104 +++++++++++++++++++++---------- set.go | 169 +++++++++++++++++++++++++++++++++++++++++++------- style.go | 102 +++++++++++++++++++++++------- style_test.go | 48 +++++++------- unset.go | 127 +++++++++++++++++++------------------ 5 files changed, 390 insertions(+), 160 deletions(-) diff --git a/get.go b/get.go index 9be3d64d..a3b096d3 100644 --- a/get.go +++ b/get.go @@ -416,74 +416,114 @@ func (s Style) GetTransform() func(string) string { // Returns whether or not the given property is set. func (s Style) isSet(k propKey) bool { - _, exists := s.rules[k] - return exists + return s.props.has(k) } func (s Style) getAsBool(k propKey, defaultVal bool) bool { - v, ok := s.rules[k] - if !ok { + if !s.isSet(k) { return defaultVal } - if b, ok := v.(bool); ok { - return b - } - return defaultVal + return s.attrs&int(k) != 0 } func (s Style) getAsColor(k propKey) TerminalColor { - v, ok := s.rules[k] - if !ok { + if !s.isSet(k) { return noColor } - if c, ok := v.(TerminalColor); ok { + + var c TerminalColor + switch k { + case foregroundKey: + c = s.fgColor + case backgroundKey: + c = s.bgColor + case marginBackgroundKey: + c = s.marginBgColor + case borderTopForegroundKey: + c = s.borderTopFgColor + case borderRightForegroundKey: + c = s.borderRightFgColor + case borderBottomForegroundKey: + c = s.borderBottomFgColor + case borderLeftForegroundKey: + c = s.borderLeftFgColor + case borderTopBackgroundKey: + c = s.borderTopBgColor + case borderRightBackgroundKey: + c = s.borderRightBgColor + case borderBottomBackgroundKey: + c = s.borderBottomBgColor + case borderLeftBackgroundKey: + c = s.borderLeftBgColor + } + + if c != nil { return c } + return noColor } func (s Style) getAsInt(k propKey) int { - v, ok := s.rules[k] - if !ok { + if !s.isSet(k) { return 0 } - if i, ok := v.(int); ok { - return i + switch k { + case widthKey: + return s.width + case heightKey: + return s.height + case paddingTopKey: + return s.paddingTop + case paddingRightKey: + return s.paddingRight + case paddingBottomKey: + return s.paddingBottom + case paddingLeftKey: + return s.paddingLeft + case marginTopKey: + return s.marginTop + case marginRightKey: + return s.marginRight + case marginBottomKey: + return s.marginBottom + case marginLeftKey: + return s.marginLeft + case maxWidthKey: + return s.maxWidth + case maxHeightKey: + return s.maxHeight + case tabWidthKey: + return s.tabWidth } return 0 } func (s Style) getAsPosition(k propKey) Position { - v, ok := s.rules[k] - if !ok { + if !s.isSet(k) { return Position(0) } - if p, ok := v.(Position); ok { - return p + switch k { + case alignHorizontalKey: + return s.alignHorizontal + case alignVerticalKey: + return s.alignVertical } return Position(0) } func (s Style) getBorderStyle() Border { - v, ok := s.rules[borderStyleKey] - if !ok { + if !s.isSet(borderStyleKey) { return noBorder } - if b, ok := v.(Border); ok { - return b - } - return noBorder + return s.borderStyle } -func (s Style) getAsTransform(k propKey) func(string) string { - v, ok := s.rules[k] - if !ok { +func (s Style) getAsTransform(propKey) func(string) string { + if !s.isSet(transformKey) { return nil } - if fn, ok := v.(func(string) string); ok { - return fn - } - return nil + return s.transform } // Split a string into lines, additionally returning the size of the widest diff --git a/set.go b/set.go index 5846b598..d4f4bff9 100644 --- a/set.go +++ b/set.go @@ -1,34 +1,161 @@ package lipgloss -// This could (should) probably just be moved into NewStyle(). We've broken it -// out, so we can call it in a lazy way. -func (s *Style) init() { - if s.rules == nil { - s.rules = make(rules) - } -} - // Set a value on the underlying rules map. func (s *Style) set(key propKey, value interface{}) { - s.init() - - switch v := value.(type) { - case int: + // We don't allow negative integers on any of our other values, so just keep + // them at zero or above. We could use uints instead, but the + // conversions are a little tedious, so we're sticking with ints for + // sake of usability. + switch key { + case foregroundKey: + s.fgColor = colorOrNil(value) + case backgroundKey: + s.bgColor = colorOrNil(value) + case widthKey: + s.width = max(0, value.(int)) + case heightKey: + s.height = max(0, value.(int)) + case alignHorizontalKey: + s.alignHorizontal = value.(Position) + case alignVerticalKey: + s.alignVertical = value.(Position) + case paddingTopKey: + s.paddingTop = max(0, value.(int)) + case paddingRightKey: + s.paddingRight = max(0, value.(int)) + case paddingBottomKey: + s.paddingBottom = max(0, value.(int)) + case paddingLeftKey: + s.paddingLeft = max(0, value.(int)) + case marginTopKey: + s.marginTop = max(0, value.(int)) + case marginRightKey: + s.marginRight = max(0, value.(int)) + case marginBottomKey: + s.marginBottom = max(0, value.(int)) + case marginLeftKey: + s.marginLeft = max(0, value.(int)) + case marginBackgroundKey: + s.marginBgColor = colorOrNil(value) + case borderStyleKey: + s.borderStyle = value.(Border) + case borderTopForegroundKey: + s.borderTopFgColor = colorOrNil(value) + case borderRightForegroundKey: + s.borderRightFgColor = colorOrNil(value) + case borderBottomForegroundKey: + s.borderBottomFgColor = colorOrNil(value) + case borderLeftForegroundKey: + s.borderLeftFgColor = colorOrNil(value) + case borderTopBackgroundKey: + s.borderTopBgColor = colorOrNil(value) + case borderRightBackgroundKey: + s.borderRightBgColor = colorOrNil(value) + case borderBottomBackgroundKey: + s.borderBottomBgColor = colorOrNil(value) + case borderLeftBackgroundKey: + s.borderLeftBgColor = colorOrNil(value) + case maxWidthKey: + s.maxWidth = max(0, value.(int)) + case maxHeightKey: + s.maxHeight = max(0, value.(int)) + case tabWidthKey: // TabWidth is the only property that may have a negative value (and // that negative value can be no less than -1). - if key == tabWidthKey { - s.rules[key] = v - break + s.tabWidth = value.(int) + case transformKey: + s.transform = value.(func(string) string) + default: + if v, ok := value.(bool); ok { + if v { + s.attrs |= int(key) + } else { + s.attrs &^= int(key) + } + } else if attrs, ok := value.(int); ok { + // bool attrs + if attrs&int(key) != 0 { + s.attrs |= int(key) + } else { + s.attrs &^= int(key) + } } + } - // We don't allow negative integers on any of our other values, so just keep - // them at zero or above. We could use uints instead, but the - // conversions are a little tedious, so we're sticking with ints for - // sake of usability. - s.rules[key] = max(0, v) + // Set the prop on + s.props = s.props.set(key) +} + +// setFrom sets the property from another style. +func (s *Style) setFrom(key propKey, i Style) { + switch key { + case foregroundKey: + s.set(foregroundKey, i.fgColor) + case backgroundKey: + s.set(backgroundKey, i.bgColor) + case widthKey: + s.set(widthKey, i.width) + case heightKey: + s.set(heightKey, i.height) + case alignHorizontalKey: + s.set(alignHorizontalKey, i.alignHorizontal) + case alignVerticalKey: + s.set(alignVerticalKey, i.alignVertical) + case paddingTopKey: + s.set(paddingTopKey, i.paddingTop) + case paddingRightKey: + s.set(paddingRightKey, i.paddingRight) + case paddingBottomKey: + s.set(paddingBottomKey, i.paddingBottom) + case paddingLeftKey: + s.set(paddingLeftKey, i.paddingLeft) + case marginTopKey: + s.set(marginTopKey, i.marginTop) + case marginRightKey: + s.set(marginRightKey, i.marginRight) + case marginBottomKey: + s.set(marginBottomKey, i.marginBottom) + case marginLeftKey: + s.set(marginLeftKey, i.marginLeft) + case marginBackgroundKey: + s.set(marginBackgroundKey, i.marginBgColor) + case borderStyleKey: + s.set(borderStyleKey, i.borderStyle) + case borderTopForegroundKey: + s.set(borderTopForegroundKey, i.borderTopFgColor) + case borderRightForegroundKey: + s.set(borderRightForegroundKey, i.borderRightFgColor) + case borderBottomForegroundKey: + s.set(borderBottomForegroundKey, i.borderBottomFgColor) + case borderLeftForegroundKey: + s.set(borderLeftForegroundKey, i.borderLeftFgColor) + case borderTopBackgroundKey: + s.set(borderTopBackgroundKey, i.borderTopBgColor) + case borderRightBackgroundKey: + s.set(borderRightBackgroundKey, i.borderRightBgColor) + case borderBottomBackgroundKey: + s.set(borderBottomBackgroundKey, i.borderBottomBgColor) + case borderLeftBackgroundKey: + s.set(borderLeftBackgroundKey, i.borderLeftBgColor) + case maxWidthKey: + s.set(maxWidthKey, i.maxWidth) + case maxHeightKey: + s.set(maxHeightKey, i.maxHeight) + case tabWidthKey: + s.set(tabWidthKey, i.tabWidth) + case transformKey: + s.set(transformKey, i.transform) default: - s.rules[key] = v + // Set attributes for set bool properties + s.set(key, i.attrs) + } +} + +func colorOrNil(c interface{}) TerminalColor { + if c, ok := c.(TerminalColor); ok { + return c } + return nil } // Bold sets a bold formatting rule. diff --git a/style.go b/style.go index ad6b52a3..5d2adfb5 100644 --- a/style.go +++ b/style.go @@ -17,13 +17,20 @@ type propKey int // Available properties. const ( - boldKey propKey = iota + // bool props come first + boldKey propKey = 1 << iota italicKey underlineKey strikethroughKey reverseKey blinkKey faintKey + underlineSpacesKey + strikethroughSpacesKey + colorWhitespaceKey + + // non-bool props + foregroundKey backgroundKey widthKey @@ -37,8 +44,6 @@ const ( paddingBottomKey paddingLeftKey - colorWhitespaceKey - // Margins. marginTopKey marginRightKey @@ -71,14 +76,27 @@ const ( maxWidthKey maxHeightKey tabWidthKey - underlineSpacesKey - strikethroughSpacesKey transformKey ) -// A set of properties. -type rules map[propKey]interface{} +// props is a set of properties. +type props int + +// set sets a property. +func (p props) set(k propKey) props { + return p | props(k) +} + +// unset unsets a property. +func (p props) unset(k propKey) props { + return p &^ props(k) +} + +// has checks if a property is set. +func (p props) has(k propKey) bool { + return p&props(k) != 0 +} // NewStyle returns a new, empty Style. While it's syntactic sugar for the // Style{} primitive, it's recommended to use this function for creating styles @@ -100,8 +118,48 @@ func (r *Renderer) NewStyle() Style { // Style contains a set of rules that comprise a style as a whole. type Style struct { r *Renderer - rules map[propKey]interface{} + props props value string + + // we store bool props values here + attrs int + + // props that have values + fgColor TerminalColor + bgColor TerminalColor + + width int + height int + + alignHorizontal Position + alignVertical Position + + paddingTop int + paddingRight int + paddingBottom int + paddingLeft int + + marginTop int + marginRight int + marginBottom int + marginLeft int + marginBgColor TerminalColor + + borderStyle Border + borderTopFgColor TerminalColor + borderRightFgColor TerminalColor + borderBottomFgColor TerminalColor + borderLeftFgColor TerminalColor + borderTopBgColor TerminalColor + borderRightBgColor TerminalColor + borderBottomBgColor TerminalColor + borderLeftBgColor TerminalColor + + maxWidth int + maxHeight int + tabWidth int + + transform func(string) string } // joinString joins a list of strings into a single string separated with a @@ -133,15 +191,10 @@ func (s Style) String() string { } // Copy returns a copy of this style, including any underlying string values. +// +// Deprecated: Copy is deprecated and will be removed in a future release. func (s Style) Copy() Style { - o := NewStyle() - o.init() - for k, v := range s.rules { - o.rules[k] = v - } - o.r = s.r - o.value = s.value - return o + return s } // Inherit overlays the style in the argument onto this style by copying each explicitly @@ -150,9 +203,11 @@ func (s Style) Copy() Style { // // Margins, padding, and underlying string values are not inherited. func (s Style) Inherit(i Style) Style { - s.init() + for k := boldKey; k <= transformKey; k <<= 1 { + if !i.isSet(k) { + continue + } - for k, v := range i.rules { switch k { //nolint:exhaustive case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey: // Margins are not inherited @@ -163,14 +218,15 @@ func (s Style) Inherit(i Style) Style { case backgroundKey: // The margins also inherit the background color if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) { - s.rules[marginBackgroundKey] = v + s.set(marginBackgroundKey, i.bgColor) } } - if _, exists := s.rules[k]; exists { + if s.isSet(k) { continue } - s.rules[k] = v + + s.setFrom(k, i) } return s } @@ -235,7 +291,7 @@ func (s Style) Render(strs ...string) string { str = transform(str) } - if len(s.rules) == 0 { + if s.props == 0 { return s.maybeConvertTabs(str) } @@ -511,7 +567,7 @@ func pad(str string, n int, style *termenv.Style) string { return b.String() } -func max(a, b int) int { +func max(a, b int) int { // nolint:unparam if a > b { return a } diff --git a/style_test.go b/style_test.go index 84620e02..f22b3590 100644 --- a/style_test.go +++ b/style_test.go @@ -213,114 +213,114 @@ func TestStyleUnset(t *testing.T) { s := NewStyle().Bold(true) requireTrue(t, s.GetBold()) - s.UnsetBold() + s = s.UnsetBold() requireFalse(t, s.GetBold()) s = NewStyle().Italic(true) requireTrue(t, s.GetItalic()) - s.UnsetItalic() + s = s.UnsetItalic() requireFalse(t, s.GetItalic()) s = NewStyle().Underline(true) requireTrue(t, s.GetUnderline()) - s.UnsetUnderline() + s = s.UnsetUnderline() requireFalse(t, s.GetUnderline()) s = NewStyle().Strikethrough(true) requireTrue(t, s.GetStrikethrough()) - s.UnsetStrikethrough() + s = s.UnsetStrikethrough() requireFalse(t, s.GetStrikethrough()) s = NewStyle().Reverse(true) requireTrue(t, s.GetReverse()) - s.UnsetReverse() + s = s.UnsetReverse() requireFalse(t, s.GetReverse()) s = NewStyle().Blink(true) requireTrue(t, s.GetBlink()) - s.UnsetBlink() + s = s.UnsetBlink() requireFalse(t, s.GetBlink()) s = NewStyle().Faint(true) requireTrue(t, s.GetFaint()) - s.UnsetFaint() + s = s.UnsetFaint() requireFalse(t, s.GetFaint()) s = NewStyle().Inline(true) requireTrue(t, s.GetInline()) - s.UnsetInline() + s = s.UnsetInline() requireFalse(t, s.GetInline()) // colors col := Color("#ffffff") s = NewStyle().Foreground(col) requireEqual(t, col, s.GetForeground()) - s.UnsetForeground() + s = s.UnsetForeground() requireNotEqual(t, col, s.GetForeground()) s = NewStyle().Background(col) requireEqual(t, col, s.GetBackground()) - s.UnsetBackground() + s = s.UnsetBackground() requireNotEqual(t, col, s.GetBackground()) // margins s = NewStyle().Margin(1, 2, 3, 4) requireEqual(t, 1, s.GetMarginTop()) - s.UnsetMarginTop() + s = s.UnsetMarginTop() requireEqual(t, 0, s.GetMarginTop()) requireEqual(t, 2, s.GetMarginRight()) - s.UnsetMarginRight() + s = s.UnsetMarginRight() requireEqual(t, 0, s.GetMarginRight()) requireEqual(t, 3, s.GetMarginBottom()) - s.UnsetMarginBottom() + s = s.UnsetMarginBottom() requireEqual(t, 0, s.GetMarginBottom()) requireEqual(t, 4, s.GetMarginLeft()) - s.UnsetMarginLeft() + s = s.UnsetMarginLeft() requireEqual(t, 0, s.GetMarginLeft()) // padding s = NewStyle().Padding(1, 2, 3, 4) requireEqual(t, 1, s.GetPaddingTop()) - s.UnsetPaddingTop() + s = s.UnsetPaddingTop() requireEqual(t, 0, s.GetPaddingTop()) requireEqual(t, 2, s.GetPaddingRight()) - s.UnsetPaddingRight() + s = s.UnsetPaddingRight() requireEqual(t, 0, s.GetPaddingRight()) requireEqual(t, 3, s.GetPaddingBottom()) - s.UnsetPaddingBottom() + s = s.UnsetPaddingBottom() requireEqual(t, 0, s.GetPaddingBottom()) requireEqual(t, 4, s.GetPaddingLeft()) - s.UnsetPaddingLeft() + s = s.UnsetPaddingLeft() requireEqual(t, 0, s.GetPaddingLeft()) // border s = NewStyle().Border(normalBorder, true, true, true, true) requireTrue(t, s.GetBorderTop()) - s.UnsetBorderTop() + s = s.UnsetBorderTop() requireFalse(t, s.GetBorderTop()) requireTrue(t, s.GetBorderRight()) - s.UnsetBorderRight() + s = s.UnsetBorderRight() requireFalse(t, s.GetBorderRight()) requireTrue(t, s.GetBorderBottom()) - s.UnsetBorderBottom() + s = s.UnsetBorderBottom() requireFalse(t, s.GetBorderBottom()) requireTrue(t, s.GetBorderLeft()) - s.UnsetBorderLeft() + s = s.UnsetBorderLeft() requireFalse(t, s.GetBorderLeft()) // tab width s = NewStyle().TabWidth(2) requireEqual(t, s.GetTabWidth(), 2) - s.UnsetTabWidth() + s = s.UnsetTabWidth() requireNotEqual(t, s.GetTabWidth(), 4) } @@ -460,10 +460,12 @@ func BenchmarkStyleRender(b *testing.B) { } func requireTrue(tb testing.TB, b bool) { + tb.Helper() requireEqual(tb, true, b) } func requireFalse(tb testing.TB, b bool) { + tb.Helper() requireEqual(tb, false, b) } diff --git a/unset.go b/unset.go index 5387bcf7..19d93370 100644 --- a/unset.go +++ b/unset.go @@ -1,159 +1,164 @@ package lipgloss +// unset unsets a property from a style. +func (s *Style) unset(key propKey) { + s.props = s.props.unset(key) +} + // UnsetBold removes the bold style rule, if set. func (s Style) UnsetBold() Style { - delete(s.rules, boldKey) + s.unset(boldKey) return s } // UnsetItalic removes the italic style rule, if set. func (s Style) UnsetItalic() Style { - delete(s.rules, italicKey) + s.unset(italicKey) return s } // UnsetUnderline removes the underline style rule, if set. func (s Style) UnsetUnderline() Style { - delete(s.rules, underlineKey) + s.unset(underlineKey) return s } // UnsetStrikethrough removes the strikethrough style rule, if set. func (s Style) UnsetStrikethrough() Style { - delete(s.rules, strikethroughKey) + s.unset(strikethroughKey) return s } // UnsetReverse removes the reverse style rule, if set. func (s Style) UnsetReverse() Style { - delete(s.rules, reverseKey) + s.unset(reverseKey) return s } // UnsetBlink removes the blink style rule, if set. func (s Style) UnsetBlink() Style { - delete(s.rules, blinkKey) + s.unset(blinkKey) return s } // UnsetFaint removes the faint style rule, if set. func (s Style) UnsetFaint() Style { - delete(s.rules, faintKey) + s.unset(faintKey) return s } // UnsetForeground removes the foreground style rule, if set. func (s Style) UnsetForeground() Style { - delete(s.rules, foregroundKey) + s.unset(foregroundKey) return s } // UnsetBackground removes the background style rule, if set. func (s Style) UnsetBackground() Style { - delete(s.rules, backgroundKey) + s.unset(backgroundKey) return s } // UnsetWidth removes the width style rule, if set. func (s Style) UnsetWidth() Style { - delete(s.rules, widthKey) + s.unset(widthKey) return s } // UnsetHeight removes the height style rule, if set. func (s Style) UnsetHeight() Style { - delete(s.rules, heightKey) + s.unset(heightKey) return s } // UnsetAlign removes the horizontal and vertical text alignment style rule, if set. func (s Style) UnsetAlign() Style { - delete(s.rules, alignHorizontalKey) - delete(s.rules, alignVerticalKey) + s.unset(alignHorizontalKey) + s.unset(alignVerticalKey) return s } // UnsetAlignHorizontal removes the horizontal text alignment style rule, if set. func (s Style) UnsetAlignHorizontal() Style { - delete(s.rules, alignHorizontalKey) + s.unset(alignHorizontalKey) return s } // UnsetAlignVertical removes the vertical text alignment style rule, if set. func (s Style) UnsetAlignVertical() Style { - delete(s.rules, alignVerticalKey) + s.unset(alignVerticalKey) return s } // UnsetPadding removes all padding style rules. func (s Style) UnsetPadding() Style { - delete(s.rules, paddingLeftKey) - delete(s.rules, paddingRightKey) - delete(s.rules, paddingTopKey) - delete(s.rules, paddingBottomKey) + s.unset(paddingLeftKey) + s.unset(paddingRightKey) + s.unset(paddingTopKey) + s.unset(paddingBottomKey) return s } // UnsetPaddingLeft removes the left padding style rule, if set. func (s Style) UnsetPaddingLeft() Style { - delete(s.rules, paddingLeftKey) + s.unset(paddingLeftKey) return s } // UnsetPaddingRight removes the right padding style rule, if set. func (s Style) UnsetPaddingRight() Style { - delete(s.rules, paddingRightKey) + s.unset(paddingRightKey) return s } // UnsetPaddingTop removes the top padding style rule, if set. func (s Style) UnsetPaddingTop() Style { - delete(s.rules, paddingTopKey) + s.unset(paddingTopKey) return s } // UnsetPaddingBottom removes the bottom padding style rule, if set. func (s Style) UnsetPaddingBottom() Style { - delete(s.rules, paddingBottomKey) + s.unset(paddingBottomKey) return s } // UnsetColorWhitespace removes the rule for coloring padding, if set. func (s Style) UnsetColorWhitespace() Style { - delete(s.rules, colorWhitespaceKey) + s.unset(colorWhitespaceKey) return s } // UnsetMargins removes all margin style rules. func (s Style) UnsetMargins() Style { - delete(s.rules, marginLeftKey) - delete(s.rules, marginRightKey) - delete(s.rules, marginTopKey) - delete(s.rules, marginBottomKey) + s.unset(marginLeftKey) + s.unset(marginRightKey) + s.unset(marginTopKey) + s.unset(marginBottomKey) return s } // UnsetMarginLeft removes the left margin style rule, if set. func (s Style) UnsetMarginLeft() Style { - delete(s.rules, marginLeftKey) + s.unset(marginLeftKey) return s } // UnsetMarginRight removes the right margin style rule, if set. func (s Style) UnsetMarginRight() Style { - delete(s.rules, marginRightKey) + s.unset(marginRightKey) return s } // UnsetMarginTop removes the top margin style rule, if set. func (s Style) UnsetMarginTop() Style { - delete(s.rules, marginTopKey) + s.unset(marginTopKey) return s } // UnsetMarginBottom removes the bottom margin style rule, if set. func (s Style) UnsetMarginBottom() Style { - delete(s.rules, marginBottomKey) + s.unset(marginBottomKey) return s } @@ -161,153 +166,153 @@ func (s Style) UnsetMarginBottom() Style { // margin's background color can be set from the background color of another // style during inheritance. func (s Style) UnsetMarginBackground() Style { - delete(s.rules, marginBackgroundKey) + s.unset(marginBackgroundKey) return s } // UnsetBorderStyle removes the border style rule, if set. func (s Style) UnsetBorderStyle() Style { - delete(s.rules, borderStyleKey) + s.unset(borderStyleKey) return s } // UnsetBorderTop removes the border top style rule, if set. func (s Style) UnsetBorderTop() Style { - delete(s.rules, borderTopKey) + s.unset(borderTopKey) return s } // UnsetBorderRight removes the border right style rule, if set. func (s Style) UnsetBorderRight() Style { - delete(s.rules, borderRightKey) + s.unset(borderRightKey) return s } // UnsetBorderBottom removes the border bottom style rule, if set. func (s Style) UnsetBorderBottom() Style { - delete(s.rules, borderBottomKey) + s.unset(borderBottomKey) return s } // UnsetBorderLeft removes the border left style rule, if set. func (s Style) UnsetBorderLeft() Style { - delete(s.rules, borderLeftKey) + s.unset(borderLeftKey) return s } // UnsetBorderForeground removes all border foreground color styles, if set. func (s Style) UnsetBorderForeground() Style { - delete(s.rules, borderTopForegroundKey) - delete(s.rules, borderRightForegroundKey) - delete(s.rules, borderBottomForegroundKey) - delete(s.rules, borderLeftForegroundKey) + s.unset(borderTopForegroundKey) + s.unset(borderRightForegroundKey) + s.unset(borderBottomForegroundKey) + s.unset(borderLeftForegroundKey) return s } // UnsetBorderTopForeground removes the top border foreground color rule, // if set. func (s Style) UnsetBorderTopForeground() Style { - delete(s.rules, borderTopForegroundKey) + s.unset(borderTopForegroundKey) return s } // UnsetBorderRightForeground removes the right border foreground color rule, // if set. func (s Style) UnsetBorderRightForeground() Style { - delete(s.rules, borderRightForegroundKey) + s.unset(borderRightForegroundKey) return s } // UnsetBorderBottomForeground removes the bottom border foreground color // rule, if set. func (s Style) UnsetBorderBottomForeground() Style { - delete(s.rules, borderBottomForegroundKey) + s.unset(borderBottomForegroundKey) return s } // UnsetBorderLeftForeground removes the left border foreground color rule, // if set. func (s Style) UnsetBorderLeftForeground() Style { - delete(s.rules, borderLeftForegroundKey) + s.unset(borderLeftForegroundKey) return s } // UnsetBorderBackground removes all border background color styles, if // set. func (s Style) UnsetBorderBackground() Style { - delete(s.rules, borderTopBackgroundKey) - delete(s.rules, borderRightBackgroundKey) - delete(s.rules, borderBottomBackgroundKey) - delete(s.rules, borderLeftBackgroundKey) + s.unset(borderTopBackgroundKey) + s.unset(borderRightBackgroundKey) + s.unset(borderBottomBackgroundKey) + s.unset(borderLeftBackgroundKey) return s } // UnsetBorderTopBackgroundColor removes the top border background color rule, // if set. func (s Style) UnsetBorderTopBackgroundColor() Style { - delete(s.rules, borderTopBackgroundKey) + s.unset(borderTopBackgroundKey) return s } // UnsetBorderRightBackground removes the right border background color // rule, if set. func (s Style) UnsetBorderRightBackground() Style { - delete(s.rules, borderRightBackgroundKey) + s.unset(borderRightBackgroundKey) return s } // UnsetBorderBottomBackground removes the bottom border background color // rule, if set. func (s Style) UnsetBorderBottomBackground() Style { - delete(s.rules, borderBottomBackgroundKey) + s.unset(borderBottomBackgroundKey) return s } // UnsetBorderLeftBackground removes the left border color rule, if set. func (s Style) UnsetBorderLeftBackground() Style { - delete(s.rules, borderLeftBackgroundKey) + s.unset(borderLeftBackgroundKey) return s } // UnsetInline removes the inline style rule, if set. func (s Style) UnsetInline() Style { - delete(s.rules, inlineKey) + s.unset(inlineKey) return s } // UnsetMaxWidth removes the max width style rule, if set. func (s Style) UnsetMaxWidth() Style { - delete(s.rules, maxWidthKey) + s.unset(maxWidthKey) return s } // UnsetMaxHeight removes the max height style rule, if set. func (s Style) UnsetMaxHeight() Style { - delete(s.rules, maxHeightKey) + s.unset(maxHeightKey) return s } // UnsetTabWidth removes the tab width style rule, if set. func (s Style) UnsetTabWidth() Style { - delete(s.rules, tabWidthKey) + s.unset(tabWidthKey) return s } // UnsetUnderlineSpaces removes the value set by UnderlineSpaces. func (s Style) UnsetUnderlineSpaces() Style { - delete(s.rules, underlineSpacesKey) + s.unset(underlineSpacesKey) return s } // UnsetStrikethroughSpaces removes the value set by StrikethroughSpaces. func (s Style) UnsetStrikethroughSpaces() Style { - delete(s.rules, strikethroughSpacesKey) + s.unset(strikethroughSpacesKey) return s } // UnsetTransform removes the value set by Transform. func (s Style) UnsetTransform() Style { - delete(s.rules, transformKey) + s.unset(transformKey) return s } From 1d4cfcb75bb76ed84db9018f834ea5b2deac4f65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:51:19 -0400 Subject: [PATCH 02/12] chore(deps): bump golangci/golangci-lint-action from 4 to 5 (#283) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4 to 5. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v4...v5) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint-soft.yml | 2 +- .github/workflows/lint.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-soft.yml b/.github/workflows/lint-soft.yml index a1eafa04..9522e9d7 100644 --- a/.github/workflows/lint-soft.yml +++ b/.github/workflows/lint-soft.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v5 with: # Optional: golangci-lint command line arguments. args: --config .golangci-soft.yml --issues-exit-code=0 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 881d0d30..dddcf841 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v5 with: # Optional: golangci-lint command line arguments. #args: From 0d3715fd494f60371f0e1ef4415fbd00c8476735 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Wed, 1 May 2024 14:15:02 -0400 Subject: [PATCH 03/12] chore: update examples to remove deprecated Copy() --- examples/go.mod | 6 +++++- examples/go.sum | 11 +++++++++++ examples/layout/main.go | 22 +++++++++++----------- examples/ssh/main.go | 2 +- examples/table/languages/main.go | 8 ++++---- examples/table/pokemon/main.go | 10 +++++----- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index b93aef15..bf6ad38d 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -19,11 +19,15 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/caarlos0/sshmarshal v0.1.0 // indirect github.com/charmbracelet/keygen v0.3.0 // indirect + github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/crypto v0.1.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/sys v0.19.0 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index d16930bd..3e74f033 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -18,11 +18,15 @@ github.com/charmbracelet/keygen v0.3.0 h1:mXpsQcH7DDlST5TddmXNXjS0L7ECk4/kLQYyBc github.com/charmbracelet/keygen v0.3.0/go.mod h1:1ukgO8806O25lUZ5s0IrNur+RlwTBERlezdgW71F5rM= github.com/charmbracelet/wish v0.5.0 h1:FkkdNBFqrLABR1ciNrAL2KCxoyWfKhXnIGZw6GfAtPg= github.com/charmbracelet/wish v0.5.0/go.mod h1:5GAn5SrDSZ7cgKjnC+3kDmiIo7I6k4/AYiRzC4+tpCk= +github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f h1:1BXkZqDueTOBECyDoFGRi0xMYgjJ6vvoPIkWyKOwzTc= +github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f/go.mod h1:yQqGHmheaQfkqiJWjklPHVAq1dKbk8uGbcoS/lcKCJ0= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.4 h1:+AXBtim7MTKaLVPgvE+3mhewYRawNLTd+jEEz/wExZw= @@ -61,6 +65,8 @@ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= @@ -82,6 +88,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -116,6 +124,7 @@ golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -123,6 +132,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/examples/layout/main.go b/examples/layout/main.go index e68cf772..e3281c6b 100644 --- a/examples/layout/main.go +++ b/examples/layout/main.go @@ -68,9 +68,9 @@ var ( BorderForeground(highlight). Padding(0, 1) - activeTab = tab.Copy().Border(activeTabBorder, true) + activeTab = tab.Border(activeTabBorder, true) - tabGap = tab.Copy(). + tabGap = tab. BorderTop(false). BorderLeft(false). BorderRight(false) @@ -109,7 +109,7 @@ var ( Padding(0, 3). MarginTop(1) - activeButtonStyle = buttonStyle.Copy(). + activeButtonStyle = buttonStyle. Foreground(lipgloss.Color("#FFF7DB")). Background(lipgloss.Color("#F25D94")). MarginRight(2). @@ -173,13 +173,13 @@ var ( Padding(0, 1). MarginRight(1) - encodingStyle = statusNugget.Copy(). + encodingStyle = statusNugget. Background(lipgloss.Color("#A550DF")). Align(lipgloss.Right) statusText = lipgloss.NewStyle().Inherit(statusBarStyle) - fishCakeStyle = statusNugget.Copy().Background(lipgloss.Color("#6124DF")) + fishCakeStyle = statusNugget.Background(lipgloss.Color("#6124DF")) // Page. @@ -215,7 +215,7 @@ func main() { for i, v := range colors { const offset = 2 c := lipgloss.Color(v[0]) - fmt.Fprint(&title, titleStyle.Copy().MarginLeft(i*offset).Background(c)) + fmt.Fprint(&title, titleStyle.MarginLeft(i*offset).Background(c)) if i < len(colors)-1 { title.WriteRune('\n') } @@ -276,7 +276,7 @@ func main() { listItem("Pomelo"), ), ), - list.Copy().Width(columnWidth).Render( + list.Width(columnWidth).Render( lipgloss.JoinVertical(lipgloss.Left, listHeader("Actual Lip Gloss Vendors"), listItem("Glossier"), @@ -300,9 +300,9 @@ func main() { doc.WriteString(lipgloss.JoinHorizontal( lipgloss.Top, - historyStyle.Copy().Align(lipgloss.Right).Render(historyA), - historyStyle.Copy().Align(lipgloss.Center).Render(historyB), - historyStyle.Copy().MarginRight(0).Render(historyC), + historyStyle.Align(lipgloss.Right).Render(historyA), + historyStyle.Align(lipgloss.Center).Render(historyB), + historyStyle.MarginRight(0).Render(historyC), )) doc.WriteString("\n\n") @@ -315,7 +315,7 @@ func main() { statusKey := statusStyle.Render("STATUS") encoding := encodingStyle.Render("UTF-8") fishCake := fishCakeStyle.Render("🍥 Fish Cake") - statusVal := statusText.Copy(). + statusVal := statusText. Width(width - w(statusKey) - w(encoding) - w(fishCake)). Render("Ravishing") diff --git a/examples/ssh/main.go b/examples/ssh/main.go index cd23b052..0871ebb5 100644 --- a/examples/ssh/main.go +++ b/examples/ssh/main.go @@ -161,7 +161,7 @@ func handler(next ssh.Handler) ssh.Handler { styles.gray, ) - fmt.Fprintf(&str, "%s %t %s\n\n", styles.bold.Copy().UnsetString().Render("Has dark background?"), + fmt.Fprintf(&str, "%s %t %s\n\n", styles.bold.UnsetString().Render("Has dark background?"), renderer.HasDarkBackground(), renderer.Output().BackgroundColor()) diff --git a/examples/table/languages/main.go b/examples/table/languages/main.go index 601cdc84..b0e94d82 100644 --- a/examples/table/languages/main.go +++ b/examples/table/languages/main.go @@ -23,9 +23,9 @@ func main() { // CellStyle is the base lipgloss style used for the table rows. CellStyle = re.NewStyle().Padding(0, 1).Width(14) // OddRowStyle is the lipgloss style used for odd-numbered table rows. - OddRowStyle = CellStyle.Copy().Foreground(gray) + OddRowStyle = CellStyle.Foreground(gray) // EvenRowStyle is the lipgloss style used for even-numbered table rows. - EvenRowStyle = CellStyle.Copy().Foreground(lightGray) + EvenRowStyle = CellStyle.Foreground(lightGray) // BorderStyle is the lipgloss style used for the table border. BorderStyle = lipgloss.NewStyle().Foreground(purple) ) @@ -55,12 +55,12 @@ func main() { // Make the second column a little wider. if col == 1 { - style = style.Copy().Width(22) + style = style.Width(22) } // Arabic is a right-to-left language, so right align the text. if row < len(rows) && rows[row-1][0] == "Arabic" && col != 0 { - style = style.Copy().Align(lipgloss.Right) + style = style.Align(lipgloss.Right) } return style diff --git a/examples/table/pokemon/main.go b/examples/table/pokemon/main.go index 628d90d6..5c25eee9 100644 --- a/examples/table/pokemon/main.go +++ b/examples/table/pokemon/main.go @@ -12,8 +12,8 @@ import ( func main() { re := lipgloss.NewRenderer(os.Stdout) baseStyle := re.NewStyle().Padding(0, 1) - headerStyle := baseStyle.Copy().Foreground(lipgloss.Color("252")).Bold(true) - selectedStyle := baseStyle.Copy().Foreground(lipgloss.Color("#01BE85")).Background(lipgloss.Color("#00432F")) + headerStyle := baseStyle.Foreground(lipgloss.Color("252")).Bold(true) + selectedStyle := baseStyle.Foreground(lipgloss.Color("#01BE85")).Background(lipgloss.Color("#00432F")) typeColors := map[string]lipgloss.Color{ "Bug": lipgloss.Color("#D7FF87"), "Electric": lipgloss.Color("#FDFF90"), @@ -101,13 +101,13 @@ func main() { } color := c[fmt.Sprint(data[row-1][col])] - return baseStyle.Copy().Foreground(color) + return baseStyle.Foreground(color) } if even { - return baseStyle.Copy().Foreground(lipgloss.Color("245")) + return baseStyle.Foreground(lipgloss.Color("245")) } - return baseStyle.Copy().Foreground(lipgloss.Color("252")) + return baseStyle.Foreground(lipgloss.Color("252")) }) fmt.Println(t) } From 8e3f4122dcb04b0401be50bc326fccc2e4b68a02 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Wed, 1 May 2024 14:22:21 -0400 Subject: [PATCH 04/12] docs: update Copy() notes --- README.md | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 22defac7..6758809d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Lip Gloss -========= +# Lip Gloss

Lip Gloss title treatment
@@ -66,7 +65,6 @@ The terminal's color profile will be automatically detected, and colors outside the gamut of the current palette will be automatically coerced to their closest available value. - ### Adaptive Colors You can also specify color options for light and dark backgrounds: @@ -117,7 +115,6 @@ var style = lipgloss.NewStyle(). Reverse(true) ``` - ## Block-Level Formatting Lip Gloss also supports rules for block-level formatting: @@ -156,7 +153,6 @@ lipgloss.NewStyle().Padding(1, 4, 2) lipgloss.NewStyle().Margin(2, 4, 3, 1) ``` - ## Aligning Text You can align paragraphs of text to the left, right, or center. @@ -169,7 +165,6 @@ var style = lipgloss.NewStyle(). Align(lipgloss.Center) // just kidding, align it in the center ``` - ## Width and Height Setting a minimum width and height is simple and straightforward. @@ -182,7 +177,6 @@ var style = lipgloss.NewStyle(). Foreground(lipgloss.Color("63")) ``` - ## Borders Adding borders is easy: @@ -230,21 +224,19 @@ lipgloss.NewStyle(). For more on borders see [the docs][docs]. - ## Copying Styles -Just use `Copy()`: +Just use assignment ```go var style = lipgloss.NewStyle().Foreground(lipgloss.Color("219")) -var wildStyle = style.Copy().Blink(true) +var wildStyle = style.Blink(true) ``` -`Copy()` performs a copy on the underlying data structure ensuring that you get -a true, dereferenced copy of a style. Without copying, it's possible to mutate -styles. - +Since `Style` data structure contains only primitive types, assigning a style +to another effectively creates a new copy of the style without mutating the +original. ## Inheritance @@ -263,7 +255,6 @@ var styleB = lipgloss.NewStyle(). Inherit(styleA) ``` - ## Unsetting Rules All rules can be unset: @@ -278,7 +269,6 @@ var style = lipgloss.NewStyle(). When a rule is unset, it won't be inherited or copied. - ## Enforcing Rules Sometimes, such as when developing a component, you want to make sure style @@ -355,7 +345,6 @@ For an example on using a custom renderer over SSH with [Wish][wish] see the In addition to pure styling, Lip Gloss also ships with some utilities to help assemble your layouts. - ### Joining Paragraphs Horizontally and vertically joining paragraphs is a cinch. @@ -372,7 +361,6 @@ lipgloss.JoinVertical(lipgloss.Center, paragraphA, paragraphB) lipgloss.JoinHorizontal(0.2, paragraphA, paragraphB, paragraphC) ``` - ### Measuring Width and Height Sometimes you’ll want to know the width and height of text blocks when building @@ -465,7 +453,7 @@ fmt.Println(t) For more on tables see [the docs](https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc) and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table). -*** +--- ## FAQ @@ -501,10 +489,11 @@ import ( lipgloss.SetColorProfile(termenv.TrueColor) ``` -*Note:* this option limits the flexibility of your application and can cause +_Note:_ this option limits the flexibility of your application and can cause ANSI escape codes to be output in cases where that might not be desired. Take careful note of your use case and environment before choosing to force a color profile. + ## What about [Bubble Tea][tea]? @@ -518,7 +507,6 @@ In simple terms, you can use Lip Gloss to help build your Bubble Tea views. [tea]: https://github.com/charmbracelet/tea - ## Under the Hood Lip Gloss is built on the excellent [Termenv][termenv] and [Reflow][reflow] @@ -528,7 +516,6 @@ For many use cases Termenv and Reflow will be sufficient for your needs. [termenv]: https://github.com/muesli/termenv [reflow]: https://github.com/muesli/reflow - ## Rendering Markdown For a more document-centric rendering solution with support for things like @@ -537,20 +524,19 @@ the stylesheet-based Markdown renderer. [glamour]: https://github.com/charmbracelet/glamour - ## Feedback We’d love to hear your thoughts on this project. Feel free to drop us a note! -* [Twitter](https://twitter.com/charmcli) -* [The Fediverse](https://mastodon.social/@charmcli) -* [Discord](https://charm.sh/chat) +- [Twitter](https://twitter.com/charmcli) +- [The Fediverse](https://mastodon.social/@charmcli) +- [Discord](https://charm.sh/chat) ## License [MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE) -*** +--- Part of [Charm](https://charm.sh). @@ -558,7 +544,6 @@ Part of [Charm](https://charm.sh). Charm热爱开源 • Charm loves open source - [docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc [wish]: https://github.com/charmbracelet/wish [ssh-example]: examples/ssh From efbfa4d2db0eb45a401b29ef43a1ed0a49673946 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Wed, 1 May 2024 14:26:50 -0400 Subject: [PATCH 05/12] chore: update README.md Co-authored-by: Christian Rocha --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6758809d..f9e01b41 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ var style = lipgloss.NewStyle().Foreground(lipgloss.Color("219")) var wildStyle = style.Blink(true) ``` -Since `Style` data structure contains only primitive types, assigning a style +Since `Style` data structures contains only primitive types, assigning a style to another effectively creates a new copy of the style without mutating the original. From 9f111af5aa9868b4cc56303b4094243d500f7dd0 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Wed, 1 May 2024 15:59:11 -0400 Subject: [PATCH 06/12] chore(lint): add nolint directives --- get.go | 6 +++--- set.go | 6 +++--- style.go | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/get.go b/get.go index 50eba512..5783f9ad 100644 --- a/get.go +++ b/get.go @@ -432,7 +432,7 @@ func (s Style) getAsColor(k propKey) TerminalColor { } var c TerminalColor - switch k { + switch k { //nolint:exhaustive case foregroundKey: c = s.fgColor case backgroundKey: @@ -468,7 +468,7 @@ func (s Style) getAsInt(k propKey) int { if !s.isSet(k) { return 0 } - switch k { + switch k { //nolint:exhaustive case widthKey: return s.width case heightKey: @@ -503,7 +503,7 @@ func (s Style) getAsPosition(k propKey) Position { if !s.isSet(k) { return Position(0) } - switch k { + switch k { //nolint:exhaustive case alignHorizontalKey: return s.alignHorizontal case alignVerticalKey: diff --git a/set.go b/set.go index d4f4bff9..6f8b0825 100644 --- a/set.go +++ b/set.go @@ -6,7 +6,7 @@ func (s *Style) set(key propKey, value interface{}) { // them at zero or above. We could use uints instead, but the // conversions are a little tedious, so we're sticking with ints for // sake of usability. - switch key { + switch key { //nolint:exhaustive case foregroundKey: s.fgColor = colorOrNil(value) case backgroundKey: @@ -66,7 +66,7 @@ func (s *Style) set(key propKey, value interface{}) { case transformKey: s.transform = value.(func(string) string) default: - if v, ok := value.(bool); ok { + if v, ok := value.(bool); ok { //nolint:nestif if v { s.attrs |= int(key) } else { @@ -88,7 +88,7 @@ func (s *Style) set(key propKey, value interface{}) { // setFrom sets the property from another style. func (s *Style) setFrom(key propKey, i Style) { - switch key { + switch key { //nolint:exhaustive case foregroundKey: s.set(foregroundKey, i.fgColor) case backgroundKey: diff --git a/style.go b/style.go index 9e752729..b9c44fc5 100644 --- a/style.go +++ b/style.go @@ -394,7 +394,7 @@ func (s Style) Render(strs ...string) string { } // Padding - if !inline { + if !inline { //nolint:nestif if leftPadding > 0 { var st *termenv.Style if colorWhitespace || styleWhitespace { @@ -564,7 +564,7 @@ func pad(str string, n int, style *termenv.Style) string { return b.String() } -func max(a, b int) int { // nolint:unparam +func max(a, b int) int { //nolint:unparam if a > b { return a } From e35d216358713610a0f82a57c51ca769ece98fed Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Wed, 1 May 2024 15:59:30 -0400 Subject: [PATCH 07/12] chore(lint): end comments with periods --- style.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/style.go b/style.go index b9c44fc5..522352c2 100644 --- a/style.go +++ b/style.go @@ -15,7 +15,7 @@ type propKey int // Available properties. const ( - // bool props come first + // Boolean props come first. boldKey propKey = 1 << iota italicKey underlineKey @@ -27,8 +27,7 @@ const ( strikethroughSpacesKey colorWhitespaceKey - // non-bool props - + // Non-boolean props. foregroundKey backgroundKey widthKey From c986440c2acbd01abe20b9cc6409f54bc31570a4 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Wed, 1 May 2024 15:59:54 -0400 Subject: [PATCH 08/12] chore(lint): remove deprecated ifshort linting option --- .golangci-soft.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci-soft.yml b/.golangci-soft.yml index ef456e06..1b6824bb 100644 --- a/.golangci-soft.yml +++ b/.golangci-soft.yml @@ -23,7 +23,6 @@ linters: - gomnd - gomoddirectives - goprintffuncname - - ifshort # - lll - misspell - nakedret From 517b1a163eac429712956ba831e97c89b2616455 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Mon, 6 May 2024 09:16:14 -0400 Subject: [PATCH 09/12] fix: remove unused type --- renderer.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/renderer.go b/renderer.go index 85ffd254..233aa7c0 100644 --- a/renderer.go +++ b/renderer.go @@ -28,9 +28,6 @@ type Renderer struct { mtx sync.RWMutex } -// RendererOption is a function that can be used to configure a [Renderer]. -type RendererOption func(r *Renderer) - // DefaultRenderer returns the default renderer. func DefaultRenderer() *Renderer { return renderer From 3ee5dcab73cbfd5edd62af2d4f63b4644b942b4c Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Mon, 6 May 2024 15:59:26 -0400 Subject: [PATCH 10/12] chore(docs): doc updates with regard to style.Copy() deprecation --- README.md | 10 ++++++---- style.go | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f9e01b41..1376c7c6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ GoDoc Build Status phorm.ai -

Style definitions for nice terminal layouts. Built with TUIs in mind. @@ -226,12 +225,15 @@ For more on borders see [the docs][docs]. ## Copying Styles -Just use assignment +Just use assignment: ```go -var style = lipgloss.NewStyle().Foreground(lipgloss.Color("219")) +style := lipgloss.NewStyle().Foreground(lipgloss.Color("219")) + +copiedStyle := style // this is a true copy + +wildStyle := style.Blink(true) // this is also true copy, with blink added -var wildStyle = style.Blink(true) ``` Since `Style` data structures contains only primitive types, assigning a style diff --git a/style.go b/style.go index 522352c2..198751ce 100644 --- a/style.go +++ b/style.go @@ -189,7 +189,7 @@ func (s Style) String() string { // Copy returns a copy of this style, including any underlying string values. // -// Deprecated: Copy is deprecated and will be removed in a future release. +// Deprecated: to copy just use assignment (i.e. a := b). All methods also return a new style. func (s Style) Copy() Style { return s } From 2fe044adcb06371220c9abff05acbf714f715dc1 Mon Sep 17 00:00:00 2001 From: Hugo Leonardo Costa e Silva Date: Thu, 9 May 2024 13:07:41 -0300 Subject: [PATCH 11/12] fix: Change the propkeys from int to int64 When I create binaries for each OS using goreleaser, the propKeys generates an int overflow since the iota duplicates for each propKey This commit changes the int type to works well with binaries generated for arm and i386 archs. --- style.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/style.go b/style.go index 198751ce..c1e05c04 100644 --- a/style.go +++ b/style.go @@ -11,7 +11,7 @@ import ( const tabWidthDefault = 4 // Property for a key. -type propKey int +type propKey int64 // Available properties. const ( @@ -78,7 +78,7 @@ const ( ) // props is a set of properties. -type props int +type props int64 // set sets a property. func (p props) set(k propKey) props { From e3596ae70d0e46c30edea00f26892a71bba5b4aa Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Thu, 9 May 2024 12:20:09 -0400 Subject: [PATCH 12/12] chore(ci): test for different GOOS & GOARCH --- .github/workflows/build.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f804b0cd..21321398 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,3 +26,22 @@ jobs: - name: Test run: go test ./... + + test-goos: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + cache: true + # https://go.dev/doc/install/source#environment + - run: GOOS=darwin GOARCH=amd64 go test -c -v ./... + - run: GOOS=darwin GOARCH=arm64 go test -c -v ./... + - run: GOOS=linux GOARCH=386 go test -c -v ./... + - run: GOOS=linux GOARCH=amd64 go test -c -v ./... + - run: GOOS=linux GOARCH=arm go test -c -v ./... + - run: GOOS=linux GOARCH=arm64 go test -c -v ./... + - run: GOOS=windows GOARCH=amd64 go test -c -v ./... + - run: GOOS=windows GOARCH=386 go test -c -v ./... + - run: GOOS=windows GOARCH=arm go test -c -v ./... + - run: GOOS=windows GOARCH=arm64 go test -c -v ./...