Compare commits
10 commits
cd70550a0a
...
afc4092824
Author | SHA1 | Date | |
---|---|---|---|
afc4092824 | |||
b8b0fbb57e | |||
cb8aacf9b8 | |||
77b109b517 | |||
77e626de5d | |||
e0bfa137d5 | |||
71c74c800b | |||
929938dcd6 | |||
55da31ffd6 | |||
6db2852d9e |
25 changed files with 424 additions and 131 deletions
|
@ -1,38 +1,37 @@
|
|||
package color_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/go/color"
|
||||
"git.urbach.dev/go/color"
|
||||
)
|
||||
|
||||
func BenchmarkRGB(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
color.RGB(1.0, 1.0, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLCH(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
color.LCH(0.5, 0.5, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFprintColorized(b *testing.B) {
|
||||
func BenchmarkPrint(b *testing.B) {
|
||||
color.Terminal = true
|
||||
c := color.RGB(1.0, 1.0, 1.0)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Fprint(io.Discard, "")
|
||||
for b.Loop() {
|
||||
c.Print("")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFprintRaw(b *testing.B) {
|
||||
func BenchmarkPrintRaw(b *testing.B) {
|
||||
color.Terminal = false
|
||||
c := color.RGB(1.0, 1.0, 1.0)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Fprint(io.Discard, "")
|
||||
for b.Loop() {
|
||||
c.Print("")
|
||||
}
|
||||
}
|
||||
|
|
53
Color.go
53
Color.go
|
@ -2,50 +2,41 @@ package color
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Value is a type definition for the data type of a single color component.
|
||||
type Value = float64
|
||||
|
||||
// Color represents an RGB color.
|
||||
// Color represents an sRGB color.
|
||||
type Color struct {
|
||||
R Value
|
||||
G Value
|
||||
B Value
|
||||
}
|
||||
|
||||
// RGB creates a new color with red, green and blue values in the range of 0.0 to 1.0.
|
||||
func RGB(r Value, g Value, b Value) Color {
|
||||
return Color{r, g, b}
|
||||
}
|
||||
|
||||
// Fprint writes the text in the given color to the writer.
|
||||
func (c Color) Fprint(writer io.Writer, text string) {
|
||||
if !Terminal {
|
||||
fmt.Fprint(writer, text)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, format, byte(c.R*255), byte(c.G*255), byte(c.B*255), text)
|
||||
R byte
|
||||
G byte
|
||||
B byte
|
||||
}
|
||||
|
||||
// Print writes the text in the given color to standard output.
|
||||
func (c Color) Print(text string) {
|
||||
if !Terminal {
|
||||
fmt.Print(text)
|
||||
func (c Color) Print(args ...any) {
|
||||
if !Terminal || !TrueColor {
|
||||
fmt.Print(args...)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(format, byte(c.R*255), byte(c.G*255), byte(c.B*255), text)
|
||||
fmt.Printf("\x1b[38;2;%d;%d;%dm%s\x1b[0m", c.R, c.G, c.B, fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes the text in the given color to standard output.
|
||||
func (c Color) Printf(format string, args ...any) {
|
||||
if !Terminal || !TrueColor {
|
||||
fmt.Printf(format, args...)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\x1b[38;2;%d;%d;%dm%s\x1b[0m", c.R, c.G, c.B, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Println writes the text in the given color to standard output and appends a newline.
|
||||
func (c Color) Println(text string) {
|
||||
if !Terminal {
|
||||
fmt.Println(text)
|
||||
func (c Color) Println(args ...any) {
|
||||
if !Terminal || !TrueColor {
|
||||
fmt.Println(args...)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(formatLine, byte(c.R*255), byte(c.G*255), byte(c.B*255), text)
|
||||
fmt.Printf("\x1b[38;2;%d;%d;%dm%s\n\x1b[0m", c.R, c.G, c.B, fmt.Sprint(args...))
|
||||
}
|
||||
|
|
|
@ -1,83 +1,55 @@
|
|||
package color_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/go/assert"
|
||||
"git.akyoto.dev/go/color"
|
||||
"git.urbach.dev/go/color"
|
||||
)
|
||||
|
||||
func TestFprint(t *testing.T) {
|
||||
color.Terminal = true
|
||||
|
||||
color.RGB(1, 0, 0).Fprint(io.Discard, "red")
|
||||
color.RGB(0, 1, 0).Fprint(io.Discard, "green")
|
||||
color.RGB(0, 0, 1).Fprint(io.Discard, "blue")
|
||||
|
||||
color.Terminal = false
|
||||
|
||||
color.RGB(1, 0, 0).Fprint(io.Discard, "red")
|
||||
color.RGB(0, 1, 0).Fprint(io.Discard, "green")
|
||||
color.RGB(0, 0, 1).Fprint(io.Discard, "blue")
|
||||
}
|
||||
|
||||
func TestPrint(t *testing.T) {
|
||||
color.Terminal = true
|
||||
color.TrueColor = true
|
||||
|
||||
color.RGB(1, 0, 0).Print("red\n")
|
||||
color.RGB(0, 1, 0).Print("green\n")
|
||||
color.RGB(0, 0, 1).Print("blue\n")
|
||||
|
||||
color.Terminal = false
|
||||
color.TrueColor = false
|
||||
|
||||
color.RGB(1, 0, 0).Print("red\n")
|
||||
color.RGB(0, 1, 0).Print("green\n")
|
||||
color.RGB(0, 0, 1).Print("blue\n")
|
||||
}
|
||||
|
||||
func TestPrintf(t *testing.T) {
|
||||
color.Terminal = true
|
||||
color.TrueColor = true
|
||||
|
||||
color.RGB(1, 0, 0).Printf("%s\n", "red")
|
||||
color.RGB(0, 1, 0).Printf("%s\n", "green")
|
||||
color.RGB(0, 0, 1).Printf("%s\n", "blue")
|
||||
|
||||
color.Terminal = false
|
||||
color.TrueColor = false
|
||||
|
||||
color.RGB(1, 0, 0).Printf("%s\n", "red")
|
||||
color.RGB(0, 1, 0).Printf("%s\n", "green")
|
||||
color.RGB(0, 0, 1).Printf("%s\n", "blue")
|
||||
}
|
||||
|
||||
func TestPrintln(t *testing.T) {
|
||||
color.Terminal = true
|
||||
color.TrueColor = true
|
||||
|
||||
color.RGB(1, 0, 0).Println("red")
|
||||
color.RGB(0, 1, 0).Println("green")
|
||||
color.RGB(0, 0, 1).Println("blue")
|
||||
|
||||
color.Terminal = false
|
||||
color.TrueColor = false
|
||||
|
||||
color.RGB(1, 0, 0).Println("red")
|
||||
color.RGB(0, 1, 0).Println("green")
|
||||
color.RGB(0, 0, 1).Println("blue")
|
||||
}
|
||||
|
||||
func TestRGB(t *testing.T) {
|
||||
color.Terminal = true
|
||||
|
||||
rgbColors := map[string]color.Color{
|
||||
"black": color.RGB(0, 0, 0),
|
||||
"white": color.RGB(1, 1, 1),
|
||||
"gray": color.RGB(0.5, 0.5, 0.5),
|
||||
"red": color.RGB(1, 0, 0),
|
||||
"green": color.RGB(0, 1, 0),
|
||||
"blue": color.RGB(0, 0, 1),
|
||||
"cyan": color.RGB(0, 1, 1),
|
||||
"yellow": color.RGB(1, 1, 0),
|
||||
"orange": color.RGB(1, 0.5, 0),
|
||||
"magenta": color.RGB(1, 0, 1),
|
||||
}
|
||||
|
||||
for name, c := range rgbColors {
|
||||
testColorRange(t, c)
|
||||
c.Println("█ " + name)
|
||||
}
|
||||
}
|
||||
|
||||
func testColorRange(t *testing.T, c color.Color) {
|
||||
assert.True(t, c.R >= 0.0)
|
||||
assert.True(t, c.G >= 0.0)
|
||||
assert.True(t, c.B >= 0.0)
|
||||
|
||||
assert.True(t, c.R <= 1.0)
|
||||
assert.True(t, c.G <= 1.0)
|
||||
assert.True(t, c.B <= 1.0)
|
||||
}
|
||||
|
|
29
HSL.go
Normal file
29
HSL.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package color
|
||||
|
||||
import "math"
|
||||
|
||||
// HSL represents a color using hue, saturation and lightness.
|
||||
func HSL(hue Value, saturation Value, lightness Value) Color {
|
||||
hue = math.Mod(hue, 360)
|
||||
c := (1 - math.Abs(2*lightness-1)) * saturation
|
||||
x := c * (1 - math.Abs(math.Mod(hue/60, 2)-1))
|
||||
var r, g, b Value
|
||||
|
||||
switch {
|
||||
case hue >= 0 && hue < 60:
|
||||
r, g, b = c, x, 0
|
||||
case hue >= 60 && hue < 120:
|
||||
r, g, b = x, c, 0
|
||||
case hue >= 120 && hue < 180:
|
||||
r, g, b = 0, c, x
|
||||
case hue >= 180 && hue < 240:
|
||||
r, g, b = 0, x, c
|
||||
case hue >= 240 && hue < 300:
|
||||
r, g, b = x, 0, c
|
||||
case hue >= 300 && hue < 360:
|
||||
r, g, b = c, 0, x
|
||||
}
|
||||
|
||||
m := lightness - c/2
|
||||
return RGB(r+m, g+m, b+m)
|
||||
}
|
26
HSL_test.go
Normal file
26
HSL_test.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package color_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.urbach.dev/go/color"
|
||||
)
|
||||
|
||||
func TestHSLSpectrum(t *testing.T) {
|
||||
color.Terminal = true
|
||||
color.TrueColor = true
|
||||
|
||||
for lightness := range 21 {
|
||||
for hue := range 80 {
|
||||
h := color.Value(hue) * 4.4
|
||||
s := color.Value(1.0)
|
||||
l := color.Value(lightness) * 0.05
|
||||
|
||||
c := color.HSL(h, s, l)
|
||||
c.Print("█")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
29
HSV.go
Normal file
29
HSV.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package color
|
||||
|
||||
import "math"
|
||||
|
||||
// HSV represents a color using hue, saturation and value.
|
||||
func HSV(hue Value, saturation Value, value Value) Color {
|
||||
hue = math.Mod(hue, 360)
|
||||
c := value * saturation
|
||||
x := c * (1 - math.Abs(math.Mod(hue/60, 2)-1))
|
||||
var r, g, b Value
|
||||
|
||||
switch {
|
||||
case hue >= 0 && hue < 60:
|
||||
r, g, b = c, x, 0
|
||||
case hue >= 60 && hue < 120:
|
||||
r, g, b = x, c, 0
|
||||
case hue >= 120 && hue < 180:
|
||||
r, g, b = 0, c, x
|
||||
case hue >= 180 && hue < 240:
|
||||
r, g, b = 0, x, c
|
||||
case hue >= 240 && hue < 300:
|
||||
r, g, b = x, 0, c
|
||||
case hue >= 300 && hue < 360:
|
||||
r, g, b = c, 0, x
|
||||
}
|
||||
|
||||
m := value - c
|
||||
return RGB(r+m, g+m, b+m)
|
||||
}
|
26
HSV_test.go
Normal file
26
HSV_test.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package color_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.urbach.dev/go/color"
|
||||
)
|
||||
|
||||
func TestHSVSpectrum(t *testing.T) {
|
||||
color.Terminal = true
|
||||
color.TrueColor = true
|
||||
|
||||
for value := range 21 {
|
||||
for hue := range 80 {
|
||||
h := color.Value(hue) * 4.4
|
||||
s := color.Value(1.0)
|
||||
v := color.Value(value) * 0.05
|
||||
|
||||
c := color.HSV(h, s, v)
|
||||
c.Print("█")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
7
LCH.go
7
LCH.go
|
@ -19,12 +19,7 @@ func LCH(lightness Value, chroma Value, hue Value) Color {
|
|||
b *= chroma
|
||||
|
||||
r, g, b := oklabToLinearRGB(lightness, a, b)
|
||||
|
||||
r = sRGB(r)
|
||||
g = sRGB(g)
|
||||
b = sRGB(b)
|
||||
|
||||
return Color{r, g, b}
|
||||
return RGB(r, g, b)
|
||||
}
|
||||
|
||||
// findChromaInSRGB tries to find the closest chroma that can be represented in sRGB color space.
|
||||
|
|
14
LCH_test.go
14
LCH_test.go
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/go/color"
|
||||
"git.urbach.dev/go/color"
|
||||
)
|
||||
|
||||
func TestLCH(t *testing.T) {
|
||||
|
@ -18,27 +18,29 @@ func TestLCH(t *testing.T) {
|
|||
"red": color.LCH(0.75, 1.0, 40),
|
||||
"orange": color.LCH(0.75, 1.0, 60),
|
||||
"yellow": color.LCH(0.9, 1.0, 100),
|
||||
"green": color.LCH(0.75, 1.0, 150),
|
||||
"green": color.LCH(0.75, 1.0, 135),
|
||||
"blue": color.LCH(0.75, 1.0, 260),
|
||||
"cyan": color.LCH(0.75, 1.0, 210),
|
||||
"magenta": color.LCH(0.75, 1.0, 320),
|
||||
}
|
||||
|
||||
for name, c := range lchColors {
|
||||
testColorRange(t, c)
|
||||
c.Println("█ " + name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLCHSpectrum(t *testing.T) {
|
||||
color.Terminal = true
|
||||
color.TrueColor = true
|
||||
|
||||
for chroma := range 4 {
|
||||
for lightness := range 21 {
|
||||
for hue := range 80 {
|
||||
c := color.LCH(color.Value(lightness)*0.05, color.Value(chroma)*0.05, color.Value(hue)*4.4)
|
||||
testColorRange(t, c)
|
||||
c.Print("█")
|
||||
l := color.Value(lightness) * 0.05
|
||||
c := color.Value(chroma) * 0.05
|
||||
h := color.Value(hue) * 4.4
|
||||
col := color.LCH(l, c, h)
|
||||
col.Print("█")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
|
42
README.md
42
README.md
|
@ -4,52 +4,60 @@ Adds color to your terminal output.
|
|||
|
||||
## Features
|
||||
|
||||
- RGB color space
|
||||
- LCH color space (oklch)
|
||||
- Truecolor terminal output
|
||||
- Zero dependencies (excluding tests)
|
||||
- ANSI colors
|
||||
- HSL colors
|
||||
- HSV colors
|
||||
- LCH colors
|
||||
- RGB colors
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
go get git.akyoto.dev/go/color
|
||||
go get git.urbach.dev/go/color
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
red := color.RGB(1.0, 0.0, 0.0)
|
||||
red.Println("red text")
|
||||
// ANSI
|
||||
ansi.Red.Println("red text")
|
||||
|
||||
orange := color.LCH(0.7, 1.0, 65)
|
||||
orange.Println("orange text")
|
||||
// LCH
|
||||
green := color.LCH(0.5, 1.0, 135)
|
||||
green.Println("green text")
|
||||
|
||||
// RGB
|
||||
blue := color.RGB(0, 0, 1)
|
||||
blue.Println("blue text")
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```
|
||||
PASS: TestFprint
|
||||
PASS: TestPrint
|
||||
PASS: TestPrintf
|
||||
PASS: TestPrintln
|
||||
PASS: TestRGB
|
||||
PASS: TestHSLSpectrum
|
||||
PASS: TestHSVSpectrum
|
||||
PASS: TestLCH
|
||||
PASS: TestLCHSpectrum
|
||||
PASS: TestRGB
|
||||
coverage: 100.0% of statements
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
```
|
||||
BenchmarkRGB-12 1000000000 0.3132 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkLCH-12 4802006 249.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFprintColorized-12 6356535 188.4 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFprintRaw-12 27374659 43.76 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkRGB-20 100000000 14.88 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkLCH-20 5075756 227.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPrint-20 1587134 755.5 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPrintRaw-20 3166090 361.5 ns/op 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Please see the [license documentation](https://akyoto.dev/license).
|
||||
Please see the [license documentation](https://urbach.dev/license).
|
||||
|
||||
## Copyright
|
||||
|
||||
© 2024 Eduard Urbach
|
||||
© 2024 Eduard Urbach
|
|
@ -2,6 +2,15 @@ package color
|
|||
|
||||
import "math"
|
||||
|
||||
// RGB creates a new sRGB color.
|
||||
func RGB(r Value, g Value, b Value) Color {
|
||||
return Color{
|
||||
byte(sRGB(r) * 255),
|
||||
byte(sRGB(g) * 255),
|
||||
byte(sRGB(b) * 255),
|
||||
}
|
||||
}
|
||||
|
||||
// inSRGB indicates whether the given color can be mapped to the sRGB color space.
|
||||
func inSRGB(l Value, a Value, b Value, chroma Value) bool {
|
||||
r, g, b := oklabToLinearRGB(l, a*chroma, b*chroma)
|
29
RGB_test.go
Normal file
29
RGB_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package color_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.urbach.dev/go/color"
|
||||
)
|
||||
|
||||
func TestRGB(t *testing.T) {
|
||||
color.Terminal = true
|
||||
color.TrueColor = true
|
||||
|
||||
rgbColors := map[string]color.Color{
|
||||
"black": color.RGB(0, 0, 0),
|
||||
"white": color.RGB(1, 1, 1),
|
||||
"gray": color.RGB(0.5, 0.5, 0.5),
|
||||
"red": color.RGB(1, 0, 0),
|
||||
"green": color.RGB(0, 1, 0),
|
||||
"blue": color.RGB(0, 0, 1),
|
||||
"cyan": color.RGB(0, 1, 1),
|
||||
"yellow": color.RGB(1, 1, 0),
|
||||
"orange": color.RGB(1, 0.5, 0),
|
||||
"magenta": color.RGB(1, 0, 1),
|
||||
}
|
||||
|
||||
for name, c := range rgbColors {
|
||||
c.Println("█ " + name)
|
||||
}
|
||||
}
|
13
Terminal.go
13
Terminal.go
|
@ -2,13 +2,12 @@ package color
|
|||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.urbach.dev/go/color/tty"
|
||||
)
|
||||
|
||||
// These constants represent the escape codes needed to display color in terminals.
|
||||
const (
|
||||
format = "\x1b[38;2;%d;%d;%dm%s\x1b[0m"
|
||||
formatLine = "\x1b[38;2;%d;%d;%dm%s\n\x1b[0m"
|
||||
)
|
||||
// Terminal indicates if we're in a terminal with truecolor support.
|
||||
var Terminal = tty.IsTerminal(os.Stdout.Fd())
|
||||
|
||||
// Terminal is a boolean that indicates if we're in a terminal with truecolor support.
|
||||
var Terminal = os.Getenv("COLORTERM") == "truecolor"
|
||||
// TrueColor indicates if the terminal has 24-bit color support.
|
||||
var TrueColor = os.Getenv("COLORTERM") == "truecolor"
|
||||
|
|
4
Value.go
Normal file
4
Value.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
package color
|
||||
|
||||
// Value is a type definition for the data type of a single color component.
|
||||
type Value = float64
|
40
ansi/Code.go
Normal file
40
ansi/Code.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/go/color"
|
||||
)
|
||||
|
||||
// Code represents an ANSI escape code.
|
||||
type Code int
|
||||
|
||||
// Print writes the text in the given color to standard output.
|
||||
func (code Code) Print(args ...any) {
|
||||
if !color.Terminal {
|
||||
fmt.Print(args...)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\x1b[%dm%s\x1b[0m", code, fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes the text in the given color to standard output.
|
||||
func (code Code) Printf(format string, args ...any) {
|
||||
if !color.Terminal {
|
||||
fmt.Printf(format, args...)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\x1b[%dm%s\x1b[0m", code, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Println writes the text in the given color to standard output and appends a newline.
|
||||
func (code Code) Println(args ...any) {
|
||||
if !color.Terminal {
|
||||
fmt.Println(args...)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\x1b[%dm%s\n\x1b[0m", code, fmt.Sprint(args...))
|
||||
}
|
55
ansi/Code_test.go
Normal file
55
ansi/Code_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package ansi_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.urbach.dev/go/color"
|
||||
"git.urbach.dev/go/color/ansi"
|
||||
)
|
||||
|
||||
func TestPrintRaw(t *testing.T) {
|
||||
color.Terminal = false
|
||||
testPrint()
|
||||
testPrintf()
|
||||
testPrintln()
|
||||
}
|
||||
|
||||
func TestPrint(t *testing.T) {
|
||||
color.Terminal = true
|
||||
testPrint()
|
||||
testPrintf()
|
||||
testPrintln()
|
||||
}
|
||||
|
||||
func testPrint() {
|
||||
ansi.Black.Print("Black\n")
|
||||
ansi.White.Print("White\n")
|
||||
ansi.Red.Print("Red\n")
|
||||
ansi.Green.Print("Green\n")
|
||||
ansi.Blue.Print("Blue\n")
|
||||
ansi.Yellow.Print("Yellow\n")
|
||||
ansi.Magenta.Print("Magenta\n")
|
||||
ansi.Cyan.Print("Cyan\n")
|
||||
}
|
||||
|
||||
func testPrintf() {
|
||||
ansi.Black.Printf("%s\n", "Black")
|
||||
ansi.White.Printf("%s\n", "White")
|
||||
ansi.Red.Printf("%s\n", "Red")
|
||||
ansi.Green.Printf("%s\n", "Green")
|
||||
ansi.Blue.Printf("%s\n", "Blue")
|
||||
ansi.Yellow.Printf("%s\n", "Yellow")
|
||||
ansi.Magenta.Printf("%s\n", "Magenta")
|
||||
ansi.Cyan.Printf("%s\n", "Cyan")
|
||||
}
|
||||
|
||||
func testPrintln() {
|
||||
ansi.Black.Println("Black")
|
||||
ansi.White.Println("White")
|
||||
ansi.Red.Println("Red")
|
||||
ansi.Green.Println("Green")
|
||||
ansi.Blue.Println("Blue")
|
||||
ansi.Yellow.Println("Yellow")
|
||||
ansi.Magenta.Println("Magenta")
|
||||
ansi.Cyan.Println("Cyan")
|
||||
}
|
12
ansi/Color.go
Normal file
12
ansi/Color.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package ansi
|
||||
|
||||
const (
|
||||
Black Code = iota + 30
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
Blue
|
||||
Magenta
|
||||
Cyan
|
||||
White
|
||||
)
|
14
ansi/Format.go
Normal file
14
ansi/Format.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package ansi
|
||||
|
||||
const (
|
||||
Reset Code = iota
|
||||
Bold
|
||||
Dim
|
||||
Italic
|
||||
Underline
|
||||
Blink
|
||||
BlinkFast
|
||||
Reverse
|
||||
Hidden
|
||||
Strikethrough
|
||||
)
|
6
go.mod
6
go.mod
|
@ -1,5 +1,5 @@
|
|||
module git.akyoto.dev/go/color
|
||||
module git.urbach.dev/go/color
|
||||
|
||||
go 1.22.0
|
||||
go 1.24
|
||||
|
||||
require git.akyoto.dev/go/assert v0.1.3
|
||||
require golang.org/x/sys v0.31.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,2 +1,2 @@
|
|||
git.akyoto.dev/go/assert v0.1.3 h1:QwCUbmG4aZYsNk/OuRBz1zWVKmGlDUHhOnnDBfn8Qw8=
|
||||
git.akyoto.dev/go/assert v0.1.3/go.mod h1:0GzMaM0eURuDwtGkJJkCsI7r2aUKr+5GmWNTFPgDocM=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
|
|
11
tty/Terminal_bsd.go
Normal file
11
tty/Terminal_bsd.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
//go:build darwin || freebsd
|
||||
|
||||
package tty
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTerminal returns true if the file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
|
||||
return err == nil
|
||||
}
|
11
tty/Terminal_linux.go
Normal file
11
tty/Terminal_linux.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
//go:build linux
|
||||
|
||||
package tty
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTerminal returns true if the file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
||||
return err == nil
|
||||
}
|
8
tty/Terminal_other.go
Normal file
8
tty/Terminal_other.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
//go:build !darwin && !freebsd && !linux && !windows
|
||||
|
||||
package tty
|
||||
|
||||
// IsTerminal is always false on unsupported platforms.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
12
tty/Terminal_test.go
Normal file
12
tty/Terminal_test.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package tty_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.urbach.dev/go/color/tty"
|
||||
)
|
||||
|
||||
func TestIsTerminal(t *testing.T) {
|
||||
tty.IsTerminal(os.Stdout.Fd())
|
||||
}
|
12
tty/Terminal_windows.go
Normal file
12
tty/Terminal_windows.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
//go:build windows
|
||||
|
||||
package tty
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
// IsTerminal returns true if the file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var mode uint32
|
||||
err := windows.GetConsoleMode(windows.Handle(fd), &mode)
|
||||
return err == nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue