From a0d1478a09bb65230ce1e95859436d994e8257d7 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Fri, 29 Mar 2024 15:59:54 -0400 Subject: [PATCH] refactor: remove Sequence and cleanup Replace Sequence with profile logic checks --- align.go | 4 +-- borders.go | 7 +++-- color.go | 61 ++-------------------------------------- color_test.go | 2 +- env.go | 2 +- examples/go.mod | 2 ++ examples/go.sum | 4 --- go.mod | 4 ++- go.sum | 6 ++-- profile.go | 75 ------------------------------------------------- style.go | 55 ++++++++++++++++++++---------------- whitespace.go | 13 +++++---- 12 files changed, 57 insertions(+), 178 deletions(-) diff --git a/align.go b/align.go index 4b4ce501..ea42b53a 100644 --- a/align.go +++ b/align.go @@ -9,7 +9,7 @@ import ( // Perform text alignment. If the string is multi-lined, we also make all lines // the same width by padding them with spaces. If a style is passed, use that // to style the spaces added. -func alignTextHorizontal(str string, pos Position, width int, style *Sequence) string { +func alignTextHorizontal(str string, pos Position, width int, style *ansi.Style) string { lines, widestLine := getLines(str) var b strings.Builder @@ -58,7 +58,7 @@ func alignTextHorizontal(str string, pos Position, width int, style *Sequence) s return b.String() } -func alignTextVertical(str string, pos Position, height int, _ *Sequence) string { +func alignTextVertical(str string, pos Position, height int, _ *ansi.Style) string { strHeight := strings.Count(str, "\n") + 1 if height < strHeight { return str diff --git a/borders.go b/borders.go index f14dcd2d..9a7d7751 100644 --- a/borders.go +++ b/borders.go @@ -406,12 +406,13 @@ func (s Style) styleBorder(border string, fg, bg TerminalColor) string { return border } - style := s.r.ColorProfile().Sequence() + p := s.r.ColorProfile() - if fg != noColor { + var style ansi.Style + if fg != noColor && p > Ascii { style = style.ForegroundColor(fg.color(s.r)) } - if bg != noColor { + if bg != noColor && p > Ascii { style = style.BackgroundColor(bg.color(s.r)) } diff --git a/color.go b/color.go index 24ccf5f9..f720d379 100644 --- a/color.go +++ b/color.go @@ -1,7 +1,7 @@ package lipgloss import ( - "strconv" + "image/color" "github.com/charmbracelet/x/exp/term/ansi" "github.com/lucasb-eyer/go-colorful" @@ -10,7 +10,6 @@ import ( // TerminalColor is a color intended to be rendered in the terminal. type TerminalColor interface { color(*Renderer) ansi.Color - RGBA() (r, g, b, a uint32) } var noColor = NoColor{} @@ -25,7 +24,7 @@ var noColor = NoColor{} type NoColor struct{} func (NoColor) color(*Renderer) ansi.Color { - return nil + return color.Black } // RGBA returns the RGBA value of this color. Because we have to return @@ -33,8 +32,6 @@ func (NoColor) color(*Renderer) ansi.Color { // black with 100% opacity. // // Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. -// -// Deprecated. func (n NoColor) RGBA() (r, g, b, a uint32) { return 0x0, 0x0, 0x0, 0xFFFF //nolint:gomnd } @@ -49,16 +46,6 @@ func (c Color) color(r *Renderer) ansi.Color { return r.ColorProfile().Color(string(c)) } -// RGBA returns the RGBA value of this color. This satisfies the Go Color -// interface. Note that on error we return black with 100% opacity, or: -// -// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. -// -// Deprecated. -func (c Color) RGBA() (r, g, b, a uint32) { - return c.color(DefaultRenderer()).RGBA() -} - // ANSIColor is a color specified by an ANSI256 color value. // // Example usage: @@ -71,17 +58,6 @@ func (ac ANSIColor) color(r *Renderer) ansi.Color { return r.ColorProfile().Convert(ansi.ExtendedColor(ac)) } -// RGBA returns the RGBA value of this color. This satisfies the Go Color -// interface. Note that on error we return black with 100% opacity, or: -// -// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. -// -// Deprecated. -func (ac ANSIColor) RGBA() (r, g, b, a uint32) { - cf := Color(strconv.FormatUint(uint64(ac), 10)) - return cf.RGBA() -} - // AdaptiveColor provides color options for light and dark backgrounds. The // appropriate color will be returned at runtime based on the darkness of the // terminal background color. @@ -101,16 +77,6 @@ func (ac AdaptiveColor) color(r *Renderer) ansi.Color { return Color(ac.Light).color(r) } -// RGBA returns the RGBA value of this color. This satisfies the Go Color -// interface. Note that on error we return black with 100% opacity, or: -// -// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. -// -// Deprecated. -func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) { - return ac.color(DefaultRenderer()).RGBA() -} - // CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color // profiles. Automatic color degradation will not be performed. type CompleteColor struct { @@ -129,21 +95,10 @@ func (c CompleteColor) color(r *Renderer) ansi.Color { case ANSI: return p.Color(c.ANSI) default: - return NoColor{} + return noColor } } -// RGBA returns the RGBA value of this color. This satisfies the Go Color -// interface. Note that on error we return black with 100% opacity, or: -// -// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. -// CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color -// -// Deprecated. -func (c CompleteColor) RGBA() (r, g, b, a uint32) { - return c.color(DefaultRenderer()).RGBA() -} - // CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color // profiles, with separate options for light and dark backgrounds. Automatic // color degradation will not be performed. @@ -159,16 +114,6 @@ func (cac CompleteAdaptiveColor) color(r *Renderer) ansi.Color { return cac.Light.color(r) } -// RGBA returns the RGBA value of this color. This satisfies the Go Color -// interface. Note that on error we return black with 100% opacity, or: -// -// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. -// -// Deprecated. -func (cac CompleteAdaptiveColor) RGBA() (r, g, b, a uint32) { - return cac.color(DefaultRenderer()).RGBA() -} - // ConvertToRGB converts a Color to a colorful.Color. func ConvertToRGB(c ansi.Color) colorful.Color { ch, _ := colorful.MakeColor(c) diff --git a/color_test.go b/color_test.go index 1d4b17c8..2825a298 100644 --- a/color_test.go +++ b/color_test.go @@ -225,7 +225,7 @@ func TestRGBA(t *testing.T) { r.SetColorProfile(tc.profile) r.SetHasDarkBackground(tc.darkBg) - r, g, b, _ := tc.input.RGBA() + r, g, b, _ := tc.input.color(r).RGBA() o := uint(r/256)<<16 + uint(g/256)<<8 + uint(b/256) if o != tc.expected { diff --git a/env.go b/env.go index d4af4811..b807098a 100644 --- a/env.go +++ b/env.go @@ -14,7 +14,7 @@ import ( // If NO_COLOR is set, this will return true, ignoring CLICOLOR/CLICOLOR_FORCE // If CLICOLOR=="0", it will be true only if CLICOLOR_FORCE is also "0" or is unset. func envNoColor(env map[string]string) bool { - return env["NO_COLOR"] != "" || (env["CLICOLOR"] == "0" && !cliColorForced(env)) + return isTrue(env["NO_COLOR"]) || (!isTrue(env["CLICOLOR"]) && !cliColorForced(env)) } // EnvColorProfile returns the color profile based on environment variables set diff --git a/examples/go.mod b/examples/go.mod index a7fbe027..04462bc5 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -4,6 +4,8 @@ go 1.17 replace github.com/charmbracelet/lipgloss => ../ +replace github.com/charmbracelet/x/exp/term => ../../x/exp/term + require ( github.com/charmbracelet/lipgloss v0.4.0 github.com/charmbracelet/wish v0.5.0 diff --git a/examples/go.sum b/examples/go.sum index 403400a1..9bb1fa7e 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -17,9 +17,6 @@ github.com/charmbracelet/keygen v0.3.0/go.mod h1:1ukgO8806O25lUZ5s0IrNur+RlwTBER github.com/charmbracelet/wish v0.5.0 h1:FkkdNBFqrLABR1ciNrAL2KCxoyWfKhXnIGZw6GfAtPg= github.com/charmbracelet/wish v0.5.0/go.mod h1:5GAn5SrDSZ7cgKjnC+3kDmiIo7I6k4/AYiRzC4+tpCk= github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= -github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd/go.mod h1:6GZ13FjIP6eOCqWU4lqgveGnYxQo9c3qBzHPeFu4HBE= -github.com/charmbracelet/x/exp/term v0.0.0-20240422203001-5cc5941b761c h1:MF4XzYBazvaM6g2IlOwgnsIuteW5q8tRfldetAHk2yg= -github.com/charmbracelet/x/exp/term v0.0.0-20240422203001-5cc5941b761c/go.mod h1:yQqGHmheaQfkqiJWjklPHVAq1dKbk8uGbcoS/lcKCJ0= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -130,7 +127,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/go.mod b/go.mod index 6cd0ec3e..2eba3e7f 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,13 @@ retract v0.7.0 // v0.7.0 introduces a bug that causes some apps to freeze. go 1.18 +replace github.com/charmbracelet/x/exp/term => ../x/exp/term + require ( github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd github.com/lucasb-eyer/go-colorful v1.2.0 github.com/rivo/uniseg v0.4.7 - golang.org/x/sys v0.18.0 + golang.org/x/sys v0.19.0 ) require ( diff --git a/go.sum b/go.sum index 998cd191..fd2e3ad1 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd h1:HqBjkSFXXfW4IgX3TMKipWoPEN08T3Pi4SA/3DLss/U= -github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd/go.mod h1:6GZ13FjIP6eOCqWU4lqgveGnYxQo9c3qBzHPeFu4HBE= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -12,5 +10,5 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/profile.go b/profile.go index e8e463ef..dedcd0f2 100644 --- a/profile.go +++ b/profile.go @@ -26,81 +26,6 @@ const ( TrueColor ) -// Sequence returns a style sequence for the profile. -func (p Profile) Sequence() Sequence { - return Sequence{Profile: p} -} - -// Sequence represents a text ANSI sequence style. -type Sequence struct { - ansi.Style - Profile -} - -// Styled returns a styled string. -func (s Sequence) Styled(str string) string { - if s.Profile <= NoTTY { - return str - } - return s.Style.Styled(str) -} - -// Bold returns a sequence with bold enabled. -func (s Sequence) Bold() Sequence { - return Sequence{s.Style.Bold(), s.Profile} -} - -// Italic returns a sequence with italic enabled. -func (s Sequence) Italic() Sequence { - return Sequence{s.Style.Italic(), s.Profile} -} - -// Underline returns a sequence with underline enabled. -func (s Sequence) Underline() Sequence { - return Sequence{s.Style.Underline(), s.Profile} -} - -// Strikethrough returns a sequence with strikethrough enabled. -func (s Sequence) Strikethrough() Sequence { - return Sequence{s.Style.Strikethrough(), s.Profile} -} - -// Inverse returns a sequence with inverse enabled. -func (s Sequence) Reverse() Sequence { - return Sequence{s.Style.Reverse(), s.Profile} -} - -// SlowBlink returns a sequence with slow blink enabled. -func (s Sequence) SlowBlink() Sequence { - return Sequence{s.Style.SlowBlink(), s.Profile} -} - -// RapidBlink returns a sequence with rapid blink enabled. -func (s Sequence) RapidBlink() Sequence { - return Sequence{s.Style.RapidBlink(), s.Profile} -} - -// Faint returns a sequence with faint enabled. -func (s Sequence) Faint() Sequence { - return Sequence{s.Style.Faint(), s.Profile} -} - -// ForegroundColor returns a sequence with the foreground color set. -func (s Sequence) ForegroundColor(c ansi.Color) Sequence { - if s.Profile <= Ascii { - return s - } - return Sequence{s.Style.ForegroundColor(c), s.Profile} -} - -// BackgroundColor returns a sequence with the background color set. -func (s Sequence) BackgroundColor(c ansi.Color) Sequence { - if s.Profile <= Ascii { - return s - } - return Sequence{s.Style.BackgroundColor(c), s.Profile} -} - // Convert transforms a given Color to a Color supported within the Profile. func (p Profile) Convert(c ansi.Color) ansi.Color { if p <= Ascii { diff --git a/style.go b/style.go index 8af399e9..37f206c1 100644 --- a/style.go +++ b/style.go @@ -185,17 +185,17 @@ func (s Style) Render(strs ...string) string { str = joinString(strs...) p = s.r.ColorProfile() - te = p.Sequence() - teSpace = p.Sequence() - teWhitespace = p.Sequence() - - bold = s.getAsBool(boldKey, false) - italic = s.getAsBool(italicKey, false) - underline = s.getAsBool(underlineKey, false) - strikethrough = s.getAsBool(strikethroughKey, false) - reverse = s.getAsBool(reverseKey, false) - blink = s.getAsBool(blinkKey, false) - faint = s.getAsBool(faintKey, false) + te ansi.Style + teSpace ansi.Style + teWhitespace ansi.Style + + bold = s.getAsBool(boldKey, false) && p >= Ascii + italic = s.getAsBool(italicKey, false) && p >= Ascii + underline = s.getAsBool(underlineKey, false) && p >= Ascii + strikethrough = s.getAsBool(strikethroughKey, false) && p >= Ascii + reverse = s.getAsBool(reverseKey, false) && p >= Ascii + blink = s.getAsBool(blinkKey, false) && p >= Ascii + faint = s.getAsBool(faintKey, false) && p >= Ascii fg = s.getAsColor(foregroundKey) bg = s.getAsColor(backgroundKey) @@ -228,6 +228,12 @@ func (s Style) Render(strs ...string) string { transform = s.getAsTransform(transformKey) ) + // Disable colors for Ascii and below profiles. + if p <= Ascii { + fg = noColor + bg = noColor + } + if transform != nil { str = transform(str) } @@ -335,7 +341,7 @@ func (s Style) Render(strs ...string) string { // Padding if !inline { if leftPadding > 0 { - var st *Sequence + var st *ansi.Style if colorWhitespace || styleWhitespace { st = &teWhitespace } @@ -343,7 +349,7 @@ func (s Style) Render(strs ...string) string { } if rightPadding > 0 { - var st *Sequence + var st *ansi.Style if colorWhitespace || styleWhitespace { st = &teWhitespace } @@ -371,7 +377,7 @@ func (s Style) Render(strs ...string) string { numLines := strings.Count(str, "\n") if !(numLines == 0 && width == 0) { - var st *Sequence + var st *ansi.Style if colorWhitespace || styleWhitespace { st = &teWhitespace } @@ -429,17 +435,18 @@ func (s Style) applyMargins(str string, inline bool) string { bottomMargin = s.getAsInt(marginBottomKey) leftMargin = s.getAsInt(marginLeftKey) - styler = s.r.ColorProfile().Sequence() + p = s.r.ColorProfile() + style ansi.Style ) bgc := s.getAsColor(marginBackgroundKey) - if bgc != noColor { - styler = styler.BackgroundColor(bgc.color(s.r)) + if bgc != noColor && p > Ascii { + style = style.BackgroundColor(bgc.color(s.r)) } // Add left and right margin - str = padLeft(str, leftMargin, &styler) - str = padRight(str, rightMargin, &styler) + str = padLeft(str, leftMargin, &style) + str = padRight(str, rightMargin, &style) // Top/bottom margin if !inline { @@ -447,10 +454,10 @@ func (s Style) applyMargins(str string, inline bool) string { spaces := strings.Repeat(" ", width) if topMargin > 0 { - str = styler.Styled(strings.Repeat(spaces+"\n", topMargin)) + str + str = style.Styled(strings.Repeat(spaces+"\n", topMargin)) + str } if bottomMargin > 0 { - str += styler.Styled(strings.Repeat("\n"+spaces, bottomMargin)) + str += style.Styled(strings.Repeat("\n"+spaces, bottomMargin)) } } @@ -458,19 +465,19 @@ func (s Style) applyMargins(str string, inline bool) string { } // Apply left padding. -func padLeft(str string, n int, style *Sequence) string { +func padLeft(str string, n int, style *ansi.Style) string { return pad(str, -n, style) } // Apply right padding. -func padRight(str string, n int, style *Sequence) string { +func padRight(str string, n int, style *ansi.Style) string { return pad(str, n, style) } // pad adds padding to either the left or right side of a string. // Positive values add to the right side while negative values // add to the left side. -func pad(str string, n int, style *Sequence) string { +func pad(str string, n int, style *ansi.Style) string { if n == 0 { return str } diff --git a/whitespace.go b/whitespace.go index d1c3c398..5846529c 100644 --- a/whitespace.go +++ b/whitespace.go @@ -10,7 +10,7 @@ import ( type whitespace struct { re *Renderer chars string - style Sequence + style ansi.Style } // newWhitespace creates a new whitespace renderer. The order of the options @@ -18,8 +18,7 @@ type whitespace struct { // other options might depend on it. func newWhitespace(r *Renderer, opts ...WhitespaceOption) *whitespace { w := &whitespace{ - re: r, - style: r.ColorProfile().Sequence(), + re: r, } for _, opt := range opts { opt(w) @@ -63,14 +62,18 @@ type WhitespaceOption func(*whitespace) // WithWhitespaceForeground sets the color of the characters in the whitespace. func WithWhitespaceForeground(c TerminalColor) WhitespaceOption { return func(w *whitespace) { - w.style = w.style.ForegroundColor(c.color(w.re)) + if w.re.ColorProfile() > Ascii { + w.style = w.style.ForegroundColor(c.color(w.re)) + } } } // WithWhitespaceBackground sets the background color of the whitespace. func WithWhitespaceBackground(c TerminalColor) WhitespaceOption { return func(w *whitespace) { - w.style = w.style.BackgroundColor(c.color(w.re)) + if w.re.ColorProfile() > Ascii { + w.style = w.style.BackgroundColor(c.color(w.re)) + } } }