Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make Lipgloss Style thread-safe #139

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions get.go
Expand Up @@ -397,12 +397,12 @@ func (s Style) GetFrameSize() (x, y int) {

// Returns whether or not the given property is set.
func (s Style) isSet(k propKey) bool {
_, exists := s.rules[k]
_, exists := s.rules.Load(k)
return exists
}

func (s Style) getAsBool(k propKey, defaultVal bool) bool {
v, ok := s.rules[k]
v, ok := s.rules.Load(k)
if !ok {
return defaultVal
}
Expand All @@ -413,7 +413,7 @@ func (s Style) getAsBool(k propKey, defaultVal bool) bool {
}

func (s Style) getAsColor(k propKey) TerminalColor {
v, ok := s.rules[k]
v, ok := s.rules.Load(k)
if !ok {
return noColor
}
Expand All @@ -424,7 +424,7 @@ func (s Style) getAsColor(k propKey) TerminalColor {
}

func (s Style) getAsInt(k propKey) int {
v, ok := s.rules[k]
v, ok := s.rules.Load(k)
if !ok {
return 0
}
Expand All @@ -435,7 +435,7 @@ func (s Style) getAsInt(k propKey) int {
}

func (s Style) getAsPosition(k propKey) Position {
v, ok := s.rules[k]
v, ok := s.rules.Load(k)
if !ok {
return Position(0)
}
Expand All @@ -446,7 +446,7 @@ func (s Style) getAsPosition(k propKey) Position {
}

func (s Style) getBorderStyle() Border {
v, ok := s.rules[borderStyleKey]
v, ok := s.rules.Load(borderStyleKey)
if !ok {
return noBorder
}
Expand Down
14 changes: 2 additions & 12 deletions set.go
@@ -1,26 +1,16 @@
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 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)
s.rules.Store(key, max(0, v))
default:
s.rules[key] = v
s.rules.Store(key, v)
}
}

Expand Down
48 changes: 28 additions & 20 deletions style.go
Expand Up @@ -2,6 +2,7 @@ package lipgloss

import (
"strings"
"sync"
"unicode"

"github.com/muesli/reflow/truncate"
Expand Down Expand Up @@ -72,9 +73,6 @@ const (
strikethroughSpacesKey
)

// A set of properties.
type rules map[propKey]interface{}

// StyleOption is a function that applies a style option to a Style.
type StyleOption func(*Style)

Expand All @@ -98,7 +96,10 @@ func NewStyle(opts ...StyleOption) Style {
// in case the underlying implementation changes. It takes an optional string
// value to be set as the underlying string value for this style.
func (r *Renderer) NewStyle(opts ...StyleOption) Style {
s := Style{r: r}
s := Style{
r: r,
rules: &sync.Map{},
}
for _, opt := range opts {
opt(&s)
}
Expand All @@ -108,7 +109,7 @@ func (r *Renderer) NewStyle(opts ...StyleOption) Style {
// Style contains a set of rules that comprise a style as a whole.
type Style struct {
r *Renderer
rules map[propKey]interface{}
rules *sync.Map
value string
}

Expand Down Expand Up @@ -143,11 +144,12 @@ func (s Style) String() string {
// Copy returns a copy of this style, including any underlying string values.
func (s Style) Copy() Style {
o := NewStyle()
o.init()
for k, v := range s.rules {
o.rules[k] = v
}
s.rules.Range(func(k, v interface{}) bool {
o.rules.Store(k, v)
return true
})
o.r = s.r

o.value = s.value
return o
}
Expand All @@ -158,28 +160,29 @@ 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, v := range i.rules {
i.rules.Range(func(k, v interface{}) bool {
switch k {
case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey:
// Margins are not inherited
continue
return true
case paddingTopKey, paddingRightKey, paddingBottomKey, paddingLeftKey:
// Padding is not inherited
continue
return true
case backgroundKey:
// The margins also inherit the background color
if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) {
s.rules[marginBackgroundKey] = v
s.rules.Store(marginBackgroundKey, v)
}
}

if _, exists := s.rules[k]; exists {
continue
if _, exists := s.rules.Load(k); exists {
return true
}
s.rules[k] = v
}

s.rules.Store(k, v)
return true
})

return s
}

Expand Down Expand Up @@ -236,7 +239,12 @@ func (s Style) Render(strs ...string) string {
useSpaceStyler = underlineSpaces || strikethroughSpaces
)

if len(s.rules) == 0 {
var l int
s.rules.Range(func(k, v interface{}) bool {
l++
return false
})
if l == 0 {
return str
}

Expand Down