Skip to content

Commit 4e94726

Browse files
committed
Add line by line coloring
Add EachLine() style option, which enables a flag that coloring should not be applied to new line sequences. Each lines has its own color sequences then.
1 parent 37d6bd0 commit 4e94726

File tree

2 files changed

+67
-21
lines changed

2 files changed

+67
-21
lines changed

convenience.go

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ import (
2828
colorful "github.com/lucasb-eyer/go-colorful"
2929
)
3030

31-
// StyleOption defines style option for strings
32-
type StyleOption func(*String)
31+
// StyleOption defines style option for colored strings
32+
type StyleOption struct {
33+
flags []string
34+
postProcess func(*String, map[string]struct{})
35+
}
3336

3437
// PlainTextLength returns the length of the input text without any escape
3538
// sequences.
@@ -66,39 +69,61 @@ func Substring(text string, start int, end int) string {
6669

6770
// Bold applies the bold text parameter
6871
func Bold() StyleOption {
69-
return func(s *String) {
70-
for i := range *s {
71-
(*s)[i].Settings |= 1 << 2
72-
}
72+
return StyleOption{
73+
postProcess: func(s *String, flags map[string]struct{}) {
74+
for i := range *s {
75+
(*s)[i].Settings |= 1 << 2
76+
}
77+
},
7378
}
7479
}
7580

7681
// Italic applies the italic text parameter
7782
func Italic() StyleOption {
78-
return func(s *String) {
79-
for i := range *s {
80-
(*s)[i].Settings |= 1 << 3
81-
}
83+
return StyleOption{
84+
postProcess: func(s *String, flags map[string]struct{}) {
85+
for i := range *s {
86+
(*s)[i].Settings |= 1 << 3
87+
}
88+
},
8289
}
8390
}
8491

8592
// Foreground sets the given color as the foreground color of the text
8693
func Foreground(color colorful.Color) StyleOption {
87-
r, g, b := color.RGB255()
88-
return func(s *String) {
89-
for i := range *s {
90-
(*s)[i].Settings |= 1
91-
(*s)[i].Settings |= uint64(r) << 8
92-
(*s)[i].Settings |= uint64(g) << 16
93-
(*s)[i].Settings |= uint64(b) << 24
94-
}
94+
return StyleOption{
95+
postProcess: func(s *String, flags map[string]struct{}) {
96+
r, g, b := color.RGB255()
97+
_, skipNewLine := flags["skipNewLine"]
98+
99+
for i := range *s {
100+
if skipNewLine && (*s)[i].Symbol == '\n' {
101+
continue
102+
}
103+
104+
(*s)[i].Settings |= 1
105+
(*s)[i].Settings |= uint64(r) << 8
106+
(*s)[i].Settings |= uint64(g) << 16
107+
(*s)[i].Settings |= uint64(b) << 24
108+
}
109+
},
95110
}
96111
}
97112

98113
// EnableTextAnnotations enables post-processing to evaluate text annotations
99114
func EnableTextAnnotations() StyleOption {
100-
return func(s *String) {
101-
processTextAnnotations(s)
115+
return StyleOption{
116+
postProcess: func(s *String, flags map[string]struct{}) {
117+
processTextAnnotations(s)
118+
},
119+
}
120+
}
121+
122+
// EachLine enables that new line sequences will be ignored during coloring,
123+
// which will lead to strings that are colored line by line and not as a block.
124+
func EachLine() StyleOption {
125+
return StyleOption{
126+
flags: []string{"skipNewLine"},
102127
}
103128
}
104129

@@ -111,8 +136,15 @@ func Style(text string, styleOptions ...StyleOption) string {
111136
panic(err)
112137
}
113138

139+
flags := map[string]struct{}{}
114140
for _, styleOption := range styleOptions {
115-
styleOption(result)
141+
for _, flag := range styleOption.flags {
142+
flags[flag] = struct{}{}
143+
}
144+
145+
if styleOption.postProcess != nil {
146+
styleOption.postProcess(result, flags)
147+
}
116148
}
117149

118150
return result.String()

convenience_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,19 @@ var _ = Describe("convenience functions", func() {
9090
Expect(Style("_text_", Foreground(YellowGreen), EnableTextAnnotations())).To(
9191
BeEquivalentTo("\x1b[3;38;2;154;205;50mtext\x1b[0m"))
9292
})
93+
94+
It("should support both line by line coloring as well as full block coloring", func() {
95+
// By default, color the whole string including new line sequences
96+
Expect(Style("text\ntext", Foreground(Yellow))).To(
97+
BeEquivalentTo("\x1b[38;2;255;255;0mtext\ntext\x1b[0m"))
98+
99+
// If EachLine is enabled before coloring, ignore new line sequences
100+
Expect(Style("text\ntext", EachLine(), Foreground(Yellow))).To(
101+
BeEquivalentTo("\x1b[38;2;255;255;0mtext\x1b[0m\n\x1b[38;2;255;255;0mtext\x1b[0m"))
102+
103+
// If EachLine is enabled after coloring, it has no effect
104+
Expect(Style("text\ntext", Foreground(Yellow), EachLine())).To(
105+
BeEquivalentTo("\x1b[38;2;255;255;0mtext\ntext\x1b[0m"))
106+
})
93107
})
94108
})

0 commit comments

Comments
 (0)