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