ref(renderer): change renderer impl

This commit is contained in:
Ayman Bagabas 2023-03-02 16:53:19 -05:00
parent d09532a91a
commit c5382b35ef
5 changed files with 55 additions and 56 deletions

View File

@ -2,11 +2,12 @@ package lipgloss
import ( import (
"io" "io"
"os"
"github.com/muesli/termenv" "github.com/muesli/termenv"
) )
var renderer = NewRenderer() var renderer = NewRenderer(os.Stdout)
// Renderer is a lipgloss terminal renderer. // Renderer is a lipgloss terminal renderer.
type Renderer struct { type Renderer struct {
@ -22,43 +23,29 @@ func DefaultRenderer() *Renderer {
return renderer return renderer
} }
// SetDefaultRenderer sets the default global renderer.
func SetDefaultRenderer(r *Renderer) {
renderer = r
}
// NewRenderer creates a new Renderer. // NewRenderer creates a new Renderer.
func NewRenderer(options ...RendererOption) *Renderer { //
// w will be used to determine the terminal's color capabilities.
func NewRenderer(w io.Writer, opts ...termenv.OutputOption) *Renderer {
r := &Renderer{ r := &Renderer{
output: termenv.DefaultOutput(), output: termenv.NewOutput(w, opts...),
}
for _, option := range options {
option(r)
} }
return r return r
} }
// WithOutput sets the io.Writer to be used for rendering. // Output returns the termenv output.
func WithOutput(w io.Writer) RendererOption { func (r *Renderer) Output() *termenv.Output {
return WithTermenvOutput(termenv.NewOutput(w)) return r.output
} }
// WithTermenvOutput sets the termenv Output to use for rendering. // SetOutput sets the termenv output.
func WithTermenvOutput(output *termenv.Output) RendererOption { func (r *Renderer) SetOutput(o *termenv.Output) {
return func(r *Renderer) { r.output = o
r.output = output
}
}
// WithDarkBackground can force the renderer to use a light/dark background.
func WithDarkBackground(dark bool) RendererOption {
return func(r *Renderer) {
r.SetHasDarkBackground(dark)
}
}
// WithColorProfile sets the color profile on the renderer. This function is
// primarily intended for testing. For details, see the note on
// [Renderer.SetColorProfile].
func WithColorProfile(p termenv.Profile) RendererOption {
return func(r *Renderer) {
r.SetColorProfile(p)
}
} }
// ColorProfile returns the detected termenv color profile. // ColorProfile returns the detected termenv color profile.

View File

@ -8,11 +8,13 @@ import (
) )
func TestRendererHasDarkBackground(t *testing.T) { func TestRendererHasDarkBackground(t *testing.T) {
r1 := NewRenderer(WithDarkBackground(false)) r1 := NewRenderer(os.Stdout)
r1.SetHasDarkBackground(false)
if r1.HasDarkBackground() { if r1.HasDarkBackground() {
t.Error("Expected renderer to have light background") t.Error("Expected renderer to have light background")
} }
r2 := NewRenderer(WithDarkBackground(true)) r2 := NewRenderer(os.Stdout)
r2.SetHasDarkBackground(true)
if !r2.HasDarkBackground() { if !r2.HasDarkBackground() {
t.Error("Expected renderer to have dark background") t.Error("Expected renderer to have dark background")
} }
@ -25,8 +27,8 @@ func TestRendererWithOutput(t *testing.T) {
} }
defer f.Close() defer f.Close()
defer os.Remove(f.Name()) defer os.Remove(f.Name())
output := termenv.NewOutput(f, termenv.WithProfile(termenv.TrueColor)) r := NewRenderer(f)
r := NewRenderer(WithTermenvOutput(output)) r.SetColorProfile(termenv.TrueColor)
if r.output.Profile != termenv.TrueColor { if r.output.Profile != termenv.TrueColor {
t.Error("Expected renderer to use true color") t.Error("Expected renderer to use true color")
} }

7
set.go
View File

@ -520,6 +520,13 @@ func (s Style) StrikethroughSpaces(v bool) Style {
return s return s
} }
// Renderer sets the renderer for the style. This is useful for changing the
// renderer for a style that is being used in a different context.
func (s Style) Renderer(r *Renderer) Style {
s.r = r
return s
}
// whichSidesInt is a helper method for setting values on sides of a block based // whichSidesInt is a helper method for setting values on sides of a block based
// on the number of arguments. It follows the CSS shorthand rules for blocks // on the number of arguments. It follows the CSS shorthand rules for blocks
// like margin, padding. and borders. Here are how the rules work: // like margin, padding. and borders. Here are how the rules work:

View File

@ -75,33 +75,20 @@ const (
// A set of properties. // A set of properties.
type rules map[propKey]interface{} type rules map[propKey]interface{}
// StyleOption is a function that applies a style option to a Style. // NewStyle returns a new, empty Style. While it's syntactic sugar for the
type StyleOption func(*Style) // Style{} primitive, it's recommended to use this function for creating styles
// in case the underlying implementation changes. It takes an optional string
// WithString sets the underlying string value for this style. // value to be set as the underlying string value for this style.
func WithString(strs ...string) StyleOption { func NewStyle() Style {
return func(s *Style) { return renderer.NewStyle()
s.value = joinString(strs...)
}
} }
// NewStyle returns a new, empty Style. While it's syntactic sugar for the // 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 // Style{} primitive, it's recommended to use this function for creating styles
// in case the underlying implementation changes. It takes an optional string // in case the underlying implementation changes. It takes an optional string
// value to be set as the underlying string value for this style. // value to be set as the underlying string value for this style.
func NewStyle(opts ...StyleOption) Style { func (r *Renderer) NewStyle() Style {
return renderer.NewStyle(opts...)
}
// 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
// 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}
for _, opt := range opts {
opt(&s)
}
return s return s
} }

View File

@ -1,6 +1,7 @@
package lipgloss package lipgloss
import ( import (
"io/ioutil"
"reflect" "reflect"
"testing" "testing"
@ -58,7 +59,9 @@ func TestStyleRender(t *testing.T) {
} }
func TestStyleCustomRender(t *testing.T) { func TestStyleCustomRender(t *testing.T) {
r := NewRenderer(WithColorProfile(termenv.TrueColor), WithDarkBackground(false)) r := NewRenderer(ioutil.Discard)
r.SetHasDarkBackground(false)
r.SetColorProfile(termenv.TrueColor)
tt := []struct { tt := []struct {
style Style style Style
expected string expected string
@ -91,6 +94,10 @@ func TestStyleCustomRender(t *testing.T) {
r.NewStyle().Faint(true), r.NewStyle().Faint(true),
"\x1b[2mhello\x1b[0m", "\x1b[2mhello\x1b[0m",
}, },
{
NewStyle().Faint(true).Renderer(r),
"\x1b[2mhello\x1b[0m",
},
} }
for i, tc := range tt { for i, tc := range tt {
@ -104,6 +111,15 @@ func TestStyleCustomRender(t *testing.T) {
} }
} }
func TestStyleRenderer(t *testing.T) {
r := NewRenderer(ioutil.Discard)
s1 := NewStyle().Bold(true)
s2 := s1.Renderer(r)
if s1.r == s2.r {
t.Fatalf("expected different renderers")
}
}
func TestValueCopy(t *testing.T) { func TestValueCopy(t *testing.T) {
t.Parallel() t.Parallel()
@ -323,7 +339,7 @@ func TestStyleValue(t *testing.T) {
}, },
{ {
name: "new style with string", name: "new style with string",
style: NewStyle(WithString("bar", "foobar")), style: NewStyle().SetString("bar", "foobar"),
expected: "bar foobar foo", expected: "bar foobar foo",
}, },
} }