Skip to content

Commit

Permalink
Change to xo/ox
Browse files Browse the repository at this point in the history
  • Loading branch information
kenshaw committed Dec 4, 2024
1 parent 6d8eb19 commit 2be8e95
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 158 deletions.
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ module github.com/kenshaw/fv
go 1.23

require (
github.com/kenshaw/colors v0.1.6
github.com/kenshaw/colors v0.2.0
github.com/kenshaw/rasterm v0.1.11
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/tdewolff/canvas v0.0.0-20241107191514-17a7fb09dce2
github.com/tdewolff/font v0.0.0-20240728193914-9dc6e3441d03
github.com/xo/ox v0.0.0-20241204042113-c44357c6c99f
)

require (
Expand All @@ -19,8 +19,6 @@ require (
github.com/go-fonts/latin-modern v0.3.3 // indirect
github.com/go-text/typesetting v0.2.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kenshaw/snaker v0.3.0 // indirect
github.com/mattn/go-sixel v0.0.5 // indirect
github.com/soniakeys/quant v1.0.0 // indirect
github.com/tdewolff/minify/v2 v2.21.1 // indirect
Expand Down
16 changes: 4 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ github.com/benoitkugler/textprocessing v0.0.3 h1:Q2X+Z6vxuW5Bxn1R9RaNt0qcprBfpc2
github.com/benoitkugler/textprocessing v0.0.3/go.mod h1:/4bLyCf1QYywunMK3Gf89Nhb50YI/9POewqrLxWhxd4=
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/go-fonts/latin-modern v0.3.3 h1:g2xNgI8yzdNzIVm+qvbMryB6yGPe0pSMss8QT3QwlJ0=
github.com/go-fonts/latin-modern v0.3.3/go.mod h1:tHaiWDGze4EPB0Go4cLT5M3QzRY3peya09Z/8KSCrpY=
github.com/go-fonts/liberation v0.3.3 h1:tM/T2vEOhjia6v5krQu8SDDegfH1SfXVRUNNKpq0Usk=
Expand All @@ -29,23 +28,16 @@ github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm6
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kenshaw/colors v0.1.6 h1:saSe9+ubchrnwWqwfNIP5CLEQcGcFp3vgiag2g538jk=
github.com/kenshaw/colors v0.1.6/go.mod h1:+Nx5c4s70YO6elThBFOk0/9InGOvV1+uAak7T29sCKk=
github.com/kenshaw/colors v0.2.0 h1:rU4H1OOWCoLgdXofFKUurVj4XBDual2IeJ1HuSqV3QU=
github.com/kenshaw/colors v0.2.0/go.mod h1:Aok7+9KpR+qEwgCxDEoLBS6IGFhY1iRJIzbcv5ijewI=
github.com/kenshaw/rasterm v0.1.11 h1:GSXJx+47APM53hEFrGLqIxfG/cQc7tIqeqcPwbVIKBY=
github.com/kenshaw/rasterm v0.1.11/go.mod h1:oKL76NdzLgqRc+d4cHAi5btI1kx5JhnLm11GIzxRlkE=
github.com/kenshaw/snaker v0.3.0 h1:9sw7vM0hfCm1kvG/LrxrgEsgoH9yjdQMVU3rwIGxBZo=
github.com/kenshaw/snaker v0.3.0/go.mod h1:DNyRUqHMZ18/zioxr6R7m4kSxxf2+QmB0BXoORsXRaY=
github.com/mattn/go-sixel v0.0.5 h1:55w2FR5ncuhKhXrM5ly1eiqMQfZsnAHIpYNGZX03Cv8=
github.com/mattn/go-sixel v0.0.5/go.mod h1:h2Sss+DiUEHy0pUqcIB6PFXo5Cy8sTQEFr3a9/5ZLNw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y=
github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/tdewolff/canvas v0.0.0-20241107191514-17a7fb09dce2 h1:M+mJcSCyEoYNTycjFO5lPgVDSxNirXtx8Q80QOioi1o=
Expand All @@ -59,6 +51,8 @@ github.com/tdewolff/parse/v2 v2.7.19/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
github.com/xo/ox v0.0.0-20241204042113-c44357c6c99f h1:FSDgZn9jAFxq2UU6yPPCC2I93kKrqUQpsM5qmqMYLc8=
github.com/xo/ox v0.0.0-20241204042113-c44357c6c99f/go.mod h1:P+6CbW2koi1wfhvTg0ygy9Q5egZX2BoWrcZc+K3MLEg=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
Expand All @@ -82,7 +76,5 @@ golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gonum.org/v1/plot v0.15.0 h1:SIFtFNdZNWLRDRVjD6CYxdawcpJDWySZehJGpv1ukkw=
gonum.org/v1/plot v0.15.0/go.mod h1:3Nx4m77J4T/ayr/b8dQ8uGRmZF6H3eTqliUExDrQHnM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
star-tex.org/x/tex v0.5.0 h1:KXxY58YioX9kLvjEzIl61JH4IvWob3FSQQ5DiU3RQ3M=
star-tex.org/x/tex v0.5.0/go.mod h1:k7py0yK2wZM4aJSgN1RonpvnxgEgIqC0i/x7kKR9GZI=
209 changes: 67 additions & 142 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
_ "embed"
"errors"
"fmt"
"image/color"
"io"
"maps"
"os"
Expand All @@ -24,146 +23,72 @@ import (

"github.com/kenshaw/colors"
"github.com/kenshaw/rasterm"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/tdewolff/canvas"
"github.com/tdewolff/canvas/renderers/rasterizer"
fontpkg "github.com/tdewolff/font"
)

var (
name = "fv"
version = "0.0.0-dev"
"github.com/xo/ox"
_ "github.com/xo/ox/color"
)

func main() {
if err := run(context.Background(), name, version, os.Args); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
args := &Args{}
ox.RunContext(
context.Background(),
ox.Exec(run(os.Stdout, args)),
ox.Usage("fv", "a command-line font view using terminal graphics"),
ox.Defaults(),
ox.From(args),
)
}

func run(ctx context.Context, name, version string, cliargs []string) error {
var all, list, match bool
fg, bg := colors.FromColor(color.Black), colors.FromColor(color.White)
var size, margin, dpi int
style, variant := canvas.FontRegular, canvas.FontNormal
var text string
var (
bashCompletion bool
zshCompletion bool
fishCompletion bool
powershellCompletion bool
noDescriptions bool
)
c := &cobra.Command{
Use: name + " [flags] <font1> [font2, ..., fontN]",
Short: name + ", a command-line font viewer using terminal graphics",
Version: version,
SilenceErrors: true,
SilenceUsage: false,
Args: func(_ *cobra.Command, args []string) error {
switch hasArgs := len(args) != 0; {
case all && hasArgs:
return errors.New("--all does not take any args")
case list && hasArgs:
return errors.New("--list does not take any args")
case match && !hasArgs:
return errors.New("--match requires one or more args")
case all && list,
all && match,
match && list:
return errors.New("--all, --list, and --match are exclusive")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
// completions and short circuits
switch {
case bashCompletion:
return cmd.GenBashCompletionV2(os.Stdout, !noDescriptions)
case zshCompletion:
if noDescriptions {
return cmd.GenZshCompletionNoDesc(os.Stdout)
}
return cmd.GenZshCompletion(os.Stdout)
case fishCompletion:
return cmd.GenFishCompletion(os.Stdout, !noDescriptions)
case powershellCompletion:
if noDescriptions {
return cmd.GenPowerShellCompletion(os.Stdout)
}
return cmd.GenPowerShellCompletionWithDesc(os.Stdout)
}
sysfonts, err := fontpkg.FindSystemFonts(fontpkg.DefaultFontDirs())
if err != nil {
return err
}
f := do
switch {
case all:
f = doAll
case list:
f = doList
case match:
f = doMatch
}
return f(os.Stdout, sysfonts, &Args{
FG: fg,
BG: bg,
Size: size,
DPI: dpi,
Margin: margin,
Style: style,
Variant: variant,
Text: text,
Args: args,
})
},
}
c.SetVersionTemplate("{{ .Name }} {{ .Version }}\n")
c.InitDefaultHelpCmd()
c.SetArgs(cliargs[1:])
flags := c.Flags()
flags.BoolVar(&all, "all", false, "show all system fonts")
flags.BoolVar(&list, "list", false, "list system fonts")
flags.BoolVar(&match, "match", false, "match system fonts")
flags.Var(fg.Pflag(), "fg", "foreground color")
flags.Var(bg.Pflag(), "bg", "background color")
flags.IntVar(&size, "size", 48, "font size")
flags.IntVar(&margin, "margin", 5, "margin")
flags.IntVar(&dpi, "dpi", 100, "dpi")
flags.Var(NewStyle(&style), "style", "font style")
flags.Var(NewVariant(&variant), "variant", "font variant")
flags.StringVar(&text, "text", "", "display text")
// completions
flags.BoolVar(&bashCompletion, "completion-script-bash", false, "output bash completion script and exit")
flags.BoolVar(&zshCompletion, "completion-script-zsh", false, "output zsh completion script and exit")
flags.BoolVar(&fishCompletion, "completion-script-fish", false, "output fish completion script and exit")
flags.BoolVar(&powershellCompletion, "completion-script-powershell", false, "output powershell completion script and exit")
flags.BoolVar(&noDescriptions, "no-descriptions", false, "disable descriptions in completion scripts")
// mark hidden
for _, name := range []string{
"completion-script-bash", "completion-script-zsh", "completion-script-fish",
"completion-script-powershell", "no-descriptions",
} {
flags.Lookup(name).Hidden = true
func run(w io.Writer, args *Args) func(context.Context, []string) error {
return func(ctx context.Context, cliargs []string) error {
// style, variant := canvas.FontRegular, canvas.FontNormal
sysfonts, err := fontpkg.FindSystemFonts(fontpkg.DefaultFontDirs())
if err != nil {
return err
}
switch hasArgs := len(cliargs) != 0; {
case args.All && hasArgs:
return errors.New("--all does not take any args")
case args.List && hasArgs:
return errors.New("--list does not take any args")
case args.Match && !hasArgs:
return errors.New("--match requires one or more args")
case args.All && args.List,
args.All && args.Match,
args.Match && args.List:
return errors.New("--all, --list, and --match are exclusive")
}
f := do
switch {
case args.All:
f = doAll
case args.List:
f = doList
case args.Match:
f = doMatch
}
return f(w, sysfonts, args, cliargs)
}
return c.ExecuteContext(ctx)
}

type Args struct {
FG color.Color
BG color.Color
Size int
DPI int
Margin int
Style canvas.FontStyle
Variant canvas.FontVariant
Text string
Args []string
once sync.Once
tpl *template.Template
All bool `ox:"show all system fonts"`
List bool `ox:"list system fonts"`
Match bool `ox:"match system fonts"`
Fg *colors.Color `ox:"foreground color,default:black"`
Bg *colors.Color `ox:"background color,default:white"`
Size int `ox:"font size,default:48"`
Dpi int `ox:"dpi,default:100"`
Margin int `ox:"margin,default:5"`
Style canvas.FontStyle `ox:"font style"`
Variant canvas.FontVariant `ox:"font variant"`
Text string `ox:"display text"`

once sync.Once
tpl *template.Template
}

func (args *Args) Template() (*template.Template, error) {
Expand Down Expand Up @@ -192,24 +117,24 @@ func (args *Args) Template() (*template.Template, error) {
}

// do renders the specified font queries to w.
func do(w io.Writer, sysfonts *fontpkg.SystemFonts, args *Args) error {
func do(w io.Writer, sysfonts *fontpkg.SystemFonts, args *Args, cliargs []string) error {
if !rasterm.Available() {
return rasterm.ErrTermGraphicsNotAvailable
}
var fonts []*Font
// collect fonts
for i := 0; i < len(args.Args); i++ {
v, err := Open(sysfonts, args.Args[i], args.Style)
for i := 0; i < len(cliargs); i++ {
v, err := Open(sysfonts, cliargs[i], args.Style)
if err != nil {
fmt.Fprintf(w, "error: unable to open arg %d: %v\n", i, err)
}
fonts = append(fonts, v...)
}
return render(w, fonts, args)
return render(w, fonts, args, cliargs)
}

// doAll renders all system fonts to w.
func doAll(w io.Writer, sysfonts *fontpkg.SystemFonts, args *Args) error {
func doAll(w io.Writer, sysfonts *fontpkg.SystemFonts, args *Args, cliargs []string) error {
if !rasterm.Available() {
return rasterm.ErrTermGraphicsNotAvailable
}
Expand All @@ -220,10 +145,10 @@ func doAll(w io.Writer, sysfonts *fontpkg.SystemFonts, args *Args) error {
fonts = append(fonts, NewFont(sysfonts.Fonts[family][style]))
}
}
return render(w, fonts, args)
return render(w, fonts, args, cliargs)
}

func doList(w io.Writer, sysfonts *fontpkg.SystemFonts, _ *Args) error {
func doList(w io.Writer, sysfonts *fontpkg.SystemFonts, _ *Args, _ []string) error {
for _, family := range slices.Sorted(maps.Keys(sysfonts.Fonts)) {
fmt.Fprintln(w, "---")
fmt.Fprintf(w, "family: %q\n", family)
Expand All @@ -235,16 +160,16 @@ func doList(w io.Writer, sysfonts *fontpkg.SystemFonts, _ *Args) error {
return nil
}

func doMatch(w io.Writer, sysfonts *fontpkg.SystemFonts, args *Args) error {
for _, name := range args.Args {
func doMatch(w io.Writer, sysfonts *fontpkg.SystemFonts, args *Args, cliargs []string) error {
for _, name := range cliargs {
if font := Match(sysfonts, name, args.Style); font != nil {
font.WriteYAML(w)
}
}
return nil
}

func render(w io.Writer, fonts []*Font, args *Args) error {
func render(w io.Writer, fonts []*Font, args *Args, cliargs []string) error {
tpl, err := args.Template()
if err != nil {
return err
Expand Down Expand Up @@ -402,12 +327,12 @@ func (font *Font) Render(w io.Writer, tpl *template.Template, args *Args) error
ctx := canvas.NewContext(c)

ctx.SetZIndex(1)
ctx.SetFillColor(args.FG)
ctx.SetFillColor(args.Fg)

// draw text
lines, sizes := breakLines(buf.Bytes(), args.Size)
for i, y := 0, float64(0); i < len(lines); i++ {
face := ff.Face(float64(sizes[i]), args.FG, args.Style, args.Variant)
face := ff.Face(float64(sizes[i]), args.Fg, args.Style, args.Variant)
txt := canvas.NewTextBox(face, strings.TrimSpace(lines[i]), 0, 0, canvas.Left, canvas.Top, 0, 0)
b := txt.Bounds()
ctx.DrawText(0, y, txt)
Expand All @@ -419,7 +344,7 @@ func (font *Font) Render(w io.Writer, tpl *template.Template, args *Args) error

// draw background
ctx.SetZIndex(-1)
ctx.SetFillColor(args.BG)
ctx.SetFillColor(args.Bg)
width, height := ctx.Size()
ctx.DrawPath(0, 0, canvas.Rectangle(width, height))

Expand All @@ -428,7 +353,7 @@ func (font *Font) Render(w io.Writer, tpl *template.Template, args *Args) error
// encode
return rasterm.Encode(w, rasterizer.Draw(
c,
canvas.DPI(float64(args.DPI)),
canvas.DPI(float64(args.Dpi)),
canvas.DefaultColorSpace,
))
}
Expand Down

0 comments on commit 2be8e95

Please sign in to comment.