Gim/internal/input/keymap.go
2026-02-18 17:25:38 -07:00

162 lines
4.5 KiB
Go

package input
import (
"git.gophernest.net/azpect/TextEditor/internal/action"
"git.gophernest.net/azpect/TextEditor/internal/command"
"git.gophernest.net/azpect/TextEditor/internal/motion"
"git.gophernest.net/azpect/TextEditor/internal/operator"
)
type Keymap struct {
motions map[string]action.Motion
operators map[string]action.Operator
actions map[string]action.Action // standalone actions: i.e., 'i', 'a'
}
func NewNormalKeymap() *Keymap {
return &Keymap{
motions: map[string]action.Motion{
"j": motion.MoveDown{Count: 1},
"k": motion.MoveUp{Count: 1},
"h": motion.MoveLeft{Count: 1},
"l": motion.MoveRight{Count: 1},
"G": motion.MoveToBottom{},
"gg": motion.MoveToTop{},
"0": motion.MoveToLineStart{},
"$": motion.MoveToLineEnd{},
"_": motion.MoveToLineContentStart{},
"w": motion.MoveForwardWord{Count: 1},
"e": motion.MoveForwardWordEnd{Count: 1},
"b": motion.MoveBackwardWord{Count: 1},
},
operators: map[string]action.Operator{
"d": operator.DeleteOperator{},
// "c": ChangeOp{},
// "y": YankOp{},
// "p": PasteOp{},
// "s": SubstitueOp{},
// "~": SwapCaseOp{},
},
actions: map[string]action.Action{
"i": action.EnterInsert{},
"a": action.EnterInsertAfter{},
"I": action.EnterInsertLineStart{},
"A": action.EnterInsertLineEnd{},
"o": action.OpenLineBelow{},
"O": action.OpenLineAbove{},
"x": action.DeleteChar{Count: 1},
"ctrl+c": action.Quit{},
":": action.EnterComandMode{},
"v": action.EnterVisualMode{},
"V": action.EnterVisualLineMode{},
"ctrl+v": action.EnterVisualBlockMode{},
"D": action.DeleteToEndOfLine{Count: 1},
},
}
}
func NewVisualKeymap() *Keymap {
return &Keymap{
motions: map[string]action.Motion{
"j": motion.MoveDown{Count: 1},
"k": motion.MoveUp{Count: 1},
"h": motion.MoveLeft{Count: 1},
"l": motion.MoveRight{Count: 1},
"G": motion.MoveToBottom{},
"gg": motion.MoveToTop{},
"0": motion.MoveToLineStart{},
"$": motion.MoveToLineEnd{},
"_": motion.MoveToLineContentStart{},
"w": motion.MoveForwardWord{Count: 1},
"e": motion.MoveForwardWordEnd{Count: 1},
"b": motion.MoveBackwardWord{Count: 1},
},
operators: map[string]action.Operator{
"d": operator.DeleteOperator{},
"x": operator.DeleteOperator{},
// "c": ChangeOp{},
// "y": YankOp{},
// "p": PasteOp{},
// "s": SubstitueOp{},
// "~": SwapCaseOp{},
},
actions: map[string]action.Action{
"ctrl+c": action.Quit{},
// ":": action.EnterComandMode{}, // Different OP
},
}
}
func NewInsertKeymap() *Keymap {
return &Keymap{
motions: map[string]action.Motion{
"down": motion.MoveDown{Count: 1},
"up": motion.MoveUp{Count: 1},
"left": motion.MoveLeft{Count: 1},
"right": motion.MoveRight{Count: 1},
},
operators: map[string]action.Operator{}, // this will likely be empty
actions: map[string]action.Action{
"enter": action.InsertNewline{},
"backspace": action.InsertBackspace{},
"delete": action.InsertDelete{},
"tab": action.InsertTab{},
"ctrl+w": action.InsertDeletePreviousWord{},
"ctrl+c": action.Quit{},
},
}
}
func NewCommandKeymap() *Keymap {
return &Keymap{
motions: map[string]action.Motion{
"left": motion.MoveCommandLeft{},
"right": motion.MoveCommandRight{},
},
operators: map[string]action.Operator{}, // this will likely be empty
actions: map[string]action.Action{
"esc": action.ExitCommandMode{},
"enter": action.CommandExecute{Registry: command.DefaultRegistry},
"backspace": action.CommandBackspace{},
"delete": action.CommandDelete{},
"ctrl+w": action.CommandDeletePreviousWord{},
},
}
}
// Lookup returns what type of binding a key is
func (km *Keymap) Lookup(key string) (kind string, value any) {
if m, ok := km.motions[key]; ok {
return "motion", m
}
if o, ok := km.operators[key]; ok {
return "operator", o
}
if a, ok := km.actions[key]; ok {
return "action", a
}
return "", nil
}
// HasPrefix returns true if any binding starts with this prefix
func (km *Keymap) HasPrefix(prefix string) bool {
for key := range km.motions {
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
return true
}
}
for key := range km.operators {
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
return true
}
}
for key := range km.actions {
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
return true
}
}
return false
}