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{}, "^": motion.MoveToLineContentStart{}, "|": motion.MoveToColumn{Count: 0}, "w": motion.MoveForwardWord{Count: 1}, "W": motion.MoveForwardWORD{Count: 1}, "e": motion.MoveForwardWordEnd{Count: 1}, "E": motion.MoveForwardWORDEnd{Count: 1}, "b": motion.MoveBackwardWord{Count: 1}, "ctrl+u": motion.ScrollUpHalfPage{}, "ctrl+d": motion.ScrollDownHalfPage{}, }, operators: map[string]action.Operator{ "d": operator.DeleteOperator{}, "y": operator.YankOperator{}, "c": operator.ChangeOperator{}, // TODO: Finish implementing // "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}, ":": action.EnterComandMode{}, "v": action.EnterVisualMode{}, "V": action.EnterVisualLineMode{}, "ctrl+v": action.EnterVisualBlockMode{}, "D": action.DeleteToEndOfLine{Count: 1}, "C": action.ChangeToEndOfLine{Count: 1}, "s": action.SubstituteChar{Count: 1}, "S": action.SubstituteLine{Count: 1}, "p": action.Paste{Count: 1}, "P": action.PasteBefore{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{}, "^": motion.MoveToLineContentStart{}, "|": motion.MoveToColumn{Count: 0}, "w": motion.MoveForwardWord{Count: 1}, "W": motion.MoveForwardWORD{Count: 1}, "e": motion.MoveForwardWordEnd{Count: 1}, "E": motion.MoveForwardWORDEnd{Count: 1}, "b": motion.MoveBackwardWord{Count: 1}, }, operators: map[string]action.Operator{ "d": operator.DeleteOperator{}, "x": operator.DeleteOperator{}, "y": operator.YankOperator{}, "c": operator.ChangeOperator{}, }, actions: map[string]action.Action{ "p": action.VisualPaste{Count: 1}, // ":": 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{}, }, } } 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 }