Documentation ¶
Overview ¶
Package cmd provides a lightweight commander focused on modern human-computer interaction through terminal command-line interfaces composed of modular subcommands with portable completion, and embedded, dynamic documentation.
Index ¶
- Constants
- Variables
- func Builtins() []string
- func Call(name string, args []string) error
- func Complete()
- func ConvertToJSON(thing interface{}) string
- func Dump(stuff ...interface{})
- func Emphasize(buf string) string
- func Exec(args ...string) error
- func Execute(name string)
- func Exit()
- func ExitError(err ...interface{})
- func ExitExec(xnargs ...string) error
- func ExitUnimplemented(thing interface{})
- func Format(input string, indent, width int) (output string)
- func Has(name string) bool
- func Hidden() map[string]*Command
- func Indent(buf string, spaces int) string
- func JSON() string
- func MapOpts(stuff []string) (opts map[string]string, args []string)
- func Print(stuff ...interface{})
- func PrintPaged(buf, status string)
- func Println(stuff ...interface{})
- func Run(args ...string) error
- func Sprint(stuff ...interface{}) string
- func String(thing Stringer) string
- func Unimplemented(thing interface{}) error
- func Visible() map[string]*Command
- func WaitForInterrupt(cancel context.CancelFunc)
- func Wrap(buf string, width int) string
- type Command
- func (c *Command) Add(names ...string)
- func (c *Command) Call(args []string) error
- func (c *Command) Complete(compline string)
- func (c *Command) Has(name string) bool
- func (c Command) Hidden() bool
- func (c Command) MarshalJSON() ([]byte, error)
- func (c Command) SprintCommandSummaries() string
- func (c Command) SprintUsage() string
- func (c Command) String() string
- func (c *Command) SubcommandUsage() []string
- func (c *Command) Subcommands() []string
- func (c Command) Title() string
- func (c *Command) Unimplemented() error
- func (c *Command) UsageError() error
- func (c Command) VersionLine() string
- type Stringer
Examples ¶
Constants ¶
const Version = `v0.1.0`
Version contains the semantic version of the cmd package used. This value is printed with the version builtin subcommand.
Variables ¶
var Args = os.Args[1:]
Args returns a reliable collection of arguments to the executable.
WARNING: Although the first the element of os.Args is usually the binary of the compiled program executed it is never reliable and significantly differs depending on operating system and method of program execution. The first argument is therefore stripped completely leaving only the arguments to be processed. The cmd.Args package variable can also be set during testing to check cmd.Execute() behavior.
var CompLine string
CompLine is set if a completion context from the shell is detected. (For Bash it is COMP_LINE. See Programmable Completion in the bash man page.)
var DisableEmphasis bool
DisableEmphasis turns off emphasis (italic, bold, bolditalic) from Format(). Note that this is package wide and must be turned on again after being disabled. Generally emphasis should be left on to provide a consistent user experience for anything built with this package
var FixCompLine bool = true
FixCompLine activates an attempt to correct the CompLine to work best with completion. For example, when an executable that uses the cmd package is renamed or is called as a path and not just the single command name. True by default. Set to false to leave the CompLine exactly as it is detected but note that depending on a specific form of CompLine may not be consistent across operating systems.
var Index = map[string]*Command{}
Index contains all the commands available as subcommands with one of them being set to Main. Commands are created and registered with New().
WARNING: Index is made public only to cover the unforeseen needs of command creators to inspect their own Command set but should not be relied upon heavily to avoid unnecessary coupling dependencies between commands. While the Index type is guaranteed never to change, direct manipulation of the Index is strongly discouraged. Use one of the helper package methods instead (or request the addition of one).
var KeepAlive bool
KeepAlive allows developers to stop Execute() from exiting. It should not be used for any purpose other than testing and should be kept out of any test examples.
var OmitBuiltins bool
OmitBuiltins turns off the injection of the Builtin subcommands into the Main command when Execute is called. It can be assigned in any init() or from main() before calling Execute().
var PagedDefStatus = `Line %lb [<space>(down), b(ack), h(elp), q(quit)]`
PagedDefStatus is the status line passed to `less` to provide information at the bottom of the screen prompting the user what to do. Helpful with implementing help in languages besides English.
var PagedOut = true
PagedOut sets the default use of a pager application and calling PrintPaged() instead of Print() for non-hidden builtin subcommands.
var TrapPanic = func() { if r := recover(); r != nil { ExitError(r) } }
TrapPanic recovers from any panic and more gracefully displays the error as an exit message. It can be redefined to behave differently or set to an empty func() to allow the panic to blow up with its full trace log.
var WinSize winsz
Functions ¶
func Builtins ¶
func Builtins() []string
Builtins are subcommands that are added to every Main command when Execute is called. This can be prevented by setting OmitBuiltins to false.
Most of the builtins are hidden (beginning with underscore '_') but the following are so standardized they are included by default:
* help [subcmd] - very long, formatted documentation * version - version, copyright, license, authors, git source
Any of these can be overridden by command authors simply by naming their own version the same. This may be desirable when creating commands in other languages although keeping these standard English names is strongly recommended due to their ubiquitous usage.
The following are hidden but can be promoted by encapsulating them in other subcommands each with its own file, name, title, and documentation:
func Call ¶
Call allows any indexed subcommand to be called directly by name. Avoid using this method as much as possible since it creates very tight coupling dependencies between commands. It is included primarily publicly so that builtin commands like help, usage, and version can be wrapped with internationalized aliases.
func Complete ¶
func Complete()
Complete calls complete on the Main command passing it CompLine. No verification of Main's existence is checked. The CompLine is always changed to match the actual name of the Main command even if the executable name has been changed or called as an alias. This ensures proper tab completion no matter what the actual executable is called.
Example (Delegated) ¶
package main import ( cmd "gitlab.com/rwxrob/cmdtab" ) func main() { list := cmd.New("list") list.Completion = func(compline string) []string { // could do fancier context analysis of compline return []string{"all", "today", "yesterday"} } cmd.Main = list // simulate Execute() cmd.New("add") cmd.New("remove") cmd.New("exe", "list", "add", "remove") cmd.CompLine = "exe list" // set by shell cmd.Complete() }
Output: all today yesterday
Example (Fullsub) ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) func main() { cmd.New("list") cmd.New("add") cmd.New("remove") cmd.Main = cmd.New("exe", "list", "add", "remove") cmd.CompLine = "exe list" // set by shell cmd.Complete() fmt.Println("nothing") }
Output: nothing
Example (Plainnosubs) ¶
package main import ( cmd "gitlab.com/rwxrob/cmdtab" ) func main() { cmd.Main = cmd.New("exe") cmd.CompLine = "exe" // set by shell cmd.Complete() }
Output:
Example (Plansubs) ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) func main() { cmd.New("list") cmd.New("add") cmd.New("remove") exe := cmd.New("exe1", "list", "add", "remove") cmd.Main = exe // simulate Execute() fmt.Println(exe.Has("list")) fmt.Println(exe.Has("add")) fmt.Println(exe.Has("remove")) fmt.Println(exe.Has("missing")) cmd.CompLine = "exe" // set by shell cmd.Complete() }
Output: true true true false list add remove
Example (Presubs) ¶
package main import ( cmd "gitlab.com/rwxrob/cmdtab" ) func main() { cmd.New("list") cmd.New("add") cmd.New("remove") cmd.Main = cmd.New("exe", "list", "add", "remove") cmd.CompLine = "exe l" // set by shell cmd.Complete() }
Output: list
Example (Subsubs) ¶
package main import ( cmd "gitlab.com/rwxrob/cmdtab" ) func main() { cmd.New("sched", "all", "today") cmd.Main = cmd.New("exe2", "sched", "add", "remove") cmd.CompLine = "exe2 sched" // set by shell cmd.Complete() }
Output: all today
func ConvertToJSON ¶
func ConvertToJSON(thing interface{}) string
ConvertToJSON converts any object to its JSON string equivalent with two spaces of human-readable indenting. While this inflates the size for most purposes this is desirable even when dealing with large data sets. Technologies like GraphQL and offline-first progressive web apps have reduced the concern for total size of JSON. Usually human readability is more important. If an error is encountered while marshalling an ERROR key will be created with the string value of the error as its value.
Example ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) func main() { sample := map[string]interface{}{} sample["int"] = 1 sample["float"] = 1 sample["string"] = "some thing" sample["map"] = map[string]interface{}{"blah": "another"} sample["array"] = []string{"blah", "another"} fmt.Println(cmd.ConvertToJSON(sample)) }
Output: { "array": [ "blah", "another" ], "float": 1, "int": 1, "map": { "blah": "another" }, "string": "some thing" }
func Dump ¶
func Dump(stuff ...interface{})
Dump simply dumps the stuff passed to it to standard output. Use for debugging. Use Print for general printing.
func Exec ¶
Exec (not to be confused with Execute) will check for the existance of the first argument as an executable on the system and then execute it using syscall.Exec(), which replaces the currently running program with the new one in all respects (stdin, stdout, stderr, process ID, etc).
Note that although this is exceptionally faster and cleaner than calling any of the os/exec variations it may be less compatible with different operating systems.
func Execute ¶
func Execute(name string)
Execute traps all panics (see Panic), detects completion and does it, or sets Main to the command name passed, injects the Builtin subcommands (unless OmitBuiltins is true), looks up the named command from the command Index and calls it passing cmd.Args. Execute alway exits the program.
func ExitError ¶
func ExitError(err ...interface{})
ExitError prints err and exits with 1 return value.
func ExitExec ¶
ExitExec exits the currently running Go program and hands off memory and control to the executable passed as the first in a string of arguments along with the arguments to pass along to the called executable. This is only supported on systems that support Go's syscall.Exec() and underlying execve() system call.
func ExitUnimplemented ¶
func ExitUnimplemented(thing interface{})
ExitUnimplemented calls Unimplemented and calls ExitError().
func Format ¶
Format takes a command documentation format string (an extremely limited version of Markdown that is also Godoc friendly) and transforms it as follows:
* Initial and trailing blank lines are removed.
- Indentation is removed - the number of spaces preceeding the first word of the first line are ignored in every line (including raw text blocks).
- Raw text ignored - any line beginning with four or more spaces (after convenience indentation is removed) will be kept as it is exactly (code examples, etc.) but should never exceed 80 characters (including the spaces).
- Blocks are unwrapped - any non-blank (without three or less initial spaces) will be trimmed line following a line will be joined to the preceding line recursively (unless hard break).
- Hard breaks kept - like Markdown any line that ends with two or more spaces will automatically force a line return.
- URL links argument names and anything else within angle brackets (<url>), will trigger italics in both text blocks and usage sections.
- Italic, Bold, and BoldItalic inline emphasis using one, two, or three stars respectivly will be observed and cannot be intermixed or intraword. Each opener must be preceded by a UNICODE space (or nothing) and followed by a non-space rune. Each closer must be preceded by a non-space rune and followed by a UNICODE space (or nothing).
For historic reasons the following environment variables will be observed if found (and also provide color support for the less pager utility):
* italic - LESS_TERMCAP_so * bold - LESS_TERMCAP_md * bolditalic = LESS_TERMCAP_mb
func JSON ¶
func JSON() string
JSON returns a JSON representation of the state of the cmd package including the main command and all subcommands from the internal index. This can be useful when providing documentation in a structured data format that can be easily shared and rendered in different ways. The json builtin simply calls this and prints it. Empty values are always omitted. (See Command.MarshalJSON() as well.)
func MapOpts ¶
MapOpts takes an array of getopt like argument strings, examines them for options, and returns the options as a map and the remaining args as an array. To be clear:
- single dash options cannot have a value (-g)
- single dash options can be combined (-tgz)
- double dash options can have value (--template template.html)
- double dash options can use equals (--template=template.html)
- options with no value will have empty string
- values with quotes will contain quotes (--template='template.html')
Note that there are many other getopt libraries. This is a minimal implementation of the most common getopt library. The most notable distinction is that single dash options cannot ever have values.
IMPORTANT: While MapOpts is useful and encouraged when called from within a Command.Method implementation requiring dashed options, very often --- in our emerging conversational user interface world --- it is discouraged to use any dashed options at all. Instead create natural language options and arguments that can be spoken into an interface rather than typed and detected by context instead of looking for the dash, which is extremely unfriendly to voice interfaces of any kind. Nevertheless the choice is up to you.
Example ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) func main() { // For a given command: // // foo README.md meta.yml \ // --template=template \ // "--withquotes='with quote'" \ // --name "Mr. Rob" \ // - anotherarg -- wasarg \ // -abc -x -y -z \ // -t notavalue // cmdArgs := []string{ "foo", "README.md", "meta.yml", "--template=template.html", "--withquotes='with quotes'", "--name", "Mr. Rob", "anotherarg", "--", "wasarg", "-", "-abc", "-x", "-y", "-z", "-t", "notavalue", } opts, args := cmd.MapOpts(cmdArgs[1:]) fmt.Println("Options:") fmt.Println(cmd.ConvertToJSON(opts)) fmt.Println("Arguments:") fmt.Println(cmd.ConvertToJSON(args)) }
Output: Options: { "a": "", "b": "", "c": "", "name": "Mr. Rob", "t": "", "template": "template.html", "withquotes": "'with quotes'", "x": "", "y": "", "z": "" } Arguments: [ "README.md", "meta.yml", "anotherarg", "--", "wasarg", "-", "notavalue" ]
func PrintPaged ¶
func PrintPaged(buf, status string)
PrintPaged prints a string to the system pager (usually less) using the second argument as the custom status string (usually at the bottom). Control is returned to the calling program after completion. If no pager application is detected the regular Print() will be called intead. If status string is empty PagedDefStatus will be used (use " " to empty). Currently only the less pager is supported.
func Println ¶
func Println(stuff ...interface{})
Println calls Sprint and prints it with a line return.
Example ¶
package main import ( cmd "gitlab.com/rwxrob/cmdtab" ) func main() { stringer := func() string { return "something 1" } cmd.Println("something 1") cmd.Println("some%v %v", "thing", 1) cmd.Println(stringer) cmd.Println() cmd.Println(nil) }
Output: something 1 something 1 something 1
func Run ¶
Run checks for existance of first argument as an executable on the system and then runs it without exiting in a way that is supported across different operating systems. The stdin, stdout, and stderr are connected directly to that of the calling program. Use more specific exec alternatives if intercepting stdout and stderr are desired.
func Sprint ¶
func Sprint(stuff ...interface{}) string
Sprint returns nothing if empty, acts like fmt.Sprint if it has one argument, or acts like Sprintf if it has more than one argument. Print can also print Stringers. Use Dump instead for debugging.
Example ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) func main() { stringer := func() string { return "something 1" } fmt.Println(cmd.Sprint("something 1")) fmt.Println(cmd.Sprint("some%v %v", "thing", 1)) fmt.Println(cmd.Sprint(stringer)) fmt.Println(cmd.Sprint()) }
Output: something 1 something 1 something 1
func String ¶
String adds 'func() string' to normal Go string coercion as well as converting nil to the empty string "".
Example ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) type ImmaStringer struct{} func (s ImmaStringer) String() string { return "Hello" } func main() { f := func() string { return "Hello" } fmt.Println(cmd.String(f)) s := "Hello" fmt.Println(cmd.String(s)) st := ImmaStringer{} // st.String() fmt.Println(cmd.String(st)) }
Output: Hello Hello Hello
func Unimplemented ¶
func Unimplemented(thing interface{}) error
Unimplemented just returns an unimplemented error for the thing passed.
func WaitForInterrupt ¶
func WaitForInterrupt(cancel context.CancelFunc)
WaitForInterrupt just blocks until an interrupt signal is received. It should only be called from the main goroutine. It takes a single context.CancelFunc that is designed to signal everything to stop cleanly before exiting.
Types ¶
type Command ¶
type Command struct { Name string // <= 14 recommended Summary string // < 65 recommended Version Stringer // semantic version (0.1.3) Usage Stringer // following docopt syntax Description Stringer // long form Examples Stringer // long form SeeAlso Stringer // links, other commands, etc. Author Stringer // email format, commas for multiple Git Stringer // same as Go Issues Stringer // full web URL Copyright Stringer // legal copyright statement, if any License Stringer // released under license(s), if any Other map[string]Stringer // long form Method func(args []string) error Parameters Stringer Completion func(compline string) []string Default string // default subcommand // contains filtered or unexported fields }
var Main *Command
Main contains the main command passed to Execute to start the program. While it can be changed by Subcommands it usually should not be.
func New ¶
New initializes a new command and subcommands (adding them to the internal subcommand index) and returns a pointer to the command. Note that the subcmds added do not create a new Command in the Index, they are merely added to the list returned by Subcommands.
func (*Command) Add ¶
Add adds new subcommands by name skipping any it already has. It is up to developers to ensure that the named subcommands has been added to the internal package index with New().
func (*Command) Call ¶
Call calls its own Method or delegates to one of the Command's subcommands. If a Default has been set and the first argument does not appear to be a subcommand then delegate to Default subcommand by name.
func (*Command) Complete ¶
Complete prints the completion replies for the current context (See Programmble Completion in the bash man page.) The line is passed exactly as detected leaving the maximum flexibility for parsing and matching up to the Completion function. The Completion method will be delegated if defined. Otherwise, the Subcommands are used to provide traditional prefix completion recursively.
func (Command) MarshalJSON ¶
MarshalJSON fulfills the Go JSON marshalling requirements and is called by String. Empty values are always omitted. Strings are trimmed and long strings such as Description and Examples are written as basic Markdown (which any Markdown engine will able to render. See cmd.Format() for specifics).
Example ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) func main() { c := cmd.New("mycmd", "subcmd1", "subcmd2") c.Author = `Rob` c.Copyright = `© Rob` c.Description = `Just for testing.` c.Examples = `examples` c.Git = `gitlab.com/rwxrob/cmdtab` c.Issues = `https://gitlab.com/rwxrob/cmdtab/issues` c.License = `Apache 2.0` //c.SeeAlso = `` c.Summary = `summary` //c.Usage = `` c.Version = `v1.0.0` // without these Usage won't return because they do not exist cmd.New("subcmd1") cmd.New("subcmd2") fmt.Println(c) }
Output: { "Author": "Rob", "Copyright": "© Rob", "Description": "Just for testing.", "Examples": "examples", "Git": "gitlab.com/rwxrob/cmdtab", "Issues": "https://gitlab.com/rwxrob/cmdtab/issues", "License": "Apache 2.0", "Name": "mycmd", "Subcommands": [ "subcmd1", "subcmd2" ], "Summary": "summary", "Version": "v1.0.0" }
func (Command) SprintCommandSummaries ¶
SprintCommandSummaries returns a printable string that includes a bold Command.Name for each line along with the summary string, if any, for that subcommand. This is helpful when creating custom builtin help commands.
func (Command) SprintUsage ¶
SprintUsage returns a usage string (without emphasis) that includes a bold Command.Name for each line along with the main command usage and each individual SprintUsage of every subcommand that is not hidden. If indentation is needed this can be passed to Indent(). To replace emphasis with terminal escapes for printing to colored terminals pass to Emphasize().
func (*Command) SubcommandUsage ¶
SubcommandUsage returns the Usage strings for each Subcommand. This is useful when creating usages that have additional notes or formatting when it is desirable to loop through the subcommand usage strings. The order of usage strings is gauranteed to match the order of Subcommands() even if the usage for a particular subcommand is empty.
func (*Command) Subcommands ¶
Subcommands returns the subcommands added with Add().
func (*Command) Unimplemented ¶
Unimplemented calls Unimplemented passing the name of the command. Useful for temporarily notifying users of commands in beta that something has not yet been implemented.
func (*Command) UsageError ¶
func (Command) VersionLine ¶
VersionLine returns a single line with the combined values of the Name, Version, Copyright, and License. If Version is empty or nil an empty string is returned instead. VersionLine() is used by the version builtin command to aggregate all the version information into a single output.
Example ¶
package main import ( "fmt" cmd "gitlab.com/rwxrob/cmdtab" ) func main() { c := cmd.New("foo") fmt.Println(c.VersionLine()) c.Version = `v1.0.0` fmt.Println(c.VersionLine()) c.Copyright = `© Rob` fmt.Println(c.VersionLine()) c.License = `Apache 2.0` fmt.Println(c.VersionLine()) c.Copyright = "" fmt.Println(c.VersionLine()) }
Output: foo v1.0.0 foo v1.0.0 © Rob foo v1.0.0 © Rob (Apache 2.0) foo v1.0.0 (Apache 2.0)