diff options
author | tomsmeding <tom.smeding@gmail.com> | 2017-01-11 10:26:57 +0100 |
---|---|---|
committer | tomsmeding <tom.smeding@gmail.com> | 2017-01-11 10:26:57 +0100 |
commit | 7ee15dee3eb9b8d0fc49206b9fd9a56e84bd0a1a (patch) | |
tree | 471956f76f34a2ed98720efe312c9ac0e6ae86ac | |
parent | 6d6f45c1e266ffb3206498301a7a6a1b1b22ec3c (diff) |
Multi-key keybindings
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | buffer.cpp | 1 | ||||
-rw-r--r-- | command.h | 1 | ||||
-rw-r--r-- | config.cpp | 86 | ||||
-rw-r--r-- | config.h | 33 | ||||
-rw-r--r-- | either.h | 43 | ||||
-rw-r--r-- | manager.cpp | 11 |
7 files changed, 158 insertions, 18 deletions
@@ -9,6 +9,7 @@ all: $(TARGET) clean: rm -f *.o $(TARGET) + rm -rf *.dSYM remake: clean make all @@ -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 { @@ -21,6 +21,7 @@ public: template <typename... A> Command(string cmd,A... a) :cmd(cmd),args{a...}{} + Command(const Command &other) = default; const string& command() const; const vector<string>& arguments() const; @@ -1,25 +1,87 @@ +#include <cassert> #include <termio.h> #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<int,TreeNode*> &p : *m){ + if(p.second!=nullptr)delete p.second; + } + } +} + +KeyInputMachine::KeyInputMachine(){} + +KeyInputMachine::KeyInputMachine(const vector<pair<vector<int>,Command>> &list){ + for(const pair<vector<int>,Command> &p : list){ + insert(p.first,p.second); + } +} + +void KeyInputMachine::insert(const vector<int> &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<int,TreeNode*>; + } + TreeNode *newnode=new TreeNode; + (*node->m)[key]=newnode; + node=newnode; + } + if(node->cmd!=nullptr)delete node->cmd; + node->cmd=new Command(cmd); +} + +Either<bool,Command> 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; +} @@ -1,12 +1,41 @@ #pragma once #include <unordered_map> +#include <vector> #include "command.h" +#include "either.h" + using namespace std; -using Keybindings = unordered_map<int,Command>; +class KeyInputMachine{ + struct TreeNode{ + Command *cmd=nullptr; + unordered_map<int,TreeNode*> *m=nullptr; + + ~TreeNode(); + }; + + TreeNode root; + TreeNode *current=&root; + +public: + KeyInputMachine(); + KeyInputMachine(const vector<pair<vector<int>,Command>> &list); + KeyInputMachine(const KeyInputMachine&) = delete; + + //Overwrites if already existent; ambiguous-prefix bindings not permitted + //at the moment + void insert(const vector<int> &keys,const Command &cmd); + + //If Left, then true indicates 'still possibilities'; false indicates 'no more + //possible sequences left, error' + Either<bool,Command> 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 <typename T,typename U> +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<T,U> &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<bool,Command> 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; } |