From e9eeb3300c59bca19b8c79c14ed85226d2262c63 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 27 Jul 2015 09:11:20 -0400 Subject: [PATCH] adding tab completion to CLI --- include/fc/rpc/api_connection.hpp | 11 +++++- src/rpc/cli.cpp | 59 +++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/include/fc/rpc/api_connection.hpp b/include/fc/rpc/api_connection.hpp index 5316285..4f632c8 100644 --- a/include/fc/rpc/api_connection.hpp +++ b/include/fc/rpc/api_connection.hpp @@ -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 get_method_names()const + { + std::vector 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 get_method_names( api_id_type local_api_id = 0 )const { return _local_apis[local_api_id]->get_method_names(); } + fc::signal closed; private: - std::vector< std::unique_ptr > _local_apis; + std::vector< std::unique_ptr > _local_apis; std::map< uint64_t, api_id_type > _handle_to_id; std::vector< std::function > _local_callbacks; diff --git a/src/rpc/cli.cpp b/src/rpc/cli.cpp index df2c0e2..96000e5 100644 --- a/src/rpc/cli.cpp +++ b/src/rpc/cli.cpp @@ -30,6 +30,12 @@ namespace fc { namespace rpc { +static std::vector& cli_commands() +{ + static std::vector* cmds = new std::vector(); + 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;