peco.peco/keyseq/keyseq.go
Daisuke Maki 9bbfaac77d Various structural changes
* Compile ahocorosick matcher laizly
* Move key mappings to keyseq, as we do not directly interface them in
  the main peco package
2014-07-06 18:39:24 +09:00

178 lines
3.2 KiB
Go

package keyseq
import (
"fmt"
"strings"
"sync"
"time"
"github.com/nsf/termbox-go"
)
var ErrInSequence = fmt.Errorf("Currently expecting a key sequence")
var ErrNoMatch = fmt.Errorf("Could not match key to any action")
type ModifierKey int
const (
ModNone ModifierKey = iota
ModAlt
ModMax
)
// Key is data in one trie node in the KeySequence
type Key struct {
Modifier ModifierKey // Alt, etc
Key termbox.Key
Ch rune
}
func (kl KeyList) String() string {
list := make([]string, len(kl))
for i := 0; i < len(kl); i++ {
list[i] = kl[i].String()
}
return strings.Join(list, ",")
}
func (m ModifierKey) String() string {
switch m {
case ModAlt:
return "M"
default:
return ""
}
}
func (k Key) String() string {
var s string
if m := k.Modifier.String(); m != "" {
s += m + "-"
}
if k.Key == 0 {
s += string([]rune{k.Ch})
} else {
s += keyToString[k.Key]
}
return s
}
func NewKeyFromKey(k termbox.Key) Key {
return Key{0, k, rune(0)}
}
// KeyList is just the list of keys
type KeyList []Key
func (k Key) Compare(x Key) int {
if k.Modifier < x.Modifier {
return -1
} else if k.Modifier > x.Modifier {
return 1
}
if k.Key < x.Key {
return -1
} else if k.Key > x.Key {
return 1
}
if k.Ch < x.Ch {
return -1
} else if k.Ch > x.Ch {
return 1
}
return 0
}
func (k KeyList) Equals(x KeyList) bool {
if len(k) != len(x) {
return false
}
for i := 0; i < len(k); i++ {
if k[i].Compare(x[i]) != 0 {
return false
}
}
return true
}
type keyseqMatcher interface {
Get(Key) Node
GetList(KeyList) Node
}
type Keyseq struct {
*Matcher
current keyseqMatcher
mutex *sync.Mutex
prevInputTime time.Time
}
func New() *Keyseq {
return &Keyseq{NewMatcher(), nil, &sync.Mutex{}, time.Time{}}
}
func (k *Keyseq) InMiddleOfChain() bool {
return k.current != nil && k.current != k.Matcher
}
func (k *Keyseq) CancelChain() {
k.mutex.Lock()
defer k.mutex.Unlock()
k.setCurrent(k.Matcher)
}
func (k *Keyseq) setCurrent(m keyseqMatcher) {
k.current = m
}
func (k *Keyseq) Current() keyseqMatcher {
if k.current == nil {
k.current = k.Matcher
}
return k.current
}
func (k *Keyseq) AcceptKey(key Key) (interface{}, error) {
// XXX should we return Action instead of interface{}?
k.mutex.Lock()
defer k.mutex.Unlock()
defer func() { k.prevInputTime = time.Now() }()
c := k.Current()
n := c.Get(key)
// nothing matched
if n == nil {
k.setCurrent(k.Matcher)
return nil, ErrNoMatch
}
// Matched node has children. It MAY BE a part of a key sequence,
// but the longest one ALWAYS wins. So for example, if you had
// "C-x,C-n" and "C-x" mapped to something, "C-x" alone will never
// fire any action
if n.HasChildren() {
// Set the current matcher to the matched node, so the next
// AcceptKey matches AFTER the current node
k.setCurrent(n)
return nil, ErrInSequence
}
// If it got here, we should just rest the matcher, and return
// whatever we matched
k.setCurrent(k.Matcher)
// This case should never be true, but we make sure to check
// for it in order to avoid the possibility of a crash
data := n.Value()
if data == nil {
return nil, ErrNoMatch
}
return data.(*nodeData).Value(), nil
}