Skip to content

Getting Started

maxlandon edited this page May 29, 2023 · 7 revisions

Creating the application

The following snippet shows how to create a new console instance with the following defaults:

  • A readline shell with a default configuration (Emacs mode).
  • A default -main- menu, with an empty command tree.
  • Bound to this menu, an in-memory history source.
package main

func main() {
    // Instantiate a new app, with a single, default menu.
    // All defaults are set, and nothing is needed to make it work.
    //
    // The call requires an application name (which can be left empty), which will be used 
    // by the underlying readline shell to load per-application options and command binds.
    // See below.
    app := console.New("ApplicationName")
        
    // ... We will include code in here as we go through the documentation...
        
    // And will run the console application.
    app.Start()
}

Global settings

To begin with, we setup a few global options, applying to all menus and contexts:

// Surround command output, asynchronous logs and interrupt handlers with a newline.
app.NewlineBefore = true
app.NewlineAfter = true

// Set the logo function
app.SetPrintLogo(func(_ *console.Console) {
    fmt.Print(`
_____            __ _           _   _              _____                      _      
|  __ \          / _| |         | | (_)            / ____|                    | |     
| |__) |___  ___| |_| | ___  ___| |_ ___   _____  | |     ___  _ __  ___  ___ | | ___ 
|  _  // _ \/ _ \  _| |/ _ \/ __| __| \ \ / / _ \ | |    / _ \| '_ \/ __|/ _ \| |/ _ \
| | \ \  __/  __/ | | |  __/ (__| |_| |\ V /  __/ | |___| (_) | | | \__ \ (_) | |  __/
|_|  \_\___|\___|_| |_|\___|\___|\__|_| \_/ \___|  \_____\___/|_| |_|___/\___/|_|\___|

`)
})

Shell settings

The underlying readline shell used by the console is highly configurable. However, for most intents and purposes, its defaults are powerful enough, and most of the customization is made through user-side inputrc files.

The shell configuration and documentation can be found here.

Of interest for developers using this console application is the fact that the shell inputrc supports that each application using readline can declare itself so that per-application user bindings and options apply, like in the legacy GNU readline library.

Therefore, it is highly recommanded to use a unique name for your console application.

An extract of such per-application settings in a user inputrc file is the following:

# github.com/reeflective/readline Go readline library
$if ApplicationName 
    set autocomplete on
    set usage-hint-always on
    set history-autosuggest off
    set autopairs on
    set prompt-transient off

    # Other per-application command bindings/macros/etc.
$endif

Some more specialized options can be passed to the readline shell if you want to reload the user configuration, although this is very likely not needed:

import "github.com/reeflective/readline/inputrc"

var opts []inputrc.Option

opts = append(opts, inputrc.WithName("/path/to/app_config.inputrc")) // Force a specific configuration file.
opts = append(opts, inputrc.WithMode("xterm-256colors"))             // Force a specific terminal

// Set the shell options and/or reload the configuration.
app.Shell().Opts = opts                // If assigned, those will be used on each user-triggered reload
app.Shell().Keymap.ReloadConfig(opts)  // If only reloaded through this call, will apply only once.

Additional Customizations

Syntax highlighting

Developers can write and bind a custom callback which will apply highlighting to the input buffer, before readline adds its own overlays when it needs to. The callback is the following:

// SyntaxHighlighter is a helper function to provide syntax highlighting.
// Once enabled, set to nil to disable again.
SyntaxHighlighter func(line []rune) string

The console application is created with a default simple syntax highlighter, which support for command, flag and strings highlighting. Should you need a more complex (or maybe more reliable) syntax highligther, you might use the famous chroma library:

import "github.com/alecthomas/chroma/quick"

// highlighter uses chroma to highlight the line in an SQL dialect.
func highlighter(line []rune) string {
    var highlighted strings.Builder

    err := quick.Highlight(&highlighter, string(input), "sql", "terminal16m", "xcode-dark")
    if err != nil {
        return string(line)
    }

    return highlighted.String()
}

// Binding the syntax highlighter
app := console.New("App")                    // The default highlighter is bound in this call.
app.Shell().SyntaxHighlighter = highlighter  // And is overridden like this.

Multiline support

By default, the console application comes with classic sh-style input line word splitter, slightly modify to add support for multiline, the latter being triggered on several conditions (and upon Return keypress, or the keybinding assigned to one of the shell's accept-line command variants):

  • If the current quote is not closed.
  • If the last character of the input line is backslash.

You can override this default behavior by overriding the AcceptMultiline() callback of the app.Shell().