"io/ioutil"
"os"
"path/filepath"
+ "regexp"
"strings"
"unicode"
}
func BindKey(k, v string) {
- event, ok := findEvent(k)
- if !ok {
- screen.TermMessage(k, "is not a bindable event")
+ event, err := findEvent(k)
+ if err != nil {
+ screen.TermMessage(err)
}
switch e := event.(type) {
case KeyEvent:
BufMapKey(e, v)
+ case KeySequenceEvent:
+ BufMapKey(e, v)
case MouseEvent:
BufMapMouse(e, v)
case RawEvent:
config.Bindings[k] = v
}
-// findEvent will find binding Key 'b' using string 'k'
-func findEvent(k string) (b Event, ok bool) {
+var r = regexp.MustCompile("<(.+?)>")
+
+func findEvents(k string) (b KeySequenceEvent, ok bool, err error) {
+ var events []Event = nil
+ for len(k) > 0 {
+ groups := r.FindStringSubmatchIndex(k)
+
+ if len(groups) > 3 {
+ if events == nil {
+ events = make([]Event, 0, 3)
+ }
+
+ e, ok := findSingleEvent(k[groups[2]:groups[3]])
+ if !ok {
+ return KeySequenceEvent{}, false, errors.New("Invalid event " + k[groups[2]:groups[3]])
+ }
+
+ events = append(events, e)
+
+ k = k[groups[3]+1:]
+ } else {
+ return KeySequenceEvent{}, false, nil
+ }
+ }
+
+ return KeySequenceEvent{events}, true, nil
+}
+
+// findSingleEvent will find binding Key 'b' using string 'k'
+func findSingleEvent(k string) (b Event, ok bool) {
modifiers := tcell.ModNone
// First, we'll strip off all the modifiers in the name and add them to the
return KeyEvent{}, false
}
+func findEvent(k string) (Event, error) {
+ var event Event
+ event, ok, err := findEvents(k)
+ if err != nil {
+ return nil, err
+ }
+
+ if !ok {
+ event, ok = findSingleEvent(k)
+ if !ok {
+ return nil, errors.New(k + " is not a bindable event")
+ }
+ }
+
+ return event, nil
+}
+
// TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
// Returns true if the keybinding already existed and a possible error
func TryBindKey(k, v string, overwrite bool) (bool, error) {
return false, errors.New("Error reading bindings.json: " + err.Error())
}
- key, ok := findEvent(k)
- if !ok {
- return false, errors.New("Invalid event " + k)
+ key, err := findEvent(k)
+ if err != nil {
+ return false, err
}
found := false
for ev := range parsed {
- if e, ok := findEvent(ev); ok {
+ if e, err := findEvent(ev); err == nil {
if e == key {
if overwrite {
parsed[ev] = v
return errors.New("Error reading bindings.json: " + err.Error())
}
- key, ok := findEvent(k)
- if !ok {
- return errors.New("Invalid event " + k)
+ key, err := findEvent(k)
+ if err != nil {
+ return err
}
for ev := range parsed {
- if e, ok := findEvent(ev); ok {
+ if e, err := findEvent(ev); err == nil {
if e == key {
delete(parsed, ev)
break
package action
import (
+ "log"
"strings"
"time"
type BufKeyAction func(*BufPane) bool
type BufMouseAction func(*BufPane, *tcell.EventMouse) bool
+var BufBindings *KeyTree
var BufKeyBindings map[Event]BufKeyAction
var BufKeyStrings map[Event]string
var BufMouseBindings map[MouseEvent]BufMouseAction
+func BufKeyActionGeneral(a BufKeyAction) PaneKeyAction {
+ return func(p Pane) bool {
+ return a(p.(*BufPane))
+ }
+}
+
+func BufMouseActionGeneral(a BufMouseAction) PaneMouseAction {
+ return func(p Pane, me *tcell.EventMouse) bool {
+ return a(p.(*BufPane), me)
+ }
+}
+
func init() {
BufKeyBindings = make(map[Event]BufKeyAction)
BufKeyStrings = make(map[Event]string)
BufMouseBindings = make(map[MouseEvent]BufMouseAction)
+
+ BufBindings = NewKeyTree()
}
func LuaAction(fn string) func(*BufPane) bool {
// BufMapKey maps a key event to an action
func BufMapKey(k Event, action string) {
- BufKeyStrings[k] = action
+ // BufKeyStrings[k] = action
var actionfns []func(*BufPane) bool
var names []string
var types []byte
}
actionfns = append(actionfns, afn)
}
- BufKeyBindings[k] = func(h *BufPane) bool {
+ bufAction := func(h *BufPane) bool {
cursors := h.Buf.GetCursors()
success := true
for i, a := range actionfns {
}
return true
}
+
+ BufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(bufAction))
}
// BufMapMouse maps a mouse event to an action
func BufMapMouse(k MouseEvent, action string) {
if f, ok := BufMouseActions[action]; ok {
- BufMouseBindings[k] = f
+ BufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
+ // BufMouseBindings[k] = f
} else {
- delete(BufMouseBindings, k)
+ // TODO
+ // delete(BufMouseBindings, k)
+ // BufMapKey(k, action)
BufMapKey(k, action)
}
}
// BufUnmap unmaps a key or mouse event from any action
func BufUnmap(k Event) {
- delete(BufKeyBindings, k)
- delete(BufKeyStrings, k)
-
- switch e := k.(type) {
- case MouseEvent:
- delete(BufMouseBindings, e)
- }
+ // TODO
+ // delete(BufKeyBindings, k)
+ // delete(BufKeyStrings, k)
+ //
+ // switch e := k.(type) {
+ // case MouseEvent:
+ // delete(BufMouseBindings, e)
+ // }
}
// The BufPane connects the buffer and the window
type BufPane struct {
display.BWindow
+ // Buf is the buffer this BufPane views
Buf *buffer.Buffer
+ // Bindings stores the association of key events and actions
+ Bindings *KeyTree
- Cursor *buffer.Cursor // the active cursor
+ // Cursor is the currently active buffer cursor
+ Cursor *buffer.Cursor
// Since tcell doesn't differentiate between a mouse release event
// and a mouse move event with no keys pressed, we need to keep
// DoKeyEvent executes a key event by finding the action it is bound
// to and executing it (possibly multiple times for multiple cursors)
func (h *BufPane) DoKeyEvent(e Event) bool {
- if action, ok := BufKeyBindings[e]; ok {
- return action(h)
+ action, more := BufBindings.NextEvent(e, nil)
+ log.Println("Next event", e, more)
+ if action != nil && !more {
+ action(h)
+ BufBindings.ResetEvents()
+ } else if action == nil && !more {
+ BufBindings.ResetEvents()
}
+ // if action, ok := BufKeyBindings[e]; ok {
+ // return action(h)
+ // }
return false
}
}
func (h *BufPane) HasKeyEvent(e Event) bool {
- _, ok := BufKeyBindings[e]
- return ok
+ // TODO
+ return true
+ // _, ok := BufKeyBindings[e]
+ // return ok
}
// DoMouseEvent executes a mouse event by finding the action it is bound
// to and executing it
func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
- if action, ok := BufMouseBindings[e]; ok {
- if action(h, te) {
+ log.Println("DOMOUSEEVENT")
+ action, _ := BufBindings.NextEvent(e, te)
+ if action != nil {
+ if action(h) {
h.Relocate()
}
+ BufBindings.ResetEvents()
return true
- } else if h.HasKeyEvent(e) {
- return h.DoKeyEvent(e)
}
+ // TODO
return false
+
+ // if action, ok := BufMouseBindings[e]; ok {
+ // if action(h, te) {
+ // h.Relocate()
+ // }
+ // return true
+ // } else if h.HasKeyEvent(e) {
+ // return h.DoKeyEvent(e)
+ // }
+ // return false
}
// DoRuneInsert inserts a given rune into the current buffer
)
type Event interface {
- String() string
+ Name() string
}
// RawEvent is simply an escape code
esc string
}
-func (r RawEvent) String() string {
+func (r RawEvent) Name() string {
return r.esc
}
any bool
}
-func (k KeyEvent) String() string {
+func (k KeyEvent) Name() string {
if k.any {
return "<any>"
}
keys []Event
}
-func (k KeySequenceEvent) String() string {
+func (k KeySequenceEvent) Name() string {
buf := bytes.Buffer{}
for _, e := range k.keys {
- buf.WriteString(e.String())
+ buf.WriteByte('<')
+ buf.WriteString(e.Name())
+ buf.WriteByte('>')
}
return buf.String()
}
mod tcell.ModMask
}
-func (m MouseEvent) String() string {
+func (m MouseEvent) Name() string {
mod := ""
if m.mod&tcell.ModShift != 0 {
mod = "Shift-"
package action
-type KeyAction func(Pane) bool
-type MouseAction func(Pane, *MouseEvent) bool
+import (
+ "log"
-type KeyAnyAction func(Pane, []KeyEvent) bool
+ "github.com/zyedidia/tcell"
+)
+
+type PaneKeyAction func(Pane) bool
+type PaneMouseAction func(Pane, *tcell.EventMouse) bool
+type PaneKeyAnyAction func(Pane, []KeyEvent) bool
// A KeyTreeNode stores a single node in the KeyTree (trie). The
// children are stored as a map, and any node may store a list of
// the action to be active.
type TreeAction struct {
// only one of these can be non-nil
- action KeyAction
- any KeyAnyAction
- mouse MouseAction
+ action PaneKeyAction
+ any PaneKeyAnyAction
+ mouse PaneMouseAction
modes []ModeConstraint
}
node *KeyTreeNode
wildcards []KeyEvent
- mouseInfo *MouseEvent
+ mouseInfo *tcell.EventMouse
}
// MakeClosure uses the information stored in a key tree cursor to construct
-// a KeyAction from a TreeAction (which may have a KeyAction, MouseAction,
+// a PaneKeyAction from a TreeAction (which may have a PaneKeyAction, PaneMouseAction,
// or AnyAction)
-func (k *KeyTreeCursor) MakeClosure(a TreeAction) KeyAction {
+func (k *KeyTreeCursor) MakeClosure(a TreeAction) PaneKeyAction {
if a.action != nil {
return a.action
} else if a.any != nil {
// NewKeyTree allocates and returns an empty key tree
func NewKeyTree() *KeyTree {
- root := new(KeyTreeNode)
+ root := NewKeyTreeNode()
tree := new(KeyTree)
tree.root = root
disabled bool
}
-// RegisterKeyBinding registers a KeyAction with an Event.
-func (k *KeyTree) RegisterKeyBinding(e Event, a KeyAction) {
+// RegisterKeyBinding registers a PaneKeyAction with an Event.
+func (k *KeyTree) RegisterKeyBinding(e Event, a PaneKeyAction) {
k.registerBinding(e, TreeAction{
action: a,
any: nil,
})
}
-// RegisterKeyAnyBinding registers a KeyAnyAction with an Event.
+// RegisterKeyAnyBinding registers a PaneKeyAnyAction with an Event.
// The event should contain an "any" event.
-func (k *KeyTree) RegisterKeyAnyBinding(e Event, a KeyAnyAction) {
+func (k *KeyTree) RegisterKeyAnyBinding(e Event, a PaneKeyAnyAction) {
k.registerBinding(e, TreeAction{
action: nil,
any: a,
})
}
-// RegisterMouseBinding registers a MouseAction with an Event.
+// RegisterMouseBinding registers a PaneMouseAction with an Event.
// The event should contain a mouse event.
-func (k *KeyTree) RegisterMouseBinding(e Event, a MouseAction) {
+func (k *KeyTree) RegisterMouseBinding(e Event, a PaneMouseAction) {
k.registerBinding(e, TreeAction{
action: nil,
any: nil,
func (k *KeyTree) registerBinding(e Event, a TreeAction) {
switch ev := e.(type) {
- case *KeyEvent, *MouseEvent:
- n, ok := k.root.children[e]
+ case KeyEvent, MouseEvent:
+ newNode, ok := k.root.children[e]
if !ok {
- newNode := NewKeyTreeNode()
+ newNode = NewKeyTreeNode()
k.root.children[e] = newNode
}
- n.actions = append(n.actions, a)
- case *KeySequenceEvent:
+ newNode.actions = append(newNode.actions, a)
+ case KeySequenceEvent:
n := k.root
for _, key := range ev.keys {
newNode, ok := n.children[key]
if !ok {
- newNode := NewKeyTreeNode()
+ newNode = NewKeyTreeNode()
n.children[key] = newNode
}
}
// NextEvent returns the action for the current sequence where e is the next
-// event. Even if the action was registered as a KeyAnyAction or MouseAction,
-// it will be returned as a KeyAction closure where the appropriate arguments
+// event. Even if the action was registered as a PaneKeyAnyAction or PaneMouseAction,
+// it will be returned as a PaneKeyAction closure where the appropriate arguments
// have been provided.
// If no action is associated with the given Event, or mode constraints are not
// met for that action, nil is returned.
// bindings associated with further sequences starting with this event. The
// calling function can decide what to do about the conflict (e.g. use a
// timeout).
-func (k *KeyTree) NextEvent(e Event) (KeyAction, bool) {
+func (k *KeyTree) NextEvent(e Event, mouse *tcell.EventMouse) (PaneKeyAction, bool) {
n := k.cursor.node
c, ok := n.children[e]
+ log.Println("NEXT EVENT", e, len(n.children), ok)
+
if !ok {
return nil, false
}
more := len(c.children) > 0
+ k.cursor.node = c
+
+ switch ev := e.(type) {
+ case KeyEvent:
+ if ev.any {
+ k.cursor.wildcards = append(k.cursor.wildcards, ev)
+ }
+ case MouseEvent:
+ k.cursor.mouseInfo = mouse
+ }
+
if len(c.actions) > 0 {
// check if actions are active
for _, a := range c.actions {
return nil, more
}
-// Reset sets the current sequence back to the initial value.
-func (k *KeyTree) Reset() {
+// ResetEvents sets the current sequence back to the initial value.
+func (k *KeyTree) ResetEvents() {
k.cursor.node = k.root
k.cursor.wildcards = []KeyEvent{}
k.cursor.mouseInfo = nil
e, err := ConstructEvent(event)
if err == nil {
- h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %s", e.String()))
+ h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %s", e.Name()))
}
h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %q\n", event.EscSeq()))