diff options
| -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;  	} | 
