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) } }