Task attempt 19940407-2ae9-4850-b151-834c900e23e3 - Final changes

This commit is contained in:
Louis Knight-Webb
2025-06-20 16:35:17 +01:00
parent 9b7303d2db
commit 0d05ab7b4e
3 changed files with 201 additions and 28 deletions

View File

@@ -1,11 +1,10 @@
use anyhow::anyhow;
use chrono::{DateTime, Utc};
use git2::build::CheckoutBuilder;
use git2::{Error as GitError, MergeOptions, Oid, RebaseOptions, Repository};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool, Type};
use std::path::Path;
use tracing::{debug, error, info};
use tracing::error;
use ts_rs::TS;
use uuid::Uuid;
@@ -690,33 +689,16 @@ impl TaskAttempt {
String::new() // File was deleted
};
// Generate diff chunks using dissimilar
// Generate line-based diff chunks
if old_content != new_content {
let chunks = dissimilar::diff(&old_content, &new_content);
let mut diff_chunks = Vec::new();
let diff_chunks = Self::generate_line_based_diff(&old_content, &new_content);
for chunk in chunks {
let diff_chunk = match chunk {
dissimilar::Chunk::Equal(text) => DiffChunk {
chunk_type: DiffChunkType::Equal,
content: text.to_string(),
},
dissimilar::Chunk::Delete(text) => DiffChunk {
chunk_type: DiffChunkType::Delete,
content: text.to_string(),
},
dissimilar::Chunk::Insert(text) => DiffChunk {
chunk_type: DiffChunkType::Insert,
content: text.to_string(),
},
};
diff_chunks.push(diff_chunk);
if !diff_chunks.is_empty() {
files.push(FileDiff {
path: path_str.to_string(),
chunks: diff_chunks,
});
}
files.push(FileDiff {
path: path_str.to_string(),
chunks: diff_chunks,
});
}
}
true // Continue processing
@@ -729,6 +711,97 @@ impl TaskAttempt {
Ok(WorktreeDiff { files })
}
/// Generate line-based diff chunks for better display
pub fn generate_line_based_diff(old_content: &str, new_content: &str) -> Vec<DiffChunk> {
let old_lines: Vec<&str> = old_content.lines().collect();
let new_lines: Vec<&str> = new_content.lines().collect();
let mut chunks = Vec::new();
// Use a simple line-by-line comparison algorithm
let mut old_idx = 0;
let mut new_idx = 0;
while old_idx < old_lines.len() || new_idx < new_lines.len() {
if old_idx < old_lines.len() && new_idx < new_lines.len() {
let old_line = old_lines[old_idx];
let new_line = new_lines[new_idx];
if old_line == new_line {
// Lines are identical
chunks.push(DiffChunk {
chunk_type: DiffChunkType::Equal,
content: format!("{}\n", old_line),
});
old_idx += 1;
new_idx += 1;
} else {
// Lines are different - look ahead to see if this is a modification, insertion, or deletion
let mut found_match = false;
// Check if the new line appears later in old lines (deletion)
for look_ahead in (old_idx + 1)..std::cmp::min(old_idx + 5, old_lines.len()) {
if old_lines[look_ahead] == new_line {
// Found the new line later in old, so old_line was deleted
chunks.push(DiffChunk {
chunk_type: DiffChunkType::Delete,
content: format!("{}\n", old_line),
});
old_idx += 1;
found_match = true;
break;
}
}
if !found_match {
// Check if the old line appears later in new lines (insertion)
for look_ahead in (new_idx + 1)..std::cmp::min(new_idx + 5, new_lines.len()) {
if new_lines[look_ahead] == old_line {
// Found the old line later in new, so new_line was inserted
chunks.push(DiffChunk {
chunk_type: DiffChunkType::Insert,
content: format!("{}\n", new_line),
});
new_idx += 1;
found_match = true;
break;
}
}
}
if !found_match {
// Lines are different - treat as modification (delete old, insert new)
chunks.push(DiffChunk {
chunk_type: DiffChunkType::Delete,
content: format!("{}\n", old_line),
});
chunks.push(DiffChunk {
chunk_type: DiffChunkType::Insert,
content: format!("{}\n", new_line),
});
old_idx += 1;
new_idx += 1;
}
}
} else if old_idx < old_lines.len() {
// Remaining old lines (deletions)
chunks.push(DiffChunk {
chunk_type: DiffChunkType::Delete,
content: format!("{}\n", old_lines[old_idx]),
});
old_idx += 1;
} else {
// Remaining new lines (insertions)
chunks.push(DiffChunk {
chunk_type: DiffChunkType::Insert,
content: format!("{}\n", new_lines[new_idx]),
});
new_idx += 1;
}
}
chunks
}
/// Get the branch status for this task attempt (ahead/behind main)
pub async fn get_branch_status(
pool: &SqlitePool,
@@ -836,7 +909,7 @@ impl TaskAttempt {
let mut last_oid: Option<Oid> = None;
while let Some(res) = reb.next() {
match res {
Ok(op) => {
Ok(_op) => {
let new_oid = reb.commit(None, &sig, None)?;
last_oid = Some(new_oid);
}
@@ -863,7 +936,7 @@ impl TaskAttempt {
repo.checkout_head(Some(CheckoutBuilder::new().force()))?;
// 🔟 final check
let final_oid = repo.head()?.peel_to_commit()?.id();
let _final_oid = repo.head()?.peel_to_commit()?.id();
Ok(main_oid.to_string())
}

View File

@@ -0,0 +1,51 @@
#[cfg(test)]
mod tests {
use crate::models::task_attempt::{TaskAttempt, DiffChunkType};
#[test]
fn test_line_based_diff() {
let old_content = "line 1\nline 2\nline 3\n";
let new_content = "line 1\nmodified line 2\nline 3\n";
let chunks = TaskAttempt::generate_line_based_diff(old_content, new_content);
// Should have: equal, delete, insert, equal
assert_eq!(chunks.len(), 4);
// First chunk should be equal
assert_eq!(chunks[0].chunk_type, DiffChunkType::Equal);
assert_eq!(chunks[0].content, "line 1\n");
// Second chunk should be delete
assert_eq!(chunks[1].chunk_type, DiffChunkType::Delete);
assert_eq!(chunks[1].content, "line 2\n");
// Third chunk should be insert
assert_eq!(chunks[2].chunk_type, DiffChunkType::Insert);
assert_eq!(chunks[2].content, "modified line 2\n");
// Fourth chunk should be equal
assert_eq!(chunks[3].chunk_type, DiffChunkType::Equal);
assert_eq!(chunks[3].content, "line 3\n");
}
#[test]
fn test_line_insertion() {
let old_content = "line 1\nline 3\n";
let new_content = "line 1\nline 2\nline 3\n";
let chunks = TaskAttempt::generate_line_based_diff(old_content, new_content);
// Should have: equal, insert, equal
assert_eq!(chunks.len(), 3);
assert_eq!(chunks[0].chunk_type, DiffChunkType::Equal);
assert_eq!(chunks[0].content, "line 1\n");
assert_eq!(chunks[1].chunk_type, DiffChunkType::Insert);
assert_eq!(chunks[1].content, "line 2\n");
assert_eq!(chunks[2].chunk_type, DiffChunkType::Equal);
assert_eq!(chunks[2].content, "line 3\n");
}
}

49
test_diff_logic.py Normal file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env python3
def test_diff_logic():
"""Test the logic of our line-based diff algorithm"""
# Test case 1: Line modification
old_content = "line 1\nline 2\nline 3\n"
new_content = "line 1\nmodified line 2\nline 3\n"
old_lines = old_content.split('\n')[:-1] # Remove empty last element
new_lines = new_content.split('\n')[:-1]
print("Test 1 - Line modification:")
print(f"Old lines: {old_lines}")
print(f"New lines: {new_lines}")
# Expected chunks: Equal, Delete, Insert, Equal
expected_chunks = [
("Equal", "line 1\n"),
("Delete", "line 2\n"),
("Insert", "modified line 2\n"),
("Equal", "line 3\n")
]
print(f"Expected chunks: {expected_chunks}")
print()
# Test case 2: Line insertion
old_content = "line 1\nline 3\n"
new_content = "line 1\nline 2\nline 3\n"
old_lines = old_content.split('\n')[:-1]
new_lines = new_content.split('\n')[:-1]
print("Test 2 - Line insertion:")
print(f"Old lines: {old_lines}")
print(f"New lines: {new_lines}")
# Expected chunks: Equal, Insert, Equal
expected_chunks = [
("Equal", "line 1\n"),
("Insert", "line 2\n"),
("Equal", "line 3\n")
]
print(f"Expected chunks: {expected_chunks}")
if __name__ == "__main__":
test_diff_logic()