This commit is contained in:
Hill Patel 2026-08-27 17:19:48 -04:00 committed by GitHub
commit 40fc879244
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 18 deletions

View file

@ -5,6 +5,8 @@ import (
"fmt"
"sort"
"strings"
"github.com/charmbracelet/x/ansi"
)
// ---------------------------------------------------------------------------
@ -37,25 +39,23 @@ func NumericValue(v any) (float64, bool) {
return 0, false
}
// truncStr and firstN keep the first n display columns of s, without splitting
// a multi-byte rune. Kept as two names since callers use both spellings for the
// same "keep the start" truncation.
func truncStr(s string, n int) string {
if len(s) > n {
return s[:n]
}
return s
return ansi.Truncate(s, n, "")
}
// lastN keeps the last n display columns of s, without splitting a multi-byte rune.
func lastN(s string, n int) string {
if len(s) > n {
return s[len(s)-n:]
if w := ansi.StringWidth(s); w > n {
return ansi.TruncateLeft(s, w-n, "")
}
return s
}
func firstN(s string, n int) string {
if len(s) > n {
return s[:n]
}
return s
return ansi.Truncate(s, n, "")
}
func joinTrunc(items []any, max, limit int) string {

View file

@ -0,0 +1,73 @@
package render
import (
"testing"
"unicode/utf8"
)
// Regression test for https://github.com/usestrix/strix/issues/1152
func TestTruncSplitsRunes(t *testing.T) {
cases := []struct {
name string
out string
}{
{"firstN", firstN("中文字ABC", 4)},
{"lastN", lastN("ABC中文字", 4)},
{"truncStr", truncStr("中文字ABC", 4)},
{"ptrunc", ptrunc("中文字ABCDEF", 5)},
{"truncateShellLine", truncateShellLine(repeatStr("中", maxLineLength))},
}
for _, c := range cases {
if !utf8.ValidString(c.out) {
t.Errorf("%s produced invalid UTF-8: %q", c.name, c.out)
}
}
}
func TestFirstNTruncStrKeepStart(t *testing.T) {
if got := firstN("中文字ABC", 4); got != "中文" {
t.Errorf("firstN(%q, 4) = %q, want %q", "中文字ABC", got, "中文")
}
if got := truncStr("中文字ABC", 4); got != "中文" {
t.Errorf("truncStr(%q, 4) = %q, want %q", "中文字ABC", got, "中文")
}
if got := firstN("hello", 3); got != "hel" {
t.Errorf("firstN(%q, 3) = %q, want %q", "hello", got, "hel")
}
if got := firstN("hi", 10); got != "hi" {
t.Errorf("firstN(%q, 10) = %q, want unchanged", "hi", got)
}
}
func TestLastNKeepsEnd(t *testing.T) {
// Widths: A=1 B=1 C=1 中=2 文=2 字=2. Keeping the last 4 *columns* (not
// characters) lands on 文+字 (2+2=4), matching the issue's "count columns,
// not bytes" requirement.
if got := lastN("ABC中文字", 4); got != "文字" {
t.Errorf("lastN(%q, 4) = %q, want %q", "ABC中文字", got, "文字")
}
if got := lastN("hello", 3); got != "llo" {
t.Errorf("lastN(%q, 3) = %q, want %q", "hello", got, "llo")
}
if got := lastN("hi", 10); got != "hi" {
t.Errorf("lastN(%q, 10) = %q, want unchanged", "hi", got)
}
}
func TestPtruncAppendsEllipsisWithinBudget(t *testing.T) {
got := ptrunc("abcdefgh", 5)
if got != "ab..." {
t.Errorf("ptrunc(%q, 5) = %q, want %q", "abcdefgh", got, "ab...")
}
if got := ptrunc("hi", 5); got != "hi" {
t.Errorf("ptrunc(%q, 5) = %q, want unchanged", "hi", got)
}
}
func repeatStr(s string, n int) string {
out := ""
for i := 0; i < n; i++ {
out += s
}
return out
}

View file

@ -5,6 +5,7 @@ import (
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/x/ansi"
)
// ---------------------------------------------------------------------------
@ -28,10 +29,7 @@ func proxyStatusStyle(code int) lipgloss.Style {
}
func ptrunc(s string, max int) string {
if len(s) > max {
return s[:max-3] + "..."
}
return s
return ansi.Truncate(s, max, "...")
}
func psanitize(s string, max int) string {

View file

@ -6,6 +6,7 @@ import (
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/x/ansi"
)
// ---------------------------------------------------------------------------
@ -83,10 +84,7 @@ func cleanShellOutput(output string) string {
}
func truncateShellLine(line string) string {
if len(line) > maxLineLength {
return line[:maxLineLength-3] + "..."
}
return line
return ansi.Truncate(line, maxLineLength, "...")
}
// formatShellOutput ports _format_output (head/tail truncation with a middle marker).