Documentation ¶
Overview ¶
Package tpcli is a golang package (and application) that provides a simple three panel terminal-based UI.
Overview ¶
tpcli is the "Three Panel Command-line Interface". It is both a golang package and an application. This page documents the package.
The UI consists of three "panels": a command entry panel, a general output panel and third panel that is either for error output or which records the history of entered commands.
The command entry panel starts with focus. It is a single row panel which supports basic shell-emacs bindings (e.g., ^a to go to the start of the line, ^e to the end of the line) and arrow key readline-style history navigation.
The UI runs is started as a goroutine. When the user enters a string in the command entry panel and hits <enter>, the command string is delivered on a channel. Text may be written to the general output panel. The third panel serves one of two functions. By default, it is an error output panel. It is just like the general output panel, and differs only semantically. It may alternatively be set to command history panel. In this case, every time the user enters a command string, it is appended to this panel, providing a command history.
The user may use <tab> to switch between the panels. Only the command input panel will accept input. If either of the other two panels has focus, the arrow keys may be used to scroll up or down through the text output.
The panels may be stacked in any order desired. The default order places the output panel first, then the error output panel, then the command entry panel.
If the user hits <esc> or <ctrl>-q, the UI exits. This mean it Stop()s, and an additional function is called. By default, that function is os.Exit(0). However, this may be overridden via OnUIExit().
For convenience, there is also a CommandProcessor that allows you to define patterns for possible commands, and associate those will callback methods when the user enters those commands.
Example
ui := tpcli.NewUI().ChangeStackingOrderTo(tpcli.CommandGeneralError) go ui.Start() for { nextCommand := <-ui.ChannelOfEnteredCommands() switch nextCommand { "quit": ui.Stop() "time": hour, min, sec := time.Now().Clock() ui.FmtToGeneralOutput("The time of day is: %02d:%02d:%02d", hour, min, sec) default: ui.AddStringToErrorOutput("You can only ask for the time, I'm afraid.") } }
Index ¶
- type CommandProcessor
- type ReadlineHistory
- type StackingOrder
- type Tpcli
- func (ui *Tpcli) AddStringToErrorOutput(additionalContent string)
- func (ui *Tpcli) AddStringToGeneralOutput(additionalContent string)
- func (ui *Tpcli) ChangeStackingOrderTo(newOrder StackingOrder) *Tpcli
- func (ui *Tpcli) ChannelOfEnteredCommands() <-chan string
- func (ui *Tpcli) FmtToErrorOutput(format string, a ...interface{})
- func (ui *Tpcli) FmtToGeneralOutput(format string, a ...interface{})
- func (ui *Tpcli) OnUIExit(functionToExecuteAfterUIExits func()) *Tpcli
- func (ui *Tpcli) ReplaceCommandStringWith(newString string)
- func (ui *Tpcli) Start()
- func (ui *Tpcli) Stop()
- func (ui *Tpcli) UsingCommandHistoryPanel() *Tpcli
- func (ui *Tpcli) Write(p []byte) (n int, err error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CommandProcessor ¶
type CommandProcessor struct {
// contains filtered or unexported fields
}
CommandProcessor is a helper for processing user commands input in the Tpcli command entry panel. It is supplied regular expression matchers, and if any of them match a user string, an associated callback function is invoked. If none of the supplied matchers match the command string, this is indicated.
Example ¶
A general example for CommandProcessor
package main import ( "fmt" "os" "regexp" "strconv" "github.com/blorticus/tpcli" ) func main() { var ui *tpcli.Tpcli cp := tpcli.NewCommandProcessor(). WhenCommandMatches(`add (\d+) to (\d+)`, func(matchGroups []string) error { x, err := strconv.Atoi(matchGroups[1]) if err != nil { return err } y, err := strconv.Atoi(matchGroups[2]) if err != nil { return nil } ui.AddStringToGeneralOutput(fmt.Sprintf("Sum is: %d", x+y)) return nil }). WhenCommandMatches(regexp.MustCompile(`^quit$`), func(matchGroups []string) error { os.Exit(0) return nil }) ui.Start() for { nextCommand := <-ui.ChannelOfEnteredCommands() commandMatchedSomething, errorReturnedByCallback := cp.ProcessCommandString(nextCommand) if !commandMatchedSomething { ui.AddStringToErrorOutput("Command not understood") } if errorReturnedByCallback != nil { ui.AddStringToErrorOutput(errorReturnedByCallback.Error()) } } }
Output:
func NewCommandProcessor ¶
func NewCommandProcessor() *CommandProcessor
NewCommandProcessor creates a new, empty command processor
func (*CommandProcessor) ProcessCommandString ¶
func (processor *CommandProcessor) ProcessCommandString(commandString string) (matchesAnyDefinedPattern bool, errorFromCallback error)
ProcessCommandString accepts a commandString and matches it against all matchers previously supplied to this ComamndProcessor, in the order that they were provided. On the first match, the asssociated callback is invoked and this method returns true and the error from the callback. If no pre-defined matchers match, then this method returns false and the string "Command not understood"
func (*CommandProcessor) WhenCommandMatches ¶
func (processor *CommandProcessor) WhenCommandMatches(pattern interface{}, doCallback func([]string) error) *CommandProcessor
WhenCommandMatches adds a matcher with its callback. 'pattern' may be either a string or a *regexp.Regexp. If it is a string, then it is fed to regexp.MustCompile (and will panic if the compilation fails). If it is neither type, this method panics
type ReadlineHistory ¶ added in v0.3.0
type ReadlineHistory struct {
// contains filtered or unexported fields
}
ReadlineHistory stores items in an inverted stack and allows moving up and down that stack without removing the stack items. One can move Up this inverted stack (toward the first entry) or Down this inverted stack (toward the last entry). These are "Up" and "Down" because they map to arrow keys used in a cli readline. The empty string is a not a valid entry and an effort to add an empty string is ignored. Logically, the stack always has at least one item, and the last item is always the empty string. If one moves Up from the first item, the first item is returned and the iterator doesn't move. If one moves Down from the real last item, the empty string entry is returned. If one attempts to move Down from this empty string, the empty string is returned and the iterator does not move. If items are added, ResetIteration() should be called before trying to move Up or Down. If this is not done, the results are undefined.
func NewReadlineHistory ¶ added in v0.3.0
func NewReadlineHistory(maximumHistoryEntries uint) *ReadlineHistory
NewReadlineHistory creates a ReadlineHistory which will contain up to maximumHistoryEntries items. If the stack already has that number of entries and an item is added (via AddItem), then the least recently added entry is popped off the stack before the new item is added.
func (*ReadlineHistory) AddItem ¶ added in v0.3.0
func (history *ReadlineHistory) AddItem(item string)
AddItem adds an item to the end of the inverted stack.
func (*ReadlineHistory) Down ¶ added in v0.3.0
func (history *ReadlineHistory) Down() string
Down moves the iterator down the inverted stack, returning the item at the new iterator location.
func (*ReadlineHistory) ResetIteration ¶ added in v0.3.0
func (history *ReadlineHistory) ResetIteration()
ResetIteration returns the iterator to the last entry in the inverted stack, which is always the implicit empty string entry.
func (*ReadlineHistory) Up ¶ added in v0.3.0
func (history *ReadlineHistory) Up() string
Up moves the iterator up the inverted stack, returning the item at the new iterator location.
type StackingOrder ¶
type StackingOrder int
StackingOrder represents the order in which the three panels should be arranged vertically. All panels consume all horizontal space (i.e., they all use the same number of columns).
const ( CommandErrorGeneral StackingOrder = iota CommandGeneralError GeneralCommandError GeneralErrorCommand ErrorCommandGeneral ErrorGeneralCommand )
Various stacking orders. "Command" is the command input panel. "General" is the general output panel. "Error" is the error (or command-history) panel.
type Tpcli ¶
type Tpcli struct {
// contains filtered or unexported fields
}
Tpcli provides a terminal text interfaces. It creates three "panels": a command entry panel, a general output panel and third panel that is either for error output or which records the history of entered commands. The command entry panel supports basic shell-emacs bindings (e.g., ^a to go to the start of the line, ^e to the end of the line) and arrow key readline-style history navigation.
func NewUI ¶
func NewUI() *Tpcli
NewUI constructs the UI interface elements for the Tpcli but does not start showing them (that happens on invocation of Start())
func (*Tpcli) AddStringToErrorOutput ¶
AddStringToErrorOutput appends additionalContent to the error panel in the same way that AddStringToGeneralOutput does. However, if UsingCommandHistoryPanel is invoked, then any additionalContent submitted here is instead written to the general output panel.
func (*Tpcli) AddStringToGeneralOutput ¶
AddStringToGeneralOutput appends additionalContent to whatever text is currently in the general output panel. A newline (\n) is appended to the text that is already there first, then the new text is appended.
func (*Tpcli) ChangeStackingOrderTo ¶
func (ui *Tpcli) ChangeStackingOrderTo(newOrder StackingOrder) *Tpcli
ChangeStackingOrderTo changes the panel stacking order to the provided ordering
func (*Tpcli) ChannelOfEnteredCommands ¶
ChannelOfEnteredCommands is a channel that emits the commands that the user enters in the command panel. A command is a string of UTF-8 text that ends with a newline (signaled by the <enter> key). The commands strings sent on this channel omit the trailing newline.
func (*Tpcli) FmtToErrorOutput ¶
FmtToErrorOutput is the same as AddStringToErrorOutput, but it takes fmt.Sprintf parameters and expands them using that mechanism
func (*Tpcli) FmtToGeneralOutput ¶
FmtToGeneralOutput is the same as AddStringToGeneralOutput, but it takes fmt.Sprintf parameters and expands them using that mechanism
func (*Tpcli) OnUIExit ¶
OnUIExit provides a function that is executed by the Tpcli immediately after it Stops, and the UI is terminated. This function is executed when a UI exit is provided, including ^q or <esc>.
func (*Tpcli) ReplaceCommandStringWith ¶
ReplaceCommandStringWith writes the newString to the command panel, replacing whatever is currently there.
func (*Tpcli) Start ¶
func (ui *Tpcli) Start()
Start instructs Tpcli to draw the UI and start handling keyboard events. This should be invoked as a goroutine.
func (*Tpcli) Stop ¶
func (ui *Tpcli) Stop()
Stop instructs Tpcli to stop the UI, clearing it. This is not the same as an exit triggered by ^q or <esc>. This only stops the UI. It does not exit the function provided by OnUIExit.
func (*Tpcli) UsingCommandHistoryPanel ¶
UsingCommandHistoryPanel instructs the Tcpli to use the error panel as a command history. When this is set, any command entered in the command panel is copied here after the user hits <enter>. Any text that the caller attempts to write to the error panel is redirected to the general output panel