package consoleimport ()// Start - Start the console application (readline loop). Blocking.// The error returned will always be an error that the console// application does not understand or cannot handle.func ( *Console) () error {return .StartContext(context.Background())}// StartContext is like console.Start(). with a user-provided context.func ( *Console) ( context.Context) error { .loadActiveHistories()// Print the console logoif .printLogo != nil { .printLogo() } := ""// used to check if last read line is empty.for { .displayPostRun()// Always ensure we work with the active menu, with freshly // generated commands, bound prompts and some other things. := .activeMenu() .resetPreRun()if := .runAllE(.PreReadlineHooks); != nil { .ErrorHandler(PreReadError{newError(, "Pre-read error")})continue }// Block and read user input. , := .shell.Readline() .displayPostRun()if != nil { .handleInterrupt() = continue }// Any call to the SwitchMenu() while we were reading user // input (through an interrupt handler) might have changed it, // so we must be sure we use the good one. = .activeMenu()// Parse the line with bash-syntax, removing comments. , := .parse()if != nil { .ErrorHandler(ParseError{newError(, "Parsing error")})continue }iflen() == 0 { = continue }// Run user-provided pre-run line hooks, // which may modify the input line args. , = .runLineHooks()if != nil { .ErrorHandler(LineHookError{newError(, "Line error")})continue }// Run all pre-run hooks and the command itself // Don't check the error: if its a cobra error, // the library user is responsible for setting // the cobra behavior. // If it's an interrupt, we take care of it.if := .execute(, , , false); != nil { .ErrorHandler(ExecutionError{newError(, "")}) } = }}// RunCommandArgs is a convenience function to run a command line in a given menu.// After running, the menu's commands are reset, and the prompts reloaded, therefore// mostly mimicking the behavior that is the one of the normal readline/run/readline// workflow.// Although state segregation is a priority for this library to be ensured as much// as possible, you should be cautious when using this function to run commands.func ( *Menu) ( context.Context, []string) ( error) {// The menu used and reset is the active menu. // Prepare its output buffer for the command. .resetPreRun()// Run the command and associated helpers.return .console.execute(, , , !.console.isExecuting)}// RunCommandLine is the equivalent of menu.RunCommandArgs(), but accepts// an unsplit command line to execute. This line is split and processed in// *sh-compliant form, identically to how lines are in normal console usage.func ( *Menu) ( context.Context, string) ( error) {iflen() == 0 {return }// Split the line into shell words. , := shellquote.Split()if != nil {returnfmt.Errorf("line error: %w", ) }return .RunCommandArgs(, )}// execute - The user has entered a command input line, the arguments have been processed:// we synchronize a few elements of the console, then pass these arguments to the command// parser for execution and error handling.// Our main object of interest is the menu's root command, and we explicitly use this reference// instead of the menu itself, because if RunCommand() is asynchronously triggered while another// command is running, the menu's root command will be overwritten.func ( *Console) ( context.Context, *Menu, []string, bool) error {if ! { .mutex.RLock() .isExecuting = true .mutex.RUnlock() }deferfunc() { .mutex.RLock() .isExecuting = false .mutex.RUnlock() }()// Our root command of interest, used throughout this function. := .Command// Find the target command: if this command is filtered, don't run it. , , := .Find()if := .CheckIsAvailable(); != nil {return }// Reset all flags to their default values.resetFlagsDefaults()// Console-wide pre-run hooks, cannot.if := .runAllE(.PreCmdRunHooks); != nil {returnfmt.Errorf("pre-run error: %s", .Error()) }// Assign those arguments to our parser. .SetArgs()// The command execution should happen in a separate goroutine, // and should notify the main goroutine when it is done. , := context.WithCancelCause() .SetContext()// Start monitoring keyboard and OS signals. := .monitorSignals()// And start the command execution.go .executeCommand(, )// Wait for the command to finish, or for an OS signal to be caught.select {case<-.Done(): := context.Cause()if !errors.Is(, context.Canceled) {return }case := <-: (errors.New(.String())) .handleInterrupt(errors.New(.String())) }returnnil}// Run the command in a separate goroutine, and cancel the context when done.func ( *Console) ( *cobra.Command, context.CancelCauseFunc) {if := .Execute(); != nil { ()return }// And the post-run hooks in the same goroutine, // because they should not be skipped even if // the command is backgrounded by the user.if := .runAllE(.PostCmdRunHooks); != nil { ()return }// Command successfully executed, cancel the context. (nil)}func ( *Console) () { .shell.History.Delete()for , := range .activeMenu().historyNames { .shell.History.Add(, .activeMenu().histories[]) }}func ( *Console) ( []func() error) error {for , := range {if := (); != nil {return } }returnnil}func ( *Console) ( []string) ([]string, error) { := // Or modify them againfor , := range .PreCmdRunLineHooks {varerrorif , = (); != nil {returnnil, } }return , nil}func ( *Console) ( string) {if .NewlineBefore {if !.NewlineWhenEmpty {if !.lineEmpty() {fmt.Println() } } else {fmt.Println() } }}func ( *Console) ( string) {if .NewlineAfter {if !.NewlineWhenEmpty {if !.lineEmpty() {fmt.Println() } } else {fmt.Println() } } .printed = false}// monitorSignals - Monitor the signals that can be sent to the process// while a command is running. We want to be able to cancel the command.func ( *Console) () <-chanos.Signal { := make(chanos.Signal, 1)signal.Notify( ,syscall.SIGINT,syscall.SIGTERM,syscall.SIGQUIT,// syscall.SIGKILL, )return}
The pages are generated with Goldsv0.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.