31 #include <readline/readline.h>
32 #include <readline/history.h>
33 #include <readline/keymaps.h>
41 #include <boost/algorithm/string/trim.hpp>
42 #include <boost/tokenizer.hpp>
43 #include <boost/function.hpp>
54 typedef std::vector<std::string> TokensStorage;
59 typedef std::vector<TokensStorage> CompletionsStorage;
64 typedef boost::function<int (int, int)> KeyCallback;
69 typedef std::map<int, KeyCallback> KeysBind;
74 const size_t DefaultHistoryLimit (64);
79 CompletionsStorage Completions;
89 std::map<Keymap, KeysBind> Keymaps;
94 bool KeymapWasSetup (
false);
99 Keymap Earlykeymap (0);
108 char* Generator (
const char* text,
int State);
118 char** UserCompletion (
const char* text,
int start,
int end);
128 int KeyDispatcher (
int Count,
int Key);
135 int StartupHook (
void);
145 template <
typename Container>
146 bool AreTokensEqual (
const Container& Pattern,
const Container& Input) {
147 if (Input.size() > Pattern.size()) {
151 typename Container::const_iterator k (Pattern.begin());
152 typename Container::const_iterator j (Input.begin());
153 for ( ; j != Input.end(); ++k, ++j) {
154 const std::string lPattern = *k;
155 if (lPattern ==
"%file") {
159 const std::string lInput = *j;
160 if (lPattern != lInput) {
168 template <
typename ContainerType>
169 void SplitTokens (
const std::string& Source, ContainerType& Container) {
170 typedef boost::tokenizer<boost::char_separator<char> > TokenizerType;
173 boost::char_separator<char> Separators (
" \t\n");
175 TokenizerType Tokenizer (Source, Separators);
178 for (TokenizerType::const_iterator k (Tokenizer.begin());
179 k != Tokenizer.end(); ++k) {
181 std::string SingleToken (*k);
183 boost::algorithm::trim (SingleToken);
184 Container.push_back (SingleToken);
189 char** UserCompletion (
const char* text,
int start,
int end) {
191 rl_attempted_completion_over = 1;
193 if (Completions.empty() ==
true) {
198 std::string PreInput (rl_line_buffer, start);
199 SplitTokens (PreInput, Tokens);
203 bool FoundPretender (
false);
205 for (CompletionsStorage::const_iterator k (Completions.begin());
206 k != Completions.end(); ++k) {
207 const TokensStorage& lTokenStorage = *k;
208 if (AreTokensEqual (lTokenStorage, Tokens) ==
false) {
212 if (lTokenStorage.size() > Tokens.size()) {
213 FoundPretender =
true;
214 if (lTokenStorage [Tokens.size()] ==
"%file") {
216 return rl_completion_matches (text, rl_filename_completion_function);
221 if (FoundPretender) {
222 return rl_completion_matches (text, Generator);
228 char* Generator (
const char* text,
int State) {
230 static CompletionsStorage::const_iterator Iterator;
233 Iterator = Completions.begin();
234 Length = strlen (text);
237 for ( ; Iterator != Completions.end(); ++Iterator) {
238 const TokensStorage& lCompletion = *Iterator;
239 if (AreTokensEqual (lCompletion, Tokens) ==
false) {
243 if (lCompletion.size() > Tokens.size()) {
244 if (lCompletion [Tokens.size()] ==
"%file") {
248 const char* lCompletionCharStr (lCompletion [Tokens.size()].c_str());
249 if (strncmp (text, lCompletionCharStr, Length) == 0) {
251 const size_t lCompletionSize = strlen (lCompletionCharStr) + 1;
252 char* NewString (static_cast<char*> (malloc (lCompletionSize)));
253 strcpy (NewString, lCompletionCharStr);
267 int KeyDispatcher (
int Count,
int Key ) {
268 std::map< Keymap, KeysBind >::iterator Set (Keymaps.find (rl_get_keymap()));
269 if (Set == Keymaps.end()) {
276 throw std::runtime_error (
"Error selecting a keymap.");
279 (Set->second)[Key] (Count, Key);
284 int StartupHook (
void) {
285 if (KeymapWasSetup) {
286 rl_set_keymap (Earlykeymap);
319 explicit SKeymap (
bool PrintableBound =
false) : keymap (NULL) {
320 if (PrintableBound ==
true) {
322 keymap = rl_make_keymap();
326 keymap = rl_make_bare_keymap();
329 if (keymap == NULL) {
330 throw std::runtime_error (
"Cannot allocate keymap.");
334 Keymaps [keymap] = KeysBind();
342 explicit SKeymap (Keymap Pattern) : keymap (rl_copy_keymap (Pattern)) {
343 if ( keymap == NULL ) {
344 throw std::runtime_error(
"Cannot allocate keymap." );
348 Keymaps [keymap] = KeysBind();
356 Keymaps.erase (keymap);
357 rl_discard_keymap (keymap);
366 void Bind (
int Key, KeyCallback Callback) {
367 Keymaps [keymap][Key] = Callback;
369 if (rl_bind_key_in_map (Key, KeyDispatcher, keymap) != 0) {
371 Keymaps [keymap].erase (Key);
372 throw std::runtime_error (
"Invalid key.");
382 rl_unbind_key_in_map (Key, keymap);
383 Keymaps [keymap].erase (Key);
399 keymap = rl_copy_keymap (rhs.keymap);
411 keymap = rl_copy_keymap (rhs.keymap);
432 HistoryLimit (Limit), HistoryFileName (
""),
433 OriginalCompletion (rl_attempted_completion_function) {
434 rl_startup_hook = StartupHook;
435 rl_attempted_completion_function = UserCompletion;
447 const size_t Limit = DefaultHistoryLimit ) :
448 HistoryLimit( Limit ),
449 HistoryFileName( historyFileName ),
450 OriginalCompletion( rl_attempted_completion_function )
452 rl_startup_hook = StartupHook;
453 rl_attempted_completion_function = UserCompletion;
463 rl_attempted_completion_function = OriginalCompletion;
473 std::string
GetLine (
const std::string& Prompt) {
475 return GetLine (Prompt, Unused);
486 template <
typename Container>
487 std::string
GetLine (
const std::string& Prompt, Container& ReadTokens) {
489 return GetLine (Prompt, ReadTokens, Unused);
501 template <
typename Container>
502 std::string
GetLine (
const std::string& Prompt, Container& ReadTokens,
504 std::string Input (
GetLine (Prompt, BreakOut));
505 SplitTokens (Input, ReadTokens);
517 std::string
GetLine (
const std::string& Prompt,
bool& BreakOut) {
520 char* ReadLine (readline (Prompt.c_str()));
521 if (ReadLine == NULL) {
522 return std::string();
527 std::string Input (ReadLine);
528 free (ReadLine); ReadLine = NULL;
530 boost::algorithm::trim (Input);
531 if (Input.empty() ==
false) {
532 if (history_length == 0
533 || Input != history_list()[ history_length - 1 ]->line) {
534 add_history (Input.c_str());
536 if (history_length >= static_cast<int> (HistoryLimit)) {
537 stifle_history (HistoryLimit);
551 template <
typename ContainerType>
553 for (
int k (0); k < history_length; ++k ) {
554 Container.push_back (history_list()[k]->line);
569 for (
int k (0); k < history_length; ++k) {
570 OS << history_list()[ k ]->line << std::endl;
582 if (FileName.empty() ==
true) {
586 std::ofstream OS (FileName.c_str());
612 while (!getline (IS, OneLine).eof()) {
613 boost::algorithm::trim( OneLine );
614 if ((history_length == 0)
615 || OneLine != history_list()[history_length - 1]->line) {
616 add_history (OneLine.c_str());
619 stifle_history (HistoryLimit);
630 if (FileName.empty() ==
true) {
634 std::ifstream IS (FileName.c_str());
657 template <
typename ContainerType>
660 for (
typename ContainerType::const_iterator k (Container.begin());
661 k != Container.end(); ++k) {
662 std::vector<std::string> OneLine;
663 const std::string& kStr =
static_cast<std::string
> (*k);
665 SplitTokens (kStr, OneLine);
666 Completions.push_back (OneLine);
676 rl_set_keymap (NewKeymap.keymap);
677 KeymapWasSetup =
true;
678 Earlykeymap = NewKeymap.keymap;
687 const size_t HistoryLimit;
692 const std::string HistoryFileName;
697 rl_completion_func_t* OriginalCompletion;
bool LoadHistory(std::istream &IS)
Loads a history from a file stream.
std::string GetLine(const std::string &Prompt, bool &BreakOut)
Gets a single line from a user.
SKeymap & operator=(const SKeymap &rhs)
operator=
~SKeymap()
Frees the allocated keymap.
SReadline(const std::string &historyFileName, const size_t Limit=DefaultHistoryLimit)
Constructs the object, sets the completion function, loads history.
The readline library wrapper.
std::string GetLine(const std::string &Prompt, Container &ReadTokens)
Gets a single line from a user.
void ClearHistory()
Clears the history. Does not affect the file where the previous session history is saved...
void SetKeymap(SKeymap &NewKeymap)
Sets the given keymap.
void RegisterCompletions(const ContainerType &Container)
Allows to register custom completers.
SReadline(const size_t Limit=DefaultHistoryLimit)
Constructs the object, sets the completion function.
void Bind(int Key, KeyCallback Callback)
Binds the given key to a function.
bool SaveHistory(const std::string &FileName)
Saves the history to the given file.
The readline keymap wrapper.
std::string GetLine(const std::string &Prompt)
Gets a single line from a user.
SKeymap(Keymap Pattern)
Creates a new keymap which is a copy of Pattern.
void GetHistory(ContainerType &Container)
Fills the given container with the current history list.
bool LoadHistory(const std::string &FileName)
Loads a history from the given file.
bool SaveHistory(std::ostream &OS)
Saves the history to the given file stream.
SKeymap(bool PrintableBound=false)
Creates a new keymap.
void Unbind(int Key)
Unbinds the given key.
~SReadline()
Saves the session history (if the file name was provided) and destroys the object.
SKeymap(const SKeymap &rhs)
Copy constructor.
std::string GetLine(const std::string &Prompt, Container &ReadTokens, bool &BreakOut)
Gets a single line from a user.