feat: Tampilkan unified diff saat tool write menimpa file yang sudah ada
This commit is contained in:
+69
-4
@@ -7,7 +7,8 @@ use super::super::Tool;
|
||||
use super::super::ToolCtx;
|
||||
use super::super::resolve_path;
|
||||
use super::super::check_graduated_checks;
|
||||
use super::helpers::arg_str;
|
||||
use super::helpers::{self, arg_str};
|
||||
use similar::TextDiff;
|
||||
|
||||
/// Tool: write content to a file, auto-creating parent directories as needed.
|
||||
pub struct Write;
|
||||
@@ -58,6 +59,7 @@ impl Tool for Write {
|
||||
}
|
||||
let check_matches = check_graduated_checks(&rel, &content, &ctx.graduated_checks);
|
||||
let path = resolve_path(&ctx.workspaces, &rel)?;
|
||||
let old_content = fs::read_to_string(&path).ok();
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
.map_err(|e| anyhow!("failed to create parent directories for '{rel}': {e}"))?;
|
||||
@@ -73,10 +75,73 @@ impl Tool for Write {
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
if check_matches.is_empty() {
|
||||
Ok(format!("wrote {} bytes to {}{}", content.len(), rel, lsp_note))
|
||||
// Only emit a diff when the file existed before and was valid UTF-8;
|
||||
// new files and binary overwrites fall back to the byte-count message.
|
||||
let diff_note = if let Some(old) = old_content {
|
||||
let text_diff = TextDiff::from_lines(old.as_str(), content.as_str());
|
||||
let diff_text = format!(
|
||||
"{}",
|
||||
text_diff.unified_diff().context_radius(3).header(&rel, &rel)
|
||||
);
|
||||
format!("\n```diff\n{}\n```", helpers::truncate_diff(&diff_text))
|
||||
} else {
|
||||
Ok(format!("wrote {} bytes to {}{}. Graduated checks matched: {}", content.len(), rel, lsp_note, check_matches.join(", ")))
|
||||
String::new()
|
||||
};
|
||||
if check_matches.is_empty() {
|
||||
Ok(format!("wrote {} bytes to {}{}{}", content.len(), rel, lsp_note, diff_note))
|
||||
} else {
|
||||
Ok(format!("wrote {} bytes to {}{}. Graduated checks matched: {}{}", content.len(), rel, lsp_note, check_matches.join(", "), diff_note))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn test_ctx(workspace: std::path::PathBuf) -> crate::tool::ToolCtx {
|
||||
crate::tool::ToolCtx::builder().workspaces(vec![workspace]).build()
|
||||
}
|
||||
|
||||
fn temp_workspace() -> std::path::PathBuf {
|
||||
let dir = std::env::temp_dir().join(format!("zesdex-write-test-{}", uuid::Uuid::new_v4()));
|
||||
fs::create_dir_all(&dir).unwrap();
|
||||
dir
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_to_a_new_file_has_no_diff_block() {
|
||||
let workspace = temp_workspace();
|
||||
let ctx = test_ctx(workspace.clone());
|
||||
let args = json!({"path": "new.txt", "content": "hello\n", "reason": "test new file"});
|
||||
let result = Write.run(&ctx, &args).unwrap();
|
||||
assert!(result.contains("wrote 6 bytes"));
|
||||
assert!(!result.contains("```diff"));
|
||||
fs::remove_dir_all(&workspace).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_overwriting_an_existing_utf8_file_includes_a_diff_block() {
|
||||
let workspace = temp_workspace();
|
||||
fs::write(workspace.join("existing.txt"), "old content\n").unwrap();
|
||||
let ctx = test_ctx(workspace.clone());
|
||||
let args = json!({"path": "existing.txt", "content": "new content\n", "reason": "test overwrite"});
|
||||
let result = Write.run(&ctx, &args).unwrap();
|
||||
assert!(result.contains("```diff"));
|
||||
assert!(result.contains("-old content"));
|
||||
assert!(result.contains("+new content"));
|
||||
fs::remove_dir_all(&workspace).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_overwriting_a_non_utf8_file_has_no_diff_block() {
|
||||
let workspace = temp_workspace();
|
||||
fs::write(workspace.join("binary.dat"), [0xFFu8, 0xFE, 0xFD]).unwrap();
|
||||
let ctx = test_ctx(workspace.clone());
|
||||
let args = json!({"path": "binary.dat", "content": "now text\n", "reason": "test binary overwrite"});
|
||||
let result = Write.run(&ctx, &args).unwrap();
|
||||
assert!(!result.contains("```diff"));
|
||||
assert!(result.contains("wrote"));
|
||||
fs::remove_dir_all(&workspace).ok();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user