mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-30 18:19:11 -04:00
refactor: Move to agent architecture
This commit is contained in:
@@ -282,175 +282,6 @@ ChangesManager::FileEdit ChangesManager::getFileEdit(const QString &editId) cons
|
||||
return m_fileEdits.value(editId);
|
||||
}
|
||||
|
||||
QList<ChangesManager::FileEdit> ChangesManager::getPendingEdits() const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
QList<FileEdit> pendingEdits;
|
||||
for (const auto &edit : m_fileEdits.values()) {
|
||||
if (edit.status == Pending) {
|
||||
pendingEdits.append(edit);
|
||||
}
|
||||
}
|
||||
return pendingEdits;
|
||||
}
|
||||
|
||||
bool ChangesManager::performFileEdit(
|
||||
const QString &filePath, const QString &oldContent, const QString &newContent, QString *errorMsg)
|
||||
{
|
||||
auto setError = [errorMsg](const QString &msg) {
|
||||
if (errorMsg) *errorMsg = msg;
|
||||
};
|
||||
|
||||
auto editors = Core::EditorManager::visibleEditors();
|
||||
for (auto *editor : editors) {
|
||||
if (!editor || !editor->document()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString editorPath = editor->document()->filePath().toFSPathString();
|
||||
if (editorPath == filePath) {
|
||||
QByteArray contentBytes = editor->document()->contents();
|
||||
QString currentContent = QString::fromUtf8(contentBytes);
|
||||
|
||||
if (oldContent.isEmpty()) {
|
||||
if (auto *textEditor
|
||||
= qobject_cast<TextEditor::TextDocument *>(editor->document())) {
|
||||
QTextDocument *doc = textEditor->document();
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
cursor.beginEditBlock();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.insertText(newContent);
|
||||
cursor.endEditBlock();
|
||||
|
||||
LOG_MESSAGE(QString("Appended to open editor: %1").arg(filePath));
|
||||
setError("Applied successfully (appended to end of file)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int matchPos = currentContent.indexOf(oldContent);
|
||||
if (matchPos != -1) {
|
||||
if (auto *textEditor
|
||||
= qobject_cast<TextEditor::TextDocument *>(editor->document())) {
|
||||
QTextDocument *doc = textEditor->document();
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
cursor.beginEditBlock();
|
||||
cursor.setPosition(matchPos);
|
||||
cursor.setPosition(matchPos + oldContent.length(), QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
cursor.insertText(newContent);
|
||||
cursor.endEditBlock();
|
||||
|
||||
LOG_MESSAGE(QString("Updated open editor (exact match): %1").arg(filePath));
|
||||
setError("Applied successfully (exact match)");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
double similarity = 0.0;
|
||||
QString matchedContent = findBestMatch(currentContent, oldContent, 0.82, &similarity);
|
||||
if (!matchedContent.isEmpty()) {
|
||||
matchPos = currentContent.indexOf(matchedContent);
|
||||
if (matchPos != -1) {
|
||||
if (auto *textEditor
|
||||
= qobject_cast<TextEditor::TextDocument *>(editor->document())) {
|
||||
QTextDocument *doc = textEditor->document();
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
cursor.beginEditBlock();
|
||||
cursor.setPosition(matchPos);
|
||||
cursor.setPosition(matchPos + matchedContent.length(), QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
cursor.insertText(newContent);
|
||||
cursor.endEditBlock();
|
||||
|
||||
LOG_MESSAGE(QString("Updated open editor (fuzzy match %1%%): %2")
|
||||
.arg(qRound(similarity * 100)).arg(filePath));
|
||||
setError(QString("Applied with fuzzy match (%1%% similarity)").arg(qRound(similarity * 100)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_MESSAGE(QString("Old content not found in open editor (best similarity: %1%%): %2")
|
||||
.arg(qRound(similarity * 100)).arg(filePath));
|
||||
setError(QString("Content not found. Best match: %1%% (threshold: 82%%). "
|
||||
"File may have changed.").arg(qRound(similarity * 100)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QString msg = QString("Cannot open file: %1").arg(file.errorString());
|
||||
LOG_MESSAGE(QString("Failed to open file for reading: %1 - %2").arg(filePath, file.errorString()));
|
||||
setError(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString currentContent = QString::fromUtf8(file.readAll());
|
||||
file.close();
|
||||
|
||||
QString updatedContent;
|
||||
|
||||
if (oldContent.isEmpty()) {
|
||||
updatedContent = currentContent + newContent;
|
||||
LOG_MESSAGE(QString("Appending to file: %1").arg(filePath));
|
||||
setError("Applied successfully (appended to end of file)");
|
||||
}
|
||||
else if (currentContent.contains(oldContent)) {
|
||||
int matchPos = currentContent.indexOf(oldContent);
|
||||
updatedContent = currentContent.left(matchPos)
|
||||
+ newContent
|
||||
+ currentContent.mid(matchPos + oldContent.length());
|
||||
LOG_MESSAGE(QString("Using exact match for file update: %1 at position %2")
|
||||
.arg(filePath).arg(matchPos));
|
||||
setError("Applied successfully (exact match)");
|
||||
} else {
|
||||
double similarity = 0.0;
|
||||
QString matchedContent = findBestMatch(currentContent, oldContent, 0.82, &similarity);
|
||||
if (!matchedContent.isEmpty()) {
|
||||
int matchPos = currentContent.indexOf(matchedContent);
|
||||
if (matchPos == -1) {
|
||||
QString msg = "Internal error: matched content not found in file";
|
||||
LOG_MESSAGE(QString("Internal error: matched content disappeared: %1").arg(filePath));
|
||||
setError(msg);
|
||||
return false;
|
||||
}
|
||||
updatedContent = currentContent.left(matchPos)
|
||||
+ newContent
|
||||
+ currentContent.mid(matchPos + matchedContent.length());
|
||||
LOG_MESSAGE(QString("Using fuzzy match (%1%%) for file update: %2 at position %3")
|
||||
.arg(qRound(similarity * 100)).arg(filePath).arg(matchPos));
|
||||
setError(QString("Applied with fuzzy match (%1%% similarity)").arg(qRound(similarity * 100)));
|
||||
} else {
|
||||
QString msg = QString("Content not found. Best match: %1%% (threshold: 82%%). "
|
||||
"File may have changed.").arg(qRound(similarity * 100));
|
||||
LOG_MESSAGE(QString("Old content not found in file (best similarity: %1%%): %2")
|
||||
.arg(qRound(similarity * 100)).arg(filePath));
|
||||
setError(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||
QString msg = QString("Cannot write file: %1").arg(file.errorString());
|
||||
LOG_MESSAGE(QString("Failed to open file for writing: %1 - %2").arg(filePath, file.errorString()));
|
||||
setError(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream out(&file);
|
||||
out << updatedContent;
|
||||
file.close();
|
||||
|
||||
LOG_MESSAGE(QString("File updated: %1").arg(filePath));
|
||||
return true;
|
||||
}
|
||||
|
||||
int ChangesManager::levenshteinDistance(const QString &s1, const QString &s2) const
|
||||
{
|
||||
const int len1 = s1.length();
|
||||
@@ -1112,138 +943,6 @@ QString ChangesManager::readFileContent(const QString &filePath) const
|
||||
return content;
|
||||
}
|
||||
|
||||
bool ChangesManager::performFileEditWithDiff(
|
||||
const QString &filePath,
|
||||
const DiffInfo &diffInfo,
|
||||
bool reverse,
|
||||
QString *errorMsg)
|
||||
{
|
||||
LOG_MESSAGE(QString("=== performFileEditWithDiff: %1 (reverse: %2) ===")
|
||||
.arg(filePath).arg(reverse ? "yes" : "no"));
|
||||
|
||||
auto setError = [errorMsg](const QString &msg) {
|
||||
if (errorMsg) *errorMsg = msg;
|
||||
};
|
||||
|
||||
auto editors = Core::EditorManager::visibleEditors();
|
||||
LOG_MESSAGE(QString(" Checking %1 visible editor(s)").arg(editors.size()));
|
||||
|
||||
for (auto *editor : editors) {
|
||||
if (!editor || !editor->document()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString editorPath = editor->document()->filePath().toFSPathString();
|
||||
if (editorPath == filePath) {
|
||||
LOG_MESSAGE(QString(" Found open editor for: %1").arg(filePath));
|
||||
|
||||
QByteArray contentBytes = editor->document()->contents();
|
||||
QString currentContent = QString::fromUtf8(contentBytes);
|
||||
|
||||
LOG_MESSAGE(QString(" Current content size: %1 bytes").arg(currentContent.size()));
|
||||
|
||||
QString modifiedContent = currentContent;
|
||||
QString diffErrorMsg;
|
||||
bool diffSuccess = applyDiffToContent(modifiedContent, diffInfo, reverse, &diffErrorMsg);
|
||||
|
||||
if (!diffSuccess) {
|
||||
LOG_MESSAGE(QString(" Failed to apply diff: %1").arg(diffErrorMsg));
|
||||
setError(diffErrorMsg);
|
||||
|
||||
LOG_MESSAGE(" Attempting fallback to old content-based method...");
|
||||
QString oldContent = reverse ? diffInfo.modifiedContent : diffInfo.originalContent;
|
||||
QString newContent = reverse ? diffInfo.originalContent : diffInfo.modifiedContent;
|
||||
|
||||
return performFileEdit(filePath, oldContent, newContent, errorMsg);
|
||||
}
|
||||
|
||||
if (auto *textEditor = qobject_cast<TextEditor::TextDocument *>(editor->document())) {
|
||||
QTextDocument *doc = textEditor->document();
|
||||
|
||||
LOG_MESSAGE(" Applying changes to text editor document...");
|
||||
|
||||
if (!doc) {
|
||||
LOG_MESSAGE(" Document is invalid");
|
||||
setError("Document pointer is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
QTextCursor cursor(doc);
|
||||
|
||||
if (cursor.isNull()) {
|
||||
LOG_MESSAGE(" Cursor is invalid");
|
||||
setError("Cannot create text cursor");
|
||||
return false;
|
||||
}
|
||||
|
||||
cursor.beginEditBlock();
|
||||
cursor.select(QTextCursor::Document);
|
||||
cursor.removeSelectedText();
|
||||
cursor.insertText(modifiedContent);
|
||||
cursor.endEditBlock();
|
||||
|
||||
LOG_MESSAGE(QString(" ✓ Successfully applied diff to open editor: %1").arg(filePath));
|
||||
setError(diffErrorMsg);
|
||||
return true;
|
||||
} catch (...) {
|
||||
LOG_MESSAGE(" Exception during document modification");
|
||||
setError("Exception during document modification");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_MESSAGE(" File not open in editor, modifying file directly...");
|
||||
LOG_MESSAGE(" Note: Undo (Ctrl+Z) will not be available for this file until it is opened");
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QString msg = QString("Cannot open file: %1").arg(file.errorString());
|
||||
LOG_MESSAGE(QString(" Failed to open file for reading: %1 - %2")
|
||||
.arg(filePath, file.errorString()));
|
||||
setError(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString currentContent = QString::fromUtf8(file.readAll());
|
||||
file.close();
|
||||
|
||||
LOG_MESSAGE(QString(" File read successfully (%1 bytes)").arg(currentContent.size()));
|
||||
|
||||
QString modifiedContent = currentContent;
|
||||
QString diffErrorMsg;
|
||||
bool diffSuccess = applyDiffToContent(modifiedContent, diffInfo, reverse, &diffErrorMsg);
|
||||
|
||||
if (!diffSuccess) {
|
||||
LOG_MESSAGE(QString(" Failed to apply diff to file: %1").arg(diffErrorMsg));
|
||||
setError(diffErrorMsg);
|
||||
|
||||
LOG_MESSAGE(" Attempting fallback to old content-based method...");
|
||||
QString oldContent = reverse ? diffInfo.modifiedContent : diffInfo.originalContent;
|
||||
QString newContent = reverse ? diffInfo.originalContent : diffInfo.modifiedContent;
|
||||
|
||||
return performFileEdit(filePath, oldContent, newContent, errorMsg);
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||
QString msg = QString("Cannot write file: %1").arg(file.errorString());
|
||||
LOG_MESSAGE(QString(" Failed to open file for writing: %1 - %2")
|
||||
.arg(filePath, file.errorString()));
|
||||
setError(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream out(&file);
|
||||
out << modifiedContent;
|
||||
file.close();
|
||||
|
||||
LOG_MESSAGE(QString(" ✓ Successfully wrote modified content to file: %1").arg(filePath));
|
||||
setError(diffErrorMsg);
|
||||
return true;
|
||||
}
|
||||
|
||||
ChangesManager::DiffInfo ChangesManager::createDiffInfo(
|
||||
const QString &originalContent,
|
||||
const QString &modifiedContent,
|
||||
@@ -1390,263 +1089,4 @@ ChangesManager::DiffInfo ChangesManager::createDiffInfo(
|
||||
return diffInfo;
|
||||
}
|
||||
|
||||
bool ChangesManager::findHunkLocation(
|
||||
const QStringList &fileLines,
|
||||
const DiffHunk &hunk,
|
||||
int &actualStartLine,
|
||||
QString *debugInfo) const
|
||||
{
|
||||
LOG_MESSAGE(QString(" Searching for hunk location (expected line: %1)").arg(hunk.oldStartLine));
|
||||
|
||||
QString debug;
|
||||
|
||||
int expectedIdx = hunk.oldStartLine - 1;
|
||||
|
||||
if (expectedIdx >= 0 && expectedIdx < fileLines.size()) {
|
||||
bool exactMatch = true;
|
||||
|
||||
int checkIdx = expectedIdx - hunk.contextBefore.size();
|
||||
if (checkIdx < 0) {
|
||||
exactMatch = false;
|
||||
debug += QString(" Context before out of bounds (need %1 lines before line %2)\n")
|
||||
.arg(hunk.contextBefore.size()).arg(expectedIdx + 1);
|
||||
} else {
|
||||
for (int i = 0; i < hunk.contextBefore.size(); ++i) {
|
||||
if (fileLines[checkIdx + i] != hunk.contextBefore[i]) {
|
||||
exactMatch = false;
|
||||
debug += QString(" Context before mismatch at offset %1: expected '%2', got '%3'\n")
|
||||
.arg(i).arg(hunk.contextBefore[i]).arg(fileLines[checkIdx + i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exactMatch) {
|
||||
for (int i = 0; i < hunk.removedLines.size(); ++i) {
|
||||
int lineIdx = expectedIdx + i;
|
||||
if (lineIdx >= fileLines.size() || fileLines[lineIdx] != hunk.removedLines[i]) {
|
||||
exactMatch = false;
|
||||
debug += QString(" Removed line mismatch at offset %1: expected '%2', got '%3'\n")
|
||||
.arg(i)
|
||||
.arg(hunk.removedLines[i])
|
||||
.arg(lineIdx < fileLines.size() ? fileLines[lineIdx] : "<EOF>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exactMatch && !hunk.contextAfter.isEmpty()) {
|
||||
int afterIdx = expectedIdx + hunk.removedLines.size();
|
||||
for (int i = 0; i < hunk.contextAfter.size(); ++i) {
|
||||
int lineIdx = afterIdx + i;
|
||||
if (lineIdx >= fileLines.size() || fileLines[lineIdx] != hunk.contextAfter[i]) {
|
||||
exactMatch = false;
|
||||
debug += QString(" Context after mismatch at offset %1: expected '%2', got '%3'\n")
|
||||
.arg(i)
|
||||
.arg(hunk.contextAfter[i])
|
||||
.arg(lineIdx < fileLines.size() ? fileLines[lineIdx] : "<EOF>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exactMatch) {
|
||||
actualStartLine = expectedIdx;
|
||||
LOG_MESSAGE(QString(" ✓ Found exact match at expected line %1").arg(hunk.oldStartLine));
|
||||
if (debugInfo) *debugInfo = "Exact match at expected location";
|
||||
return true;
|
||||
} else {
|
||||
debug += " Exact match at expected location failed, trying fuzzy search...\n";
|
||||
}
|
||||
} else {
|
||||
debug += QString(" Expected location %1 is out of bounds (file has %2 lines)\n")
|
||||
.arg(hunk.oldStartLine).arg(fileLines.size());
|
||||
}
|
||||
|
||||
LOG_MESSAGE(" Trying fuzzy search within ±20 lines...");
|
||||
|
||||
int searchStart = qMax(0, expectedIdx - 20);
|
||||
int searchEnd = qMin(fileLines.size(), expectedIdx + 20);
|
||||
|
||||
int bestMatchLine = -1;
|
||||
int bestMatchScore = 0;
|
||||
|
||||
for (int searchIdx = searchStart; searchIdx < searchEnd; ++searchIdx) {
|
||||
int matchScore = 0;
|
||||
int totalChecks = 0;
|
||||
|
||||
int checkIdx = searchIdx - hunk.contextBefore.size();
|
||||
if (checkIdx >= 0) {
|
||||
for (int i = 0; i < hunk.contextBefore.size(); ++i) {
|
||||
totalChecks++;
|
||||
if (fileLines[checkIdx + i] == hunk.contextBefore[i]) {
|
||||
matchScore++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < hunk.removedLines.size(); ++i) {
|
||||
int lineIdx = searchIdx + i;
|
||||
if (lineIdx < fileLines.size()) {
|
||||
totalChecks++;
|
||||
if (fileLines[lineIdx] == hunk.removedLines[i]) {
|
||||
matchScore++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int afterIdx = searchIdx + hunk.removedLines.size();
|
||||
for (int i = 0; i < hunk.contextAfter.size(); ++i) {
|
||||
int lineIdx = afterIdx + i;
|
||||
if (lineIdx < fileLines.size()) {
|
||||
totalChecks++;
|
||||
if (fileLines[lineIdx] == hunk.contextAfter[i]) {
|
||||
matchScore++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchScore > bestMatchScore) {
|
||||
bestMatchScore = matchScore;
|
||||
bestMatchLine = searchIdx;
|
||||
}
|
||||
}
|
||||
|
||||
int totalPossibleScore = hunk.contextBefore.size() + hunk.removedLines.size() + hunk.contextAfter.size();
|
||||
double matchPercentage = totalPossibleScore > 0 ? (double)bestMatchScore / totalPossibleScore * 100.0 : 0.0;
|
||||
|
||||
if (bestMatchLine != -1 && matchPercentage >= 70.0) {
|
||||
actualStartLine = bestMatchLine;
|
||||
debug += QString(" ✓ Found fuzzy match at line %1 (score: %2/%3 = %4%%)\n")
|
||||
.arg(bestMatchLine + 1)
|
||||
.arg(bestMatchScore)
|
||||
.arg(totalPossibleScore)
|
||||
.arg(matchPercentage, 0, 'f', 1);
|
||||
LOG_MESSAGE(QString(" ✓ Found fuzzy match at line %1 (%2%% confidence)")
|
||||
.arg(bestMatchLine + 1).arg(matchPercentage, 0, 'f', 1));
|
||||
if (debugInfo) *debugInfo = debug;
|
||||
return true;
|
||||
}
|
||||
|
||||
debug += QString(" ✗ No suitable match found (best: %1%% at line %2)\n")
|
||||
.arg(matchPercentage, 0, 'f', 1)
|
||||
.arg(bestMatchLine != -1 ? bestMatchLine + 1 : -1);
|
||||
LOG_MESSAGE(QString(" ✗ Hunk location not found (best match: %1%%)").arg(matchPercentage, 0, 'f', 1));
|
||||
|
||||
if (debugInfo) *debugInfo = debug;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChangesManager::applyDiffToContent(
|
||||
QString &content,
|
||||
const DiffInfo &diffInfo,
|
||||
bool reverse,
|
||||
QString *errorMsg)
|
||||
{
|
||||
LOG_MESSAGE(QString("=== Applying %1 to content ===").arg(reverse ? "REVERSE diff" : "diff"));
|
||||
|
||||
auto setError = [errorMsg](const QString &msg) {
|
||||
if (errorMsg) *errorMsg = msg;
|
||||
};
|
||||
|
||||
if (diffInfo.useFallback) {
|
||||
LOG_MESSAGE(" Using fallback mode (direct content replacement)");
|
||||
|
||||
QString searchContent = reverse ? diffInfo.modifiedContent : diffInfo.originalContent;
|
||||
QString replaceContent = reverse ? diffInfo.originalContent : diffInfo.modifiedContent;
|
||||
|
||||
int matchPos = content.indexOf(searchContent);
|
||||
if (matchPos != -1) {
|
||||
content = content.left(matchPos)
|
||||
+ replaceContent
|
||||
+ content.mid(matchPos + searchContent.length());
|
||||
setError("Applied using fallback mode (direct replacement)");
|
||||
LOG_MESSAGE(QString(" ✓ Fallback: Direct replacement successful at position %1").arg(matchPos));
|
||||
return true;
|
||||
} else {
|
||||
setError("Fallback failed: Original content not found in file");
|
||||
LOG_MESSAGE(" ✗ Fallback: Content not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (diffInfo.hunks.isEmpty()) {
|
||||
LOG_MESSAGE(" No hunks to apply (content unchanged)");
|
||||
setError("No changes to apply");
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList fileLines = content.split('\n');
|
||||
LOG_MESSAGE(QString(" File has %1 lines, applying %2 hunk(s)")
|
||||
.arg(fileLines.size()).arg(diffInfo.hunks.size()));
|
||||
|
||||
QList<DiffHunk> hunksToApply = diffInfo.hunks;
|
||||
|
||||
std::sort(hunksToApply.begin(), hunksToApply.end(),
|
||||
[](const DiffHunk &a, const DiffHunk &b) {
|
||||
return a.oldStartLine > b.oldStartLine;
|
||||
});
|
||||
|
||||
LOG_MESSAGE(" Hunks sorted in descending order for application");
|
||||
|
||||
int appliedHunks = 0;
|
||||
int failedHunks = 0;
|
||||
|
||||
for (int hunkIdx = 0; hunkIdx < hunksToApply.size(); ++hunkIdx) {
|
||||
const DiffHunk &hunk = hunksToApply[hunkIdx];
|
||||
|
||||
LOG_MESSAGE(QString(" --- Applying hunk %1/%2 ---")
|
||||
.arg(hunkIdx + 1).arg(hunksToApply.size()));
|
||||
|
||||
int actualStartLine = -1;
|
||||
QString debugInfo;
|
||||
|
||||
if (!findHunkLocation(fileLines, hunk, actualStartLine, &debugInfo)) {
|
||||
LOG_MESSAGE(QString(" ✗ Failed to locate hunk %1:\n%2")
|
||||
.arg(hunkIdx + 1).arg(debugInfo));
|
||||
failedHunks++;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_MESSAGE(QString(" Applying hunk at line %1 (remove %2 lines, add %3 lines)")
|
||||
.arg(actualStartLine + 1)
|
||||
.arg(hunk.removedLines.size())
|
||||
.arg(hunk.addedLines.size()));
|
||||
|
||||
for (int i = 0; i < hunk.removedLines.size(); ++i) {
|
||||
if (actualStartLine < fileLines.size()) {
|
||||
LOG_MESSAGE(QString(" Removing line %1: '%2'")
|
||||
.arg(actualStartLine + 1)
|
||||
.arg(fileLines[actualStartLine]));
|
||||
fileLines.removeAt(actualStartLine);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < hunk.addedLines.size(); ++i) {
|
||||
LOG_MESSAGE(QString(" Inserting line %1: '%2'")
|
||||
.arg(actualStartLine + i + 1)
|
||||
.arg(hunk.addedLines[i]));
|
||||
fileLines.insert(actualStartLine + i, hunk.addedLines[i]);
|
||||
}
|
||||
|
||||
appliedHunks++;
|
||||
LOG_MESSAGE(QString(" ✓ Hunk %1 applied successfully").arg(hunkIdx + 1));
|
||||
}
|
||||
|
||||
if (failedHunks > 0) {
|
||||
QString msg = QString("Partially applied: %1 of %2 hunks succeeded")
|
||||
.arg(appliedHunks).arg(hunksToApply.size());
|
||||
setError(msg);
|
||||
LOG_MESSAGE(QString(" ⚠ %1").arg(msg));
|
||||
|
||||
content = fileLines.join('\n');
|
||||
return false;
|
||||
}
|
||||
|
||||
content = fileLines.join('\n');
|
||||
setError(QString("Successfully applied %1 hunk(s)").arg(appliedHunks));
|
||||
LOG_MESSAGE(QString("=== All %1 hunk(s) applied successfully ===").arg(appliedHunks));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
|
||||
Reference in New Issue
Block a user