mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-11-15 06:22:52 -05:00
feat: Improve code suggestions (#256)
This commit is contained in:
@ -418,12 +418,20 @@ void LLMClientInterface::sendCompletionToClient(
|
|||||||
: completion;
|
: completion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (processedCompletion.endsWith('\n')) {
|
||||||
|
QString withoutTrailing = processedCompletion.chopped(1);
|
||||||
|
if (!withoutTrailing.contains('\n')) {
|
||||||
|
LOG_MESSAGE(QString("Removed trailing newline from single-line completion"));
|
||||||
|
processedCompletion = withoutTrailing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
completionItem[LanguageServerProtocol::textKey] = processedCompletion;
|
completionItem[LanguageServerProtocol::textKey] = processedCompletion;
|
||||||
|
|
||||||
QJsonObject range;
|
QJsonObject range;
|
||||||
range["start"] = position;
|
range["start"] = position;
|
||||||
QJsonObject end = position;
|
range["end"] = position;
|
||||||
end["character"] = position["character"].toInt() + processedCompletion.length();
|
|
||||||
range["end"] = end;
|
|
||||||
completionItem[LanguageServerProtocol::rangeKey] = range;
|
completionItem[LanguageServerProtocol::rangeKey] = range;
|
||||||
completionItem[LanguageServerProtocol::positionKey] = position;
|
completionItem[LanguageServerProtocol::positionKey] = position;
|
||||||
completions.append(completionItem);
|
completions.append(completionItem);
|
||||||
|
|||||||
@ -29,34 +29,57 @@
|
|||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
QString mergeWithRightText(const QString &suggestion, const QString &rightText)
|
static QStringList extractTokens(const QString &str)
|
||||||
{
|
{
|
||||||
if (suggestion.isEmpty() || rightText.isEmpty()) {
|
QStringList tokens;
|
||||||
return suggestion;
|
QString currentToken;
|
||||||
}
|
for (const QChar &ch : str) {
|
||||||
|
if (ch.isLetterOrNumber() || ch == '_') {
|
||||||
int j = 0;
|
currentToken += ch;
|
||||||
QString processed = rightText;
|
} else {
|
||||||
QSet<int> matchedPositions;
|
if (!currentToken.isEmpty() && currentToken.length() > 1) {
|
||||||
|
tokens.append(currentToken);
|
||||||
for (int i = 0; i < suggestion.length() && j < processed.length(); ++i) {
|
}
|
||||||
if (suggestion[i] == processed[j]) {
|
currentToken.clear();
|
||||||
matchedPositions.insert(j);
|
|
||||||
++j;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!currentToken.isEmpty() && currentToken.length() > 1) {
|
||||||
|
tokens.append(currentToken);
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
if (matchedPositions.isEmpty()) {
|
int LLMSuggestion::calculateReplaceLength(const QString &suggestion,
|
||||||
return suggestion + rightText;
|
const QString &rightText,
|
||||||
|
const QString &entireLine)
|
||||||
|
{
|
||||||
|
if (rightText.isEmpty()) {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> positions = matchedPositions.values();
|
QString structuralChars = "{}[]()<>;,";
|
||||||
std::sort(positions.begin(), positions.end(), std::greater<int>());
|
bool hasStructuralOverlap = false;
|
||||||
for (int pos : positions) {
|
for (const QChar &ch : structuralChars) {
|
||||||
processed.remove(pos, 1);
|
if (suggestion.contains(ch) && rightText.contains(ch)) {
|
||||||
|
hasStructuralOverlap = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasStructuralOverlap) {
|
||||||
|
return rightText.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
return suggestion;
|
const QStringList suggestionTokens = extractTokens(suggestion);
|
||||||
|
const QStringList lineTokens = extractTokens(entireLine);
|
||||||
|
|
||||||
|
for (const auto &token : suggestionTokens) {
|
||||||
|
if (lineTokens.contains(token)) {
|
||||||
|
return rightText.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLMSuggestion::LLMSuggestion(
|
LLMSuggestion::LLMSuggestion(
|
||||||
@ -66,10 +89,8 @@ LLMSuggestion::LLMSuggestion(
|
|||||||
const auto &data = suggestions[currentCompletion];
|
const auto &data = suggestions[currentCompletion];
|
||||||
|
|
||||||
int startPos = data.range.begin.toPositionInDocument(sourceDocument);
|
int startPos = data.range.begin.toPositionInDocument(sourceDocument);
|
||||||
int endPos = data.range.end.toPositionInDocument(sourceDocument);
|
|
||||||
|
|
||||||
startPos = qBound(0, startPos, sourceDocument->characterCount());
|
startPos = qBound(0, startPos, sourceDocument->characterCount());
|
||||||
endPos = qBound(startPos, endPos, sourceDocument->characterCount());
|
|
||||||
|
|
||||||
QTextCursor cursor(sourceDocument);
|
QTextCursor cursor(sourceDocument);
|
||||||
cursor.setPosition(startPos);
|
cursor.setPosition(startPos);
|
||||||
@ -77,17 +98,27 @@ LLMSuggestion::LLMSuggestion(
|
|||||||
QString blockText = block.text();
|
QString blockText = block.text();
|
||||||
|
|
||||||
int cursorPositionInBlock = cursor.positionInBlock();
|
int cursorPositionInBlock = cursor.positionInBlock();
|
||||||
|
QString leftText = blockText.left(cursorPositionInBlock);
|
||||||
QString rightText = blockText.mid(cursorPositionInBlock);
|
QString rightText = blockText.mid(cursorPositionInBlock);
|
||||||
|
|
||||||
if (!data.text.contains('\n')) {
|
QString suggestionText = data.text;
|
||||||
QString processedRightText = mergeWithRightText(data.text, rightText);
|
QString entireLine = blockText;
|
||||||
processedRightText = processedRightText.mid(data.text.length());
|
|
||||||
QString displayText = blockText.left(cursorPositionInBlock) + data.text
|
if (!suggestionText.contains('\n')) {
|
||||||
+ processedRightText;
|
int replaceLength = calculateReplaceLength(suggestionText, rightText, entireLine);
|
||||||
|
QString remainingRightText = (replaceLength > 0) ? rightText.mid(replaceLength) : rightText;
|
||||||
|
|
||||||
|
QString displayText = leftText + suggestionText + remainingRightText;
|
||||||
replacementDocument()->setPlainText(displayText);
|
replacementDocument()->setPlainText(displayText);
|
||||||
} else {
|
} else {
|
||||||
QString displayText = blockText.left(cursorPositionInBlock) + data.text;
|
int firstLineEnd = suggestionText.indexOf('\n');
|
||||||
|
QString firstLine = suggestionText.left(firstLineEnd);
|
||||||
|
QString restOfCompletion = suggestionText.mid(firstLineEnd);
|
||||||
|
|
||||||
|
int replaceLength = calculateReplaceLength(firstLine, rightText, entireLine);
|
||||||
|
QString remainingRightText = (replaceLength > 0) ? rightText.mid(replaceLength) : rightText;
|
||||||
|
|
||||||
|
QString displayText = leftText + firstLine + remainingRightText + restOfCompletion;
|
||||||
replacementDocument()->setPlainText(displayText);
|
replacementDocument()->setPlainText(displayText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,10 +135,12 @@ bool LLMSuggestion::applyLine(TextEditor::TextEditorWidget *widget)
|
|||||||
|
|
||||||
bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
|
bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
const Utils::Text::Range range = suggestions()[currentSuggestion()].range;
|
const auto ¤tSuggestions = suggestions();
|
||||||
|
const auto ¤tData = currentSuggestions[currentSuggestion()];
|
||||||
|
const Utils::Text::Range range = currentData.range;
|
||||||
const QTextCursor cursor = range.begin.toTextCursor(sourceDocument());
|
const QTextCursor cursor = range.begin.toTextCursor(sourceDocument());
|
||||||
QTextCursor currentCursor = widget->textCursor();
|
QTextCursor currentCursor = widget->textCursor();
|
||||||
const QString text = suggestions()[currentSuggestion()].text;
|
const QString text = currentData.text;
|
||||||
|
|
||||||
const int startPos = currentCursor.positionInBlock() - cursor.positionInBlock()
|
const int startPos = currentCursor.positionInBlock() - cursor.positionInBlock()
|
||||||
+ (cursor.selectionEnd() - cursor.selectionStart());
|
+ (cursor.selectionEnd() - cursor.selectionStart());
|
||||||
@ -131,6 +164,19 @@ bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (startPos == 0) {
|
||||||
|
QTextBlock currentBlock = cursor.block();
|
||||||
|
QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock());
|
||||||
|
QString entireLine = currentBlock.text();
|
||||||
|
|
||||||
|
int replaceLength = calculateReplaceLength(text, textAfterCursor, entireLine);
|
||||||
|
|
||||||
|
if (replaceLength > 0) {
|
||||||
|
currentCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength);
|
||||||
|
currentCursor.removeSelectedText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!subText.contains('\n')) {
|
if (!subText.contains('\n')) {
|
||||||
currentCursor.insertText(subText);
|
currentCursor.insertText(subText);
|
||||||
|
|
||||||
@ -167,34 +213,47 @@ bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
|
|||||||
|
|
||||||
bool LLMSuggestion::apply()
|
bool LLMSuggestion::apply()
|
||||||
{
|
{
|
||||||
const Utils::Text::Range range = suggestions()[currentSuggestion()].range;
|
const auto ¤tSuggestions = suggestions();
|
||||||
|
const auto ¤tData = currentSuggestions[currentSuggestion()];
|
||||||
|
const Utils::Text::Range range = currentData.range;
|
||||||
const QTextCursor cursor = range.begin.toTextCursor(sourceDocument());
|
const QTextCursor cursor = range.begin.toTextCursor(sourceDocument());
|
||||||
const QString text = suggestions()[currentSuggestion()].text;
|
QString text = currentData.text;
|
||||||
|
|
||||||
QTextBlock currentBlock = cursor.block();
|
QTextBlock currentBlock = cursor.block();
|
||||||
|
QString textBeforeCursor = currentBlock.text().left(cursor.positionInBlock());
|
||||||
QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock());
|
QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock());
|
||||||
|
QString entireLine = currentBlock.text();
|
||||||
|
|
||||||
QTextCursor editCursor = cursor;
|
QTextCursor editCursor = cursor;
|
||||||
|
editCursor.beginEditBlock();
|
||||||
|
|
||||||
int firstLineEnd = text.indexOf('\n');
|
int firstLineEnd = text.indexOf('\n');
|
||||||
if (firstLineEnd != -1) {
|
if (firstLineEnd != -1) {
|
||||||
QString firstLine = text.left(firstLineEnd);
|
QString firstLine = text.left(firstLineEnd);
|
||||||
QString restOfText = text.mid(firstLineEnd);
|
QString restOfText = text.mid(firstLineEnd);
|
||||||
|
|
||||||
editCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
int replaceLength = calculateReplaceLength(firstLine, textAfterCursor, entireLine);
|
||||||
editCursor.removeSelectedText();
|
|
||||||
|
if (replaceLength > 0) {
|
||||||
QString mergedFirstLine = mergeWithRightText(firstLine, textAfterCursor);
|
editCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength);
|
||||||
editCursor.insertText(mergedFirstLine + restOfText);
|
editCursor.removeSelectedText();
|
||||||
|
}
|
||||||
|
|
||||||
|
editCursor.insertText(firstLine + restOfText);
|
||||||
} else {
|
} else {
|
||||||
editCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
int replaceLength = calculateReplaceLength(text, textAfterCursor, entireLine);
|
||||||
editCursor.removeSelectedText();
|
|
||||||
|
if (replaceLength > 0) {
|
||||||
QString mergedText = mergeWithRightText(text, textAfterCursor);
|
editCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength);
|
||||||
editCursor.insertText(mergedText);
|
editCursor.removeSelectedText();
|
||||||
|
}
|
||||||
|
|
||||||
|
editCursor.insertText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editCursor.endEditBlock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|
||||||
|
|||||||
@ -41,5 +41,9 @@ public:
|
|||||||
bool applyLine(TextEditor::TextEditorWidget *widget) override;
|
bool applyLine(TextEditor::TextEditorWidget *widget) override;
|
||||||
bool applyPart(Part part, TextEditor::TextEditorWidget *widget);
|
bool applyPart(Part part, TextEditor::TextEditorWidget *widget);
|
||||||
bool apply() override;
|
bool apply() override;
|
||||||
|
|
||||||
|
static int calculateReplaceLength(const QString &suggestion,
|
||||||
|
const QString &rightText,
|
||||||
|
const QString &entireLine);
|
||||||
};
|
};
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
add_executable(QodeAssistTest
|
add_executable(QodeAssistTest
|
||||||
../CodeHandler.cpp
|
../CodeHandler.cpp
|
||||||
../LLMClientInterface.cpp
|
../LLMClientInterface.cpp
|
||||||
|
../LLMSuggestion.cpp
|
||||||
CodeHandlerTest.cpp
|
CodeHandlerTest.cpp
|
||||||
DocumentContextReaderTest.cpp
|
DocumentContextReaderTest.cpp
|
||||||
|
LLMSuggestionTest.cpp
|
||||||
# LLMClientInterfaceTests.cpp
|
# LLMClientInterfaceTests.cpp
|
||||||
unittest_main.cpp
|
unittest_main.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
157
test/LLMSuggestionTest.cpp
Normal file
157
test/LLMSuggestionTest.cpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "LLMSuggestion.hpp"
|
||||||
|
#include "TestUtils.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
using namespace QodeAssist;
|
||||||
|
|
||||||
|
class LLMSuggestionTest : public QObject, public testing::Test
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
// Basic tests
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthEmptyRight)
|
||||||
|
{
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("foo", "", "foo");
|
||||||
|
EXPECT_EQ(result, 0); // No rightText to replace
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthNoOverlap)
|
||||||
|
{
|
||||||
|
// No structural or token overlap
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("foo", "bar", "foobar");
|
||||||
|
EXPECT_EQ(result, 0); // Just insert, don't replace
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structural overlap tests
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthStructuralBraces)
|
||||||
|
{
|
||||||
|
// suggestion contains {}, rightText contains {}
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("= {\"red\"}", "{};", "colors{};");
|
||||||
|
EXPECT_EQ(result, 3); // Replace all rightText
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthStructuralSemicolon)
|
||||||
|
{
|
||||||
|
// suggestion contains ;, rightText contains ;
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("x;", ";", "int x;");
|
||||||
|
EXPECT_EQ(result, 1); // Replace the ;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthStructuralParens)
|
||||||
|
{
|
||||||
|
// suggestion contains (), rightText contains )
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("arg1, arg2)", ")", "foo(arg1, arg2)");
|
||||||
|
EXPECT_EQ(result, 1); // Replace the )
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthStructuralBrackets)
|
||||||
|
{
|
||||||
|
// suggestion contains [], rightText contains ]
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("[0]", "];", "arr[0];");
|
||||||
|
EXPECT_EQ(result, 2); // Replace ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token overlap tests
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthCommonToken)
|
||||||
|
{
|
||||||
|
// suggestion contains "colors", entireLine contains "colors"
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("colors << \"red\"", "colors{};", "QStringList colors{};");
|
||||||
|
EXPECT_EQ(result, 9); // Replace all rightText due to common token
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCalculateReplaceLengthMultipleCommonTokens)
|
||||||
|
{
|
||||||
|
// Multiple tokens in common
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("engine.load()", "engine.rootContext()", "QmlEngine engine.rootContext()");
|
||||||
|
EXPECT_EQ(result, 20); // Replace all rightText
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-world scenarios
|
||||||
|
TEST_F(LLMSuggestionTest, testCursorInBraces)
|
||||||
|
{
|
||||||
|
// Cursor in braces: QStringList colors{<cursor>};
|
||||||
|
// LLM sends: "\"red\", \"green\"", rightText: "};"
|
||||||
|
// No common tokens ("red" and "green" are strings, not identifiers in entireLine)
|
||||||
|
// No structural overlap (suggestion doesn't contain } or ;)
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("\"red\", \"green\"", "};", "QStringList colors{};");
|
||||||
|
EXPECT_EQ(result, 0); // No overlap, just insert
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCursorBeforeBraces)
|
||||||
|
{
|
||||||
|
// Cursor before braces: QStringList colors<cursor>{};
|
||||||
|
// LLM sends: " = {\"red\"}", rightText: "{};"
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength(" = {\"red\"}", "{};", "QStringList colors{};");
|
||||||
|
EXPECT_EQ(result, 3); // Structural overlap - replace all
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCursorAfterType)
|
||||||
|
{
|
||||||
|
// Cursor after type: QStringList <cursor>colors{};
|
||||||
|
// LLM sends: "colors << \"red\"", rightText: "colors{};"
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("colors << \"red\"", "colors{};", "QStringList colors{};");
|
||||||
|
EXPECT_EQ(result, 9); // Common token - replace all
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCursorInMiddleNoConflict)
|
||||||
|
{
|
||||||
|
// Cursor in middle: int <cursor>myVar = 5;
|
||||||
|
// LLM sends: "myVar", rightText: " = 5;", entireLine: "int myVar = 5;"
|
||||||
|
// "myVar" is a common token -> replace rightText
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("myVar", " = 5;", "int myVar = 5;");
|
||||||
|
EXPECT_EQ(result, 5); // Common token found, replace all rightText
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testCursorWithEqualsSign)
|
||||||
|
{
|
||||||
|
// LLM sends code with = and ;
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("= 5;", ";", "int x;");
|
||||||
|
EXPECT_EQ(result, 1); // Structural overlap on ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge cases
|
||||||
|
TEST_F(LLMSuggestionTest, testNoStructuralButHasToken)
|
||||||
|
{
|
||||||
|
// Token overlap but no structural
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("myVar", "myVariable", "int myVariable");
|
||||||
|
EXPECT_EQ(result, 0); // No structural overlap, tokens too different (length > 1 check)
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testOnlyWhitespace)
|
||||||
|
{
|
||||||
|
// rightText is just whitespace, but "code" is common token
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("code", " ", "code ");
|
||||||
|
EXPECT_EQ(result, 3); // Common token "code", replace rightText
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LLMSuggestionTest, testSingleCharTokenIgnored)
|
||||||
|
{
|
||||||
|
// Tokens must be > 1 character
|
||||||
|
int result = LLMSuggestion::calculateReplaceLength("a", "b", "ab");
|
||||||
|
EXPECT_EQ(result, 0); // Single char tokens ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "LLMSuggestionTest.moc"
|
||||||
Reference in New Issue
Block a user