Skip to content

Commit 460e895

Browse files
authored
table: process Carriage-Return codes correctly (#291)
1 parent 50f17e4 commit 460e895

File tree

6 files changed

+72
-3
lines changed

6 files changed

+72
-3
lines changed

list/list.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"io"
66
"strings"
77
"unicode/utf8"
8+
9+
"github.com/jedib0t/go-pretty/v6/text"
810
)
911

1012
const (
@@ -102,7 +104,7 @@ func (l *List) Style() *Style {
102104
func (l *List) analyzeAndStringify(item interface{}) *listItem {
103105
itemStr := fmt.Sprint(item)
104106
itemStr = strings.ReplaceAll(itemStr, "\t", " ")
105-
itemStr = strings.ReplaceAll(itemStr, "\r", "")
107+
itemStr = text.ProcessCRLF(itemStr)
106108
return &listItem{
107109
Level: l.level,
108110
Text: itemStr,

progress/render.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func (p *Progress) renderPinnedMessages(out *strings.Builder) {
195195
func (p *Progress) renderTracker(out *strings.Builder, t *Tracker, hint renderHint) {
196196
message := t.message()
197197
message = strings.ReplaceAll(message, "\t", " ")
198-
message = strings.ReplaceAll(message, "\r", "")
198+
message = strings.ReplaceAll(message, "\r", "") // replace with text.ProcessCRLF?
199199
if p.lengthMessage > 0 {
200200
messageLen := text.RuneWidthWithoutEscSequences(message)
201201
if messageLen < p.lengthMessage {

table/render_init.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (t *Table) analyzeAndStringifyColumn(colIdx int, col interface{}, hint rend
4444
colStr = fmt.Sprint(col)
4545
}
4646
colStr = strings.ReplaceAll(colStr, "\t", " ")
47-
colStr = strings.ReplaceAll(colStr, "\r", "")
47+
colStr = text.ProcessCRLF(colStr)
4848
return fmt.Sprintf("%s%s", t.style.Format.Direction.Modifier(), colStr)
4949
}
5050

table/render_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,27 @@ func TestTable_Render_ColumnConfigs(t *testing.T) {
616616
)
617617
}
618618

619+
func TestTable_Render_CRLF(t *testing.T) {
620+
tw := NewWriter()
621+
tw.AppendHeader(testHeader)
622+
tw.AppendRows(testRows)
623+
tw.AppendRow(Row{5000, "Night", "King", 10000, "Was once a\r\nMortal \rMan"})
624+
tw.AppendFooter(testFooter)
625+
626+
compareOutput(t, tw.Render(), `
627+
+------+------------+-----------+--------+-----------------------------+
628+
| # | FIRST NAME | LAST NAME | SALARY | |
629+
+------+------------+-----------+--------+-----------------------------+
630+
| 1 | Arya | Stark | 3000 | |
631+
| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
632+
| 300 | Tyrion | Lannister | 5000 | |
633+
| 5000 | Night | King | 10000 | Was once a |
634+
| | | | | Man |
635+
+------+------------+-----------+--------+-----------------------------+
636+
| | | TOTAL | 10000 | |
637+
+------+------------+-----------+--------+-----------------------------+`)
638+
}
639+
619640
func TestTable_Render_Empty(t *testing.T) {
620641
tw := NewWriter()
621642
assert.Empty(t, tw.Render())

text/string.go

+24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package text
22

33
import (
4+
"regexp"
45
"strings"
56
"unicode/utf8"
67

@@ -107,6 +108,29 @@ func Pad(str string, maxLen int, paddingChar rune) string {
107108
return str
108109
}
109110

111+
var (
112+
reCarriageReturn = regexp.MustCompile(`(.*)\r`)
113+
)
114+
115+
// ProcessCRLF converts "\r\n" to "\n", and erases everything preceding a lone
116+
// "\r" in each line of the string.
117+
func ProcessCRLF(str string) string {
118+
str = strings.ReplaceAll(str, "\r\n", "\n")
119+
120+
// process \r by erasing everything preceding it in the line
121+
if strings.Contains(str, "\r") {
122+
lines := strings.Split(str, "\n")
123+
for idx := range lines {
124+
for reCarriageReturn.MatchString(lines[idx]) {
125+
lines[idx] = reCarriageReturn.ReplaceAllString(lines[idx], "")
126+
}
127+
}
128+
str = strings.Join(lines, "\n")
129+
}
130+
131+
return str
132+
}
133+
110134
// RepeatAndTrim repeats the given string until it is as long as maxRunes.
111135
// For ex.:
112136
//

text/string_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,28 @@ func TestPad(t *testing.T) {
134134
assert.Equal(t, "\x1b]8;;http://example.com\x1b\\Ghost\x1b]8;;\x1b\\.....", Pad("\x1b]8;;http://example.com\x1b\\Ghost\x1b]8;;\x1b\\", 10, '.'))
135135
}
136136

137+
func ExampleProcessCRLF() {
138+
fmt.Printf("%#v\n", ProcessCRLF("abc"))
139+
fmt.Printf("%#v\n", ProcessCRLF("abc\r\ndef"))
140+
fmt.Printf("%#v\n", ProcessCRLF("abc\r\ndef\rghi"))
141+
fmt.Printf("%#v\n", ProcessCRLF("abc\r\ndef\rghi\njkl"))
142+
fmt.Printf("%#v\n", ProcessCRLF("abc\r\ndef\rghi\njkl\r"))
143+
144+
// Output: "abc"
145+
// "abc\ndef"
146+
// "abc\nghi"
147+
// "abc\nghi\njkl"
148+
// "abc\nghi\n"
149+
}
150+
151+
func TestProcessCRLF(t *testing.T) {
152+
assert.Equal(t, "abc", ProcessCRLF("abc"))
153+
assert.Equal(t, "abc\ndef", ProcessCRLF("abc\r\ndef"))
154+
assert.Equal(t, "abc\nghi", ProcessCRLF("abc\r\ndef\rghi"))
155+
assert.Equal(t, "abc\nghi\njkl", ProcessCRLF("abc\r\ndef\rghi\njkl"))
156+
assert.Equal(t, "abc\nghi\n", ProcessCRLF("abc\r\ndef\rghi\njkl\r"))
157+
}
158+
137159
func ExampleRepeatAndTrim() {
138160
fmt.Printf("RepeatAndTrim(\"\", 5): %#v\n", RepeatAndTrim("", 5))
139161
fmt.Printf("RepeatAndTrim(\"Ghost\", 0): %#v\n", RepeatAndTrim("Ghost", 0))

0 commit comments

Comments
 (0)