Skip to content

Commit

Permalink
zsh completion with help
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianPommerening committed Jul 26, 2024
1 parent 87c569f commit 1a4ad87
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 28 deletions.
28 changes: 24 additions & 4 deletions TABCOMPLETION.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,31 @@ eval "$(register-python-argcomplete build.py)"
eval "$(register-python-argcomplete translate.py)"

function _downward_complete() {
local IFS=$'\n'
COMPREPLY=( $( "$1" --bash-complete \
"$COMP_POINT" "$COMP_LINE" "$COMP_CWORD" ${COMP_WORDS[@]}))
local IFS=$'\013'
if [[ -n "${ZSH_VERSION-}" ]]; then
local DFS=":"
local completions
local COMP_CWORD=$((CURRENT - 1))
completions=( $( "${words[1]}" --bash-complete \
"$IFS" "$DFS" "$CURSOR" "$BUFFER" "$COMP_CWORD" ${words[@]}))
if [[ $? == 0 ]]; then
_describe "${words[1]}" completions -o nosort
fi
else
local DFS=""
COMPREPLY=( $( "$1" --bash-complete \
"$IFS" "$DFS" "$COMP_POINT" "$COMP_LINE" "$COMP_CWORD" ${COMP_WORDS[@]}))
if [[ $? != 0 ]]; then
unset COMPREPLY
fi
fi
}
complete -o nosort -o default -o bashdefault -F _downward_complete downward

if [[ -n "${ZSH_VERSION-}" ]]; then
compdef _downward_complete downward
else
complete -o nosort -F _downward_complete downward
fi
```

Restart your shell afterwards.
7 changes: 4 additions & 3 deletions driver/tab_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,16 @@ def _call_argcomplete(python_file, comp_line, comp_point):
return _split_argcomplete_ouput(f.read(), entry_separator, help_separator)

def _get_completions_from_downward(downward, options, prefix):
# TODO: entry_separator, help_separator = _get_field_separators(os.environ)
entry_separator, help_separator = _get_field_separators(os.environ)
help_separator = help_separator or ":"
simulated_commandline = [str(downward)] + 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", # TODO: "--ifs", entry_separator, "--dfs", help_separator,
cmd = [str(downward), "--bash-complete", entry_separator, help_separator,
comp_point, comp_line, comp_cword] + simulated_commandline
output = subprocess.check_output(cmd, text=True)
return _split_argcomplete_ouput(output, "\n", ":") # TODO: entry_separator, help_separator)
return _split_argcomplete_ouput(output, entry_separator, help_separator)


def _get_completions_from_translator(translator, options, prefix):
Expand Down
51 changes: 30 additions & 21 deletions src/search/command_line.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,18 +184,18 @@ shared_ptr<SearchAlgorithm> parse_cmd_line(
return parse_cmd_line_aux(args);
}

static vector<string> complete_args(
static vector<pair<string, string>> complete_args(
const vector<string> &parsed_args, const string &current_word,
int cursor_pos) {
string prefix = current_word.substr(0, cursor_pos);
assert(!parsed_args.empty()); // args[0] is always the program name.
const string &last_arg = parsed_args.back();
vector<string> suggestions;
vector<pair<string, string>> suggestions;
if (find(parsed_args.begin(), parsed_args.end(), "--help") != parsed_args.end()) {
suggestions.push_back("--txt2tags");
suggestions.emplace_back("--txt2tags", "");
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());
suggestions.emplace_back(feature->get_key(), feature->get_title());
}
} else if (last_arg == "--internal-plan-file") {
/* Suggest filename starting with current_word.
Expand All @@ -210,22 +210,22 @@ static vector<string> complete_args(
exit(1);
} else {
// not completing an argument
suggestions.push_back("--help");
suggestions.push_back("--search");
suggestions.push_back("--internal-plan-file");
suggestions.push_back("--internal-previous-portfolio-plans");
suggestions.push_back("--if-unit-cost");
suggestions.push_back("--if-non-unit-cost");
suggestions.push_back("--always");
suggestions.emplace_back("--help", "");
suggestions.emplace_back("--search", "");
suggestions.emplace_back("--internal-plan-file", "");
suggestions.emplace_back("--internal-previous-portfolio-plans", "");
suggestions.emplace_back("--if-unit-cost", "");
suggestions.emplace_back("--if-non-unit-cost", "");
suggestions.emplace_back("--always", "");
// remove suggestions not starting with current_word
}

if (!current_word.empty()) {
// Suggest only words that match with current_word
suggestions.erase(
remove_if(suggestions.begin(), suggestions.end(),
[&](const string &value) {
return !value.starts_with(current_word);
[&](const pair<string, string> &value) {
return !value.first.starts_with(current_word);
}), suggestions.end());
}
return suggestions;
Expand Down Expand Up @@ -279,23 +279,27 @@ void handle_tab_completion(int argc, const char **argv) {
if (argc < 2 || string(argv[1]) != "--bash-complete") {
return;
}
if (argc < 5) {
if (argc < 7) {
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 "
"Usage:\n ./downward --bash-complete $IFS $DFS\n"
"$COMP_POINT \"$COMP_LINE\" $COMP_CWORD ${COMP_WORDS[@]}\n"
"where the environment variables have their usual meaning for bash completion:\n"
"$IFS is a character used to separate different suggestions.\n"
"$DFS is a character used within a suggestion to separate the value from its description.\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", argv[2]);
string command_line(argv[3]);
int cursor_word_index = parse_int_arg("COMP_CWORD", argv[4]);
vector<string> words(&argv[5], &argv[argc]);
string entry_separator(argv[2]);
string help_separator(argv[3]);
int cursor_pos = parse_int_arg("COMP_POINT", argv[4]);
string command_line(argv[5]);
int cursor_word_index = parse_int_arg("COMP_CWORD", argv[6]);
vector<string> words(&argv[7], &argv[argc]);
// Sentinel for cases where the cursor is after the last word.
words.push_back("");

Expand All @@ -309,8 +313,13 @@ void handle_tab_completion(int argc, const char **argv) {
int pos_in_word = get_position_in_current_word(
cursor_word_index, command_line, cursor_pos, words);

for (const string &suggestion : complete_args(preceding_words, current_word, pos_in_word)) {
cout << suggestion << endl;
for (const auto &[suggestion, description] : complete_args(
preceding_words, current_word, pos_in_word)) {
cout << suggestion;
if (!description.empty() && !help_separator.empty()) {
cout << help_separator << description;
}
cout << entry_separator;
}
// Do not use exit_with here because it would generate additional output.
exit(0);
Expand Down

0 comments on commit 1a4ad87

Please sign in to comment.