mirror of
https://github.com/zyedidia/micro.git
synced 2026-02-05 14:40:20 +09:00
Preliminary support for key sequences
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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-"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()))
|
||||
|
||||
Reference in New Issue
Block a user