Source File
readline.go
Belonging Package
github.com/reeflective/readline
// Package readline provides a modern, pure Go `readline` shell implementation,// with full `.inputrc` and legacy readline command/option support, and extended// with various commands,options and tools commonly found in more modern shells.//// Example usage://// // Create a new shell with a custom prompt.// rl := readline.NewShell()// rl.Prompt.Primary(func() string { return "> "} )//// // Display the prompt, read user input.// for {// line, err := rl.Readline()// if err != nil {// break// }// fmt.Println(line)// }package readlineimport ()// ErrInterrupt is returned when the interrupt sequence// is pressed on the keyboard. The sequence is usually Ctrl-C.var ErrInterrupt = errors.New(os.Interrupt.String())// Readline displays the readline prompt and reads user input.// It can return from the call because of different things://// - When the user accepts the line (generally with Enter).// - If a particular keystroke mapping returns an error.// (Ctrl-C returns ErrInterrupt, Ctrl-D returns io.EOF).//// In all cases, the current input line is returned along with any error,// and it is up to the caller to decide what to do with the line result.// When the error is not nil, the returned line is not written to history.func ( *Shell) () (string, error) {:= int(os.Stdin.Fd()), := term.MakeRaw()if != nil {return "",}defer term.Restore(, )// Prompts and cursor styles.Display.PrintPrimaryPrompt()defer .Display.RefreshTransient()defer fmt.Print(keymap.CursorStyle("default")).init()// Terminal resize events:= display.WatchResize(.Display)defer close()for {// Whether or not the command is resolved, let the macro// engine record the keys if currently recording a macro.// This is done before flushing all used keys, on purpose.macro.RecordKeys(.Macros)// Get the rid of the keys that were consumed during the// previous command run. This may include keys that have// been consumed but did not match any command.core.FlushUsed(.Keys)// Since we always update helpers after being asked to read// for user input again, we do it before actually reading it..Display.Refresh()// Block and wait for available user input keys.// These might be read on stdin, or already available because// the macro engine has fed some keys in bulk when running one.core.WaitAvailableKeys(.Keys, .Config)// 1 - Local keymap (Completion/Isearch/Vim operator pending)., , := keymap.MatchLocal(.Keymap)if {continue}, , := .run(false, , )if {return ,} else if != nil {continue}// Past the local keymap, our actions have a direct effect// on the line or on the cursor position, so we must first// "reset" or accept any completion state we're in, if any,// such as a virtually inserted candidate.completion.UpdateInserted(.completer)// 2 - Main keymap (Vim command/insertion, Emacs)., , = keymap.MatchMain(.Keymap)if {continue}, , = .run(true, , )if {return ,}// Reaching this point means the last key/sequence has not// been dispatched down to a command: therefore this key is// undefined for the current local/main keymaps..handleUndefined(, )}}// init gathers all steps to perform at the beginning of readline loop.func ( *Shell) () {// Reset core editor components.core.FlushUsed(.Keys).line.Set().cursor.Set(0).cursor.ResetMark().selection.Reset().Buffers.Reset().History.Reset().Iterations.Reset()// Some accept-* commands must fetch a specific// line outright, or keep the accepted one.history.Init(.History).History.Save()// Reset/initialize user interface components..Hint.Reset().completer.ResetForce()display.Init(.Display, .SyntaxHighlighter)}// run wraps the execution of a target command/sequence with various pre/post actions// and setup steps (buffers setup, cursor checks, iterations, key flushing, etc...)func ( *Shell) ( bool, inputrc.Bind, func()) (bool, string, error) {// An empty bind match in the local keymap means nothing// should be done, the main keymap must work it out.if ! && .Action == "" {return false, "", nil}// If the resolved bind is a macro itself, reinject its// bound sequence back to the key stack.if .Macro {:= inputrc.Unescape(.Action).Keys.Feed(false, []rune()...)}// The completion system might have control of the// input line and be using it with a virtual insertion,// so it knows which line and cursor we should work on..line, .cursor, .selection = .completer.GetBuffer()// The command might be nil, because the provided key sequence// did not match any. We regardless execute everything related// to the command, like any pending ones, and cursor checks..execute()// Either print/clear iterations/active registers hints..updatePosRunHints()// If the command just run was using the incremental search// buffer (acting on it), update the list of matches..completer.UpdateIsearch()// Work is done: ask the completion system to// return the correct input line and cursor..line, .cursor, .selection = .completer.GetBuffer()// History: save the last action to the line history,// and return with the call to the history system that// checks if the line has been accepted (entered), in// which case this will automatically write the history// sources and set up errors/returned line values..History.SaveWithCommand()return .History.LineAccepted()}// Run the dispatched command, any pending operator// commands (Vim mode) and some post-run checks.func ( *Shell) ( func()) {if != nil {()}// Only run pending-operator commands when the command we// just executed has not had any influence on iterations.if !.Iterations.IsPending() {.Keymap.RunPending()}// Update/check cursor positions after run.switch .Keymap.Main() {case keymap.ViCommand, keymap.ViMove, keymap.Vi:.cursor.CheckCommand()default:.cursor.CheckAppend()}}// Some commands show their current status as a hint (iterations/macro).func ( *Shell) () {:= core.ResetPostRunIterations(.Iterations), := .Buffers.IsSelected()if == "" && ! && !.Macros.Recording() {.Hint.ResetPersist()return}if != "" {.Hint.Persist()} else if {.Hint.Persist(color.Dim + fmt.Sprintf("(register: %s)", ))}}// handleUndefined is in charge of all actions to take when the// last key/sequence was not dispatched down to a readline command.func ( *Shell) ( inputrc.Bind, func()) {if .Action != "" || != nil {return}// Undefined keys incremental-search mode cancels it.if .Keymap.Local() == keymap.Isearch {.Hint.Reset().completer.Reset()}}
![]() |
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. |