Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added RGBStyle #492

Merged
merged 7 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
73 changes: 73 additions & 0 deletions _examples/coloring/fade-colors-rgb-style/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"strings"

"github.com/pterm/pterm"
)

func main() {
white := pterm.NewRGB(255, 255, 255) // This RGB value is used as the gradients start point.
grey := pterm.NewRGB(128, 128, 128) // This RGB value is used as the gradients start point.
black := pterm.NewRGB(0, 0, 0) // This RGB value is used as the gradients start point.
red := pterm.NewRGB(255, 0, 0) // This RGB value is used as the gradients start point.
purple := pterm.NewRGB(255, 0, 255) // This RGB value is used as the gradients start point.
green := pterm.NewRGB(0, 255, 0) // This RGB value is used as the gradients start point.

str := "RGB colors only work in Terminals which support TrueColor."
strs := strings.Split(str, "")
var fadeInfo string // String which will be used to print.
// For loop over the range of the string length.
floaust marked this conversation as resolved.
Show resolved Hide resolved
for i := 0; i < len(str); i++ {
// Append faded letter to info string.
fadeInfo += pterm.NewRGBStyle(white.Fade(0, float32(len(str)), float32(i), purple), grey.Fade(0, float32(len(str)), float32(i), black)).Sprint(strs[i])
}

// Print info.
floaust marked this conversation as resolved.
Show resolved Hide resolved
pterm.Info.Println(fadeInfo)

str = "The background and foreground colors can be customized individually."
strs = strings.Split(str, "")
var fade2 string // String which will be used to print info.
// For loop over the range of the string length.
floaust marked this conversation as resolved.
Show resolved Hide resolved
for i := 0; i < len(str); i++ {
// Append faded letter to info string.
fade2 += pterm.NewRGBStyle(black, purple.Fade(0, float32(len(str)), float32(i), red)).Sprint(strs[i])
}

// Print string.
floaust marked this conversation as resolved.
Show resolved Hide resolved
pterm.Println(fade2)

str = "Styles can also be applied. For example: Bold or Italic."
strs = strings.Split(str, "")
var fade3 string // String which will be used to print.

bold := 0
boldStr := strings.Split("Bold", "")
italic := 0
italicStr := strings.Split("Italic", "")

// For loop over the range of the string length.
for i := 0; i < len(str); i++ {
// Append faded letter to info string.
s := pterm.NewRGBStyle(white.Fade(0, float32(len(str)), float32(i), green), red.Fade(0, float32(len(str)), float32(i), black))

// if the next letters are "Bold", then add the style "Bold".
// else if the next letters are "Italic", then add the style "Italic".
if bold < len(boldStr) && i+len(boldStr) <= len(strs) {
if strings.Join(strs[i:i+len(boldStr)-bold], "") == strings.Join(boldStr[bold:], "") {
s = s.AddOptions(pterm.Bold)
bold++
}
} else if italic < len(italicStr) && i+len(italicStr)-italic < len(strs) {
if strings.Join(strs[i:i+len(italicStr)-italic], "") == strings.Join(italicStr[italic:], "") {
s = s.AddOptions(pterm.Italic)
italic++
}
}
fade3 += s.Sprint(strs[i])
}

// Print string.
floaust marked this conversation as resolved.
Show resolved Hide resolved
pterm.Println(fade3)
}
19 changes: 19 additions & 0 deletions _examples/coloring/print-color-rgb-style/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"github.com/pterm/pterm"
)

func main() {
rgb1 := pterm.RGB{R: 187, G: 80, B: 0}
floaust marked this conversation as resolved.
Show resolved Hide resolved
rgb2 := pterm.RGB{R: 0, G: 50, B: 123}
floaust marked this conversation as resolved.
Show resolved Hide resolved

// Print string with a custom foreground and background RGB color.
pterm.NewRGBStyle(rgb1, rgb2).Println("This text is not styled.")

// Print string with a custom foreground and background RGB color and style bold.
pterm.NewRGBStyle(rgb1, rgb2).AddOptions(pterm.Bold).Println("This text is bold.")

// Print string with a custom foreground and background RGB color and style italic.
pterm.NewRGBStyle(rgb1, rgb2).AddOptions(pterm.Italic).Println("This text is italic.")
}
138 changes: 138 additions & 0 deletions rgb.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,136 @@
Background bool
}

type RGBStyle struct {
Opts []Color
floaust marked this conversation as resolved.
Show resolved Hide resolved
Fg, Bg RGB
floaust marked this conversation as resolved.
Show resolved Hide resolved

hasBg bool
}

// NewRGBStyle returns a new RGBStyle.
func NewRGBStyle(foreground RGB, background ...RGB) RGBStyle {
var s RGBStyle
s.Fg = foreground
if len(background) > 0 {
s.Bg = background[0]
s.hasBg = true
}
return s
}

// AddOptions adds options to the RGBStyle.
func (p RGBStyle) AddOptions(opts ...Color) RGBStyle {
p.Opts = append(p.Opts, opts...)
return p
}

// GetValues returns the foreground, background and options of the RGBStyle.
func (p RGBStyle) GetValues() (RGB, RGB, []Color) {
return p.Fg, p.Bg, p.Opts

Check warning on line 47 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L46-L47

Added lines #L46 - L47 were not covered by tests
}

// Print formats using the default formats for its operands and writes to standard output.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Print(a ...interface{}) *TextPrinter {
Print(p.Sprint(a...))
tp := TextPrinter(p)
return &tp

Check warning on line 56 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L53-L56

Added lines #L53 - L56 were not covered by tests
}

// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Println(a ...interface{}) *TextPrinter {
Println(p.Sprint(a...))
tp := TextPrinter(p)
return &tp
}

// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Printf(format string, a ...interface{}) *TextPrinter {
Printf(format, p.Sprint(a...))
tp := TextPrinter(p)
return &tp

Check warning on line 73 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L70-L73

Added lines #L70 - L73 were not covered by tests
}

// Printfln formats according to a format specifier and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Printfln(format string, a ...interface{}) *TextPrinter {
Printf(format, p.Sprint(a...))
tp := TextPrinter(p)
return &tp

Check warning on line 82 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L79-L82

Added lines #L79 - L82 were not covered by tests
}

// PrintOnError prints every error which is not nil.
// If every error is nil, nothing will be printed.
// This can be used for simple error checking.
func (p RGBStyle) PrintOnError(a ...interface{}) *TextPrinter {
for _, arg := range a {
if err, ok := arg.(error); ok {
if err != nil {
p.Println(err)
}
}
}

tp := TextPrinter(p)
return &tp
}

// PrintOnErrorf wraps every error which is not nil and prints it.
// If every error is nil, nothing will be printed.
// This can be used for simple error checking.
func (p RGBStyle) PrintOnErrorf(format string, a ...interface{}) *TextPrinter {
for _, arg := range a {
if err, ok := arg.(error); ok {
if err != nil {
p.Println(fmt.Errorf(format, err))
}
}
}

tp := TextPrinter(p)
return &tp
}

// Sprint formats using the default formats for its operands and returns the resulting string.
// Spaces are added between operands when neither is a string.
func (p RGBStyle) Sprint(a ...interface{}) string {
var rgbStyle *color.RGBStyle
if !p.hasBg {
rgbStyle = color.NewRGBStyle(color.RGB(p.Fg.R, p.Fg.G, p.Fg.B))
} else {
rgbStyle = color.NewRGBStyle(color.RGB(p.Fg.R, p.Fg.G, p.Fg.B), color.RGB(p.Bg.R, p.Bg.G, p.Bg.B))
}

Check warning on line 125 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L124-L125

Added lines #L124 - L125 were not covered by tests
if len(p.Opts) > 0 {
for _, opt := range p.Opts {
rgbStyle.AddOpts(color.Color(opt))
}
}
return rgbStyle.Sprint(a...)
}

// Sprintln formats using the default formats for its operands and returns the resulting string.
// Spaces are always added between operands and a newline is appended.
func (p RGBStyle) Sprintln(a ...interface{}) string {
return p.Sprint(a...) + "\n"

Check warning on line 137 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L136-L137

Added lines #L136 - L137 were not covered by tests
}

// Sprintf formats according to a format specifier and returns the resulting string.
func (p RGBStyle) Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, p.Sprint(a...))

Check warning on line 142 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L141-L142

Added lines #L141 - L142 were not covered by tests
}

// Sprintfln formats according to a format specifier and returns the resulting string.
// Spaces are always added between operands and a newline is appended.
func (p RGBStyle) Sprintfln(format string, a ...interface{}) string {
return fmt.Sprintf(format, p.Sprint(a...)) + "\n"

Check warning on line 148 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L147-L148

Added lines #L147 - L148 were not covered by tests
}

// GetValues returns the RGB values separately.
func (p RGB) GetValues() (r, g, b uint8) {
return p.R, p.G, p.B
Expand Down Expand Up @@ -160,3 +290,11 @@
tp := TextPrinter(p)
return &tp
}

func (p RGB) ToRGBStyle() RGBStyle {
if p.Background {
return RGBStyle{Bg: p}
}

Check warning on line 297 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L294-L297

Added lines #L294 - L297 were not covered by tests

return RGBStyle{Fg: p}

Check warning on line 299 in rgb.go

View check run for this annotation

Codecov / codecov/patch

rgb.go#L299

Added line #L299 was not covered by tests
}
68 changes: 68 additions & 0 deletions rgb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,71 @@ func TestRGB_PrintIfError_WithoutErrorf(t *testing.T) {
})
}
}

func TestRGBStyle_PrintOnError(t *testing.T) {
RGBs := []pterm.RGBStyle{{Fg: pterm.RGB{R: 10, G: 10, B: 10}}, {Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold}},
{Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintOnError", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnError(errors.New("hello world"))
})
testza.AssertContains(t, result, "hello world")
})
}
}

func TestRGBStyle_PrintIfError_WithoutError(t *testing.T) {
RGBs := []pterm.RGBStyle{{Fg: pterm.RGB{R: 10, G: 10, B: 10}}, {Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold}},
{Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintIfError_WithoutError", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnError(nil)
})
testza.AssertZero(t, result)
})
}
}

func TestRGBStyle_PrintOnErrorf(t *testing.T) {
RGBs := []pterm.RGBStyle{{Fg: pterm.RGB{R: 10, G: 10, B: 10}}, {Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold}},
{Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintOnErrorf", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnErrorf("wrapping error : %w", errors.New("hello world"))
})
testza.AssertContains(t, result, "hello world")
})
}
}

func TestRGBStyle_PrintIfError_WithoutErrorf(t *testing.T) {
RGBs := []pterm.RGBStyle{{Fg: pterm.RGB{R: 10, G: 10, B: 10}}, {Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold}},
{Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintIfError_WithoutErrorf", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnErrorf("", nil)
})
testza.AssertZero(t, result)
})
}
}

func TestRGBStyle_NewRGBStyle(t *testing.T) {
RGBs := []pterm.RGBStyle{{Fg: pterm.RGB{R: 10, G: 10, B: 10}}, {Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold}},
{Fg: pterm.RGB{R: 0, G: 0, B: 255}, Bg: pterm.RGB{R: 255, G: 0, B: 255}, Opts: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("NewRGBStyle", func(t *testing.T) {
result := pterm.NewRGBStyle(rgb.Fg, rgb.Bg)
if len(rgb.Opts) > 0 {
result = result.AddOptions(rgb.Opts...)
}
testza.AssertEqual(t, rgb.Fg, result.Fg)
testza.AssertEqual(t, rgb.Bg, result.Bg)
testza.AssertEqual(t, rgb.Opts, result.Opts)
})
}
}