Source File
console.go
Belonging Package
github.com/reeflective/console
package consoleimport ()// Console is an integrated console application instance.type Console struct {// Applicationname string // Used in the prompt, and for readline `.inputrc` application-specific settings.shell *readline.Shell // Provides readline functionality (inputs, completions, hints, history)printLogo func(c *Console) // Simple logo printer.cmdHighlight string // Ansi code for highlighting of command in default highlighter. Green by default.flagHighlight string // Ansi code for highlighting of flag in default highlighter. Grey by default.menus map[string]*Menu // Different command trees, prompt engines, etc.filters []string // Hide commands based on their attributes and current context.isExecuting bool // Used by log functions, which need to adapt behavior (print the prompt, etc.)printed bool // Used to adjust asynchronous messages too.mutex *sync.RWMutex // Concurrency management.// Execution// Leave an empty line before executing the command.NewlineBefore bool// Leave an empty line after executing the command.// Note that if you also want this newline to be used when logging messages// with TransientPrintf(), Printf() calls, you should leave this to false,// and add a leading newline to your prompt instead: the readline shell will// know how to handle it in all situations.NewlineAfter bool// Leave empty lines with NewlineBefore and NewlineAfter, even if the provided input was empty.// Empty characters are defined as any number of spaces and tabs. The 'empty' character set// can be changed by modifying Console.EmptyChars// This field is false by default.NewlineWhenEmpty bool// Characters that are used to determine whether an input line was empty. If a line is not entirely// made up by any of these characters, then it is not considered empty. The default characters// are ' ' and '\t'.EmptyChars []rune// PreReadlineHooks - All the functions in this list will be executed,// in their respective orders, before the console starts reading// any user input (ie, before redrawing the prompt).PreReadlineHooks []func() error// PreCmdRunLineHooks - Same as PreCmdRunHooks, but will have an effect on the// input line being ultimately provided to the command parser. This might// be used by people who want to apply supplemental, specific processing// on the command input line.PreCmdRunLineHooks []func(args []string) ([]string, error)// PreCmdRunHooks - Once the user has entered a command, but before executing// the target command, the console will execute every function in this list.// These hooks are distinct from the cobra.PreRun() or OnInitialize hooks,// and might be used in combination with them.PreCmdRunHooks []func() error// PostCmdRunHooks are run after the target cobra command has been executed.// These hooks are distinct from the cobra.PreRun() or OnFinalize hooks,// and might be used in combination with them.PostCmdRunHooks []func() error}// New - Instantiates a new console application, with sane but powerful defaults.// This instance can then be passed around and used to bind commands, setup additional// things, print asynchronous messages, or modify various operating parameters on the fly.// The app parameter is an optional name of the application using this console.func ( string) *Console {:= &Console{name: ,shell: readline.NewShell(inputrc.WithApp(strings.ToLower())),menus: make(map[string]*Menu),mutex: &sync.RWMutex{},}// Quality of life improvements..setupShell()// Make a default menu and make it current.// Each menu is created with a default prompt engine.:= .NewMenu("").active = true// Set the history for this menufor , := range .historyNames {.shell.History.Add(, .histories[])}// Syntax highlighting, multiline callbacks, etc..cmdHighlight = line.GreenFG.flagHighlight = line.BrightWhiteFG.shell.AcceptMultiline = line.AcceptMultiline.shell.SyntaxHighlighter = .highlightSyntax// Completion.shell.Completer = .completecompletion.DefaultStyleConfig()// Defaults.EmptyChars = []rune{' ', '\t'}return}// Shell returns the console readline shell instance, so that the user can// further configure it or use some of its API for lower-level stuff.func ( *Console) () *readline.Shell {return .shell}//// Settings & Initialisation Functions ------------------------------------------------------------- ////// SetPrintLogo - Sets the function that will be called to print the logo.func ( *Console) ( func( *Console)) {.printLogo =}// SetDefaultCommandHighlight allows the user to change the highlight color for// a command in the default syntax highlighter using an ansi code.// This action has no effect if a custom syntax highlighter for the shell is set.// By default, the highlight code is green ("\x1b[32m").func ( *Console) ( string) {.cmdHighlight =}// SetDefaultFlagHighlight allows the user to change the highlight color for// a flag in the default syntax highlighter using an ansi color code.// This action has no effect if a custom syntax highlighter for the shell is set.// By default, the highlight code is grey ("\x1b[38;05;244m").func ( *Console) ( string) {.flagHighlight =}//// Menu Management --------------------------------------------------------------------------------- ////// NewMenu - Create a new command menu, to which the user// can attach any number of commands (with any nesting), as// well as some specific items like history sources, prompt// configurations, sets of expanded variables, and others.func ( *Console) ( string) *Menu {.mutex.RLock()defer .mutex.RUnlock():= newMenu(, ).menus[] =return}// ActiveMenu - Return the currently used console menu.func ( *Console) () *Menu {.mutex.Lock()defer .mutex.Unlock()return .activeMenu()}// Menu returns one of the console menus by name, or nil if no menu is found.func ( *Console) ( string) *Menu {.mutex.Lock()defer .mutex.Unlock()return .menus[]}// SwitchMenu - Given a name, the console switches its command menu:// The next time the console rebinds all of its commands, it will only bind those// that belong to this new menu. If the menu is invalid, i.e that no commands// are bound to this menu name, the current menu is kept.func ( *Console) ( string) {.mutex.Lock(), := .menus[].mutex.Unlock()if && != nil {// Only switch if the target menu was found.:= .activeMenu()if != nil && == {return}if != nil {.active = false}.active = true// Remove the currently bound history sources// (old menu) and bind the ones peculiar to this one..shell.History.Delete()for , := range .historyNames {.shell.History.Add(, .histories[])}// Regenerate the commands, outputs and everything related..resetPreRun()}}//// Message Display Functions ----------------------------------------------------------------------- ////// TransientPrintf prints a string message (a log, or more broadly, an asynchronous event)// without bothering the user, displaying the message and "pushing" the prompt below it.// The message is printed regardless of the current menu.//// If this function is called while a command is running, the console will simply print the log// below the line, and will not print the prompt. In any other case this function works normally.func ( *Console) ( string, ...any) ( int, error) {if .isExecuting {return fmt.Printf(, ...)}// If the last message we printed asynchronously// immediately precedes this new message, move up// another row, so we don't waste too much space.if .printed && .NewlineAfter {fmt.Print("\x1b[1A")}if .NewlineAfter {+= "\n"}.printed = truereturn .shell.PrintTransientf(, ...)}// Printf prints a string message (a log, or more broadly, an asynchronous event)// below the current prompt. The message is printed regardless of the current menu.//// If this function is called while a command is running, the console will simply print the log// below the line, and will not print the prompt. In any other case this function works normally.func ( *Console) ( string, ...any) ( int, error) {if .isExecuting {return fmt.Printf(, ...)}return .shell.Printf(, ...)}//// Other Utility Functions ------------------------------------------------------------------------- ////// SystemEditor - This function is a renamed-reexport of the underlying readline.StartEditorWithBuffer// function, which enables you to conveniently edit files/buffers from within the console application.// Naturally, the function will block until the editor is exited, and the updated buffer is returned.// The filename parameter can be used to pass a specific filename.ext pattern, which might be useful// if the editor has builtin filetype plugin functionality.func ( *Console) ( []byte, string) ([]byte, error) {:= .shell.Config.GetString("editing-mode") == "emacs", := .shell.Buffers.EditBuffer([]rune(string()), "", , )return []byte(string()),}func ( *Console) () {// Some options should be set to on because they// are quite neceessary for efficient console use.:= .shell.Config// Input line.Set("autopairs", true).Set("blink-matching-paren", true)// Completion.Set("completion-ignore-case", true).Set("skip-completed-text", true).Set("menu-complete-display-prefix", true)// General UI.Set("usage-hint-always", true).Set("history-autosuggest", true)}func ( *Console) () *Menu {for , := range .menus {if .active {return}}// Else return the default menu.return .menus[""]}
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |