@@ -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,40 @@ 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 ,
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