Skip to content

Commit

Permalink
forward cursor point to c++
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianPommerening committed Jul 17, 2024
1 parent d7c9000 commit da81a52
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 26 deletions.
8 changes: 6 additions & 2 deletions driver/tab_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,14 @@ def _planner_args_completion(prefix, parsed_args, **kwargs):

downward = Path(util.REPO_ROOT_DIR) / "builds" / build / "bin" / "downward"
if downward.exists():
cmd = [str(downward), "--bash-complete", prefix, str(downward)] + search_options
simulated_commandline = [str(downward)] + search_options + [prefix]
comp_line = " ".join(simulated_commandline)
comp_point = str(len(comp_line))
comp_cword = str(len(simulated_commandline) - 1)
cmd = [str(downward), "--bash-complete",
comp_point, comp_line, comp_cword] + simulated_commandline
output = subprocess.check_output(cmd, text=True)
completions += output.split()

else:
tranlator_arguments_path = Path(util.REPO_ROOT_DIR) / "builds" / build / "bin" / "translate" / "arguments.py"
if tranlator_arguments_path.exists():
Expand Down
78 changes: 74 additions & 4 deletions src/search/command_line.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,13 @@ shared_ptr<SearchAlgorithm> parse_cmd_line(
return parse_cmd_line_aux(args);
}

vector<string> complete_args(const string &current_word, const vector<string> &args) {
assert(!args.empty()); // args[0] is always the program name.
const string &last_arg = args.back();
static vector<string> complete_args(
const vector<string> &parsed_args, const string &current_word,
int /*cursor_pos*/) {
assert(!parsed_args.empty()); // args[0] is always the program name.
const string &last_arg = parsed_args.back();
vector<string> suggestions;
if (find(args.begin(), args.end(), "--help") != args.end()) {
if (find(parsed_args.begin(), parsed_args.end(), "--help") != parsed_args.end()) {
suggestions.push_back("--txt2tags");
plugins::Registry registry = plugins::RawRegistry::instance()->construct_registry();
for (const shared_ptr<const plugins::Feature> &feature : registry.get_features()) {
Expand All @@ -200,6 +202,10 @@ vector<string> complete_args(const string &current_word, const vector<string> &a
// no suggestions, integer expected
} else if (last_arg == "--search") {
// suggestions in search string based on current_word
plugins::Registry registry = plugins::RawRegistry::instance()->construct_registry();
for (const shared_ptr<const plugins::Feature> &feature : registry.get_features()) {
suggestions.push_back(feature->get_key() + "(");
}
} else {
// not completing an argument
suggestions.push_back("--help");
Expand All @@ -223,6 +229,70 @@ vector<string> complete_args(const string &current_word, const vector<string> &a
return suggestions;
}

void handle_tab_completion(int argc, const char **argv) {
if (argc < 2 || static_cast<string>(argv[1]) != "--bash-complete") {
return;
}
if (argc < 5) {
input_error(
"The option --bash-complete is only meant to be called "
"internally to generate suggestions for tab completion.\n"
"Usage:\n ./downward --bash-complete "
"$COMP_POINT \"$COMP_LINE\" $COMP_CWORD ${COMP_WORDS[@]}\n"
"where the environment variables have their usual meaning for bash completion:\n"
"$COMP_POINT is the position of the cursor in the command line.\n"
"$COMP_LINE is the current command line.\n"
"$COMP_CWORD is an index into ${COMP_WORDS} of the word under the cursor.\n"
"$COMP_WORDS is the current command line split into words.\n"
);
}
int cursor_pos = parse_int_arg("COMP_POINT", static_cast<string>(argv[2]));
const string command_line = static_cast<string>(argv[3]);
int cursor_word_index = parse_int_arg("COMP_CWORD", static_cast<string>(argv[4]));
vector<string> words;
words.reserve(argc - 5);
for (int i = 5; i < argc; ++i) {
words.emplace_back(argv[i]);
}

vector<string> parsed_args;
string current_word;
int pos = 0;
int end = static_cast<int>(command_line.size());
int num_words = static_cast<int>(words.size());
for (int i = 0; i < num_words; ++i) {
current_word = words[i];
int word_len = static_cast<int>(current_word.size());
if (command_line.substr(pos, word_len) != current_word) {
input_error("Expected '" + current_word + "' in $COMP_LINE at position "
+ to_string(pos));
}
if (i == cursor_word_index) {
if (cursor_pos < 0 || cursor_pos > word_len) {
input_error("Inconsistent information in COMP_LINE and COMP_WORDS");
}
break;
}

// Skip word
pos += word_len;
cursor_pos -= word_len;
parsed_args.push_back(current_word);

// Skip whitespace between words.
while (pos < end && isspace(command_line[pos])) {
++pos;
--cursor_pos;
}
}

for (const string &suggestion : complete_args(parsed_args, current_word, cursor_pos)) {
cout << suggestion << endl;
}
// Do not use exit_with here because it would generate additional output.
exit(0);
}

string usage(const string &progname) {
return "usage: \n" +
progname + " [OPTIONS] --search SEARCH < OUTPUT\n\n"
Expand Down
3 changes: 1 addition & 2 deletions src/search/command_line.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ class SearchAlgorithm;

extern std::shared_ptr<SearchAlgorithm> parse_cmd_line(
int argc, const char **argv, bool is_unit_cost);
extern std::vector<std::string> complete_args(
const std::string &current_word, const std::vector<std::string> &args);
extern void handle_tab_completion(int argc, const char **argv);

extern std::string usage(const std::string &progname);

Expand Down
24 changes: 6 additions & 18 deletions src/search/planner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,12 @@ using utils::ExitCode;

int main(int argc, const char **argv) {
try {
if (static_cast<string>(argv[1]) == "--bash-complete") {
if (argc < 3) {
utils::g_log << "TODO error" << endl;
utils::exit_with(ExitCode::SEARCH_INPUT_ERROR);
}
string current_word = static_cast<string>(argv[2]);
vector<string> args;
args.reserve(argc - 3);
for (int i = 3; i < argc; ++i) {
args.emplace_back(argv[i]);
}
for (const string &suggestion : complete_args(current_word, args)) {
cout << suggestion << endl;
}
// Do not use exit_with here because it will generate additional output.
exit(0);
}

/*
We have to handle tab completion before registering event handlers
because event handlers will print to stdout when the program exits
and everything on stdout counts as a suggestion for tab completion.
*/
handle_tab_completion(argc, argv);
utils::register_event_handlers();

if (argc < 2) {
Expand Down

0 comments on commit da81a52

Please sign in to comment.