package zli
import "zgo.at/zli"
Index ¶
- Constants
- Variables
- func AskPassword(minlen int) (string, error)
- func Colorf(format string, c Color, a ...any)
- func Colorize(text string, c Color) string
- func Colorln(text string, c Color)
- func DeColor(text string) string
- func Erase()
- func EraseScreen()
- func Errorf(s any, args ...any)
- func F(err error)
- func Fatalf(s any, args ...any)
- func GetVersion() (tag string, commit string, date time.Time)
- func HideCursor() func()
- func InputOrArgs(args []string, sep string, quiet bool) ([]string, error)
- func InputOrFile(path string, quiet bool) (io.ReadCloser, error)
- func MakeRaw(hideCursor bool) func()
- func Modify(line, char int, text string, a ...any)
- func Move(row, col int, text string, a ...any)
- func OutputOrFile(path string, create func(string) (*os.File, error)) (io.WriteCloser, error)
- func Pager(text io.Reader)
- func PagerStdout() func()
- func PrintVersion(verbose bool)
- func Program() string
- func Replacef(text string, a ...any)
- func TerminalSizeChange() <-chan struct{}
- func To(row, col int, text string, a ...any)
- func Usage(opts int, text string) string
- type Color
- func Color256(n uint8) Color
- func ColorHex(h string) Color
- func (c Color) Bg() Color
- func (c Color) Brighten(n int) Color
- func (c Color) String() string
- type ErrCommandAmbiguous
- type ErrCommandNoneGiven
- type ErrCommandUnknown
- type ErrFlagDouble
- type ErrFlagInvalid
- type ErrFlagUnknown
- type ErrPositional
- type Flags
- func NewFlags(args []string) Flags
- func (f *Flags) Bool(def bool, name string, aliases ...string) flagBool
- func (f *Flags) Float64(def float64, name string, aliases ...string) flagFloat64
- func (f *Flags) Int(def int, name string, aliases ...string) flagInt
- func (f *Flags) Int64(def int64, name string, aliases ...string) flagInt64
- func (f *Flags) IntCounter(def int, name string, aliases ...string) flagIntCounter
- func (f *Flags) IntList(def []int, name string, aliases ...string) flagIntList
- func (f *Flags) Optional() *Flags
- func (f *Flags) Parse(opts ...parseOpt) error
- func (f *Flags) Profile() func()
- func (f *Flags) Shift() string
- func (f *Flags) ShiftCommand(cmds ...string) (string, error)
- func (f *Flags) String(def, name string, aliases ...string) flagString
- func (f *Flags) StringList(def []string, name string, aliases ...string) flagStringList
- type TestExit
Examples ¶
Constants ¶
const ( ColorOffsetFg = 16 ColorOffsetBg = 40 )
Offsets where foreground and background colors are stored.
const ( // UsageTrim removes leading and trailing whitespace and appends a newline. // // This makes it easier to write usage strings without worrying too much // about leading/trailing whitespace, and with the trailing newline it's // easy to add a blank line between the usage and any error message // (fmt.Println if you wnat a blank line, fmt.Print if you don't). UsageTrim = 1 // UsageHeaders formats headers in the form of: // // Header: // // A header must be at the start of the line, preceded by a blank line, and // end with a double colon (:). UsageHeaders = 2 // UsageFlags formats flags in the form of: // // -f // -flag // -flag=foo // -flag=[foo] UsageFlags = 4 // UsageProgram replaces "%(prog)" with filepath.Base(os.Args[0]). UsageProgram = 8 )
Formatting flags for Usage.
Variables ¶
var ( // AllowUnknown indicates that unknown flags are not an error; unknown flags // are added to the Args list. // // This is useful if you have subcommands with different flags, for example: // // f := zli.NewFlags(os.Args) // globalFlag := f.String(..) // f.Parse(zli.AllowUnknown()) // // switch cmd := f.ShiftCommand(..) { // case "serve": // serveFlag := f.String(..) // f.Parse() // *Will* error out on unknown flags. // } AllowUnknown = func() parseOpt { return func(o *parseOpts) { o.allowUnknown = true } } // AllowMultiple indicates that specifying a flag more than once is not an // error. // // For boolean flags any repeated flags are simply ignored. // // For flags that accept a value the last is used; so for: // // % prog -w 50 -w 90 // // "-w" will have the value of "80". AllowMultiple = func() parseOpt { return func(o *parseOpts) { o.allowMultiple = true } } // Positional sets the lower and upper bounds for the number of positional // arguments. // // Positional(0, 0) No limit and accept everything (the default). // Positional(1, 0) Must have at least one positional argument. // Positional(1, 1) Must have exactly one positional argument. // Positional(0, 3) May optionally have up to three positional arguments. // Positional(-1, 0) Don't accept any conditionals (the max is ignored).only the min is Positional = func(min, max int) parseOpt { return func(o *parseOpts) { o.pos = [2]int{min, max} } } // NoPositional is a shortcut for Positional(-1, 0) NoPositional = func() parseOpt { return func(o *parseOpts) { o.pos = [2]int{-1, -1} } } )
var ( // FormatHeader is the formatting to apply for a header. FormatHeader = Bold // FormatFlag is the formatting to apply for a flag. FormatFlag = Underline )
var ( Exit func(int) = os.Exit Stdin io.Reader = os.Stdin Stdout io.Writer = os.Stdout Stderr io.Writer = os.Stderr )
var ExitCode = 1
ExitCode is the exit code to use for Fatalf() and F()
var IsTerminal = func(fd uintptr) bool { return term.IsTerminal(int(fd)) }
IsTerminal reports if this file descriptor is an interactive terminal.
var StdinMessage = "reading from stdin..."
StdinMessage is the message InputOrFile() and InputOnArgs() use to notify the user the program is reading from stdin.
TerminalSize gets the dimensions of the given terminal.
var WantColor = func() bool { _, ok := os.LookupEnv("NO_COLOR") return os.Getenv("TERM") != "dumb" && term.IsTerminal(int(os.Stdout.Fd())) && !ok }()
WantColor indicates if the program should output any colors. This is automatically set from from the output terminal and NO_COLOR environment variable.
You can override this if the user sets "--color=force" or the like.
TODO: maybe expand this a bit with WantMonochrome or some such, so you can still output bold/underline/reverse text for people who don't want colors.
Functions ¶
func AskPassword ¶
AskPassword interactively asks the user for a password and confirmation.
Just a convenient wrapper for term.ReadPassword() to call it how you want to use it much of the time to ask for a new password.
func Colorf ¶
Colorf prints colorized output if WantColor is true.
The text will end with the reset code. Note that this is always added at the end, after any newlines in the string.
func Colorize ¶
Colorize the text with a color if WantColor is true.
The text will end with the reset code.
func Colorln ¶
Colorln prints colorized output if WantColor is true.
The text will end with the reset code.
func DeColor ¶
DeColor removes ANSI color escape sequences from a string.
func Erase ¶
func Erase()
Erase line from the cursor to the end, leaving the cursor in the current position.
func EraseScreen ¶
func EraseScreen()
EraseScreen erases the entire screen and puts the cursor at position 1, 1.
func Errorf ¶
Error prints an error message to stderr prepended with the program name and with a newline appended.
func F ¶
func F(err error)
F prints the err.Error() to stderr with Errorf() and exits, but it won't do anything if the error is nil.
func Fatalf ¶
Fatalf is like Errorf(), but will exit with a code of 1.
func GetVersion ¶
GetVersion gets this program's version.
func HideCursor ¶
func HideCursor() func()
HideCursor hides the cursor, returning a function to display it again.
func InputOrArgs ¶
InputOrArgs reads arguments separated by sep from stdin if args is empty, or returns args unmodified if it's not.
The argument are split on newline; the following are all identical:
prog foo bar printf "foo\nbar\n" | prog prog 'foo bar' 'x y' printf "foo bar\nx y\n" | prog
It will print StdinMessage to stderr notifying the user it's reading from stdin if the terminal is interactive and quiet is false. See: https://www.arp242.net/read-stdin.html
func InputOrFile ¶
func InputOrFile(path string, quiet bool) (io.ReadCloser, error)
InputOrFile returns a reader connected to stdin if path is "" or "-", or open a path for any other value. The Close method for stdin is a no-op.
It prints StdinMessage to stderr notifying the user it's reading from stdin if the terminal is interactive and quiet is false. See: https://www.arp242.net/read-stdin.html
func MakeRaw ¶
func MakeRaw(hideCursor bool) func()
MakeRaw puts the terminal in "raw mode", returning a function to restore the state.
If hideCursor is true the cursor will be hidden, and the returned function will restore that as well.
func Modify ¶
Modify text, inserting or deleting lines, and print the text.
On positive values it will insert text, moving existing text below (for lines) or to the right (for characters). On negative values it will delete text, moving existing text upwards (for lines) or to the left (for characters). On 0 nothing is modified.
func Move ¶
Move the cursor relative to current position and print the text.
Positive values move down or right, negative values move up or left, and 0 doesn't move anything.
func OutputOrFile ¶
OutputOrFile returns a writer connected to stdout if path is "" or "-", or open a path for any other value. The Close method for stdout is a no-op.
The create function is used to open the file; the simplest is to use os.Create:
zli.OutputOrFile(path, os.Create)
Or to prevent clobbering existing files:
out, err := zli.OutputOrFile(outFlag.String(), func(p string) (*os.File, error) { return os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666) })
func Pager ¶
Pager pipes the content of text to $PAGER, or prints it to stdout of this fails.
func PagerStdout ¶
func PagerStdout() func()
PagerStdout replaces Stdout with a buffer and pipes the content of it to $PAGER.
The typical way to use this is at the start of a function like so:
defer zli.PageStdout()()
You need to be a bit careful when calling Exit() explicitly, since that will exit immediately without running any defered functions. You have to either use a wrapper or call the returned function explicitly.
func PrintVersion ¶
func PrintVersion(verbose bool)
PrintVersion prints this program's version.
The format is:
prog 336b4c73 2024-06-07; go1.22.4 linux/amd64; race=false; cgo=false
Where prog is os.Args[0], followed by the commit and date of the commit. You can print a tagged version by setting zgo.at/zli.version at build time:
go build -ldflags "-X zgo.at/zli.version=v1.2.3"
After which it will print:
elles v1.2.3 336b4c73 2024-06-07; go1.22.4 linux/amd64; race=false; cgo=true
In addition, zgo.at/zli.progname can be set to override os.Args[0]:
go build -ldflags '-X "zgo.at/zli.version=VERSION" -X "zgo.at/zli.progname=PROG"'
If verbose is true it also prints detailed build information (similar to "go version -m bin")
func Program ¶
func Program() string
Program gets the program name from argv.
func Replacef ¶
Replacef replaces the current line.
func TerminalSizeChange ¶
func TerminalSizeChange() <-chan struct{}
TerminalSizeChange sends on the channel if the terminal window is resized.
func To ¶
To sets the cursor at the given position and prints the text.
The top-left corner is 1, 1.
func Usage ¶
Usage applies some formatting to a usage message. See the Usage* constants.
Types ¶
type Color ¶
type Color uint64
Color is a set of attributes to apply; the attributes are stored as follows:
fg true, 256, 16 color mode ─┬──┐ bg true, 256, 16 color mode ─┬─┐│ │ │ ││ │┌── error parsing hex color ┌───── bg color ────────────┐ ┌───── fg color ────────────┐ │ ││ ││┌─ term attr v v v v v vv vvv v 0b 0000_0000 0000_0000 0000_0000 0000_0000 0000_0000 0000_0000 0000_0000 0000_0000
The terminal attributes are bold, underline, etc. are stored as flags. The error flag signals there was an error parsing a hex color with ColorHex().
The colors are stored for the background and foreground and are applied depending on the values of the color mode bitmasks.
The biggest advantage of storing it like this is that we can can use a single variable/function parameter to represent all terminal attributes, which IMHO gives a rather nicer API than using a slice or composing the colors with functions or some such.
const ( Reset Color = 0 Bold Color = 1 << (iota - 1) Dim Italic Underline Undercurl Overline Reverse Concealed StrikeOut )
Basic terminal attributes.
const ( ColorMode16Fg Color = ColorError << (iota + 1) ColorMode256Fg ColorModeTrueFg ColorMode16Bg ColorMode256Bg ColorModeTrueBg )
Color modes.
const ( Black Color = (iota << ColorOffsetFg) | ColorMode16Fg Red Green Yellow Blue Magenta Cyan White )
First 8 colors; use Brighten(1) to get the bright variants.
const ColorError Color = StrikeOut << 1
ColorError signals there was an error in parsing a color hex attribute.
Code:play
Output:Example¶
package main
import (
"fmt"
"os"
"zgo.at/zli"
)
func main() {
zli.Stdout = os.Stdout
zli.Colorln("You're looking rather red", zli.Red) // Apply a color.
zli.Colorln("A bold move", zli.Bold) // Or an attribute.
zli.Colorln("Tomato", zli.Red.Bg()) // Transform to background color.
zli.Colorln("Wow, such beautiful text", // Can be combined.
zli.Bold|zli.Underline|zli.Red|zli.Green.Bg())
zli.Colorln("Contrast ratios is for suckers (and web devs)", // 256 color
zli.Color256(56)|zli.Color256(99).Bg())
zli.Colorln("REAL men use TRUE color!", // True color
zli.ColorHex("#678")|zli.ColorHex("#abc").Bg())
zli.Colorf("Hello, %s!\n", zli.Red, "Mars") // Like fmt.Printf
smurf := zli.Colorize("Smurfs!", zli.Blue) // Colorize a string (don't print)
fmt.Println(smurf)
// .String() method outputs escape sequence
fmt.Printf("%sc%so%sl%so%sr%s\n", zli.Red, zli.Magenta, zli.Cyan, zli.Blue, zli.Yellow, zli.Reset)
}
[31mYou're looking rather red[0m
[1mA bold move[0m
[41mTomato[0m
[1;4;31;42mWow, such beautiful text[0m
[38;5;56;48;5;99mContrast ratios is for suckers (and web devs)[0m
[38;2;102;119;136;48;2;170;187;204mREAL men use TRUE color![0m
[31mHello, Mars!
[0m[34mSmurfs![0m
[31mc[35mo[36ml[34mo[33mr[0m
func Color256 ¶
Color256 creates a new 256-mode color.
The first 16 (starting at 0) are the same as the color names (Black, Red, etc.) 16 to 231 are various colors. 232 to 255 are greyscale tones.
The 16-231 colors should always be identical on every display (unlike the basic colors, whose exact color codes are undefined and differ per implementation).
See ./cmd/colortest for a little CLI to display the colors.
func ColorHex ¶
ColorHex gets a 24-bit "true color" from a hex string such as "#f44" or "#ff4444". The leading "#" is optional.
Parsing errors are signaled with by setting the ColorError flag, which String() shows as "(zli.Color ERROR invalid hex color)".
func (Color) Bg ¶
Bg returns the background variant of this color. If doesn't do anything if this is already a background color.
func (Color) Brighten ¶
Brighten or darken (for negative values) a color.
Operations will never go out-of-bounds; brighting the brightest variant will do nothing and will simply return the same color.
For 16 colors it will convert a normal color to a "bright" variant, or vice versa.
For 256 colors it will shift to the same column position in the next "square"; see the chart printed by ./cmd/colortest. The scale of n is 6.
For true colors it will brighten the color; the scale of n is 255.
func (Color) String ¶
String gets the escape sequence for this color code.
This will always return an empty string if WantColor is false or if the error flag is set.
You can use this to set colors directly with fmt.Print:
fmt.Println(zli.Red|zli.Bold, "red!") // Set colors "directly"; Println() will call String() fmt.Println("and bold!", zli.Reset) // Don't forget to reset it! fmt.Printf("%sc%so%sl%so%sr%s\n", zli.Red, zli.Magenta, zli.Cyan, zli.Blue, zli.Yellow, zli.Reset)
type ErrCommandAmbiguous ¶
Sentinel return values for ShiftCommand()
func (ErrCommandAmbiguous) Error ¶
func (e ErrCommandAmbiguous) Error() string
type ErrCommandNoneGiven ¶
type ErrCommandNoneGiven struct{}
Sentinel return values for ShiftCommand()
func (ErrCommandNoneGiven) Error ¶
func (e ErrCommandNoneGiven) Error() string
type ErrCommandUnknown ¶
type ErrCommandUnknown string
Sentinel return values for ShiftCommand()
func (ErrCommandUnknown) Error ¶
func (e ErrCommandUnknown) Error() string
type ErrFlagDouble ¶
type ErrFlagDouble struct {
// contains filtered or unexported fields
}
ErrFlagDouble is used when a flag is given more than once.
func (ErrFlagDouble) Error ¶
func (e ErrFlagDouble) Error() string
type ErrFlagInvalid ¶
type ErrFlagInvalid struct {
// contains filtered or unexported fields
}
ErrFlagInvalid is used when a flag has an invalid syntax (e.g. "no" for an int flag).
func (ErrFlagInvalid) Error ¶
func (e ErrFlagInvalid) Error() string
func (ErrFlagInvalid) Unwrap ¶
func (e ErrFlagInvalid) Unwrap() error
type ErrFlagUnknown ¶
type ErrFlagUnknown struct {
// contains filtered or unexported fields
}
ErrFlagUnknown is used when the flag parsing encounters unknown flags.
func (ErrFlagUnknown) Error ¶
func (e ErrFlagUnknown) Error() string
type ErrPositional ¶
type ErrPositional struct {
// contains filtered or unexported fields
}
ErrPositional is used when there are too few or too many positional arguments.
func (ErrPositional) Error ¶
func (e ErrPositional) Error() string
type Flags ¶
type Flags struct { Program string // Program name. Args []string // List of arguments, after parsing this will be reduces to non-flags. // contains filtered or unexported fields }
Flags are a set of parsed flags.
The rules for parsing are as follows:
Flags start with one or more '-'s; '-a' and '--a' are identical, as are '-long' and '--long'.
Flags are separated with arguments by one space or '='. This is required: '-vVALUE' is invalid; you must use '-v VALUE' or '-v=VALUE'.
Single-letter flags can be grouped; '-ab' is identical to '-a -b', and '-ab VAL' is identical to '-a -b VAL'. "Long" flags cannot be grouped.
Long flag names take precedence over single-letter ones, e.g. if you define the flags '-long', '-l', '-o', '-n', and '-g' then '-long' will be parsed as '-long'.
Anything that doesn't start with a '-' or follows '--' is treated as a positional argument. This can be freely interspersed with flags.
Example¶
Code:play
package main import ( "fmt" "zgo.at/zli" ) func main() { // Create new flags from os.Args. f := zli.NewFlags([]string{"example", "-vv", "-f=csv", "-a", "xx", "yy"}) // Add a string, bool, and "counter" flag. var ( verbose = f.IntCounter(0, "v", "verbose") all = f.Bool(false, "a", "all") format = f.String("", "f", "format") ) // Shift the first argument (i.e. os.Args[1], if any, empty string if there // isn't). Useful to get the "subcommand" name. This works before and after // Parse(). switch f.Shift() { case "help": // Run help case "install": // Run install case "": // Error: need a command (or just print the usage) default: // Error: Unknown command } // Parse the shebang! err := f.Parse() if err != nil { // Print error, usage. } // You can check if the flag was present on the CLI with Set(). This way you // can distinguish between "was an empty value passed" // (-format '') and // "this flag wasn't on the CLI". if format.Set() { fmt.Println("Format was set to", format.String()) } // The IntCounter adds 1 for every time the -v flag is on the CLI. if verbose.Int() > 1 { // ...Print very verbose info. } else if verbose.Int() > 0 { // ...Print less verbose info. } // Just a bool! fmt.Println("All:", all.Bool()) // f.Args is set to everything that's not a flag or argument. fmt.Println("Remaining:", f.Args) }
Output:
Format was set to csv All: true Remaining: [xx yy]
func NewFlags ¶
NewFlags creates a new Flags from os.Args.
func (*Flags) Bool ¶
func (*Flags) Float64 ¶
func (*Flags) Int ¶
func (*Flags) Int64 ¶
func (*Flags) IntCounter ¶
func (*Flags) IntList ¶
func (*Flags) Optional ¶
Optional indicates the next flag may optionally have value.
By default String(), Int(), etc. require a value, but with Optional() set both "-str" and "-str foo" will work. The default value will be used if "-str" was used.
func (*Flags) Parse ¶
Parse the set of flags in f.Args.
func (*Flags) Profile ¶
func (f *Flags) Profile() func()
Profile enables CPU and memory profiling via the -cpuprofile and -memprofile flags.
f := zli.NewFlags(os.Args) zli.F(f.Parse()) defer f.Profile()()
func (*Flags) Shift ¶
Shift a value from the argument list.
func (*Flags) ShiftCommand ¶
ShiftCommand shifts the first non-flag value from the argument list.
This can work both before or after f.Parse(); this is useful if you want to have different flags for different arguments, and both of these will work:
$ prog -flag cmd $ prog cmd -flag
If cmds is given then it matches commands with this list; commands can be matched as an abbreviation as long as it's unambiguous; if you have "search" and "identify" then "i", "id", etc. will all return "identify". If you have the commands "search" and "see", then "s" or "se" are ambiguous, and it will return an ErrCommandAmbiguous error.
Commands can also contain aliases as "alias=cmd"; for example "ci=commit".
Return ErrCommandNoneGiven if there is no command, and ErrCommandUnknown
if the command is not found.
Code:play
Output:Example¶
package main
import (
"fmt"
"zgo.at/zli"
)
func main() {
f := zli.NewFlags(append([]string{"prog", "i"}))
// Known commands.
commands := []string{"help", "version", "verbose", "install"}
switch cmd, err := f.ShiftCommand(commands...); cmd {
// On error the return value is "" and err is set to something useful; for
// example:
//
// % prog
// prog: no command given
//
// % prog hello
// prog: unknown command: "hello"
//
// % prog v
// prog: ambigious command: "v"; matches: "verbose", "version"
case "":
zli.F(err)
// The full command is returned, e.g. "prog h" will return "help".
case "help":
fmt.Println("cmd: help")
case "version":
fmt.Println("cmd: version")
case "install":
fmt.Println("cmd: install")
}
}
cmd: install
func (*Flags) String ¶
func (*Flags) StringList ¶
type TestExit ¶
type TestExit int
TestExit records the exit code and aborts the normal program execution. It's intended to test exit codes in a program.
The Exit() method call is a replacement for zli.Exit:
exit := TestExit(-1) Exit = exit.Exit defer func() { Exit = os.Exit }()
This can be recovered like so:
func() { defer exit.Recover() Fatal("oh noes!") }() fmt.Println("Exit", exit)
The function wrapper is needed so that the test function itself doesn't get aborted.
TODO: this isn't thread-safe in cases where os.Exit() gets called from a goroutine in the running program.
type TextExit struct { mu *sync.Mutex exits []int }
func Test ¶
Test replaces Stdin, Stdout, Stderr, and Exit for testing.
The state will be reset when the test finishes.
The code points to the latest zli.Exit() return code.
func (*TestExit) Exit ¶
Exit sets TestExit to the given status code and panics with itself.
func (*TestExit) Recover ¶
func (t *TestExit) Recover()
Recover any panics where the argument is this TestExit instance. it will re-panic on any other errors (including other TestExit instances).
func (*TestExit) Want ¶
Want checks that the recorded exit code matches the given code and issues a t.Error() if it doesn't.
Source Files ¶
buildinfo.go color.go csi.go flag.go signal_unix.go term.go test.go usage.go zli.go
Directories ¶
Path | Synopsis |
---|---|
cmd | |
cmd/clock | |
cmd/colortest | Command colortest prints an overview of colors for testing. |
cmd/csi | Command csi tests CSI escapes. |
cmd/grep | Command grep is a simple grep implementation for demo purposes. |
internal |
- Version
- v0.0.0-20240922172047-d7bc84b1106f (latest)
- Published
- Sep 22, 2024
- Platform
- linux/amd64
- Imports
- 20 packages
- Last checked
- 2 weeks ago –
Tools for package owners.