Skip to content

Commit

Permalink
Add command suggestions (#6688)
Browse files Browse the repository at this point in the history
  • Loading branch information
Descacharrado committed May 13, 2022
1 parent 7a4037b commit 87d5746
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
39 changes: 39 additions & 0 deletions src/formula/string_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,42 @@ std::string vngettext_impl(const char* domain,
const std::string msg = utils::interpolate_variables_into_string(orig, &symbols);
return msg;
}

int edit_distance_approx(const std::string &str_1, const std::string &str_2)
{
int edit_distance = 0;
if(str_1.length() == 0) {
return str_2.length();
}
else if(str_2.length() == 0) {
return str_1.length();
}
else {
int j = 0;
int len_max = std::max(str_1.length(), str_2.length());
for(int i = 0; i < len_max; i++) {
if(str_1[i] != str_2[j]) {
//SWAP
if(str_1[i+1] == str_2[j] && str_1[i] == str_2[j+1]) {
// No need to test the next letter
i++;j++;
}
//ADDITION
else if(str_1[i+1] == str_2[j]) {
j--;
}
//DELETION
else if(str_1[i] == str_2[j+1]) {
i--;
}
// CHANGE (no need to do anything, next letter MAY be successful).
edit_distance++;
if(edit_distance * 100 / std::min(str_1.length(), str_2.length()) > 33) {
break;
}
}
j++;
}
}
return edit_distance;
}
16 changes: 16 additions & 0 deletions src/formula/string_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,19 @@ std::string vngettext_impl(const char* domain,

#define VNGETTEXT(msgid, msgid_plural, count, ...) \
vngettext_impl(GETTEXT_DOMAIN, msgid, msgid_plural, count, __VA_ARGS__)

/**
* Approximately calculates the distance between two strings
*
* Inspired in the Levenshtein distance, but made simpler
* to avoid using recursion and wasting resources.
*
* The consequence is that the function gets "lost"
* after two consecutive differences.
*
* @param str_1 First string to compare
* @param str_2 Second string to compare
*/

int edit_distance_approx(const std::string &str_1, const std::string &str_2);

33 changes: 31 additions & 2 deletions src/map_command_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,39 @@ class map_command_handler
}
else if (help_on_unknown_) {
utils::string_map symbols;
std::string string_user = get_cmd();
int distance = 0;
// Minimum length of the two compared strings.
int len_min = 0;
bool has_command_proposal = false;
// Compare the input with every command (excluding alias).
for(const auto& [key, index] : command_map_) {
// No need to test commands that are not enabled.
if(is_enabled(index)) {
distance = edit_distance_approx(string_user, key);
len_min = std::min(string_user.length(), key.length());
// Maximum of a third of the letters are wrong. The ratio
// between the edit distance and the minimum length, multiplied
// by a hundred gives us the percentage of errors.
if(distance * 100 / len_min < 34) {
symbols["command_proposal"] = key;
has_command_proposal = true;
// If a good enough candidate is found, exit the loop.
break;
}
}
}
symbols["command"] = get_cmd();
symbols["help_command"] = cmd_prefix_ + "help";
print("help", VGETTEXT("Unknown command '$command', try $help_command "
"for a list of available commands.", symbols));
// If a proposal for a command is found, print it
if(has_command_proposal) {
print("help", VGETTEXT("Unknown command '$command', did you mean '$command_proposal'? try $help_command "
"for a list of available commands.", symbols));
}
else {
print("help", VGETTEXT("Unknown command '$command', try $help_command "
"for a list of available commands.", symbols));
}
}
}

Expand Down

0 comments on commit 87d5746

Please sign in to comment.