aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-07-04 10:29:59 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-07-04 10:32:54 -0700
commitb0454e1de6ff0cf310b3acb599a7d42d7a079423 (patch)
treebf5be14f666dd51440d48e68c466051d95caa815
downloadhi-b0454e1de6ff0cf310b3acb599a7d42d7a079423.tar.zst
Initial commit
-rw-r--r--LICENSE12
-rw-r--r--ansicolor_string.go40
-rw-r--r--go.mod3
-rw-r--r--hi.go131
4 files changed, 186 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2180e82
--- /dev/null
+++ b/LICENSE
@@ -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) + ")"
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..3c1ec54
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/rhogenson/hi
+
+go 1.24.3
diff --git a/hi.go b/hi.go
new file mode 100644
index 0000000..ec927b3
--- /dev/null
+++ b/hi.go
@@ -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)
+ }
+}