Skip to content

Commit 44ef6ce

Browse files
committed
Introduce more advanced method of commands comparison
1 parent 275d004 commit 44ef6ce

File tree

1 file changed

+40
-5
lines changed

1 file changed

+40
-5
lines changed

invoke/program.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -999,13 +999,11 @@ def print_columns(
999999
print("")
10001000

10011001
def _possible_commands_msg(self, unknown_cmd: str) -> str:
1002-
try:
1002+
all_tasks = {}
1003+
if hasattr(self, "scoped_collection"):
10031004
all_tasks = self.scoped_collection.task_names
1004-
except AttributeError:
1005-
all_tasks = {}
1006-
10071005
possible_cmds = list(all_tasks.keys())
1008-
suggestions = difflib.get_close_matches(
1006+
suggestions = _get_best_match(
10091007
unknown_cmd, possible_cmds, n=3, cutoff=0.7
10101008
)
10111009
output_message = f"'{unknown_cmd}' is not an invoke command. "
@@ -1017,3 +1015,40 @@ def _possible_commands_msg(self, unknown_cmd: str) -> str:
10171015
else:
10181016
output_message += "\nNo suggestions was found.\n"
10191017
return output_message
1018+
1019+
1020+
def _get_best_match(
1021+
word: str,
1022+
possibilities: List[str],
1023+
n: int = 3,
1024+
cutoff: float = 0.7
1025+
) -> List[str]:
1026+
"""Return a list of the top `n` best-matching commands for a given word.
1027+
1028+
This function accounts for dot-separated commands by normalizing them—
1029+
splitting them into parts, sorting them alphabetically, and rejoining them.
1030+
This allows for matching commands that contain the same elements but in
1031+
different orders.
1032+
1033+
For example, 'task1.task2' and 'task2.task1' will have a similarity score
1034+
of 0.98.
1035+
"""
1036+
normalized_unknown_cmd = ".".join(sorted(word.split('.')))
1037+
matches = []
1038+
for cmd in possibilities:
1039+
normalized_cmd = ".".join(sorted(cmd.split('.')))
1040+
similarity_normalized = difflib.SequenceMatcher(
1041+
None,
1042+
normalized_unknown_cmd,
1043+
normalized_cmd
1044+
).ratio()
1045+
similarity_raw = difflib.SequenceMatcher(None, word, cmd).ratio()
1046+
# The idea here is to decrease the similarity score if we have
1047+
# reordered the given word
1048+
similarity = max(similarity_normalized * 0.98, similarity_raw)
1049+
if similarity >= cutoff:
1050+
matches.append((similarity, cmd))
1051+
1052+
matches.sort(reverse=True)
1053+
1054+
return [match[1] for match in matches[:n]]

0 commit comments

Comments
 (0)