Add StyleRunes function for styling specific runes in a string

This commit is contained in:
Christian Rocha 2021-05-25 13:59:20 -04:00
parent d9fae9f03a
commit 66eb23093a
2 changed files with 113 additions and 0 deletions

43
runes.go Normal file
View File

@ -0,0 +1,43 @@
package lipgloss
import (
"strings"
)
// StyleRunes applys a given style to runes at the given indicesin the string.
// Note that you must provide styling options for both matched and unmatched
// runes. Indices out of bounds will be ignored.
func StyleRunes(str string, indices []int, matched, unmatched Style) string {
// Convert slice of indices to a map for easier lookups
m := make(map[int]struct{})
for _, i := range indices {
m[i] = struct{}{}
}
var (
out strings.Builder
group strings.Builder
style Style
runes = []rune(str)
)
for i, r := range runes {
group.WriteRune(r)
_, matches := m[i]
_, nextMatches := m[i+1]
if matches != nextMatches || i == len(runes)-1 {
// Flush
if matches {
style = matched
} else {
style = unmatched
}
out.WriteString(style.Render(group.String()))
group.Reset()
}
}
return out.String()
}

70
runes_test.go Normal file
View File

@ -0,0 +1,70 @@
package lipgloss
import (
"strings"
"testing"
)
func TestStyleRunes(t *testing.T) {
t.Parallel()
matchedStyle := NewStyle().Reverse(true)
unmatchedStyle := NewStyle()
tt := []struct {
input string
indices []int
expected string
}{
{
"hello",
[]int{0},
"\x1b[7mh\x1b[0mello",
},
{
"你好",
[]int{1},
"你\x1b[7m好\x1b[0m",
},
{
"hello 你好",
[]int{6, 7},
"hello \x1b[7m你好\x1b[0m",
},
{
"hello",
[]int{1, 3},
"h\x1b[7me\x1b[0ml\x1b[7ml\x1b[0mo",
},
{
"你好",
[]int{0, 1},
"\x1b[7m你好\x1b[0m",
},
}
fn := func(str string, indices []int) string {
return StyleRunes(str, indices, matchedStyle, unmatchedStyle)
}
for i, tc := range tt {
res := fn(tc.input, tc.indices)
if fn(tc.input, tc.indices) != tc.expected {
t.Errorf("Test %d, expected:\n\n`%s`\n`%s`\n\nActual Output:\n\n`%s`\n`%s`\n\n",
i, tc.expected, formatEscapes(tc.expected),
res, formatEscapes(res))
}
}
}
func formatEscapes(str string) string {
var b strings.Builder
for _, r := range str {
if r == '\x1b' {
b.WriteString("\\x1b")
continue
}
b.WriteRune(r)
}
return b.String()
}