Skip to content

Commit e0f5606

Browse files
committed
feat: integrate autoformatting in WriteFile and EditFile commands
We're going to integrate autoformatting into the WriteFile and EditFile commands. After doing the file write with these commands, run the "format" command (as defined by RunCommand) if it exists. You should NOT run the pre/post commit associated with RunCommand (because the formatting should be directly integrated with the edit/write), so make sure you hook into the run command infrastructure at the correct spot. ```git-revs 3f698ba (Base revision) c56fb59 Add run_formatter_without_commit to __all__ 3a9183d Add run_formatter_without_commit function to format a specific file without commit operations 13c9c8d Add run_formatter_without_commit import to write_file.py d5459f2 Add autoformatting to write_file_content function 4d419ac Add run_formatter_without_commit import to edit_file.py 2ac534f Add autoformatting to edit_file_content function 77cf162 Improve run_formatter_without_commit to handle different formatter types 2bffaf4 Auto-commit format changes HEAD Replace code_command.py with a simplified implementation of run_formatter_without_commit ``` codemcp-id: 280-feat-integrate-autoformatting-in-writefile-and-edi ghstack-source-id: af84f8e Pull-Request-resolved: #273
1 parent fde1b20 commit e0f5606

File tree

3 files changed

+78
-3
lines changed

3 files changed

+78
-3
lines changed

codemcp/code_command.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import os
55
import subprocess
6-
from typing import Any, Dict, List, Optional, cast
6+
from typing import Any, Dict, List, Optional, Tuple, cast
77

88
import tomli
99

@@ -15,6 +15,7 @@
1515
"get_command_from_config",
1616
"check_for_changes",
1717
"run_code_command",
18+
"run_formatter_without_commit",
1819
]
1920

2021

@@ -215,3 +216,46 @@ async def run_code_command(
215216
error_msg = f"Error during {command_name}: {e}"
216217
logging.error(error_msg)
217218
return f"Error: {error_msg}"
219+
220+
221+
async def run_formatter_without_commit(file_path: str) -> Tuple[bool, str]:
222+
"""Run the formatter on a specific file without performing pre/post commit operations.
223+
224+
Args:
225+
file_path: Absolute path to the file to format
226+
227+
Returns:
228+
A tuple containing (success_status, message)
229+
230+
Raises:
231+
Propagates any unexpected errors during formatting
232+
"""
233+
# Get the project directory (repository root)
234+
project_dir = os.path.dirname(file_path)
235+
try:
236+
project_dir = await get_repository_root(project_dir)
237+
except (subprocess.SubprocessError, OSError, ValueError):
238+
# Fall back to the directory containing the file if not in a git repo
239+
pass
240+
241+
# Get the format command from config - this is the only expected failure mode
242+
format_command = get_command_from_config(project_dir, "format")
243+
if not format_command:
244+
return False, "No format command configured in codemcp.toml"
245+
246+
# Use relative path from project_dir for the formatting command
247+
rel_path = os.path.relpath(file_path, project_dir)
248+
249+
# Run the formatter with the specific file path
250+
command = format_command + [rel_path]
251+
result = await run_command(
252+
command,
253+
cwd=project_dir,
254+
check=True,
255+
capture_output=True,
256+
text=True,
257+
)
258+
259+
# If we get here, the formatter ran successfully
260+
truncated_stdout = truncate_output_content(result.stdout, prefer_end=True)
261+
return True, f"File formatted successfully:\n{truncated_stdout}"

codemcp/tools/edit_file.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from difflib import SequenceMatcher
1010
from typing import Any, Dict, List, Tuple
1111

12+
from ..code_command import run_formatter_without_commit
1213
from ..common import get_edit_snippet
1314
from ..file_utils import (
1415
async_open_text,
@@ -773,6 +774,22 @@ async def edit_file_content(
773774
if read_file_timestamps is not None:
774775
read_file_timestamps[full_file_path] = os.stat(full_file_path).st_mtime
775776

777+
# Try to run the formatter on the file
778+
format_message = ""
779+
formatter_success, formatter_output = await run_formatter_without_commit(
780+
full_file_path
781+
)
782+
if formatter_success:
783+
logger.info(f"Auto-formatted {full_file_path}")
784+
if formatter_output.strip():
785+
format_message = "\nAuto-formatted the file"
786+
else:
787+
# Only log warning if there was actually a format command configured but it failed
788+
if not "No format command configured" in formatter_output:
789+
logger.warning(
790+
f"Failed to auto-format {full_file_path}: {formatter_output}"
791+
)
792+
776793
# Generate a snippet of the edited file to show in the response
777794
snippet = get_edit_snippet(content, old_string, new_string)
778795

@@ -787,4 +804,4 @@ async def edit_file_content(
787804
else:
788805
git_message = f"\n\nFailed to commit changes to git: {message}"
789806

790-
return f"Successfully edited {full_file_path}\n\nHere's a snippet of the edited file:\n{snippet}{git_message}"
807+
return f"Successfully edited {full_file_path}\n\nHere's a snippet of the edited file:\n{snippet}{format_message}{git_message}"

codemcp/tools/write_file.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#!/usr/bin/env python3
22

3+
import logging
34
import os
45

6+
from ..code_command import run_formatter_without_commit
57
from ..file_utils import (
68
check_file_path_and_permissions,
79
check_git_tracking_for_existing_file,
@@ -61,6 +63,18 @@ async def write_file_content(
6163
# Write the content with UTF-8 encoding and proper line endings
6264
await write_text_content(file_path, content, "utf-8", line_endings)
6365

66+
# Try to run the formatter on the file
67+
format_message = ""
68+
formatter_success, formatter_output = await run_formatter_without_commit(file_path)
69+
if formatter_success:
70+
logging.info(f"Auto-formatted {file_path}")
71+
if formatter_output.strip():
72+
format_message = f"\nAuto-formatted the file"
73+
else:
74+
# Only log warning if there was actually a format command configured but it failed
75+
if not "No format command configured" in formatter_output:
76+
logging.warning(f"Failed to auto-format {file_path}: {formatter_output}")
77+
6478
# Commit the changes
6579
git_message = ""
6680
success, message = await commit_changes(file_path, description, chat_id)
@@ -69,4 +83,4 @@ async def write_file_content(
6983
else:
7084
git_message = f"\nFailed to commit changes to git: {message}"
7185

72-
return f"Successfully wrote to {file_path}{git_message}"
86+
return f"Successfully wrote to {file_path}{format_message}{git_message}"

0 commit comments

Comments
 (0)