package readlineimport ()//// API ----------------------------------------------------------------//// History is an interface to allow you to write your own history logging tools.// By default readline will just use an in-memory history satisfying this interface,// which only logs the history to memory ([]string to be precise).// Users who want an easy to use, file-based history should use NewHistoryFromFile().typeHistory = history.Source// NewHistoryFromFile creates a new command history source writing to and reading// from a file. The caller should bind the history source returned from this call// to the readline instance, with shell.History.Add().varNewHistoryFromFile = history.NewSourceFromFile// NewInMemoryHistory creates a new in-memory command history source.// The caller should bind the history source returned from this call// to the readline instance, with shell.History.Add().varNewInMemoryHistory = history.NewInMemoryHistory// historyCommands returns all history commands.// Under each comment are gathered all commands related to the comment's// subject. When there are two subgroups separated by an empty line, the// second one comprises commands that are not legacy readline commands.func ( *Shell) () commands { := map[string]func(){"accept-line": .acceptLine,"next-history": .downHistory,"previous-history": .upHistory,"beginning-of-history": .beginningOfHistory,"end-of-history": .endOfHistory,"operate-and-get-next": .acceptLineAndDownHistory,"fetch-history": .fetchHistory,"forward-search-history": .forwardSearchHistory,"reverse-search-history": .reverseSearchHistory,"non-incremental-forward-search-history": .nonIncrementalForwardSearchHistory,"non-incremental-reverse-search-history": .nonIncrementalReverseSearchHistory,"history-search-forward": .historySearchForward,"history-search-backward": .historySearchBackward,"history-substring-search-forward": .historySubstringSearchForward,"history-substring-search-backward": .historySubstringSearchBackward,"yank-last-arg": .yankLastArg,"insert-last-argument": .yankLastArg,"yank-nth-arg": .yankNthArg,"magic-space": .magicSpace,"accept-and-hold": .acceptAndHold,"accept-and-infer-next-history": .acceptAndInferNextHistory,"down-line-or-history": .downLineOrHistory,"vi-down-line-or-history": .viDownLineOrHistory,"up-line-or-history": .upLineOrHistory,"up-line-or-search": .upLineOrSearch,"down-line-or-select": .downLineOrSelect,"infer-next-history": .inferNextHistory,"beginning-of-buffer-or-history": .beginningOfBufferOrHistory,"end-of-buffer-or-history": .endOfBufferOrHistory,"beginning-of-line-hist": .beginningOfLineHist,"end-of-line-hist": .endOfLineHist,"incremental-forward-search-history": .incrementalForwardSearchHistory,"incremental-reverse-search-history": .incrementalReverseSearchHistory,"save-line": .saveLine,"history-source-next": .historySourceNext,"history-source-prev": .historySourcePrev,"autosuggest-accept": .autosuggestAccept,"autosuggest-execute": .autosuggestExecute,"autosuggest-enable": .autosuggestEnable,"autosuggest-disable": .autosuggestDisable,"autosuggest-toggle": .autosuggestToggle, }return}//// Standard ----------------------------------------------------------------//// Finish editing the buffer. Normally this causes the buffer to be executed as a shell command.func ( *Shell) () { .acceptLineWith(false, false)}// Move to the next event in the history list.func ( *Shell) () { .History.Save() .History.Walk(-1)}// Move to the previous event in the history list.func ( *Shell) () { .History.Save() .History.Walk(1)}// Move to the first event in the history list.func ( *Shell) () { .History.SkipSave() := .History.Current()if == nil {return } .History.Walk(.Len())}// Move to the last event in the history list.func ( *Shell) () { := .History.Current()if == nil {return } .History.Walk(-.Len() + 1)}// Execute the current line, and push the next history event on the buffer stack.func ( *Shell) () { .acceptLineWith(true, false)}// With a numeric argument, fetch that entry from the history// list and make it the current line. Without an argument,// move back to the first entry in the history list.func ( *Shell) () {if .Iterations.IsSet() { .History.Fetch(.Iterations.Get()) } else { .History.Fetch(0) }}// Search forward starting at the current line and moving `down' through// the history as necessary. This is an incremental search, opening and// showing matching completions.func ( *Shell) () { .History.SkipSave() := true := false := true .historyCompletion(, , )}// Search backward starting at the current line and moving `up' through// the history as necessary. This is an incremental search, opening and// showing matching completions.func ( *Shell) () { .History.SkipSave() := false := false := true .historyCompletion(, , )}// Search forward through the history starting at the current line// using a non-incremental search for a string supplied by the user.func ( *Shell) () { := false := true := true .completer.NonIsearchStart(.History.Name(), , , )}// Search backward through the history starting at the current line// using a non-incremental search for a string supplied by the user.func ( *Shell) () { := false := false := true .completer.NonIsearchStart(.History.Name(), , , )}// Search forward through the history for the string of characters// between the start of the current line and the point. The search// string must match at the beginning of a history line.func ( *Shell) () { .History.Save() := true := true := false .History.InsertMatch(nil, nil, , , )}// Search backward through the history for the string of characters// between the start of the current line and the point. The search// string must match at the beginning of a history line.func ( *Shell) () { .History.Save() := true := false := false .History.InsertMatch(nil, nil, , , )}// Search forward through the history for the string of characters// between the start of the current line and the current cursor position.// The search string may match anywhere in a history line.// This is a non-incremental search.func ( *Shell) () { := true := true := true .History.InsertMatch(.line, .cursor, , , )}// Search backward through the history for the string of characters// between the start of the current line and the current cursor position.// The search string may match anywhere in a history line.// This is a non-incremental search.func ( *Shell) () { := true := false := true .History.InsertMatch(.line, .cursor, , , )}// Insert the last argument to the previous command (the last// word of the previous history entry). With a numeric// argument, behave exactly like yank-nth-arg. Successive// calls to yank-last-arg move back through the history list,// inserting the last word (or the word specified by the// argument to the first call) of each line in turn.// Any numeric argument supplied to these successive calls// determines the direction to move through the history.// A negative argument switches the direction through the// history (back or forward). The history expansion// facilities are used to extract the last argument, as if// the "!$" history expansion had been specified.func ( *Shell) () {// Get the last history line. := .History.GetLast()if == "" {return }// Split it into words, and get the last one. , := strutil.Split()if != nil || len() == 0 {return }// Get the last word, and quote it if it contains spaces. := [len()-1]ifstrings.ContainsAny(, " \t") {ifstrings.Contains(, "\"") { = "'" + + "'" } else { = "\"" + + "\"" } }// And append it to the end of the line. .line.Insert(.cursor.Pos(), []rune()...) .cursor.Move(len())}// Insert the first argument to the previous command (usually// the second word on the previous line) at point. With an// argument n, insert the nth word from the previous command// (the words in the previous command begin with word 0).// A negative argument inserts the nth word from the end of the// previous command. Once the argument n is computed, the argument// is extracted as if the "!n" history expansion had been specified.func ( *Shell) () {// Get the last history line. := .History.GetLast()if == "" {return }// Split it into words, and get the last one. , := strutil.Split()if != nil || len() == 0 {return }varstring// Abort if the required position is out of bounds. := .Iterations.Get()iflen() < {return } = [-1]// Quote if required.ifstrings.ContainsAny(, " \t") {ifstrings.Contains(, "\"") { = "'" + + "'" } else { = "\"" + + "\"" } }// And append it to the end of the line. .line.Insert(.line.Len(), []rune()...) .cursor.Move(len())}// Perform history expansion on the current line and insert a space.// If the current blank word under cursor starts with an exclamation// mark, the word up to the cursor is matched as a prefix against// the history lines, and the first match is inserted in place of it.func ( *Shell) () { := .cursor.Pos() := .line.Len()// If no line, or the cursor is on a space, we can't perform.if == 0 || ( == && (*.line)[-1] == inputrc.Space) { .selfInsert()return }// Select the word around cursor. .viSelectInBlankWord() , , , := .selection.Pop() .cursor.Set()// Fail if empty or not prefixed expandable.iflen(strings.TrimSpace()) == 0 { .selfInsert()return }if !strings.HasPrefix(, "!") || == "!" { .selfInsert()return }// Else, perform expansion on the remainder. := (*.line)[+1:] := .History.Suggest(&)ifstring() == string() { .selfInsert()return } .History.Save() .line.Cut(, ) .line.Insert(, ...) .cursor.Set( + .Len())}//// Added -------------------------------------------------------------------//// Accept the current input line (execute it) and// keep it as the buffer on the next readline loop.func ( *Shell) () { .acceptLineWith(false, true)}// Execute the contents of the buffer. Then search the history list for a line// matching the current one and push the event following onto the buffer stack.func ( *Shell) () { .acceptLineWith(true, false)}// Move down a line in the buffer, or if already at the// bottom line, move to the next event in the history list.func ( *Shell) () { := .Iterations.Get() := .line.Lines() - .cursor.LinePos()// If we can go down some lines out of // the available iterations, use them.if > 0 { .cursor.LineMove() -= }if > 0 { .History.Walk( * -1) }}// Move down a line in the buffer, or if already at the// bottom line, move to the next event in the history list.// Then move to the first non-blank character on the line.func ( *Shell) () { .downLineOrHistory() .viFirstPrint()}// Move up a line in the buffer, or if already at the top// line, move to the previous event in the history list.func ( *Shell) () { := .Iterations.Get() := .cursor.LinePos()// If we can go down some lines out of // the available iterations, use them.if > 0 { .cursor.LineMove( * -1) -= }if > 0 { .History.Walk() }}// If the cursor is on the first line of the buffer, start an incremental// search backward on the history lines. Otherwise, move up a line in the buffer.func ( *Shell) () { .History.SkipSave()switch {case .cursor.LinePos() > 0: .cursor.LineMove(-1)default: .historySearchBackward() }}// If the cursor is on the last line of the buffer, start an incremental// search forward on the history lines. Otherwise, move up a line in the buffer.func ( *Shell) () { .History.SkipSave()switch {case .cursor.LinePos() < .line.Lines(): .cursor.LineMove(1)default: .menuComplete() }}// Attempt to find a line in history matching the current line buffer as a prefix,// and if one is found, fetch the next history event and make it the current buffer.func ( *Shell) () { .History.SkipSave() .History.InferNext()}// If the cursor is not at the beginning of the buffer, go to it.// Otherwise, go to the beginning of history.func ( *Shell) () {if .cursor.Pos() > 0 { .History.SkipSave() .cursor.Set(0)return } .History.Save() .beginningOfHistory()}// If the cursor is not at the end of the buffer, go to it.// Otherwise, go to the end of history.func ( *Shell) () {if .cursor.Pos() < .line.Len()-1 { .History.SkipSave() .cursor.Set(.line.Len())return } .History.Save() .endOfHistory()}// Go to the beginning of the current line, if the cursor is not yet.// If at the beginning of the line, attempt to move one line up.// If at the beginning of the buffer, move up one history line.func ( *Shell) () {switch {case .cursor.Pos() > 0: .History.SkipSave()if .cursor.AtBeginningOfLine() { .cursor.Dec() } .beginningOfLine()default: .History.Save() .History.Walk(1) }}// Go to the end of the current line, if the cursor is not yet.// If at the end of the line, attempt to move one line down.// If at the end of the buffer, move up one history line.func ( *Shell) () { .History.SkipSave()switch {case .cursor.Pos() < .line.Len()-1: .History.SkipSave()if .cursor.AtEndOfLine() { .cursor.Inc() } .endOfLine()default: .History.Save() .History.Walk(-1) }}// Start an forward history autocompletion mode, starting at the// current line and moving `down' through the history as necessary.func ( *Shell) () { .History.SkipSave() := true := true := false .historyCompletion(, , )}// Start an backward history autocompletion mode, starting at the// current line and moving `down' through the history as necessary.func ( *Shell) () { .History.SkipSave() := false := true := false .historyCompletion(, , )}// Write the current line to the history if it is not empty// (without executing it), and clear the line buffer.func ( *Shell) () { .History.Write(false) .History.Revert()}// If more than one source of command history is bound to the shell,// cycle to the next one and use it for all history search operations,// movements across lines, their respective undo histories, etc.func ( *Shell) () { .History.Cycle(true)}// If more than one source of command history is bound to the shell,// cycle to the previous one and use it for all history search operations,// movements across lines, their respective undo histories, etc.func ( *Shell) () { .History.Cycle(false)}// If a line is currently auto-suggested, make it the buffer.func ( *Shell) () { := .History.Suggest(.line)if .Len() <= .line.Len() {return } .line.Set(...) .cursor.Set(len())}// If a line is currently auto-suggested, make it the buffer and execute it.func ( *Shell) () { := .History.Suggest(.line)if .Len() <= .line.Len() {return } .line.Set(...) .cursor.Set(len()) .acceptLine()}// Toggle line history autosuggestions on/off.func ( *Shell) () {if .Config.GetBool("history-autosuggest") { .autosuggestDisable() } else { .autosuggestEnable() }}// Enable history line autosuggestions.// When enabled and if a line is suggested, forward-word commands, will// take the first word of the non-inserted part of this suggestion and// will insert it in the real input line.// The forward-char* commands, if at the end of the line, will accept it.func ( *Shell) () { .History.SkipSave() .Config.Vars["history-autosuggest"] = true}// Disable history line autosuggestions.func ( *Shell) () { .History.SkipSave() .Config.Vars["history-autosuggest"] = false}//// Utils -------------------------------------------------------------------//func ( *Shell) (, bool) {// If we are currently using the incremental-search buffer, // we should cancel this mode so as to run the rest of this // function on (with) the input line itself, not the minibuffer. .completer.Reset()// Non-incremental search modes are the only mode not cancelled // by the completion engine. If it's active, match the line result // and return without returning the line to the readline caller. , , := .completer.NonIncrementallySearching()if {defer .completer.NonIsearchStop() , , := .completer.GetBuffer() .History.InsertMatch(, , true, , )return }// Use the correct buffer for the rest of the function. .line, .cursor, .selection = .completer.GetBuffer()// Without multiline support, we always return the line.if .AcceptMultiline == nil { .Macros.StopRecord(.Keys.Caller()...) .Display.AcceptLine() .History.Accept(, , nil)return }// Ask the caller if the line should be accepted // as is, save the command line and accept it.if .AcceptMultiline(*.line) { .Macros.StopRecord(.Keys.Caller()...) .Display.AcceptLine() .History.Accept(, , nil)return }// If not, we should start editing another line, // and insert a newline where our cursor value is. // This has the nice advantage of being able to work // in multiline mode even in the middle of the buffer. .line.Insert(.cursor.Pos(), '\n') .cursor.Inc()}func ( *Shell) ( bool) { := .cursor.Pos()if < .line.Len()-1 {return }if !.Config.GetBool("history-autosuggest") {return } := .History.Suggest(.line)if .Len() > .line.Len() {varintif { = .ForwardEnd(.Tokenize, ) } else { = .Forward(.Tokenize, ) }if +1+ > .Len() { = .Len() - - 1 } .line.Insert(+1, [+1:++1]...) }}
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.