From 797b40d05e7c9440e76340de7b3b55b3f879e774 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Sun, 27 Apr 2025 16:32:09 -0400 Subject: [PATCH 1/2] Update [ghstack-poisoned] --- codemcp/code_command.py | 22 ++++++++++++++++---- codemcp/tools/edit_file.py | 40 ++++++++++++++++++++++++++++++++----- codemcp/tools/write_file.py | 20 +++++++++++++++++-- 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/codemcp/code_command.py b/codemcp/code_command.py index 6789bbb..a042df4 100644 --- a/codemcp/code_command.py +++ b/codemcp/code_command.py @@ -8,6 +8,7 @@ import tomli from .common import normalize_file_path, truncate_output_content +from .file_utils import async_open_text from .git import commit_changes, get_repository_root, is_git_repository from .shell import run_command @@ -218,14 +219,15 @@ async def run_code_command( return f"Error: {error_msg}" -async def run_formatter_without_commit(file_path: str) -> Tuple[bool, str]: +async def run_formatter_without_commit(file_path: str) -> Tuple[bool, str, str]: """Run the formatter on a specific file without performing pre/post commit operations. Args: file_path: Absolute path to the file to format Returns: - A tuple containing (success_status, message) + A tuple containing (success_status, message, post_format_content) + The post_format_content will be an empty string if formatting failed Raises: Propagates any unexpected errors during formatting @@ -236,7 +238,7 @@ async def run_formatter_without_commit(file_path: str) -> Tuple[bool, str]: # Get the format command from config - this is the only expected failure mode format_command = get_command_from_config(project_dir, "format") if not format_command: - return False, "No format command configured in codemcp.toml" + return False, "No format command configured in codemcp.toml", "" # Use relative path from project_dir for the formatting command rel_path = os.path.relpath(file_path, project_dir) @@ -251,6 +253,18 @@ async def run_formatter_without_commit(file_path: str) -> Tuple[bool, str]: text=True, ) + # Read the file after formatting to get the current content + post_format_content = "" + if os.path.exists(file_path): + try: + post_format_content = await async_open_text(file_path, encoding="utf-8") + except Exception as e: + logging.warning(f"Failed to read formatted file: {e}") + # If we get here, the formatter ran successfully truncated_stdout = truncate_output_content(result.stdout, prefer_end=True) - return True, f"File formatted successfully:\n{truncated_stdout}" + return ( + True, + f"File formatted successfully:\n{truncated_stdout}", + post_format_content, + ) diff --git a/codemcp/tools/edit_file.py b/codemcp/tools/edit_file.py index 081947a..0d584e1 100644 --- a/codemcp/tools/edit_file.py +++ b/codemcp/tools/edit_file.py @@ -776,13 +776,20 @@ async def edit_file_content( # Try to run the formatter on the file format_message = "" - formatter_success, formatter_output = await run_formatter_without_commit( - full_file_path - ) + post_format_content = updated_file + ( + formatter_success, + formatter_output, + post_format_content, + ) = await run_formatter_without_commit(full_file_path) if formatter_success: logger.info(f"Auto-formatted {full_file_path}") if formatter_output.strip(): - format_message = "\nAuto-formatted the file" + # If the file was actually changed by the formatter + if post_format_content and post_format_content != updated_file: + # Update the content in memory (it's already updated on disk) + updated_file = post_format_content + format_message = "\nAuto-formatted the file (snippet below shows post-formatting content)" else: # Only log warning if there was actually a format command configured but it failed if not "No format command configured" in formatter_output: @@ -791,7 +798,30 @@ async def edit_file_content( ) # Generate a snippet of the edited file to show in the response - snippet = get_edit_snippet(content, old_string, new_string) + # Use the post-formatting content for the snippet + if post_format_content and post_format_content != updated_file: + # When the file was changed by formatting, show the post-format version + # Since we don't have direct diff information between formatted and unformatted, + # we'll show a relevant portion of the file + lines = post_format_content.split("\n") + context_lines = 3 + + # Try to find the relevant section based on the edit + search_text = new_string[ + :40 + ] # Use part of the new string to locate where the edit was + for i, line in enumerate(lines): + if search_text in line: + start = max(0, i - context_lines) + end = min(len(lines), i + context_lines + 1) + snippet = "\n".join(lines[start:end]) + break + else: + # If we can't find the edit point, just show the first few lines + snippet = "\n".join(lines[: min(7, len(lines))]) + else: + # When no formatting changes, use the normal edit snippet + snippet = get_edit_snippet(content, old_string, new_string) # Commit the changes git_message = "" diff --git a/codemcp/tools/write_file.py b/codemcp/tools/write_file.py index 3efa8f4..2877253 100644 --- a/codemcp/tools/write_file.py +++ b/codemcp/tools/write_file.py @@ -65,11 +65,27 @@ async def write_file_content( # Try to run the formatter on the file format_message = "" - formatter_success, formatter_output = await run_formatter_without_commit(file_path) + post_format_content = content + ( + formatter_success, + formatter_output, + post_format_content, + ) = await run_formatter_without_commit(file_path) if formatter_success: logging.info(f"Auto-formatted {file_path}") if formatter_output.strip(): - format_message = f"\nAuto-formatted the file" + # If the file was actually changed by the formatter + if post_format_content and post_format_content != content: + # Update the content in memory (it's already updated on disk) + content = post_format_content + + # Show a snippet of the formatted content (first few lines) + lines = post_format_content.split("\n") + snippet_lines = min(7, len(lines)) # Show up to 7 lines + snippet = "\n".join(lines[:snippet_lines]) + format_message = ( + f"\nAuto-formatted the file. Here's how it looks now:\n{snippet}" + ) else: # Only log warning if there was actually a format command configured but it failed if not "No format command configured" in formatter_output: From f312ab3c9a6fe5c075ccb761e5908a9d04089fce Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Sun, 27 Apr 2025 16:35:19 -0400 Subject: [PATCH 2/2] Update [ghstack-poisoned] --- codemcp/tools/edit_file.py | 40 +++++--------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/codemcp/tools/edit_file.py b/codemcp/tools/edit_file.py index 0d584e1..081947a 100644 --- a/codemcp/tools/edit_file.py +++ b/codemcp/tools/edit_file.py @@ -776,20 +776,13 @@ async def edit_file_content( # Try to run the formatter on the file format_message = "" - post_format_content = updated_file - ( - formatter_success, - formatter_output, - post_format_content, - ) = await run_formatter_without_commit(full_file_path) + formatter_success, formatter_output = await run_formatter_without_commit( + full_file_path + ) if formatter_success: logger.info(f"Auto-formatted {full_file_path}") if formatter_output.strip(): - # If the file was actually changed by the formatter - if post_format_content and post_format_content != updated_file: - # Update the content in memory (it's already updated on disk) - updated_file = post_format_content - format_message = "\nAuto-formatted the file (snippet below shows post-formatting content)" + format_message = "\nAuto-formatted the file" else: # Only log warning if there was actually a format command configured but it failed if not "No format command configured" in formatter_output: @@ -798,30 +791,7 @@ async def edit_file_content( ) # Generate a snippet of the edited file to show in the response - # Use the post-formatting content for the snippet - if post_format_content and post_format_content != updated_file: - # When the file was changed by formatting, show the post-format version - # Since we don't have direct diff information between formatted and unformatted, - # we'll show a relevant portion of the file - lines = post_format_content.split("\n") - context_lines = 3 - - # Try to find the relevant section based on the edit - search_text = new_string[ - :40 - ] # Use part of the new string to locate where the edit was - for i, line in enumerate(lines): - if search_text in line: - start = max(0, i - context_lines) - end = min(len(lines), i + context_lines + 1) - snippet = "\n".join(lines[start:end]) - break - else: - # If we can't find the edit point, just show the first few lines - snippet = "\n".join(lines[: min(7, len(lines))]) - else: - # When no formatting changes, use the normal edit snippet - snippet = get_edit_snippet(content, old_string, new_string) + snippet = get_edit_snippet(content, old_string, new_string) # Commit the changes git_message = ""