-
Notifications
You must be signed in to change notification settings - Fork 6
History Sources
The console uses the underlying readline history mechanism for providing command history in your application.
- Each menu has a distinct list of command history sources. You can add as many sources as you want.
- By default, each menu comes with an in-memory command history, wiped when the application exits.
- The behavior for history search/completion and movements can be customized with the readline keymaps configuration.
The console library offers a method to create a history source from a file, shown below. Note that by default, the in-memory history source is not removed in the call below: the following appends to the list of histories for the given menu.
func main() {
menu := app.CurrentMenu()
// "local history" is the title of the history, used when completing/searching it.
menu.AddHistorySourceFile("file history", "/path/to/.example-history")
// Add a custom source, like the one implemented in the next section
menu.AddHistorySource("custom history", new(ClientHistory))
}
Taking the menu example from the menus page, we here consider that our application has a local (main) menu, and a remote (client) menu, which might be used when connecting to a remote host. Therefore, we might want to have a history source with a file located on this host.
For this, readline offers the History
interface, which allows you to implement a custom history:
type History interface {
// Append takes the line and returns an updated number of lines or an error
Write(string) (int, error)
// GetLine takes the historic line number and returns the line or an error
GetLine(int) (string, error)
// Len returns the number of history lines
Len() int
// Dump returns everything in readline. The return is an interface{} because
// not all LineHistory implementations will want to structure the history in
// the same way. And since Dump() is not actually used by the readline API
// internally, this methods return can be structured in whichever way is most
// convenient for your own applications (or even just create an empty
//function which returns `nil` if you don't require Dump() either)
Dump() interface{}
}
An example implementation using gRPC could look like this:
// ClientHistory - Writes and queries only the Client's history
type ClientHistory struct {
LinesSinceStart int // Keeps count of line since session
items []string
}
// Write - Sends the last command to the server for saving
func (h *ClientHistory) Write(s string) (int, error) {
res, err := transport.RPC.AddToHistory(context.Background(),
&clientpb.AddCmdHistoryRequest{Line: s})
if err != nil {
return 0, err
}
// The server sent us back the whole user history,
// so we give it to the user history (the latter never
// actually uses its Write() method.
UserHist.cache = res.Lines
h.items = append(h.items, s)
return len(h.items), nil
}
// GetLine returns a line from history
func (h *ClientHistory) GetLine(i int) (string, error) {
if len(h.items) == 0 {
return "", nil
}
return h.items[i], nil
}
// Len returns the number of lines in history
func (h *ClientHistory) Len() int {
return len(h.items)
}
// Dump returns the entire history
func (h *ClientHistory) Dump() interface{} {
return h.items
}