charmbracelet / lipgloss

Style definitions for nice terminal layouts 👄
MIT License
8.25k stars 231 forks source link

style.go::Render at 'Render core text' is not ansi aware when using a spaced styler #233

Open Sojamann opened 1 year ago

Sojamann commented 1 year ago

Describe the bug The Render function gumbles up a potentially already rendered string or some other input which contains ansi sequences when using a spaced styler (useSpaceStyler==true). This happens because the processing happens rune wise and is not ansi aware.

Setup

To Reproduce see source code which results in

Source Code

package main

import (
    "fmt"

    "github.com/charmbracelet/lipgloss"
)

func main() {
    fancy := "Fancy"
    fmt.Println(fancy)

    fancyBold := lipgloss.NewStyle().Bold(true).Render(fancy)
    fmt.Println(fancyBold)

    fancyBoldStrikethrough := lipgloss.NewStyle().Strikethrough(true).Render(fancyBold)
    fmt.Println(fancyBoldStrikethrough)
}
Result

Fancy
Fancy
1mFancy[0m

Expected behavior Fancy Fancy Fancy (with strike-through effect)

Additional context space styling can be enabled other ways as well but ".Strikethrough(true)" will do just fine.

Sojamann commented 1 year ago

I thought of something like:

if useSpaceStyler {
         // Look for spaces and apply a different styler
         // while remaining ansi aware
    withinAnsi := false
    for _, r := range l[i] {
        switch {
        case withinAnsi && ansi.IsTerminator(r):
            withinAnsi = false
            b.WriteRune(r)
        case r == ansi.Marker || withinAnsi:
            withinAnsi = true
            b.WriteRune(r)
        default: // aka !withinAnsi
            if unicode.IsSpace(r) {
                b.WriteString(teSpace.Styled(string(r)))
                continue
            }
            b.WriteString(te.Styled(string(r)))
        }
    }
} // ....

while this solution correctly applies the strike through effect only the first letter 'F' is bold as the first render call places the reset at the end which ends the bold effect after rendering the 'F' ...