Add back removed text functions

Yang Zhang created

Change summary

util/text/text.go      | 48 ++++++++++++++++++++++++++++++
util/text/text_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 118 insertions(+)

Detailed changes

util/text/text.go 🔗

@@ -127,3 +127,51 @@ 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) {
+	runes := []rune(word)
+	var result []rune
+	added := 0
+	escape := false
+
+	if length == 0 {
+		return "", word
+	}
+
+	for _, r := range runes {
+		if r == '\x1b' {
+			escape = true
+		}
+
+		width := runewidth.RuneWidth(r)
+		if width+added > length {
+			// wide character made the length overflow
+			break
+		}
+
+		result = append(result, r)
+
+		if !escape {
+			added += width
+			if added >= length {
+				break
+			}
+		}
+
+		if r == 'm' {
+			escape = false
+		}
+	}
+
+	leftover := runes[len(result):]
+
+	return string(result), string(leftover)
+}
+
+func minInt(a, b int) int {
+	if a > b {
+		return b
+	}
+	return a
+}

util/text/text_test.go 🔗

@@ -203,3 +203,73 @@ func TestWordLen(t *testing.T) {
 		}
 	}
 }
+
+func TestSplitWord(t *testing.T) {
+	cases := []struct {
+		Input            string
+		Length           int
+		Result, Leftover string
+	}{
+		// A simple word passes through.
+		{
+			"foo",
+			4,
+			"foo", "",
+		},
+		// Cut at the right place
+		{
+			"foobarHoy",
+			4,
+			"foob", "arHoy",
+		},
+		// A simple word passes through with colors
+		{
+			"\x1b[31mbar\x1b[0m",
+			4,
+			"\x1b[31mbar\x1b[0m", "",
+		},
+		// Cut at the right place with colors
+		{
+			"\x1b[31mfoobarHoy\x1b[0m",
+			4,
+			"\x1b[31mfoob", "arHoy\x1b[0m",
+		},
+		// Handle prefix and suffix properly
+		{
+			"foo\x1b[31mfoobarHoy\x1b[0mbaaar",
+			4,
+			"foo\x1b[31mf", "oobarHoy\x1b[0mbaaar",
+		},
+		// Cut properly with length = 0
+		{
+			"foo",
+			0,
+			"", "foo",
+		},
+		// Handle chinese
+		{
+			"快檢什麼望對",
+			4,
+			"快檢", "什麼望對",
+		},
+		{
+			"快檢什麼望對",
+			5,
+			"快檢", "什麼望對",
+		},
+		// Handle chinese with colors
+		{
+			"快\x1b[31m檢什麼\x1b[0m望對",
+			4,
+			"快\x1b[31m檢", "什麼\x1b[0m望對",
+		},
+	}
+
+	for i, tc := range cases {
+		result, leftover := splitWord(tc.Input, tc.Length)
+		if result != tc.Result || leftover != tc.Leftover {
+			t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n\n`%s` - `%s`\n\nActual Output:\n\n`%s` - `%s`",
+				i, tc.Input, tc.Result, tc.Leftover, result, leftover)
+		}
+	}
+}