package uiimport ()const ( secondaryPromptDefault = "\x1b[1;30m\U00002514 \x1b[0m" multilineColumnDefault = "\x1b[1;30m\U00002502 \x1b[0m")// Prompt stores all prompt rendering/generation functions and is// in charge of displaying them, as well as computing their offsets.typePromptstruct { primaryF func() string primaryRows int primaryCols int secondaryF func() string transientF func() string rightF func() string tooltipF func() string// True if some logs have printed asynchronously // since last loop. Check refresh prompt funcs. refreshing bool// Shell parameters line *core.Line cursor *core.Cursor keymaps *keymap.Engine opts *inputrc.Config}// NewPrompt is a required constructor to initialize the prompt system.func ( *core.Line, *core.Cursor, *keymap.Engine, *inputrc.Config) *Prompt {return &Prompt{line: ,cursor: ,keymaps: ,opts: , }}// Primary uses a function returning the string to use as the primary prompt.func ( *Prompt) ( func() string) { .primaryF = }// Right uses a function returning the string to use as the right prompt.func ( *Prompt) ( func() string) { .rightF = }// Secondary uses a function returning the prompt to use as the secondary prompt.func ( *Prompt) ( func() string) { .secondaryF = }// Transient uses a function returning the prompt to use as a transient prompt.func ( *Prompt) ( func() string) { .transientF = }// Tooltip uses a function returning the prompt to use as a tooltip prompt.func ( *Prompt) ( func( string) string) {if == nil {return }// Wrap the user-provided function into a callback using out input line. .tooltipF = func() string {varstring := strings.Split(string(*.line), " ")iflen() > 0 { = [0] }return () }}// PrimaryPrint prints the primary prompt string, excluding// the last line if the primary prompt spans on several lines.func ( *Prompt) () { .refreshing = falseif .primaryF == nil {return } := .primaryF() , := .formatPrimaryLines()// Format the last line with the editing status. = .formatLastPrompt()// Print the various lines.if != "" {fmt.Print() }fmt.Print()// And compute coordinates .primaryRows = strings.Count(, "\n") .primaryCols = strutil.RealLength()if .primaryCols > 0 { .primaryCols-- }}// PrimaryUsed returns the number of terminal rows on which// the primary prompt string spans, excluding the last line// if it contains newlines.func ( *Prompt) () int {return .primaryRows}// LastPrint prints the last line of the primary prompt, if the latter// spans on several lines. If not, this function will actually print// the entire primary prompt, and PrimaryPrint() will not print anything.func ( *Prompt) () {if .primaryF == nil {return }// Only display the last line, but overwrite the number of // rows used since any redisplay of all lines but the last // will trigger their own recomputation. := strings.Split(.primaryF(), "\n")// Print the prompt and compute columns.iflen() == 0 {return } := .formatLastPrompt([len()-1])fmt.Print() .primaryCols = strutil.RealLength()if .primaryCols > 0 { .primaryCols-- }}// LastUsed returns the number of terminal columns used by the last// part of the primary prompt (of the entire string if not multiline).// This, in effect, returns the X coordinate at which the input line// should be printed, and indentation for subsequent lines if several.func ( *Prompt) () int {if .primaryF == nil {return0 }// Only display the last line, but overwrite the number of // rows used since any redisplay of all lines but the last // will trigger their own recomputation. := strings.Split(.primaryF(), "\n")iflen() == 0 {return0 } := .formatLastPrompt([len()-1]) .primaryCols = strutil.RealLength()if .primaryCols > 0 { .primaryCols-- }return .primaryCols}// SecondaryPrint prints the last cursor in secondary prompt mode,// which is always activated when the current input line is a multiline one.func ( *Prompt) () {if .secondaryF != nil {fmt.Print(.secondaryF())return }fmt.Print(secondaryPromptDefault)}// MultilineColumnPrint prints the multiline editor column status indicator.// It either prints a default, numbered or user-defined column.func ( *Prompt) () { := .opts.GetBool("multiline-column-numbered") := .opts.GetString("multiline-column-custom") := .opts.GetBool("multiline-column")switch {case : := ""for := range .line.Lines() { += fmt.Sprintf("\n\x1b[1;30m%d\x1b[0m", +2) }fmt.Print()caselen() > 0: := ""forrange .line.Lines() { += fmt.Sprintf("\n%s\x1b[0m", ) }fmt.Print()case : := ""forrange .line.Lines() { += "\n" + multilineColumnDefault }fmt.Print() }}// RightPrint prints the right-sided prompt strings, which might be either// a traditional RPROMPT string, or a tooltip prompt if any must be rendered.// If force is true, whatever rprompt or tooltip exists will be printed.// If false, only the rprompt, if it exists, will be printed.func ( *Prompt) ( int, bool) {varstringif .tooltipF != nil && { = .tooltipF() }if == "" && .rightF != nil { = .rightF() }if == "" {return }if , := .formatRightPrompt(, ); {fmt.Print() } else {fmt.Print(term.ClearLineAfter) }}// TransientPrint prints the transient prompt.func ( *Prompt) () {if .transientF == nil {return }// Clean everything below where the prompt will be printed.term.MoveCursorBackwards(term.GetWidth())term.MoveCursorUp(.primaryRows)fmt.Print(term.ClearScreenBelow)// And print the promptfmt.Print(.transientF())}// Refreshing returns true if the prompt is currently redisplaying// itself (at least the primary prompt), or false if not.func ( *Prompt) () bool {return .refreshing}func ( *Prompt) ( string) string {if !.opts.GetBool("show-mode-in-prompt") {return }varstringswitch {case .keymaps.IsEmacs(): = .opts.GetString("emacs-mode-string")case .keymaps.Main() == keymap.ViCommand: = .opts.GetString("vi-cmd-mode-string")case .keymaps.Main() == keymap.ViInsert: = .opts.GetString("vi-ins-mode-string") }// Fix parsing of inputrc which sometimes preserves quotes on some // values, and remove bash readline begin/end non-printable delimiters. = strings.Trim(, "\"") := regexp.MustCompile(`\\1`) := regexp.MustCompile(`\\2`)// Remove delimiters, and replace quoted escape sequences = .ReplaceAllString(, "") = .ReplaceAllString(, "") = strings.ReplaceAll(, "\\e", "\x1b")return + }func ( *Prompt) ( string, int) ( string, bool) {// Dimensions := term.GetWidth() := strutil.RealLength() := - - // Adjust padding when the last line is as large as terminal.if == { = - }// Check that we have room for a right/tooltip prompt. = (+ < ) || == if { = fmt.Sprintf("%s%s", strings.Repeat(" ", ), ) }return}func ( *Prompt) ( string) (, string) {// Get all the lines but the last. := strings.Split(, "\n")iflen() > 1 { = strings.Join([:len()-1], "\n") + "\n" = [len()-1] } else { , = , "" }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.