= How to use Kommandant for Fun and Profit =

Kommandant is a Golang module to handle the boilerplate of running a
command line interface. It is derived from the built-in Python module
Cmd, and in its early form was a straight port of that code.

== What Kommandant does ==

Running a command-line interpreter has a lot of boilerplate that is
the same across many programs and is an unneeded hassle to
write. Kommandant exists to handle this so that the programmer does
not have to worry about it or introduce errors doing so. The
programmer supplies the handlers for the commands that are supported,
as well as some special case hooks. Kommandant deals with the main
loop, parsing, and other finicky details.

== Terminology ==

Throughout the documentation the following jargon is used:

* "core": the Kmdt class

* "client": the programmer provided class containing handlers and other hooks

* "handler": any method in the client that is called to execute commands

* "completer": client methods called to handle completion of arguments

* "hooks": special functions that can be provided

* "basic mode": operating without command line history, or completion features

* "readline mode": operating with those features

== Theory of operation ==

Kommandant works by doing introspection magic on a programmer-supplied
class to find the handlers within. It is implemented this way rather
than by registering handlers manually both to maintain similarity to
Python's Cmd as well as to simplify usage. Cmd provided an excelent
lesson to follow in its execution, and because of that when there is
not a compelling reason to do otherwise Kommandant attempts to hold
closely to the way Cmd behaves. This also carries the benefit of
reducing the transition cost between languages.

Once a data source has been attached to a Kommandant instance all reads MUST
be executed through Kommandant. This is because of quirks in how go-readline
reads input, combined with differences in how data arrives when directly
connected to a terminal, versus a file, versus a pipe.

== Worked example ==

--------------------------------------------------------
include::demos/demo-readline.go[]
--------------------------------------------------------

== Core API description ==

Handlers are defined as methods to a struct which implements the KmdtClient
interface. The handlers are automatically detected by the NewKommandant
function by their names and signatures.

The uniquely named handlers (such as PreCmd) have shim methods defined within
the Kmdt class. Calling these will check to see if a hook has been defined and
if so call the hook, otherwise reducing to a nop.

Handler methods may optionally include a Context argument for
integration with the Go standard library request context, tracing, and
logging facilities. You may declare your handler methods either with
or without this argument; Kommandant will call them correctly in
either case. Core methods which call your handlers will require a
context argument; simply pass context.Background() if you're not using
contexts.

=== SetCore(*Kmdt) ===

The only required function for a KmdtClient. This is used by the
kommandant initializer to pass its address to the client.

=== core.Readline() line string, err error ===

Reads a line from Kommandant's input. This is what handlers MUST use if they
wish to read input in addition to what kommandant itself handles.

=== core.Write(data []byte) ===

Kmdt implements the io.Writer interface. Functions printing data to the user
should use this method, which will automatically route the string to the
correct stream.

=== core.WriteString(data string) ===

A wrapper function to feed a string to Write instead of a []byte.

=== EmptyLine([ctx context.Context]) stopREPL bool ===

Called when the user enters a blank line. If not provided the REPL
will ignore empty lines.

=== DoEOF([ctx context.Context]line string) stopREPL bool ===

Technically a normal Do<Name> handler, "eof" is sent when the input
stream raises an EOF. For this reason the programmer should avoid use
of the command name "eof".

=== DoQuit([ctx context.Context]line string) stopREPL bool ===

Like DoEOF this is just a normal Do<Name> handler. Unlike DoEOF it is
never called in a special way. Rather, any program is *expected* to
have a quit command. One could also have a DoExit, or possibly have
both aliases for the same function.

=== Do<Name>([ctx context.Context]arguments string) stopREPL bool ===

Methods with names and call signatures in this format are used as
command handlers by Kommandant. Entering <Name> on the command line
will call the method DoName(), with a string argument containing the
remaining arguments on the command line. The stopREPL argument if true
will halt Kommandant's main loop and return control to the caller,
this usage can be seen in the DoEOF and DoQuit methods in the worked
example. The command name is not case sensitive.

=== Help<[ctx context.Context]Name>() ===

Methods of this form are called when the user enters "help <Name>". While
they could in theory do anything, in practice they should simply call
Kmdt.Write() with the help string and exit. A help method does not have to
correspond to any Do method.

=== Complete<Name>(line string) options []string ===

Completers are called when the user attempts to complete an argument
for the corresponding command. The line argument is the current
command line minus the command, and the options list are the possible
completions the method is returning. Due to a quirk of go-readline the
completors will complete up to 32 arguments.

=== NewKommandant(KmdtClient) new *Kmdt ===

The initializer for Kommandant that passes it the user's
command-handler class. Initializes in Basic mode

=== core.SetStdin(stdin io.ReadCloser) ===

Set the source that Kommandant tries to get input from. If unset it will
default to os.Stdin.

=== core.SetStdout(stdout io.Writer) ===

Set the destination that Kommandant sends output to. If unset it will default
to os.Stdout.

=== core.EnableReadline(enable bool) ===

Use to enable (or disable) readline features.

=== core.ReadlineEnabled() (enabled bool) ===

Returns whether the readline features are currently enabled.

=== core.SetPrompt(prompt string) ===

Set the string used for the prompt. This will correctly route the data to
readline as necessary.

=== core.CmdLoop(ctx context.Context, intro string) ===

This is the main loop for Kommandant's REPL. When called it does not
return until a handler returns a true stopREPL boolean. Before
starting the loop the contents of intro are printed.

=== core.OneCmd(ctx context.Context, line string) stopREPL bool ===

Parses and runs a single command line. The core of CmdLoop, and also
useful if executing commands from a file or that were recieved as
arguments.

=== core.OneCmdHook(ctx context.Context, line string) stopREPL bool ===

If the OneCmdHook struct member is filled it will be called instead of
OneCmd_core when the OneCmd method is called. This hook cannot be filled by
KmdtClient as it is intended for debugging and panic capturing purposes. It
must be set manually.

=== PreLoop([ctx context.Context]) ===

PreLoop is called before the start of the main loop in REPL mode.

=== PostLoop([ctx context.Context]) ===

PostLoop is called after the end of the main loop in REPL mode.

=== PreCmd([ctx context.Context], line string) (line string) ===

PreCmd is called before executing each command when in REPL mode. The line
argument contains the command line arguments, the line result contains the
arguments that will be sent to the handler.

=== PostCmd([ctx context.Context], stopREPL bool, line string) (stopREPL bool) ===

PostCmd is called after executing each command when in REPL mode. The stopREPL
argument is what is returned by the handler, the line argument is what was
given to the handler. The stopREPL result is the final decision on the matter.

=== Default([ctx context.Context], line string) (stopREPL bool) ===

Default is called when Kommandant receives a command that has no handler
associated with it. The line argument contains the entire command line.

=== CompleteDefault(line string) (options []string) ===

CompleteDefault is called when the user tries to complete a command argument
that does not have a coresponding CompleteFoo() handler. The line argument
contains the command argument to be completed. The options result is a list of
possible completions for the line.

== Gotchas ==

* Once Kommandant is attached to an input all reads should be done through
  Kmdt's Readline() method. Due to some quirks in how go-readline works it
  will eat everything it can, which can cause problems when attached to a file.

* The completors can only complete the first 32 arguments. This is on the
  to-fix list.

* For a custom Prompt to be updated every time it is displayed the
  prompt updating code needs to be called from both PreCommand and
  PreLoop, as the first prompt is displayed before the first call to
  PreCommand.

* All lines sent to the output must be newline terminated, or they will be
  silently dropped / overwritten by readline.

== Credits ==

Author: Ian Bruene

Contributors: Daniel Brooks, Eric S. Raymond

The code originated as a translation of Cmd.py, which was written around
1992 by Guido van Rossum and is part of the Python standard library.

The interface style of Cmd emulates that of GNU gdb (the symbolic
debugger) and/or some earlier C symbolic debuggers on which gdb was
itself modeled; the lineage goes back to sdb under UNIX/32V in 1979.

This project was sponsored by the Internet Civil Engineering Institute.

The initial proof of concept for Kommandant is the Python-to-Go
translation of reposurgeon.

== Revision history ==

v0.1: Initial release

v0.1.1: bugfix, added a helper function for handler registration

v0.2: Converted Kommandant to use introspection to acquire handlers

v0.3: Changed Kommandant init, toggaleable readline, recursive completion.

v0.4: Fixed readline input gluttony, naming issues, unneeded boilerplate.

v0.5: Reverted input gluttony, changed input model to only go through Kmdt.

v0.6: Fixed more input issues, support for Go's Context system added.
