Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/output/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,9 @@ func formatCell(val any) string {
return ""
case string:
v = ansi.Strip(v)
if strings.ContainsAny(v, "\n\r") {
v = strings.Join(strings.Fields(v), " ")
}
// Truncate long strings (rune-safe for multi-byte UTF-8)
if utf8.RuneCountInString(v) > 40 {
runes := []rune(v)
Expand Down
13 changes: 9 additions & 4 deletions internal/presenter/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,22 @@ func formatPerson(val any) string {
return ""
}

// singleLine returns the first non-empty line from s, trimmed.
// singleLine collapses multiline text into a single line by joining all
// non-empty lines with spaces. Leading/trailing whitespace is trimmed.
func singleLine(s string) string {
if strings.IndexByte(s, '\n') == -1 {
if !strings.ContainsAny(s, "\n\r") {
return strings.TrimSpace(s)
}
// Normalize \r\n and bare \r to \n before splitting.
s = strings.ReplaceAll(s, "\r\n", "\n")
s = strings.ReplaceAll(s, "\r", "\n")
var parts []string
for _, line := range strings.Split(s, "\n") {
if trimmed := strings.TrimSpace(line); trimmed != "" {
return trimmed
parts = append(parts, trimmed)
}
}
return ""
return strings.Join(parts, " ")
}

// formatText converts any value to a string representation.
Expand Down
29 changes: 24 additions & 5 deletions internal/presenter/presenter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1646,10 +1646,10 @@ func TestFormatTextBcAttachmentOnly(t *testing.T) {
}
}

func TestSingleLineSkipsLeadingBlanks(t *testing.T) {
func TestSingleLineCollapsesMultiline(t *testing.T) {
got := singleLine("\n\nfirst\nsecond")
if got != "first" {
t.Errorf("singleLine = %q, want %q", got, "first")
if got != "first second" {
t.Errorf("singleLine = %q, want %q", got, "first second")
}
}

Expand All @@ -1660,6 +1660,25 @@ func TestSingleLinePlainText(t *testing.T) {
}
}

func TestSingleLineCollapsesAllLines(t *testing.T) {
got := singleLine("line one\nline two\nline three")
if got != "line one line two line three" {
t.Errorf("singleLine = %q, want %q", got, "line one line two line three")
}
}

func TestSingleLineCollapsesBareCarriageReturn(t *testing.T) {
got := singleLine("a\rb")
if got != "a b" {
t.Errorf("singleLine = %q, want %q", got, "a b")
}

got = singleLine("x\r\ny\rz")
if got != "x y z" {
t.Errorf("singleLine(mixed) = %q, want %q", got, "x y z")
}
}

func TestRenderHeadlineHTMLContent(t *testing.T) {
schema := &EntitySchema{
Identity: Identity{Label: "content"},
Expand All @@ -1671,8 +1690,8 @@ func TestRenderHeadlineHTMLContent(t *testing.T) {
if strings.Contains(got, "<") {
t.Errorf("RenderHeadline should strip HTML tags, got: %q", got)
}
if got != "Title" {
t.Errorf("RenderHeadline = %q, want %q", got, "Title")
if got != "Title subtitle" {
t.Errorf("RenderHeadline = %q, want %q", got, "Title subtitle")
}
}

Expand Down
Loading