Skip to content

Commit

Permalink
fix: combining both conditional and unconditional wrapping
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Mar 28, 2024
1 parent ae403ea commit e19e49f
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 2 deletions.
3 changes: 1 addition & 2 deletions style.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,7 @@ func (s Style) Render(strs ...string) string {
// Word wrap
if !inline && width > 0 {
wrapAt := width - leftPadding - rightPadding
str = ansi.Wordwrap(str, wrapAt, "")
str = ansi.Wrap(str, wrapAt, false) // force-wrap long strings
str = wrap(str, wrapAt)
}

// Render core text
Expand Down
46 changes: 46 additions & 0 deletions wrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package lipgloss

import (
"strings"

"github.com/charmbracelet/x/exp/term/ansi"
)

// wrap wraps a string to a given width. It will break the string at spaces
// and hyphens, and will remove any leading or trailing whitespace from the
// wrapped lines.
func wrap(str string, width int) string {
wrapped := ansi.Wordwrap(str, width, "")
lines := strings.Split(wrapped, "\n")
for i := 0; i < len(lines); i++ {
line := lines[i]
linew := ansi.StringWidth(line)
if linew <= width {
continue
}

wline := ansi.Wordwrap(line, width, "")
wlines := strings.Split(wline, "\n")
for j := 0; j < len(wlines); j++ {
if ansi.StringWidth(wlines[j]) > width {
wline = ansi.Wrap(line, width, false)
wlines = strings.Split(wline, "\n")
break
}
}

if len(wlines) > 0 {
lines[i] = wlines[0]
}

if len(wlines) > 1 && i+1 < len(lines) {
endsWithHyphen := strings.HasSuffix(wlines[1], "-")
if endsWithHyphen {
lines[i+1] = wlines[1] + lines[i+1]
} else {
lines[i+1] = wlines[1] + " " + lines[i+1]
}
}
}
return strings.Join(lines, "\n")
}
54 changes: 54 additions & 0 deletions wrap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package lipgloss

import (
"testing"
)

var cases = []struct {
name string
input string
expected string
width int
}{
{
name: "simple",
input: "I really \x1B[38;2;249;38;114mlove\x1B[0m Go!",
expected: "I really\n\x1B[38;2;249;38;114mlove\x1B[0m Go!",
width: 8,
},
{
name: "passthrough",
input: "hello world",
expected: "hello world",
width: 11,
},
{
name: "longer",
input: "the quick brown foxxxxxxxxxxxxxxxx jumped over the lazy dog.",
expected: "the quick brown\nfoxxxxxxxxxxxxxx\nxx jumped over\nthe lazy dog.",
width: 16,
},
{
name: "long input",
input: "Rotated keys for a-good-offensive-cheat-code-incorporated/animal-like-law-on-the-rocks.",
expected: "Rotated keys for a-good-offensive-cheat-code-incorporated/animal-like-law-on\n-the-rocks.",
width: 76,
},
{
name: "long input2",
input: "Rotated keys for a-good-offensive-cheat-code-incorporated/crypto-line-operating-system.",
expected: "Rotated keys for a-good-offensive-cheat-code-incorporated/crypto-line-operat\ning-system.",
width: 76,
},
}

func TestWrapWordwrap(t *testing.T) {
for i, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
output := wrap(tc.input, tc.width)
if output != tc.expected {
t.Errorf("case %d, expected %q, got %q", i+1, tc.expected, output)
}
})
}
}

0 comments on commit e19e49f

Please sign in to comment.