package completionimport ()// UpdateInserted should be called only once in between the two shell keymaps// (local/main) in the main readline loop, to either drop or confirm a virtually// inserted candidate.func ( *Engine) {// If the user currently has a completion selected, any change // in the input line will drop the current completion list, in // effect deactivating the completion engine. // This is so that when a user asks for the list of choices, but // then deletes or types something in the input line, the list // is still displayed to the user, otherwise it's removed. // This does not apply when autocomplete is on. := len(.selected.Value) != 0if !.auto {defer .ClearMenu() }// If autocomplete is on, we also drop the list of generated // completions, because it will be recomputed shortly after. // Do the same when using incremental search, except if the // last key typed is an escape, in which case the user wants // to quit incremental search but keeping any selected comp. := .mustRemoveInserted() := .keymap.Local() != keymap.Isearch && !.autoForce .Cancel(, )if && .autoForce && len(.selected.Value) == 0 { .Reset() }}// TrimSuffix removes the last inserted completion's suffix if the required constraints// are satisfied (among which the index position, the suffix matching patterns, etc).func ( *Engine) () {if .line.Len() == 0 || .cursor.Pos() == 0 || len(.selected.Value) > 0 {return }// If our suffix matcher was registered at a different // place in our line, then it's an orphan.if .sm.pos != .cursor.Pos()-1 || .sm.string == "" { .sm = SuffixMatcher{}return } := (*.line)[.cursor.Pos()-1] := .keys.Caller() := [0]// Special case when completing paths: if the comp is ended // by a slash, only remove this slash if the inserted key is // one of the suffix matchers, otherwise keep it.if == '/' && != inputrc.Space && notMatcher(, .sm.string) {return }// If the key is a space or matches the suffix matcher, cut the suffix.if .sm.Matches(string()) || unicode.IsSpace() { .cursor.Dec() .line.CutRune(.cursor.Pos()) }// But when the key is a space, we also drop the suffix matcher, // because the user is done with this precise completion (or group of).ifunicode.IsSpace() { .sm = SuffixMatcher{} }}// refreshLine - Either insert the only candidate in the real line// and drop the current completion list, prefix, keymaps, etc, or// swap the formerly selected candidate with the new one.func ( *Engine) () {if .noCompletions() { .Cancel(true, true)return }if .currentGroup() == nil {return }// Incremental search is a special case, because the user may // want to keep searching for another match, so we don't drop // the completion list and exit the incremental search mode.if .hasUniqueCandidate() && .keymap.Local() != keymap.Isearch { .acceptCandidate() .ResetForce() } else { .insertCandidate() }}// acceptCandidate inserts the currently selected candidate into the real input line.func ( *Engine) () { := .currentGroup()if == nil {return } .selected = .selected()// Prepare the completion candidate, remove the // prefix part and save its sufffixes for later. := .prepareSuffix() .inserted = []rune()// Remove the line prefix and insert the candidate. .cursor.Move(-1 * len(.prefix)) .line.Cut(.cursor.Pos(), .cursor.Pos()+len(.prefix)) .cursor.InsertAt(.inserted...)// And forget about this inserted completion. .inserted = make([]rune, 0) .prefix = "" .suffix = ""}// insertCandidate inserts a completion candidate into the virtual (completed) line.func ( *Engine) () { := .currentGroup()if == nil {return } .selected = .selected()iflen(.selected.Value) < len(.prefix) {return }// Prepare the completion candidate, remove the // prefix part and save its sufffixes for later. := .prepareSuffix() .inserted = []rune()// Copy the current (uncompleted) line/cursor. := core.Line(string(*.line)) .compLine = & .compCursor = core.NewCursor(.compLine) .compCursor.Set(.cursor.Pos())// Remove the line prefix and insert the candidate. .compCursor.Move(-1 * len(.prefix)) .compLine.Cut(.compCursor.Pos(), .compCursor.Pos()+len(.prefix)) .compCursor.InsertAt(.inserted...)}// prepareSuffix caches any suffix matcher associated with the completion candidate// to be inserted/accepted into the input line, and trims it if required at this point.func ( *Engine) () ( string) { := .currentGroup()if == nil {return } = .selected.Value := len(.prefix)// When the completion has a size of 1, don't remove anything: // stacked flags, for example, will never be inserted otherwise.iflen() > 0 && len([:]) <= 1 {return }// If we are to even consider removing a suffix, we keep the suffix // matcher for later: whatever the decision we take here will be identical // to the one we take while removing suffix in "non-virtual comp" mode. .sm = .noSpace .sm.pos = .cursor.Pos() + len() - - 1return}func ( *Engine) () {// The completed line includes any currently selected // candidate, just overwrite it with the normal line. .compLine.Set(*.line...) .compCursor.Set(.cursor.Pos())// And no virtual candidate anymore. .selected = Candidate{}}func ( *Engine) () bool {// All other completion modes do not want // the candidate to be removed from the line.if .keymap.Local() != keymap.Isearch {returnfalse }// Normally, we should have a key. , := core.PeekKey(.keys)if {returnfalse }// Some keys trigger behavior different from the normal one: // Ex: if the key is a letter, the isearch buffer is updated // and the line-inserted match might be different, so remove. // If the key is 'Enter', the line will likely be accepted // with the currently inserted candidate.switchrune() {caseinputrc.Esc, inputrc.Return:returnfalsedefault:returntrue }}func notMatcher( rune, string) bool {for , := range {if == {returnfalse } }returntrue}
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.