Skip to content

Commit 848faa5

Browse files
authored
Merge pull request #924 from codeflash-ai/small-fixes
Language update and more helpful error message
2 parents b28521a + ce19abf commit 848faa5

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

codeflash/code_utils/config_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def parse_config_file(
105105
if lsp_mode:
106106
# don't fail in lsp mode if codeflash config is not found.
107107
return {}, config_file_path
108-
msg = f"Could not find the 'codeflash' block in the config file {config_file_path}. Please run 'codeflash init' to create the config file."
108+
msg = f"Could not find the 'codeflash' block in the config file {config_file_path}. Please run 'codeflash init' to add Codeflash config in the pyproject.toml config file."
109109
raise ValueError(msg) from e
110110
assert isinstance(config, dict)
111111

codeflash/discovery/functions_to_optimize.py

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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}\nor 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}\nor the function does not have a 'return' statement or is a property"
236+
f"Function {only_get_this_function} not found in file {file}\nor 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+
262340
def 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

Comments
 (0)