diff --git a/src/command_manager.cc b/src/command_manager.cc index 0cf2cff9..d62fbdc2 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -17,9 +17,10 @@ void CommandManager::register_command(const std::vector& command_na register_command(command_name, command); } -static std::vector split(const std::string& line) +typedef std::vector> TokenList; +static TokenList split(const std::string& line) { - std::vector result; + TokenList result; size_t pos = 0; while (pos != line.length()) @@ -32,7 +33,7 @@ static std::vector split(const std::string& line) while((line[pos] != ' ' or line[pos-1] == '\\') and pos != line.length()) ++pos; - result.push_back(line.substr(token_start, pos - token_start)); + result.push_back(std::make_pair(token_start, pos)); } return result; } @@ -45,16 +46,57 @@ struct command_not_found : runtime_error void CommandManager::execute(const std::string& command_line) { - std::vector tokens = split(command_line); + TokenList tokens = split(command_line); if (tokens.empty()) return; - auto command_it = m_commands.find(tokens[0]); - if (command_it == m_commands.end()) - throw command_not_found(tokens[0]); + std::string command_name = + command_line.substr(tokens[0].first, + tokens[0].second - tokens[0].first); + + auto command_it = m_commands.find(command_name); + if (command_it == m_commands.end()) + throw command_not_found(command_name); + + CommandParameters params; + for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) + { + params.push_back(command_line.substr(it->first, + it->second - it->first)); + } - CommandParameters params(tokens.begin() + 1, tokens.end()); command_it->second(params); } +Completions CommandManager::complete(const std::string& command_line, size_t cursor_pos) +{ + TokenList tokens = split(command_line); + + size_t token_to_complete = -1; + for (size_t i = 0; i < tokens.size(); ++i) + { + if (tokens[i].first < cursor_pos and tokens[i].second >= cursor_pos) + { + token_to_complete = i; + break; + } + } + + if (token_to_complete == 0) // command name completion + { + Completions result(tokens[0].first, cursor_pos); + std::string prefix = command_line.substr(tokens[0].first, + cursor_pos - tokens[0].first); + + for (auto& command : m_commands) + { + if (command.first.substr(0, prefix.length()) == prefix) + result.candidates.push_back(command.first); + } + + return result; + } + return Completions(cursor_pos, cursor_pos); +} + } diff --git a/src/command_manager.hh b/src/command_manager.hh index 2f014838..fc3d81f3 100644 --- a/src/command_manager.hh +++ b/src/command_manager.hh @@ -20,10 +20,21 @@ struct wrong_argument_count : runtime_error typedef std::vector CommandParameters; typedef std::function Command; +struct Completions +{ + CommandParameters candidates; + size_t start; + size_t end; + + Completions(size_t start, size_t end) + : start(start), end(end) {} +}; + class CommandManager { public: void execute(const std::string& command_line); + Completions complete(const std::string& command_line, size_t cursor_pos); void register_command(const std::string& command_name, Command command); void register_command(const std::vector& command_names, Command command); diff --git a/src/main.cc b/src/main.cc index d53a059e..3f65c18f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -88,7 +88,16 @@ void deinit_ncurses() struct prompt_aborted {}; -std::string prompt(const std::string& text) +struct NullCompletion +{ + Completions operator() (const std::string&, size_t cursor_pos) + { + return Completions(cursor_pos, cursor_pos); + } +}; + +std::string prompt(const std::string& text, + std::function completer = NullCompletion()) { int max_x, max_y; getmaxyx(stdscr, max_y, max_x); @@ -116,6 +125,19 @@ std::string prompt(const std::string& text) break; case 27: throw prompt_aborted(); + case '\t': + { + Completions completions = completer(result, result.length()); + if (not completions.candidates.empty()) + { + const std::string& completion = completions.candidates[0]; + move(max_y-1, text.length()); + result = result.substr(0, completions.start) + completion; + addstr(result.c_str()); + refresh(); + } + break; + } default: result += c; addch(c); @@ -213,7 +235,10 @@ void do_command() { try { - command_manager.execute(prompt(":")); + command_manager.execute(prompt(":", std::bind(&CommandManager::complete, + &command_manager, + std::placeholders::_1, + std::placeholders::_2))); } catch (prompt_aborted&) {} }