diff --git a/align.go b/align.go index c3997038..5970b05e 100644 --- a/align.go +++ b/align.go @@ -3,7 +3,6 @@ package lipgloss import ( "strings" - "github.com/muesli/reflow/ansi" "github.com/muesli/termenv" ) @@ -15,7 +14,7 @@ func alignTextHorizontal(str string, pos Position, width int, style *termenv.Sty var b strings.Builder for i, l := range lines { - lineWidth := ansi.PrintableRuneWidth(l) + lineWidth := PrintableStringWidth(l) shortAmount := widestLine - lineWidth // difference from the widest line shortAmount += max(0, width-(shortAmount+lineWidth)) // difference from the total width, if set diff --git a/borders.go b/borders.go index b33b8e58..a141a750 100644 --- a/borders.go +++ b/borders.go @@ -4,7 +4,6 @@ import ( "strings" "github.com/mattn/go-runewidth" - "github.com/muesli/reflow/ansi" "github.com/muesli/termenv" ) @@ -354,8 +353,8 @@ func renderHorizontalEdge(left, middle, right string, width int) string { middle = " " } - leftWidth := ansi.PrintableRuneWidth(left) - rightWidth := ansi.PrintableRuneWidth(right) + leftWidth := PrintableStringWidth(left) + rightWidth := PrintableStringWidth(right) runes := []rune(middle) j := 0 @@ -368,7 +367,7 @@ func renderHorizontalEdge(left, middle, right string, width int) string { if j >= len(runes) { j = 0 } - i += ansi.PrintableRuneWidth(string(runes[j])) + i += PrintableStringWidth(string(runes[j])) } out.WriteString(right) diff --git a/get.go b/get.go index 856d1f61..483725fa 100644 --- a/get.go +++ b/get.go @@ -3,9 +3,11 @@ package lipgloss import ( "strings" - "github.com/muesli/reflow/ansi" + "github.com/mattn/go-runewidth" ) +const marker = '\x1B' + // GetBold returns the style's bold value. If no value is set false is returned. func (s Style) GetBold() bool { return s.getAsBool(boldKey, false) @@ -462,7 +464,7 @@ func getLines(s string) (lines []string, widest int) { lines = strings.Split(s, "\n") for _, l := range lines { - w := ansi.PrintableRuneWidth(l) + w := PrintableStringWidth(l) if widest < w { widest = w } @@ -470,3 +472,29 @@ func getLines(s string) (lines []string, widest int) { return lines, widest } + +// PrintableStringWidth returns the width of the given string. +func PrintableStringWidth(s string) int { + var ansi bool + var sr []rune + + for _, c := range s { + if c == marker { + // ANSI escape sequence + ansi = true + } else if ansi { + if isTerminator(c) { + // ANSI sequence terminated + ansi = false + } + } else { + sr = append(sr, c) + } + } + + return runewidth.StringWidth(string(sr)) +} + +func isTerminator(c rune) bool { + return (c >= 0x40 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) +} diff --git a/join.go b/join.go index cc16600a..71603638 100644 --- a/join.go +++ b/join.go @@ -3,8 +3,6 @@ package lipgloss import ( "math" "strings" - - "github.com/muesli/reflow/ansi" ) // JoinHorizontal is a utility function for horizontally joining two @@ -85,7 +83,7 @@ func JoinHorizontal(pos Position, strs ...string) string { b.WriteString(block[i]) // Also make lines the same length - b.WriteString(strings.Repeat(" ", maxWidths[j]-ansi.PrintableRuneWidth(block[i]))) + b.WriteString(strings.Repeat(" ", maxWidths[j]-PrintableStringWidth(block[i]))) } if i < len(blocks[0])-1 { b.WriteRune('\n') @@ -137,7 +135,7 @@ func JoinVertical(pos Position, strs ...string) string { var b strings.Builder for i, block := range blocks { for j, line := range block { - w := maxWidth - ansi.PrintableRuneWidth(line) + w := maxWidth - PrintableStringWidth(line) switch pos { case Left: diff --git a/position.go b/position.go index f419452b..574055ff 100644 --- a/position.go +++ b/position.go @@ -3,8 +3,6 @@ package lipgloss import ( "math" "strings" - - "github.com/muesli/reflow/ansi" ) // Position represents a position along a horizontal or vertical axis. It's in @@ -56,7 +54,7 @@ func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOpti var b strings.Builder for i, l := range lines { // Is this line shorter than the longest line? - short := max(0, contentWidth-ansi.PrintableRuneWidth(l)) + short := max(0, contentWidth-PrintableStringWidth(l)) switch pos { case Left: diff --git a/size.go b/size.go index 439a5cb8..068c0877 100644 --- a/size.go +++ b/size.go @@ -2,8 +2,6 @@ package lipgloss import ( "strings" - - "github.com/muesli/reflow/ansi" ) // Width returns the cell width of characters in the string. ANSI sequences are @@ -14,7 +12,7 @@ import ( // will give you accurate results. func Width(str string) (width int) { for _, l := range strings.Split(str, "\n") { - w := ansi.PrintableRuneWidth(l) + w := PrintableStringWidth(l) if w > width { width = w } diff --git a/whitespace.go b/whitespace.go index 6c510a78..d9416fa8 100644 --- a/whitespace.go +++ b/whitespace.go @@ -3,7 +3,6 @@ package lipgloss import ( "strings" - "github.com/muesli/reflow/ansi" "github.com/muesli/termenv" ) @@ -30,12 +29,12 @@ func (w whitespace) render(width int) string { if j >= len(r) { j = 0 } - i += ansi.PrintableRuneWidth(string(r[j])) + i += PrintableStringWidth(string(r[j])) } // Fill any extra gaps white spaces. This might be necessary if any runes // are more than one cell wide, which could leave a one-rune gap. - short := width - ansi.PrintableRuneWidth(b.String()) + short := width - PrintableStringWidth(b.String()) if short > 0 { b.WriteString(strings.Repeat(" ", short)) }