feat: Merge suggestion with existing code if looks similar

This commit is contained in:
Gres 2025-04-30 10:09:22 +03:00
parent 637a4d9d4c
commit 63c6a38384

View File

@ -59,6 +59,100 @@ QString mergeWithRightText(const QString &suggestion, const QString &rightText)
return suggestion;
}
static int levenshteinDistance(const QString &source, const QString &target)
{
if (source == target) {
return 0;
}
int sourceCount = source.size();
int targetCount = target.size();
if (sourceCount == 0) {
return targetCount;
}
if (targetCount == 0) {
return sourceCount;
}
if (sourceCount > targetCount) {
return levenshteinDistance(target, source);
}
QVector<int> previousColumn;
previousColumn.reserve(targetCount + 1);
for (int i = 0; i < targetCount + 1; ++i) {
previousColumn.append(i);
}
QVector<int> column(targetCount + 1, 0);
for (int i = 0; i < sourceCount; ++i) {
column[0] = i + 1;
for (int j = 0; j < targetCount; ++j) {
column[j + 1] = std::min(
{1 + column.at(j),
1 + previousColumn.at(1 + j),
previousColumn.at(j) + ((source.at(i) == target.at(j)) ? 0 : 1)});
}
column.swap(previousColumn);
}
return previousColumn.at(targetCount);
}
static int linesToReplace(const QString &suggestion, const QTextCursor &cursor)
{
int suggestedLineCount = 0;
for (int idx = suggestion.indexOf('\n'); idx != -1; idx = suggestion.indexOf('\n', idx + 1)) {
++suggestedLineCount;
}
if (suggestedLineCount == 0) {
return 0;
}
QTextCursor lineReadCursor = cursor;
lineReadCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
int minDifference = 1e6;
int bestLineCount = 0;
for (int i = 0; i < suggestedLineCount; ++i) {
lineReadCursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
lineReadCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
QString curText = lineReadCursor.selectedText();
int difference = levenshteinDistance(curText, suggestion);
if (difference > minDifference) {
continue;
}
bestLineCount = i + 1;
minDifference = difference;
}
double minSimilarity = 0.4; // configurable?
int threshold = suggestion.length() * minSimilarity;
return minDifference < threshold ? bestLineCount : 0;
}
static QString existingTailToKeep(const QString &suggestedLine, const QString &existingLine)
{
int minDifference = 1e6;
int bestLength = 0;
for (int i = 0; i <= existingLine.length(); ++i) {
int difference = levenshteinDistance(suggestedLine, existingLine.left(i));
if (difference > minDifference) {
continue;
}
minDifference = difference;
bestLength = i;
}
return existingLine.mid(bestLength);
}
LLMSuggestion::LLMSuggestion(
const QList<Data> &suggestions, QTextDocument *sourceDocument, int currentCompletion)
: TextEditor::CyclicSuggestion(suggestions, sourceDocument, currentCompletion)
@ -173,14 +267,34 @@ bool LLMSuggestion::apply()
int firstLineEnd = text.indexOf('\n');
if (firstLineEnd != -1) {
QString firstLine = text.left(firstLineEnd);
QString restOfText = text.mid(firstLineEnd);
int toReplace = linesToReplace(text, cursor);
editCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
editCursor.removeSelectedText();
if (toReplace == 0) {
editCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
editCursor.removeSelectedText();
QString mergedFirstLine = mergeWithRightText(firstLine, textAfterCursor);
editCursor.insertText(mergedFirstLine + restOfText);
QString firstLine = text.left(firstLineEnd);
QString mergedFirstLine = mergeWithRightText(firstLine, textAfterCursor);
QString restOfText = text.mid(firstLineEnd);
editCursor.insertText(mergedFirstLine + restOfText);
} else {
for (int i = 0; i < toReplace; ++i)
editCursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
editCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
QTextCursor lastLineCursor = editCursor;
lastLineCursor.setPosition(lastLineCursor.position(), QTextCursor::MoveAnchor);
lastLineCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
QString lastExistingLine = lastLineCursor.selectedText();
QString lastSuggestedLine = text.mid(text.lastIndexOf('\n') + 1);
qDebug() << lastExistingLine;
qDebug() << lastSuggestedLine;
QString tail = existingTailToKeep(lastSuggestedLine, lastExistingLine);
qDebug() << tail;
editCursor.insertText(text + tail);
}
} else {
editCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
editCursor.removeSelectedText();