@@ -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 += "\n No 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