@@ -201,7 +201,7 @@ def get_functions_to_optimize(
201201 elif file is not None :
202202 logger .info ("!lsp|Finding all functions in the file '%s'…" , file )
203203 console .rule ()
204- functions = find_all_functions_in_file (file )
204+ functions : dict [ Path , list [ FunctionToOptimize ]] = find_all_functions_in_file (file )
205205 if only_get_this_function is not None :
206206 split_function = only_get_this_function .split ("." )
207207 if len (split_function ) > 2 :
@@ -224,8 +224,16 @@ def get_functions_to_optimize(
224224 if found_function is None :
225225 if is_lsp :
226226 return functions , 0 , None
227+ found = closest_matching_file_function_name (only_get_this_function , functions )
228+ if found is not None :
229+ file , found_function = found
230+ exit_with_message (
231+ f"Function { only_get_this_function } not found in file { file } \n or the function does not have a 'return' statement or is a property.\n "
232+ f"Did you mean { found_function .qualified_name } instead?"
233+ )
234+
227235 exit_with_message (
228- f"Function { only_function_name } not found in file { file } \n or the function does not have a 'return' statement or is a property"
236+ f"Function { only_get_this_function } not found in file { file } \n or the function does not have a 'return' statement or is a property"
229237 )
230238 functions [file ] = [found_function ]
231239 else :
@@ -259,6 +267,76 @@ def get_functions_within_git_diff(uncommitted_changes: bool) -> dict[str, list[F
259267 return get_functions_within_lines (modified_lines )
260268
261269
270+ def closest_matching_file_function_name (
271+ qualified_fn_to_find : str , found_fns : dict [Path , list [FunctionToOptimize ]]
272+ ) -> tuple [Path , FunctionToOptimize ] | None :
273+ """Find the closest matching function name using Levenshtein distance.
274+
275+ Args:
276+ qualified_fn_to_find: Function name to find in format "Class.function" or "function"
277+ found_fns: Dictionary of file paths to list of functions
278+
279+ Returns:
280+ Tuple of (file_path, function) for closest match, or None if no matches found
281+
282+ """
283+ min_distance = 4
284+ closest_match = None
285+ closest_file = None
286+
287+ qualified_fn_to_find_lower = qualified_fn_to_find .lower ()
288+
289+ # Cache levenshtein_distance locally for improved lookup speed
290+ _levenshtein = levenshtein_distance
291+
292+ for file_path , functions in found_fns .items ():
293+ for function in functions :
294+ # Compare either full qualified name or just function name
295+ fn_name = function .qualified_name .lower ()
296+ # If the absolute length difference is already >= min_distance, skip calculation
297+ if abs (len (qualified_fn_to_find_lower ) - len (fn_name )) >= min_distance :
298+ continue
299+ dist = _levenshtein (qualified_fn_to_find_lower , fn_name )
300+
301+ if dist < min_distance :
302+ min_distance = dist
303+ closest_match = function
304+ closest_file = file_path
305+
306+ if closest_match is not None :
307+ return closest_file , closest_match
308+ return None
309+
310+
311+ def levenshtein_distance (s1 : str , s2 : str ) -> int :
312+ if len (s1 ) > len (s2 ):
313+ s1 , s2 = s2 , s1
314+ len1 = len (s1 )
315+ len2 = len (s2 )
316+ # Use a preallocated list instead of creating a new list every iteration
317+ previous = list (range (len1 + 1 ))
318+ current = [0 ] * (len1 + 1 )
319+
320+ for index2 in range (len2 ):
321+ char2 = s2 [index2 ]
322+ current [0 ] = index2 + 1
323+ for index1 in range (len1 ):
324+ char1 = s1 [index1 ]
325+ if char1 == char2 :
326+ current [index1 + 1 ] = previous [index1 ]
327+ else :
328+ # Fast min calculation without tuple construct
329+ a = previous [index1 ]
330+ b = previous [index1 + 1 ]
331+ c = current [index1 ]
332+ min_val = min (b , a )
333+ min_val = min (c , min_val )
334+ current [index1 + 1 ] = 1 + min_val
335+ # Swap references instead of copying
336+ previous , current = current , previous
337+ return previous [len1 ]
338+
339+
262340def get_functions_inside_a_commit (commit_hash : str ) -> dict [str , list [FunctionToOptimize ]]:
263341 modified_lines : dict [str , list [int ]] = get_git_diff (only_this_commit = commit_hash )
264342 return get_functions_within_lines (modified_lines )
0 commit comments