diff options
| -rw-r--r-- | LICENSE | 12 | ||||
| -rw-r--r-- | ansicolor_string.go | 40 | ||||
| -rw-r--r-- | go.mod | 3 | ||||
| -rw-r--r-- | hi.go | 131 |
4 files changed, 186 insertions, 0 deletions
@@ -0,0 +1,12 @@ +Copyright (C) 2025 by Rose Hogenson <rosehogenson@posteo.net> + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/ansicolor_string.go b/ansicolor_string.go new file mode 100644 index 0000000..b811166 --- /dev/null +++ b/ansicolor_string.go @@ -0,0 +1,40 @@ +// Code generated by "stringer -type=ansiColor -linecomment"; DO NOT EDIT. + +package main + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[black-0] + _ = x[red-1] + _ = x[green-2] + _ = x[yellow-3] + _ = x[blue-4] + _ = x[magenta-5] + _ = x[cyan-6] + _ = x[white-7] + _ = x[defaultColor-9] +} + +const ( + _ansiColor_name_0 = "blackredgreenyellowbluemagentacyanwhite" + _ansiColor_name_1 = "default" +) + +var ( + _ansiColor_index_0 = [...]uint8{0, 5, 8, 13, 19, 23, 30, 34, 39} +) + +func (i ansiColor) String() string { + switch { + case 0 <= i && i <= 7: + return _ansiColor_name_0[_ansiColor_index_0[i]:_ansiColor_index_0[i+1]] + case i == 9: + return _ansiColor_name_1 + default: + return "ansiColor(" + strconv.FormatInt(int64(i), 10) + ")" + } +} @@ -0,0 +1,3 @@ +module github.com/rhogenson/hi + +go 1.24.3 @@ -0,0 +1,131 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "regexp" + "strings" +) + +func init() { + highlightColor = red + flag.Var((*colorFlag)(&highlightColor), "c", fmt.Sprintf("highlight `color` %s", colorsChoices)) + background = defaultColor + flag.Var((*colorFlag)(&background), "bg", fmt.Sprintf("background `color` default|%s", colorsChoices)) +} + +var ( + highlightColor ansiColor + background ansiColor +) + +//go:generate stringer -type=ansiColor -linecomment +type ansiColor int + +const ( + black ansiColor = iota + red + green + yellow + blue + magenta + cyan + white + defaultColor ansiColor = 9 // default +) + +var colorsChoices = func() string { + nColors := white - black + 1 + colors := make([]string, nColors) + for i := range nColors { + colors[i] = (black + i).String() + } + return strings.Join(colors, "|") +}() + +func (c ansiColor) Foreground() int { + return 30 + int(c) +} + +func (c ansiColor) Background() int { + return 40 + int(c) +} + +type colorFlag int + +func (c *colorFlag) Set(val string) error { + var ac ansiColor + switch strings.ToLower(val) { + case "default": + ac = defaultColor + case "black": + ac = black + case "red": + ac = red + case "green": + ac = green + case "yellow": + ac = yellow + case "blue": + ac = blue + case "magenta": + ac = magenta + case "cyan": + ac = cyan + case "white": + ac = white + default: + return fmt.Errorf("unknown color %q (valid choices: %s)", val, colorsChoices) + } + *c = colorFlag(ac) + return nil +} + +func (c *colorFlag) String() string { + return ansiColor(*c).String() +} + +func run() error { + args := flag.Args() + if len(args) == 0 { + fmt.Fprintf(os.Stderr, "missing regexp to highlight\n") + flag.Usage() + } + if len(args) > 2 { + fmt.Fprintf(os.Stderr, "too many positional arguments\n") + flag.Usage() + } + highlightRE, err := regexp.CompilePOSIX(args[0]) + if err != nil { + return fmt.Errorf("invalid highlight regexp: %s", err) + } + foregroundColor := highlightColor.Foreground() + backgroundColor := background.Background() + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + line := scanner.Text() + loc := highlightRE.FindStringIndex(line) + if loc == nil { + fmt.Printf("%s\n", line) + continue + } + startIdx, endIdx := loc[0], loc[1] + fmt.Printf("%s\033[%d;%dm%s\033[39;49m%s\n", line[:startIdx], foregroundColor, backgroundColor, line[startIdx:endIdx], line[endIdx:]) + } + return scanner.Err() +} + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: hi [OPTIONS] REGEXP\n") + flag.PrintDefaults() + os.Exit(2) + } + flag.Parse() + if err := run(); err != nil { + fmt.Fprintf(os.Stderr, "hi: %s\n", err) + os.Exit(1) + } +} |
