mirror of
https://github.com/charmbracelet/lipgloss.git
synced 2024-09-11 14:07:21 +03:00
ref(renderer): change renderer impl
This commit is contained in:
parent
d09532a91a
commit
c5382b35ef
47
renderer.go
47
renderer.go
@ -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.
|
||||||
|
@ -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
7
set.go
@ -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:
|
||||||
|
27
style.go
27
style.go
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user