]> git.lizzy.rs Git - micro.git/commitdiff
Preliminary support for key sequences
authorZachary Yedidia <zyedidia@gmail.com>
Wed, 1 Jul 2020 01:25:54 +0000 (21:25 -0400)
committerZachary Yedidia <zyedidia@gmail.com>
Sun, 9 Aug 2020 20:42:03 +0000 (16:42 -0400)
This commit adds support for binding key sequences such as
"<Ctrl-x><Ctrl-c>". This commit does not solve the problem
of global bindings yet, and therefore the command bar doesn't
work properly in this commit.

internal/action/bindings.go
internal/action/bufpane.go
internal/action/events.go
internal/action/keytree.go
internal/action/rawpane.go

index fd57eb9c70968bed99b35231d1e2808295651487..92715f8b11492ea203caeca486c4d1c7f77c7c4e 100644 (file)
@@ -6,6 +6,7 @@ import (
        "io/ioutil"
        "os"
        "path/filepath"
+       "regexp"
        "strings"
        "unicode"
 
@@ -53,14 +54,16 @@ func InitBindings() {
 }
 
 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:
@@ -70,8 +73,36 @@ func BindKey(k, v string) {
        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
@@ -162,6 +193,23 @@ modSearch:
        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) {
@@ -181,14 +229,14 @@ 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
@@ -231,13 +279,13 @@ func UnbindKey(k string) error {
                        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
index 203a5b3a240acd76d70f70c2435d045711cb1b0e..1e18da0f7856e057ebe637cfcb266c835f2f04f7 100644 (file)
@@ -1,6 +1,7 @@
 package action
 
 import (
+       "log"
        "strings"
        "time"
 
@@ -19,14 +20,29 @@ import (
 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 {
@@ -54,7 +70,7 @@ 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
@@ -109,7 +125,7 @@ func BufMapKey(k Event, action string) {
                }
                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 {
@@ -132,27 +148,33 @@ func BufMapKey(k Event, action string) {
                }
                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
@@ -163,9 +185,13 @@ func BufUnmap(k Event) {
 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
@@ -399,9 +425,17 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
 // 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
 }
 
@@ -436,22 +470,36 @@ func (h *BufPane) completeAction(action string) {
 }
 
 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
index 037ba2711545e9c93a2530af35c3cafa32ea336c..3378f5ccb50a41f3454d9c980ff354f9d30e8a67 100644 (file)
@@ -10,7 +10,7 @@ import (
 )
 
 type Event interface {
-       String() string
+       Name() string
 }
 
 // RawEvent is simply an escape code
@@ -20,7 +20,7 @@ type RawEvent struct {
        esc string
 }
 
-func (r RawEvent) String() string {
+func (r RawEvent) Name() string {
        return r.esc
 }
 
@@ -36,7 +36,7 @@ type KeyEvent struct {
        any  bool
 }
 
-func (k KeyEvent) String() string {
+func (k KeyEvent) Name() string {
        if k.any {
                return "<any>"
        }
@@ -82,10 +82,12 @@ type KeySequenceEvent struct {
        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()
 }
@@ -97,7 +99,7 @@ type MouseEvent struct {
        mod tcell.ModMask
 }
 
-func (m MouseEvent) String() string {
+func (m MouseEvent) Name() string {
        mod := ""
        if m.mod&tcell.ModShift != 0 {
                mod = "Shift-"
index ffef5a348d7c7112d5df9ffc62a8b76720a9a093..8bd0078ab44fe6dbb99870443c4d5482e44eec29 100644 (file)
@@ -1,9 +1,14 @@
 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
@@ -30,9 +35,9 @@ func NewKeyTreeNode() *KeyTreeNode {
 // 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
 }
@@ -56,13 +61,13 @@ type KeyTreeCursor struct {
        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 {
@@ -80,7 +85,7 @@ func (k *KeyTreeCursor) MakeClosure(a TreeAction) KeyAction {
 
 // NewKeyTree allocates and returns an empty key tree
 func NewKeyTree() *KeyTree {
-       root := new(KeyTreeNode)
+       root := NewKeyTreeNode()
        tree := new(KeyTree)
 
        tree.root = root
@@ -101,8 +106,8 @@ type ModeConstraint struct {
        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,
@@ -111,9 +116,9 @@ func (k *KeyTree) RegisterKeyBinding(e Event, a KeyAction) {
        })
 }
 
-// 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,
@@ -122,9 +127,9 @@ func (k *KeyTree) RegisterKeyAnyBinding(e Event, a KeyAnyAction) {
        })
 }
 
-// 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,
@@ -135,19 +140,19 @@ func (k *KeyTree) RegisterMouseBinding(e Event, a MouseAction) {
 
 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
                        }
 
@@ -158,8 +163,8 @@ func (k *KeyTree) registerBinding(e Event, a TreeAction) {
 }
 
 // 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.
@@ -168,15 +173,28 @@ func (k *KeyTree) registerBinding(e Event, a TreeAction) {
 // 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 {
@@ -199,8 +217,8 @@ func (k *KeyTree) NextEvent(e Event) (KeyAction, bool) {
        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
index 7541c2885310b2a45179ad90cdae47318ce9a782..bc8ade2a968d3c6e96df17093fee006a4ec05b06 100644 (file)
@@ -38,7 +38,7 @@ func (h *RawPane) HandleEvent(event tcell.Event) {
 
        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()))