main
Raw Download raw file
  1package lipgloss
  2
  3import (
  4	"io"
  5	"sync"
  6
  7	"github.com/muesli/termenv"
  8)
  9
 10// We're manually creating the struct here to avoid initializing the output and
 11// query the terminal multiple times.
 12var renderer = &Renderer{
 13	output: termenv.DefaultOutput(),
 14}
 15
 16// Renderer is a lipgloss terminal renderer.
 17type Renderer struct {
 18	output            *termenv.Output
 19	colorProfile      termenv.Profile
 20	hasDarkBackground bool
 21
 22	getColorProfile      sync.Once
 23	explicitColorProfile bool
 24
 25	getBackgroundColor      sync.Once
 26	explicitBackgroundColor bool
 27
 28	mtx sync.RWMutex
 29}
 30
 31// DefaultRenderer returns the default renderer.
 32func DefaultRenderer() *Renderer {
 33	return renderer
 34}
 35
 36// SetDefaultRenderer sets the default global renderer.
 37func SetDefaultRenderer(r *Renderer) {
 38	renderer = r
 39}
 40
 41// NewRenderer creates a new Renderer.
 42//
 43// w will be used to determine the terminal's color capabilities.
 44func NewRenderer(w io.Writer, opts ...termenv.OutputOption) *Renderer {
 45	r := &Renderer{
 46		output: termenv.NewOutput(w, opts...),
 47	}
 48	return r
 49}
 50
 51// Output returns the termenv output.
 52func (r *Renderer) Output() *termenv.Output {
 53	r.mtx.RLock()
 54	defer r.mtx.RUnlock()
 55	return r.output
 56}
 57
 58// SetOutput sets the termenv output.
 59func (r *Renderer) SetOutput(o *termenv.Output) {
 60	r.mtx.Lock()
 61	defer r.mtx.Unlock()
 62	r.output = o
 63}
 64
 65// ColorProfile returns the detected termenv color profile.
 66func (r *Renderer) ColorProfile() termenv.Profile {
 67	r.mtx.RLock()
 68	defer r.mtx.RUnlock()
 69
 70	if !r.explicitColorProfile {
 71		r.getColorProfile.Do(func() {
 72			// NOTE: we don't need to lock here because sync.Once provides its
 73			// own locking mechanism.
 74			r.colorProfile = r.output.EnvColorProfile()
 75		})
 76	}
 77
 78	return r.colorProfile
 79}
 80
 81// ColorProfile returns the detected termenv color profile.
 82func ColorProfile() termenv.Profile {
 83	return renderer.ColorProfile()
 84}
 85
 86// SetColorProfile sets the color profile on the renderer. This function exists
 87// mostly for testing purposes so that you can assure you're testing against
 88// a specific profile.
 89//
 90// Outside of testing you likely won't want to use this function as the color
 91// profile will detect and cache the terminal's color capabilities and choose
 92// the best available profile.
 93//
 94// Available color profiles are:
 95//
 96//	termenv.Ascii     // no color, 1-bit
 97//	termenv.ANSI      //16 colors, 4-bit
 98//	termenv.ANSI256   // 256 colors, 8-bit
 99//	termenv.TrueColor // 16,777,216 colors, 24-bit
100//
101// This function is thread-safe.
102func (r *Renderer) SetColorProfile(p termenv.Profile) {
103	r.mtx.Lock()
104	defer r.mtx.Unlock()
105
106	r.colorProfile = p
107	r.explicitColorProfile = true
108}
109
110// SetColorProfile sets the color profile on the default renderer. This
111// function exists mostly for testing purposes so that you can assure you're
112// testing against a specific profile.
113//
114// Outside of testing you likely won't want to use this function as the color
115// profile will detect and cache the terminal's color capabilities and choose
116// the best available profile.
117//
118// Available color profiles are:
119//
120//	termenv.Ascii     // no color, 1-bit
121//	termenv.ANSI      //16 colors, 4-bit
122//	termenv.ANSI256   // 256 colors, 8-bit
123//	termenv.TrueColor // 16,777,216 colors, 24-bit
124//
125// This function is thread-safe.
126func SetColorProfile(p termenv.Profile) {
127	renderer.SetColorProfile(p)
128}
129
130// HasDarkBackground returns whether or not the terminal has a dark background.
131func HasDarkBackground() bool {
132	return renderer.HasDarkBackground()
133}
134
135// HasDarkBackground returns whether or not the renderer will render to a dark
136// background. A dark background can either be auto-detected, or set explicitly
137// on the renderer.
138func (r *Renderer) HasDarkBackground() bool {
139	r.mtx.RLock()
140	defer r.mtx.RUnlock()
141
142	if !r.explicitBackgroundColor {
143		r.getBackgroundColor.Do(func() {
144			// NOTE: we don't need to lock here because sync.Once provides its
145			// own locking mechanism.
146			r.hasDarkBackground = r.output.HasDarkBackground()
147		})
148	}
149
150	return r.hasDarkBackground
151}
152
153// SetHasDarkBackground sets the background color detection value for the
154// default renderer. This function exists mostly for testing purposes so that
155// you can assure you're testing against a specific background color setting.
156//
157// Outside of testing you likely won't want to use this function as the
158// backgrounds value will be automatically detected and cached against the
159// terminal's current background color setting.
160//
161// This function is thread-safe.
162func SetHasDarkBackground(b bool) {
163	renderer.SetHasDarkBackground(b)
164}
165
166// SetHasDarkBackground sets the background color detection value on the
167// renderer. This function exists mostly for testing purposes so that you can
168// assure you're testing against a specific background color setting.
169//
170// Outside of testing you likely won't want to use this function as the
171// backgrounds value will be automatically detected and cached against the
172// terminal's current background color setting.
173//
174// This function is thread-safe.
175func (r *Renderer) SetHasDarkBackground(b bool) {
176	r.mtx.Lock()
177	defer r.mtx.Unlock()
178
179	r.hasDarkBackground = b
180	r.explicitBackgroundColor = true
181}