A simple, modern colour/style package for CLI applications in Go
The dominant package in this space for Go is fatih/color which I've used before and is very good! However, I want to see if I can make something that improves on it. Specifically I want to try and address the following:
- Alignment/width of colourised text is maintained for text/tabwriter
- Sort of... I cheated and shipped a fork of tabwriter with the right modifications to support ANSI colours (
hue/tabwriter
)
- Sort of... I cheated and shipped a fork of tabwriter with the right modifications to support ANSI colours (
- Support both
$NO_COLOR
and$FORCE_COLOR
- Smaller public interface
- Make it so simple you don't even have to think about it
- As low performance overhead as possible
Like most libraries that do this sort of thing, hue uses ANSI Escape Codes to instruct the terminal emulator to render particular colours. See here for a helpful breakdown of how these codes work.
Important
Windows support is best effort, I don't own or use any windows devices so it's not a super high priority for me. If Windows support is important to you, you should use fatih/color
go get github.com/FollowTheProcess/hue@latest
Colours and styles in hue
are implemented as a bitmask and are therefore compile time constants! This means you can do this...
package main
import "github.com/FollowTheProcess/hue"
const (
success = hue.Green | hue.Bold
failure = hue.Red | hue.Underline
)
func main() {
success.Println("It worked!")
failure.Println("Not really")
}
Tip
Most functions from the fmt
package are implemented for hue styles including Sprintf
, Fprintln
etc.
hue
has been designed such that each new style is not a new allocated struct, plus the use of bitmasks to encode style leads to some nice performance benefits!
This benchmark measures returning a bold cyan string, one from fatih/color and one from hue
:
❯ go test ./... -run None -benchmem -bench BenchmarkColour
goos: darwin
goarch: arm64
pkg: github.com/FollowTheProcess/hue
cpu: Apple M1 Pro
BenchmarkColour/hue-8 7497607 138.7 ns/op 80 B/op 3 allocs/op
BenchmarkColour/color-8 2893501 415.2 ns/op 248 B/op 12 allocs/op
PASS
ok github.com/FollowTheProcess/hue 3.044s
- Nearly 70% faster
- Nearly 70% less bytes copied
- 9 fewer heap allocations!
Note
This benchmark used to be in here and run as part of CI, but I found that fatih/color
would show up as an indirect dependency
in code that imported hue
so I got rid of it, you'll just have to trust me I guess 😂
A common issue with ANSI colour libraries in Go are that they don't play well with text/tabwriter. This is because tabwriter includes the ANSI escape sequence in the cell width calculations, throwing off where it aligns the columns.
This has always annoyed me so when making my own ANSI colour library I had to tackle the tabwriter issue!
Enter hue/tabwriter
a drop in replacement for text/tabwriter that doesn't care about ANSI colours ✨
Note
The actual change is incredibly simple, just teaching text/tabwriter to ignore ANSI codes when it sees them so compatibility should be seamless
This package was created with copier and the FollowTheProcess/go_copier project template.