adding tab completion to CLI
This commit is contained in:
parent
d79855b491
commit
e9eeb3300c
2 changed files with 69 additions and 1 deletions
|
|
@ -93,6 +93,13 @@ namespace fc {
|
|||
|
||||
fc::api_connection& get_connection(){ auto tmp = _api_connection.lock(); FC_ASSERT( tmp, "connection closed"); return *tmp; }
|
||||
|
||||
std::vector<std::string> get_method_names()const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
result.reserve( _by_name.size() );
|
||||
for( auto& m : _by_name ) result.push_back(m.first);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct api_visitor;
|
||||
|
|
@ -221,9 +228,11 @@ namespace fc {
|
|||
return _local_callbacks.size() - 1;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_method_names( api_id_type local_api_id = 0 )const { return _local_apis[local_api_id]->get_method_names(); }
|
||||
|
||||
fc::signal<void()> closed;
|
||||
private:
|
||||
std::vector< std::unique_ptr<generic_api> > _local_apis;
|
||||
std::vector< std::unique_ptr<generic_api> > _local_apis;
|
||||
std::map< uint64_t, api_id_type > _handle_to_id;
|
||||
std::vector< std::function<variant(const variants&)> > _local_callbacks;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@
|
|||
|
||||
namespace fc { namespace rpc {
|
||||
|
||||
static std::vector<std::string>& cli_commands()
|
||||
{
|
||||
static std::vector<std::string>* cmds = new std::vector<std::string>();
|
||||
return *cmds;
|
||||
}
|
||||
|
||||
cli::~cli()
|
||||
{
|
||||
if( _run_complete.valid() )
|
||||
|
|
@ -55,6 +61,7 @@ void cli::send_notice( uint64_t callback_id, variants args /* = variants() */ )
|
|||
|
||||
void cli::start()
|
||||
{
|
||||
cli_commands() = get_method_names(0);
|
||||
_run_complete = fc::async( [&](){ run(); } );
|
||||
}
|
||||
|
||||
|
|
@ -118,6 +125,55 @@ void cli::run()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
char * dupstr (const char* s) {
|
||||
char *r;
|
||||
|
||||
r = (char*) malloc ((strlen (s) + 1));
|
||||
strcpy (r, s);
|
||||
return (r);
|
||||
}
|
||||
|
||||
char* my_generator(const char* text, int state)
|
||||
{
|
||||
static int list_index, len;
|
||||
const char *name;
|
||||
|
||||
if (!state) {
|
||||
list_index = 0;
|
||||
len = strlen (text);
|
||||
}
|
||||
|
||||
auto& cmd = cli_commands();
|
||||
|
||||
while( list_index < cmd.size() )
|
||||
{
|
||||
name = cmd[list_index].c_str();
|
||||
list_index++;
|
||||
|
||||
if (strncmp (name, text, len) == 0)
|
||||
return (dupstr(name));
|
||||
}
|
||||
|
||||
/* If no names matched, then return NULL. */
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
|
||||
static char** cli_completion( const char * text , int start, int end)
|
||||
{
|
||||
char **matches;
|
||||
matches = (char **)NULL;
|
||||
|
||||
if (start == 0)
|
||||
matches = rl_completion_matches ((char*)text, &my_generator);
|
||||
else
|
||||
rl_bind_key('\t',rl_abort);
|
||||
|
||||
return (matches);
|
||||
}
|
||||
|
||||
|
||||
void cli::getline( const fc::string& prompt, fc::string& line)
|
||||
{
|
||||
// getting file descriptor for C++ streams is near impossible
|
||||
|
|
@ -134,6 +190,8 @@ void cli::getline( const fc::string& prompt, fc::string& line)
|
|||
if( _isatty( _fileno( stdin ) ) )
|
||||
#endif
|
||||
{
|
||||
rl_attempted_completion_function = cli_completion;
|
||||
|
||||
static fc::thread getline_thread("getline");
|
||||
getline_thread.async( [&](){
|
||||
char* line_read = nullptr;
|
||||
|
|
@ -141,6 +199,7 @@ void cli::getline( const fc::string& prompt, fc::string& line)
|
|||
line_read = readline(prompt.c_str());
|
||||
if( line_read == nullptr )
|
||||
FC_THROW_EXCEPTION( fc::eof_exception, "" );
|
||||
rl_bind_key( '\t', rl_complete );
|
||||
if( *line_read )
|
||||
add_history(line_read);
|
||||
line = line_read;
|
||||
|
|
|
|||
Loading…
Reference in a new issue