Skip to content
Snippets Groups Projects
Commit 46b1b5d2 authored by Jan Trávníček's avatar Jan Trávníček
Browse files

enclose readline prompt completion in a class

parent cce69eae
No related branches found
No related tags found
1 merge request!50aql: basic tab-completion using readline
...@@ -68,7 +68,7 @@ Prompt::Prompt ( cli::Environment environment ) : m_history_file ( std::string ( ...@@ -68,7 +68,7 @@ Prompt::Prompt ( cli::Environment environment ) : m_history_file ( std::string (
} }
   
// register readline completion function, pass environment // register readline completion function, pass environment
rl_attempted_completion_function = readline_completion; rl_attempted_completion_function = ReadlinePromptCompletion::readline_completion;
} }
   
Prompt::~Prompt ( ) { Prompt::~Prompt ( ) {
......
...@@ -5,18 +5,11 @@ ...@@ -5,18 +5,11 @@
* Author: Tomas Pecka * Author: Tomas Pecka
*/ */
   
#include <alib/set>
#include <alib/vector>
#include <alib/string>
#include <readline/readline.h>
#include <registry/Registry.h>
#include "ReadlinePromptCompletion.h" #include "ReadlinePromptCompletion.h"
#include "Prompt.h"
   
/* ========================================================================= */ /* ========================================================================= */
   
std::set < std::string > addPrefix ( const std::set < std::string > & collection, const std::string & prefix ) { std::set < std::string > ReadlinePromptCompletion::addPrefix ( const std::set < std::string > & collection, const std::string & prefix ) {
std::set < std::string > res; std::set < std::string > res;
for ( const std::string & s : collection ) for ( const std::string & s : collection )
res.insert ( prefix + s ); res.insert ( prefix + s );
...@@ -24,14 +17,14 @@ std::set < std::string > addPrefix ( const std::set < std::string > & collection ...@@ -24,14 +17,14 @@ std::set < std::string > addPrefix ( const std::set < std::string > & collection
return res; return res;
} }
   
bool startswith ( const std::string & subject, const std::string & prefix ) { bool ReadlinePromptCompletion::startswith ( const std::string & subject, const std::string & prefix ) {
if ( prefix.size ( ) > subject.size ( ) ) if ( prefix.size ( ) > subject.size ( ) )
return false; return false;
   
return subject.substr ( 0, prefix.size ( ) ) == prefix; return subject.substr ( 0, prefix.size ( ) ) == prefix;
} }
   
std::set < std::string > filter_completions ( const std::set < std::string > & choices, const char *text ) { std::set < std::string > ReadlinePromptCompletion::filter_completions ( const std::set < std::string > & choices, const char *text ) {
std::pair < std::set < std::string > :: const_iterator, std::set < std::string > :: const_iterator > range; std::pair < std::set < std::string > :: const_iterator, std::set < std::string > :: const_iterator > range;
const std::string prefix = text; const std::string prefix = text;
   
...@@ -43,124 +36,7 @@ std::set < std::string > filter_completions ( const std::set < std::string > & c ...@@ -43,124 +36,7 @@ std::set < std::string > filter_completions ( const std::set < std::string > & c
   
/* ========================================================================= */ /* ========================================================================= */
   
std::set < std::string > fetchAlgorithms ( const char *text ) { ReadlinePromptCompletion::CompletionContext ReadlinePromptCompletion::context ( const char *text, const int start, const int end ) {
std::set < std::string > res;
const ext::set < ext::pair < std::string, ext::vector < std::string > > >& algos = abstraction::Registry::listAlgorithms ( );
for ( const std::pair < std::string, std::vector < std::string > > & algo : algos ) {
res.insert ( algo.first );
}
return filter_completions ( res, text );
}
std::set < std::string > fetchCommands ( const char *text ) {
return filter_completions ( { "execute", "introspect", "quit", "help" }, text );
}
std::set < std::string > fetchCommandsIntrospect ( const char *text ) {
return filter_completions ( { "algorithms", "overloads", "casts", "datatypes" }, text );
}
std::set < std::string > fetchBindings ( const char *text ) {
return filter_completions ( addPrefix ( Prompt::getPrompt ( ).getEnvironment ( ).getBindingNames ( ), "#" ), text );
}
std::set < std::string > fetchVariables ( const char *text ) {
return filter_completions ( addPrefix ( Prompt::getPrompt ( ).getEnvironment ( ).getVariableNames ( ), "$" ), text );
}
std::set < std::string > fetchFilepath ( const char *text ) {
std::set < std::string > res;
char *str;
int state = 0;
while ( ( str = rl_filename_completion_function ( text, state++ ) ) != nullptr ) {
res.insert ( str );
}
return res;
}
/* ========================================================================= */
/**
* @param text Prefix
* @param state Invocation number of this completion
* @param generator Function that generates the completion-strings
*/
template < typename... CompletionGeneratorFunc >
char * completion_generator ( const char *text, int state, const CompletionGeneratorFunc & ... generators ) {
static std::string prefix;
static std::set < std::string > choices;
static std::set < std::string > :: const_iterator iter;
/* on first call initialize choices */
if ( state == 0 ) {
prefix = text;
choices = std::set < std::string > ( );
/* merge choices from all generators */
const std::vector < std::function < std::set < std::string > ( const char* ) > > gens = { generators... };
for ( const auto & gen : gens ) {
std::set < std::string > tmpres, tmpg = gen ( text );
std::set_union ( std::begin ( choices ), std::end ( choices ),
std::begin ( tmpg ), std::end ( tmpg ),
std::inserter ( tmpres, std::begin ( tmpres ) ) );
choices = tmpres;
}
iter = choices.begin ( );
}
/* iterate through choices */
while ( iter != choices.end ( ) ) {
return strdup ( iter ++ -> c_str ( ) );
}
return nullptr;
}
/* ========================================================================= */
/**
* http://www.delorie.com/gnu/docs/readline/rlman_45.html
* @param state Function invocation number
* @return malloc-allocated string or NULL if no more strings can be generated.
* */
char * complete_algorithm ( const char *text, int state ) {
return completion_generator ( text, state, fetchAlgorithms );
}
char * complete_command ( const char *text, int state ) {
return completion_generator ( text, state, fetchCommands );
}
char * complete_command_introspect ( const char *text, int state ) {
return completion_generator ( text, state, fetchCommandsIntrospect );
}
char * complete_variable ( const char *text, int state ) {
return completion_generator ( text, state, fetchVariables );
}
char * complete_binding ( const char *text, int state ) {
return completion_generator ( text, state, fetchBindings );
}
char * complete_filepath ( const char *text, int state ) {
return completion_generator ( text, state, fetchFilepath );
}
char * complete_filepath_or_variable ( const char *text, int state ) {
return completion_generator ( text, state, fetchFilepath, fetchVariables );
}
/* ========================================================================= */
CompletionContext context ( const char *text, const int start, const int end ) {
std::string line ( rl_line_buffer ); std::string line ( rl_line_buffer );
   
if ( start == 0 ) if ( start == 0 )
...@@ -198,7 +74,7 @@ CompletionContext context ( const char *text, const int start, const int end ) { ...@@ -198,7 +74,7 @@ CompletionContext context ( const char *text, const int start, const int end ) {
   
/* ========================================================================= */ /* ========================================================================= */
   
char ** readline_completion ( const char *text, int start, int end ) { char ** ReadlinePromptCompletion::readline_completion ( const char *text, int start, int end ) {
// for variables and bindings // for variables and bindings
rl_special_prefixes = "$#@"; rl_special_prefixes = "$#@";
rl_attempted_completion_over = 1; rl_attempted_completion_over = 1;
......
...@@ -8,26 +8,152 @@ ...@@ -8,26 +8,152 @@
#ifndef _READLINE_PROMPT_COMPLETION_H #ifndef _READLINE_PROMPT_COMPLETION_H
#define _READLINE_PROMPT_COMPLETION_H #define _READLINE_PROMPT_COMPLETION_H
   
#include <readline/readline.h>
#include <alib/vector> #include <alib/vector>
#include <alib/set> #include <alib/set>
#include <alib/map> #include <alib/map>
#include <alib/string>
#include <registry/Registry.h>
   
#include <readline/readline.h>
#include <environment/Environment.h> #include <environment/Environment.h>
#include <prompt/Prompt.h>
   
enum class CompletionContext { class ReadlinePromptCompletion {
COMMAND, static std::set < std::string > fetchAlgorithms ( const char *text ) {
COMMAND_INTROSPECT, std::set < std::string > res;
const ext::set < ext::pair < std::string, ext::vector < std::string > > >& algos = abstraction::Registry::listAlgorithms ( );
   
ALGORITHM, for ( const std::pair < std::string, std::vector < std::string > > & algo : algos ) {
res.insert ( algo.first );
}
   
FILEPATH, return filter_completions ( res, text );
FILEPATH_OR_VARIABLE, }
   
VARIABLE, static std::set < std::string > fetchCommands ( const char *text ) {
BINDING, return filter_completions ( { "execute", "introspect", "quit", "help" }, text );
}; }
static std::set < std::string > fetchCommandsIntrospect ( const char *text ) {
return filter_completions ( { "algorithms", "overloads", "casts", "datatypes" }, text );
}
static std::set < std::string > fetchBindings ( const char *text ) {
return filter_completions ( addPrefix ( Prompt::getPrompt ( ).getEnvironment ( ).getBindingNames ( ), "#" ), text );
}
static std::set < std::string > fetchVariables ( const char *text ) {
return filter_completions ( addPrefix ( Prompt::getPrompt ( ).getEnvironment ( ).getVariableNames ( ), "$" ), text );
}
static std::set < std::string > fetchFilepath ( const char *text ) {
std::set < std::string > res;
char *str;
int state = 0;
while ( ( str = rl_filename_completion_function ( text, state++ ) ) != nullptr ) {
res.insert ( str );
}
return res;
}
public:
enum class CompletionContext {
COMMAND,
COMMAND_INTROSPECT,
ALGORITHM,
FILEPATH,
FILEPATH_OR_VARIABLE,
VARIABLE,
BINDING,
};
static char** readline_completion ( const char *text, int start, int end );
static CompletionContext context ( const char *text, const int start, const int end );
private:
static std::set < std::string > addPrefix ( const std::set < std::string > & collection, const std::string & prefix );
static bool startswith ( const std::string & subject, const std::string & prefix );
static std::set < std::string > filter_completions ( const std::set < std::string > & choices, const char *text );
   
char** readline_completion ( const char *text, int start, int end ); /**
* @param text Prefix
* @param state Invocation number of this completion
* @param generator Function that generates the completion-strings
*/
template < typename... CompletionGeneratorFunc >
static char * completion_generator ( const char *text, int state, const CompletionGeneratorFunc & ... generators ) {
static std::string prefix;
static std::set < std::string > choices;
static std::set < std::string > :: const_iterator iter;
/* on first call initialize choices */
if ( state == 0 ) {
prefix = text;
choices = std::set < std::string > ( );
/* merge choices from all generators */
const std::vector < std::function < std::set < std::string > ( const char* ) > > gens = { generators... };
for ( const auto & gen : gens ) {
std::set < std::string > tmpres, tmpg = gen ( text );
std::set_union ( std::begin ( choices ), std::end ( choices ),
std::begin ( tmpg ), std::end ( tmpg ),
std::inserter ( tmpres, std::begin ( tmpres ) ) );
choices = tmpres;
}
iter = choices.begin ( );
}
/* iterate through choices */
while ( iter != choices.end ( ) ) {
return strdup ( iter ++ -> c_str ( ) );
}
return nullptr;
}
/**
* http://www.delorie.com/gnu/docs/readline/rlman_45.html
* @param state Function invocation number
* @return malloc-allocated string or NULL if no more strings can be generated.
*/
static char * complete_algorithm ( const char *text, int state ) {
return completion_generator ( text, state, fetchAlgorithms );
}
static char * complete_command ( const char *text, int state ) {
return completion_generator ( text, state, fetchCommands );
}
static char * complete_command_introspect ( const char *text, int state ) {
return completion_generator ( text, state, fetchCommandsIntrospect );
}
static char * complete_variable ( const char *text, int state ) {
return completion_generator ( text, state, fetchVariables );
}
static char * complete_binding ( const char *text, int state ) {
return completion_generator ( text, state, fetchBindings );
}
static char * complete_filepath ( const char *text, int state ) {
return completion_generator ( text, state, fetchFilepath );
}
static char * complete_filepath_or_variable ( const char *text, int state ) {
return completion_generator ( text, state, fetchFilepath, fetchVariables );
}
};
   
#endif /* _READLINE_PROMPT_COMPLETION_H */ #endif /* _READLINE_PROMPT_COMPLETION_H */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment