package keymapimport ()// MatchLocal incrementally attempts to match cached input keys against the local keymap.// Returns the bind if matched, the corresponding command, and if we only matched by prefix.func ( *Engine) ( inputrc.Bind, func(), bool) {if .local == "" {return , , }// Several local keymaps are empty by default: instead we use restricted // lists of commands, regardless of the key-sequence their bound to. := .getContextBinds(false)iflen() == 0 {return , , } , , , := .dispatchKeys()if !.Macro { = .commands[.Action] }if {core.MatchedPrefix(.keys, ...) } else {core.MatchedKeys(.keys, , [len():]...) }// Similarly to the MatchMain() function, give a special treatment to the escape key // (if it's alone): using escape in Viopp/menu-complete/isearch should cancel the // current mode, thus we return either a Vim movement-mode command, or nothing.if .isEscapeKey() && ( || == nil) { , , = .handleEscape(false) }return , , }// MatchMain incrementally attempts to match cached input keys against the local keymap.// Returns the bind if matched, the corresponding command, and if we only matched by prefix.func ( *Engine) ( inputrc.Bind, func(), bool) {if .main == "" {return , , }// Get all binds present in the main keymap. Here, contrary // to the local keymap matching, no keymap should be empty. := .getContextBinds(true)iflen() == 0 {return , , }// Find the target action, macro or command. , , , := .dispatchKeys()if !.Macro { = .commands[.Action] }// In the main menu, all keys that have been tested against // the binds will be dropped after command execution (whether // or not there's actually a command to execute).if {core.MatchedPrefix(.keys, ...) } else {core.MatchedKeys(.keys, ) }// Non-incremental search mode should always insert the keys // if they did not exactly match one of the valid commands.if .nonIncSearch && ( == nil || ) { = inputrc.Bind{Action: "self-insert"} .active = = .resolve() = false }// Adjusting for the ESC key: when convert-meta is enabled, // many binds will actually match ESC as a prefix. This makes // commands like vi-movement-mode unreachable, so if the bind // is vi-movement-mode, we return it to be ran regardless of // the other binds matching by prefix.if .isEscapeKey() && !.IsEmacs() && { , , = .handleEscape(true) }return , , }func ( *Engine) ( map[string]inputrc.Bind) ( inputrc.Bind, bool, , []byte) {// Support for Unicode: if the character is multi-byte (UTF-8), consume all its bytes // and treat it as a single self-insert action. Note that we just peek the characters // here, so if it's actually not a UTF-8 character, we just keep going and re-peek later. , := core.PeekChar(.keys)if {return .active, , , }iflen() > 1 { = append(, ...) := inputrc.Bind{Action: "self-insert",Macro: false, } = append(, ...) = .makeMatch(, inputrc.Bind{})core.PopChar(.keys)return .active, , , }for {// Read a single byte from the input buffer. // This mimics the way Bash reads input when the inputrc option `byte-oriented` is set. // This is because the default binds map is built with byte sequences, not runes, and this // has some implications if the terminal is sending 8-bit characters (extended alphabet). , := core.PeekKey(.keys)if {break } = append(, ) , := .matchBind(, )// If the current keys have no matches but the previous // matching process found a prefix, use it with the keys.if .Action == "" && len() == 0 { = .makeMatch(.prefixed, inputrc.Bind{})// FIX related to Github issue #73, where someone // complains not being able to input Unicode characters // correctly. Explanation: // The call to PeekKey at the beginning of this function // used to be PopKey. We don't pop the key unless we have // an empty byte.core.PopKey(.keys)break }// FIX related to Github issue #73, also pop the key here.core.PopKey(.keys)// From here, there is at least one bind matched, by prefix // or exactly, so the key we popped is considered matched. = append(, )// If we matched a prefix, keep the matched bind for later.iflen() > 0 { = trueif .Action != "" { .prefixed = }continue }// Or an exact match, so drop any prefixed one. = .makeMatch(, inputrc.Bind{})break }return .active, , , }func ( *Engine) ( []byte, map[string]inputrc.Bind) (inputrc.Bind, []inputrc.Bind) {varinputrc.Bindvar []inputrc.Bind// Make a sorted list with all keys in the binds map.var []stringfor := range { = append(, ) }// Sort the list of sequences by alphabetical order and length.sort.Slice(, func(, int) bool {iflen([]) == len([]) {return [] < [] }returnlen([]) < len([]) })// Iterate over the sorted list of sequences and find all binds // that match the sequence either by prefix or exactly.for , := range { := strutil.ConvertMeta([]rune())iflen(string()) < len() && strings.HasPrefix(, string()) { = append(, []) }ifstring() == { = [] } }return , }func ( *Engine) ( inputrc.Bind) func() {if .Macro {returnnil }if .Action == "" {returnnil }return .commands[.Action]}// handleEscape is used to override or change the matched command when the escape key has// been pressed: it might exit completion/isearch menus, use the vi-movement-mode, etc.func ( *Engine) ( bool) ( inputrc.Bind, func(), bool) {switch {case .prefixed.Action == "vi-movement-mode":// The vi-movement-mode command always has precedence over // other binds when we are currently using the main keymap. = .prefixedcase ! && .IsEmacs() && .Local() == Isearch:// There is no dedicated "soft-escape" of the incremental-search // mode when in Emacs keymap, so we use the escape key to cancel // the search and return to the main keymap. = inputrc.Bind{Action: "emacs-editing-mode"}core.PopForce(.keys)case !:// When using the local keymap, we simply drop any prefixed // or matched bind, so that the key will be matched against // the main keymap: between both, completion/isearch menus // will likely be cancelled. = inputrc.Bind{} }// Drop what needs to, and resolve the command. .prefixed = inputrc.Bind{}if .Action != "" && !.Macro { = .resolve() }// Drop the escape key in the stackif {core.PopForce(.keys) }return , , }// makeMatch populates currently used binds.func ( *Engine) (, inputrc.Bind) ( bool) { .active = .prefixed = return .prefixed.Action != ""}func ( *Engine) () bool { := .keys.Caller()iflen() == 0 {returnfalse }iflen() > 1 {returnfalse }if [0] != inputrc.Esc {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.