aboutsummaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/colors/colors.go1
-rw-r--r--util/text/text.go32
-rw-r--r--util/text/text_test.go82
3 files changed, 103 insertions, 12 deletions
diff --git a/util/colors/colors.go b/util/colors/colors.go
index c57e9019..f8c6d188 100644
--- a/util/colors/colors.go
+++ b/util/colors/colors.go
@@ -13,6 +13,7 @@ var (
YellowBg = color.New(color.BgYellow, color.FgBlack).SprintFunc()
Green = color.New(color.FgGreen).SprintFunc()
GreenBg = color.New(color.BgGreen, color.FgBlack).SprintFunc()
+ GreyBold = color.New(color.BgBlack, color.Bold).SprintfFunc()
Red = color.New(color.FgRed).SprintFunc()
Cyan = color.New(color.FgCyan).SprintFunc()
CyanBg = color.New(color.BgCyan, color.FgBlack).SprintFunc()
diff --git a/util/text/text.go b/util/text/text.go
index 10b70b01..c000596c 100644
--- a/util/text/text.go
+++ b/util/text/text.go
@@ -58,7 +58,7 @@ func WrapLeftPadded(text string, lineWidth int, leftPad int) (string, int) {
} else {
// Break a word longer than a line
if wordLength > lineWidth {
- for wordLength > 0 && len(word) > 0 {
+ for wordLength > 0 && wordLen(word) > 0 {
l := minInt(spaceLeft, wordLength)
part, leftover := splitWord(word, l)
word = leftover
@@ -76,6 +76,10 @@ func WrapLeftPadded(text string, lineWidth int, leftPad int) (string, int) {
nbLine++
spaceLeft = lineWidth - leftPad
}
+
+ if wordLength <= 0 {
+ break
+ }
}
} else {
// Normal break
@@ -84,20 +88,24 @@ func WrapLeftPadded(text string, lineWidth int, leftPad int) (string, int) {
lineBuffer.Reset()
lineBuffer.WriteString(word)
firstWord = false
- spaceLeft = lineWidth - wordLength
+ spaceLeft = lineWidth - leftPad - wordLength
nbLine++
}
}
}
- textBuffer.WriteString(pad + strings.TrimRight(lineBuffer.String(), " "))
- lineBuffer.Reset()
+ if lineBuffer.Len() > 0 {
+ textBuffer.WriteString(pad + strings.TrimRight(lineBuffer.String(), " "))
+ lineBuffer.Reset()
+ }
+
firstLine = false
}
return textBuffer.String(), nbLine
}
+// wordLen return the length of a word, while ignoring the terminal escape sequences
func wordLen(word string) int {
length := 0
escape := false
@@ -119,8 +127,10 @@ func wordLen(word string) int {
return length
}
+// splitWord split a word at the given length, while ignoring the terminal escape sequences
func splitWord(word string, length int) (string, string) {
- result := ""
+ runes := []rune(word)
+ var result []rune
added := 0
escape := false
@@ -128,12 +138,12 @@ func splitWord(word string, length int) (string, string) {
return "", word
}
- for _, char := range word {
- if char == '\x1b' {
+ for _, r := range runes {
+ if r == '\x1b' {
escape = true
}
- result += string(char)
+ result = append(result, r)
if !escape {
added++
@@ -142,14 +152,14 @@ func splitWord(word string, length int) (string, string) {
}
}
- if char == 'm' {
+ if r == 'm' {
escape = false
}
}
- leftover := word[len(result):]
+ leftover := runes[len(result):]
- return result, leftover
+ return string(result), string(leftover)
}
func minInt(a, b int) int {
diff --git a/util/text/text_test.go b/util/text/text_test.go
index 96234464..cf72431e 100644
--- a/util/text/text_test.go
+++ b/util/text/text_test.go
@@ -89,12 +89,24 @@ func TestWrap(t *testing.T) {
" This\nis a\nlist:\n\n\n *\nfoo\n *\nbar\n\n\n *\nbaz\nBAM\n",
6,
},
+ // Handle chinese
+ {
+ "婞一枳郲逴靲屮蜧曀殳,掫乇峔掮傎溒兀緉冘仜。",
+ "婞一枳郲逴靲屮蜧曀殳,掫\n乇峔掮傎溒兀緉冘仜。",
+ 12,
+ },
+ // Handle chinese with colors
+ {
+ "婞一枳郲逴\x1b[31m靲屮蜧曀殳,掫乇峔掮傎溒\x1b[0m兀緉冘仜。",
+ "婞一枳郲逴\x1b[31m靲屮蜧曀殳,掫\n乇峔掮傎溒\x1b[0m兀緉冘仜。",
+ 12,
+ },
}
for i, tc := range cases {
actual, lines := Wrap(tc.Input, tc.Lim)
if actual != tc.Output {
- t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n\n`%s`\n\nActual Output:\n\n`%s`",
+ t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n\n`%s`\n\nActual Output:\n`\n%s`",
i, tc.Input, tc.Output, actual)
}
@@ -106,6 +118,47 @@ func TestWrap(t *testing.T) {
}
}
+func TestWrapLeftPadded(t *testing.T) {
+ cases := []struct {
+ input, output string
+ lim, pad int
+ }{
+ {
+ "The Lorem ipsum text is typically composed of pseudo-Latin words. It is commonly used as placeholder text to examine or demonstrate the visual effects of various graphic design.",
+ ` The Lorem ipsum text is typically composed of
+ pseudo-Latin words. It is commonly used as placeholder
+ text to examine or demonstrate the visual effects of
+ various graphic design.`,
+ 59, 4,
+ },
+ // Handle Chinese
+ {
+ "婞一枳郲逴靲屮蜧曀殳,掫乇峔掮傎溒兀緉冘仜。郼牪艽螗媷錵朸一詅掜豗怙刉笀丌,楀棶乇矹迡搦囷圣亍昄漚粁仈祂。覂一洳袶揙楱亍滻瘯毌,掗屮柅軡菵腩乜榵毌夯。勼哻怌婇怤灟葠雺奷朾恦扰衪岨坋誁乇芚誙腞。冇笉妺悆浂鱦賌廌灱灱觓坋佫呬耴跣兀枔蓔輈。嵅咍犴膰痭瘰机一靬涽捊矷尒玶乇,煚塈丌岰陊鉖怞戉兀甿跾觓夬侄。棩岧汌橩僁螗玎一逭舴圂衪扐衲兀,嵲媕亍衩衿溽昃夯丌侄蒰扂丱呤。毰侘妅錣廇螉仴一暀淖蚗佶庂咺丌,輀鈁乇彽洢溦洰氶乇构碨洐巿阹。",
+ ` 婞一枳郲逴靲屮蜧曀殳,掫乇峔掮傎溒兀緉冘仜。郼牪艽螗媷錵朸一詅掜豗怙刉笀丌,楀棶乇矹迡搦囷圣亍昄漚粁仈祂。覂一
+ 洳袶揙楱亍滻瘯毌,掗屮柅軡菵腩乜榵毌夯。勼哻怌婇怤灟葠雺奷朾恦扰衪岨坋誁乇芚誙腞。冇笉妺悆浂鱦賌廌灱灱觓坋佫呬
+ 耴跣兀枔蓔輈。嵅咍犴膰痭瘰机一靬涽捊矷尒玶乇,煚塈丌岰陊鉖怞戉兀甿跾觓夬侄。棩岧汌橩僁螗玎一逭舴圂衪扐衲兀,嵲
+ 媕亍衩衿溽昃夯丌侄蒰扂丱呤。毰侘妅錣廇螉仴一暀淖蚗佶庂咺丌,輀鈁乇彽洢溦洰氶乇构碨洐巿阹。`,
+ 59, 4,
+ },
+ }
+
+ for i, tc := range cases {
+ actual, lines := WrapLeftPadded(tc.input, tc.lim, tc.pad)
+ if actual != tc.output {
+ t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n`\n%s`\n\nActual Output:\n`\n%s\n%s`",
+ i, tc.input, tc.output,
+ "|"+strings.Repeat("-", tc.lim-2)+"|",
+ actual)
+ }
+
+ expected := len(strings.Split(tc.output, "\n"))
+ if expected != lines {
+ t.Fatalf("Case %d Nb lines mismatch\nExpected:%d\nActual:%d",
+ i, expected, lines)
+ }
+ }
+}
+
func TestWordLen(t *testing.T) {
cases := []struct {
Input string
@@ -126,6 +179,16 @@ func TestWordLen(t *testing.T) {
"foo\x1b[31mfoobarHoy\x1b[0mbaaar",
17,
},
+ // Handle chinese
+ {
+ "快檢什麼望對",
+ 6,
+ },
+ // Handle chinese with colors
+ {
+ "快\x1b[31m檢什麼\x1b[0m望對",
+ 6,
+ },
}
for i, tc := range cases {
@@ -179,6 +242,23 @@ func TestSplitWord(t *testing.T) {
0,
"", "foo",
},
+ // Handle chinese
+ {
+ "快檢什麼望對",
+ 2,
+ "快檢", "什麼望對",
+ },
+ {
+ "快檢什麼望對",
+ 3,
+ "快檢什", "麼望對",
+ },
+ // Handle chinese with colors
+ {
+ "快\x1b[31m檢什麼\x1b[0m望對",
+ 2,
+ "快\x1b[31m檢", "什麼\x1b[0m望對",
+ },
}
for i, tc := range cases {