From 7ee15dee3eb9b8d0fc49206b9fd9a56e84bd0a1a Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Wed, 11 Jan 2017 10:26:57 +0100 Subject: Multi-key keybindings --- Makefile | 1 + buffer.cpp | 1 + command.h | 1 + config.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------- config.h | 33 ++++++++++++++++++++++-- either.h | 43 +++++++++++++++++++++++++++++++ manager.cpp | 11 +++++--- 7 files changed, 158 insertions(+), 18 deletions(-) create mode 100644 either.h diff --git a/Makefile b/Makefile index 1db70d1..aff6045 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ all: $(TARGET) clean: rm -f *.o $(TARGET) + rm -rf *.dSYM remake: clean make all diff --git a/buffer.cpp b/buffer.cpp index afb1c93..05bbffa 100644 --- a/buffer.cpp +++ b/buffer.cpp @@ -63,6 +63,7 @@ void Buffer::handleCommand(const Command &cmd){ cursor.x=tb.lineLen(cursor.line); tb.erase(cursor.line,cursor.x); } + relayoutScreen(); if(findCursorInScreen().x==-1){ scrollUp(); } else { diff --git a/command.h b/command.h index 5ee7d24..f0b708d 100644 --- a/command.h +++ b/command.h @@ -21,6 +21,7 @@ public: template Command(string cmd,A... a) :cmd(cmd),args{a...}{} + Command(const Command &other) = default; const string& command() const; const vector& arguments() const; diff --git a/config.cpp b/config.cpp index 76cf3a2..8bd86f7 100644 --- a/config.cpp +++ b/config.cpp @@ -1,25 +1,87 @@ +#include #include #include "command.h" #include "config.h" +#include "throw.h" using namespace std; -Keybindings global_keybindings={ - {KEY_CTRL+'Q',{"quit_app"}}, - {KEY_BACKSPACE,{"delete_backward"}}, - {KEY_DELETE,{"delete_forward"}}, - {'\n',{"insert_newline"}}, - {'\t',{"insert_char","\t"}}, - {KEY_RIGHT,{"move_forward"}}, - {KEY_LEFT,{"move_backward"}}, - {KEY_DOWN,{"move_downward"}}, - {KEY_UP,{"move_upward"}}, -}; +KeyInputMachine global_keyinput({ + {{KEY_CTRL+'X',KEY_CTRL+'Q'},{"quit_app"}}, + {{KEY_BACKSPACE},{"delete_backward"}}, + {{KEY_DELETE},{"delete_forward"}}, + {{'\n'},{"insert_newline"}}, + {{'\t'},{"insert_char","\t"}}, + {{KEY_RIGHT},{"move_forward"}}, + {{KEY_LEFT},{"move_backward"}}, + {{KEY_DOWN},{"move_downward"}}, + {{KEY_UP},{"move_upward"}}, +}); class Init{public: Init(){ for(int i=32;i<127;i++){ - global_keybindings.emplace(i,Command("insert_char",string(1,(char)i))); + global_keyinput.insert({i},Command("insert_char",string(1,(char)i))); } }} init_object; + + +KeyInputMachine::TreeNode::~TreeNode(){ + if(cmd)delete cmd; + if(m){ + for(const pair &p : *m){ + if(p.second!=nullptr)delete p.second; + } + } +} + +KeyInputMachine::KeyInputMachine(){} + +KeyInputMachine::KeyInputMachine(const vector,Command>> &list){ + for(const pair,Command> &p : list){ + insert(p.first,p.second); + } +} + +void KeyInputMachine::insert(const vector &keys,const Command &cmd){ + assert(keys.size()>0); + TreeNode *node=&root; + for(int key : keys){ + if(node->m==nullptr){ + if(node->cmd){ + THROW("Ambiguous keybinding!"); + } + node->m=new unordered_map; + } + TreeNode *newnode=new TreeNode; + (*node->m)[key]=newnode; + node=newnode; + } + if(node->cmd!=nullptr)delete node->cmd; + node->cmd=new Command(cmd); +} + +Either KeyInputMachine::input(int key){ + if(current->m==nullptr){ + current=&root; + return {Left(),false}; + } + auto it=current->m->find(key); + if(it==current->m->end()){ + current=&root; + return {Left(),false}; + } + current=it->second; + if(current->cmd!=nullptr){ + Command *cmd=current->cmd; + current=&root; + return {Right(),*cmd}; + } else { + return {Left(),true}; + } +} + +void KeyInputMachine::cancel(){ + current=&root; +} diff --git a/config.h b/config.h index 0f9ab5d..f9de97a 100644 --- a/config.h +++ b/config.h @@ -1,12 +1,41 @@ #pragma once #include +#include #include "command.h" +#include "either.h" + using namespace std; -using Keybindings = unordered_map; +class KeyInputMachine{ + struct TreeNode{ + Command *cmd=nullptr; + unordered_map *m=nullptr; + + ~TreeNode(); + }; + + TreeNode root; + TreeNode *current=&root; + +public: + KeyInputMachine(); + KeyInputMachine(const vector,Command>> &list); + KeyInputMachine(const KeyInputMachine&) = delete; + + //Overwrites if already existent; ambiguous-prefix bindings not permitted + //at the moment + void insert(const vector &keys,const Command &cmd); + + //If Left, then true indicates 'still possibilities'; false indicates 'no more + //possible sequences left, error' + Either input(int key); + + //Cancel the current input combination + void cancel(); +}; -extern Keybindings global_keybindings; +extern KeyInputMachine global_keyinput; diff --git a/either.h b/either.h new file mode 100644 index 0000000..33808a0 --- /dev/null +++ b/either.h @@ -0,0 +1,43 @@ +#pragma once + +struct Left{}; +struct Right{}; + +template +class Either{ + T *left; + U *right; + +public: + Either(Left,const T &l) + :left(new T(l)),right(nullptr){} + + Either(Right,const U &r) + :left(nullptr),right(new U(r)){} + + Either(const Either &other){ + if(other.left)left=new T(*other.left); + if(other.right)right=new U(*other.right); + } + + ~Either(void){ + if(left)delete left; + if(right)delete right; + } + + T fromLeft(void) const { + return *left; + } + + U fromRight(void) const { + return *right; + } + + bool isLeft(void) const { + return (bool)left; + } + + bool isRight(void) const { + return (bool)right; + } +}; diff --git a/manager.cpp b/manager.cpp index 7f9fa89..951d84b 100644 --- a/manager.cpp +++ b/manager.cpp @@ -137,11 +137,14 @@ int Manager::io(){ int key=tgetkey(); - auto it=global_keybindings.find(key); - if(it!=global_keybindings.end()){ - receive(it->second); + Either ret=global_keyinput.input(key); + if(ret.isLeft()){ + if(!ret.fromLeft()){ + bel(); + receive({"error","Unbound key sequence"}); + } } else { - receive({"error","Unbound key: "+to_string(key)}); + receive(ret.fromRight()); } if(should_quit)break; } -- cgit v1.2.3-54-g00ecf