Skip to content

Commit

Permalink
feat(cellbuf): add color profile support
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Oct 29, 2024
1 parent e01e068 commit 4c6de79
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 10 deletions.
5 changes: 5 additions & 0 deletions cellbuf/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ module github.com/charmbracelet/x/cellbuf
go 1.18

require (
github.com/charmbracelet/colorprofile v0.1.4
github.com/charmbracelet/x/ansi v0.4.0
github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91
)

require (
github.com/charmbracelet/x/term v0.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.19.0 // indirect
)
11 changes: 11 additions & 0 deletions cellbuf/go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
github.com/charmbracelet/colorprofile v0.1.4 h1:UZm1273VW+rD7gfH6NqfTWi9TGyPsFvGDNcIFlZw/As=
github.com/charmbracelet/colorprofile v0.1.4/go.mod h1:zZrJB+Qq2tyGEprGJwY1u05me+TmRqfECZOxWTxyoVU=
github.com/charmbracelet/x/ansi v0.4.0 h1:NqwHA4B23VwsDn4H3VcNX1W1tOmgnvY1NDx5tOXdnOU=
github.com/charmbracelet/x/ansi v0.4.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91 h1:D5OO0lVavz7A+Swdhp62F9gbkibxmz9B2hZ/jVdMPf0=
github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91/go.mod h1:Ey8PFmYwH+/td9bpiEx07Fdx9ZVkxfIjWXxBluxF4Nw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
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.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
11 changes: 11 additions & 0 deletions cellbuf/link.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package cellbuf

import "github.com/charmbracelet/colorprofile"

// Link represents a hyperlink in the terminal screen.
type Link struct {
URL string
Expand All @@ -26,3 +28,12 @@ func (h Link) Equal(o Link) bool {
func (h Link) Empty() bool {
return h.URL == "" && h.URLID == ""
}

// Convert converts a hyperlink to respect the given color profile.
func (h Link) Convert(p colorprofile.Profile) Link {
if p == colorprofile.NoTTY {
return Link{}
}

return h
}
36 changes: 26 additions & 10 deletions cellbuf/screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"strings"

"github.com/charmbracelet/colorprofile"
"github.com/charmbracelet/x/ansi"
)

Expand Down Expand Up @@ -37,12 +38,17 @@ func SetContent(d Screen, m Method, content string) []int {
}

// Render returns a string representation of the grid with ANSI escape sequences.
// Use [ansi.Strip] to remove them.
func Render(d Screen) string {
return RenderWithProfile(d, colorprofile.TrueColor)
}

// RenderWithProfile returns a string representation of the grid with ANSI escape
// sequences converting styles and colors to the given color profile.
func RenderWithProfile(d Screen, p colorprofile.Profile) string {
var buf bytes.Buffer
height := d.Height()
for y := 0; y < height; y++ {
_, line := RenderLine(d, y)
_, line := RenderLineWithProfile(d, y, p)
buf.WriteString(line)
if y < height-1 {
buf.WriteString("\r\n")
Expand All @@ -54,6 +60,13 @@ func Render(d Screen) string {
// RenderLine returns a string representation of the yth line of the grid along
// with the width of the line.
func RenderLine(d Screen, n int) (w int, line string) {
return RenderLineWithProfile(d, n, colorprofile.TrueColor)
}

// RenderLineWithProfile returns a string representation of the nth line of the
// grid along with the width of the line converting styles and colors to the
// given color profile.
func RenderLineWithProfile(d Screen, n int, p colorprofile.Profile) (w int, line string) {
var pen Style
var link Link
var buf bytes.Buffer
Expand All @@ -73,28 +86,31 @@ func RenderLine(d Screen, n int) (w int, line string) {

for x := 0; x < d.Width(); x++ {
if cell, ok := d.Cell(x, n); ok && cell.Width > 0 {
if cell.Style.Empty() && !pen.Empty() {
// Convert the cell's style and link to the given color profile.
cellStyle := cell.Style.Convert(p)
cellLink := cell.Link.Convert(p)
if cellStyle.Empty() && !pen.Empty() {
writePending()
buf.WriteString(ansi.ResetStyle) //nolint:errcheck
pen.Reset()
}
if !cell.Style.Equal(pen) {
if !cellStyle.Equal(pen) {
writePending()
seq := cell.Style.DiffSequence(pen)
seq := cellStyle.DiffSequence(pen)
buf.WriteString(seq) // nolint:errcheck
pen = cell.Style
pen = cellStyle
}

// Write the URL escape sequence
if cell.Link != link && link.URL != "" {
if cellLink != link && link.URL != "" {
writePending()
buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
link.Reset()
}
if cell.Link != link {
if cellLink != link {
writePending()
buf.WriteString(ansi.SetHyperlink(cell.Link.URL, cell.Link.URLID)) //nolint:errcheck
link = cell.Link
buf.WriteString(ansi.SetHyperlink(cellLink.URL, cellLink.URLID)) //nolint:errcheck
link = cellLink
}

// We only write the cell content if it's not empty. If it is, we
Expand Down
27 changes: 27 additions & 0 deletions cellbuf/style.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cellbuf

import (
"github.com/charmbracelet/colorprofile"
"github.com/charmbracelet/x/ansi"
)

Expand Down Expand Up @@ -370,3 +371,29 @@ func (s *Style) Reset() *Style {
func (s *Style) Empty() bool {
return s.Fg == nil && s.Bg == nil && s.Ul == nil && s.Attrs == ResetAttr && s.UlStyle == NoUnderline
}

// Convert converts a style to respect the given color profile.
func (s Style) Convert(p colorprofile.Profile) Style {
switch p {
case colorprofile.TrueColor:
return s
case colorprofile.Ascii:
s.Fg = nil
s.Bg = nil
s.Ul = nil
case colorprofile.NoTTY:
return Style{}
}

if s.Fg != nil {
s.Fg = p.Convert(s.Fg)
}
if s.Bg != nil {
s.Bg = p.Convert(s.Bg)
}
if s.Ul != nil {
s.Ul = p.Convert(s.Ul)
}

return s
}

0 comments on commit 4c6de79

Please sign in to comment.