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 (
"io"
"os"
"github.com/muesli/termenv"
)
var renderer = NewRenderer()
var renderer = NewRenderer(os.Stdout)
// Renderer is a lipgloss terminal renderer.
type Renderer struct {
@ -22,43 +23,29 @@ func DefaultRenderer() *Renderer {
return renderer
}
// SetDefaultRenderer sets the default global renderer.
func SetDefaultRenderer(r *Renderer) {
renderer = r
}
// 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{
output: termenv.DefaultOutput(),
}
for _, option := range options {
option(r)
output: termenv.NewOutput(w, opts...),
}
return r
}
// WithOutput sets the io.Writer to be used for rendering.
func WithOutput(w io.Writer) RendererOption {
return WithTermenvOutput(termenv.NewOutput(w))
// Output returns the termenv output.
func (r *Renderer) Output() *termenv.Output {
return r.output
}
// WithTermenvOutput sets the termenv Output to use for rendering.
func WithTermenvOutput(output *termenv.Output) RendererOption {
return func(r *Renderer) {
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)
}
// SetOutput sets the termenv output.
func (r *Renderer) SetOutput(o *termenv.Output) {
r.output = o
}
// ColorProfile returns the detected termenv color profile.

View File

@ -8,11 +8,13 @@ import (
)
func TestRendererHasDarkBackground(t *testing.T) {
r1 := NewRenderer(WithDarkBackground(false))
r1 := NewRenderer(os.Stdout)
r1.SetHasDarkBackground(false)
if r1.HasDarkBackground() {
t.Error("Expected renderer to have light background")
}
r2 := NewRenderer(WithDarkBackground(true))
r2 := NewRenderer(os.Stdout)
r2.SetHasDarkBackground(true)
if !r2.HasDarkBackground() {
t.Error("Expected renderer to have dark background")
}
@ -25,8 +27,8 @@ func TestRendererWithOutput(t *testing.T) {
}
defer f.Close()
defer os.Remove(f.Name())
output := termenv.NewOutput(f, termenv.WithProfile(termenv.TrueColor))
r := NewRenderer(WithTermenvOutput(output))
r := NewRenderer(f)
r.SetColorProfile(termenv.TrueColor)
if r.output.Profile != termenv.TrueColor {
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
}
// 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
// on the number of arguments. It follows the CSS shorthand rules for blocks
// like margin, padding. and borders. Here are how the rules work:

View File

@ -75,33 +75,20 @@ const (
// 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)
// WithString sets the underlying string value for this style.
func WithString(strs ...string) StyleOption {
return func(s *Style) {
s.value = joinString(strs...)
}
// 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 NewStyle() Style {
return renderer.NewStyle()
}
// 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 NewStyle(opts ...StyleOption) 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 {
func (r *Renderer) NewStyle() Style {
s := Style{r: r}
for _, opt := range opts {
opt(&s)
}
return s
}

View File

@ -1,6 +1,7 @@
package lipgloss
import (
"io/ioutil"
"reflect"
"testing"
@ -58,7 +59,9 @@ func TestStyleRender(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 {
style Style
expected string
@ -91,6 +94,10 @@ func TestStyleCustomRender(t *testing.T) {
r.NewStyle().Faint(true),
"\x1b[2mhello\x1b[0m",
},
{
NewStyle().Faint(true).Renderer(r),
"\x1b[2mhello\x1b[0m",
},
}
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) {
t.Parallel()
@ -323,7 +339,7 @@ func TestStyleValue(t *testing.T) {
},
{
name: "new style with string",
style: NewStyle(WithString("bar", "foobar")),
style: NewStyle().SetString("bar", "foobar"),
expected: "bar foobar foo",
},
}