diff --git a/LLMClientInterface.cpp b/LLMClientInterface.cpp index 5c9b421..370bb05 100644 --- a/LLMClientInterface.cpp +++ b/LLMClientInterface.cpp @@ -267,7 +267,7 @@ LLMCore::ContextData LLMClientInterface::prepareContext( Context::DocumentContextReader reader( textDocument->document(), textDocument->mimeType(), textDocument->filePath().toString()); - return reader.prepareContext(lineNumber, cursorPosition); + return reader.prepareContext(lineNumber, cursorPosition, Settings::codeCompletionSettings()); } void LLMClientInterface::sendCompletionToClient( diff --git a/context/DocumentContextReader.cpp b/context/DocumentContextReader.cpp index 7875c95..f76d9a1 100644 --- a/context/DocumentContextReader.cpp +++ b/context/DocumentContextReader.cpp @@ -244,39 +244,29 @@ CopyrightInfo DocumentContextReader::copyrightInfo() const return m_copyrightInfo; } -LLMCore::ContextData DocumentContextReader::prepareContext(int lineNumber, int cursorPosition) const +LLMCore::ContextData DocumentContextReader::prepareContext( + int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const { - QString contextBefore = getContextBefore(lineNumber, cursorPosition); - QString contextAfter = getContextAfter(lineNumber, cursorPosition); + QString contextBefore; + QString contextAfter; + if (settings.readFullFile()) { + contextBefore = readWholeFileBefore(lineNumber, cursorPosition); + contextAfter = readWholeFileAfter(lineNumber, cursorPosition); + } else { + contextBefore + = getContextBefore(lineNumber, cursorPosition, settings.readStringsBeforeCursor()); + contextAfter + = getContextAfter(lineNumber, cursorPosition, settings.readStringsAfterCursor()); + } QString fileContext; fileContext.append("\n ").append(getLanguageAndFileInfo()); - if (Settings::codeCompletionSettings().useProjectChangesCache()) + if (settings.useProjectChangesCache()) fileContext.append("\n ").append( ChangesManager::instance().getRecentChangesContext(m_textDocument)); return {.prefix = contextBefore, .suffix = contextAfter, .fileContext = fileContext}; } -QString DocumentContextReader::getContextBefore(int lineNumber, int cursorPosition) const -{ - if (Settings::codeCompletionSettings().readFullFile()) { - return readWholeFileBefore(lineNumber, cursorPosition); - } else { - int linesCount = Settings::codeCompletionSettings().readStringsBeforeCursor(); - return getContextBefore(lineNumber, cursorPosition, linesCount); - } -} - -QString DocumentContextReader::getContextAfter(int lineNumber, int cursorPosition) const -{ - if (Settings::codeCompletionSettings().readFullFile()) { - return readWholeFileAfter(lineNumber, cursorPosition); - } else { - int linesCount = Settings::codeCompletionSettings().readStringsAfterCursor(); - return getContextAfter(lineNumber, cursorPosition, linesCount); - } -} - } // namespace QodeAssist::Context diff --git a/context/DocumentContextReader.hpp b/context/DocumentContextReader.hpp index 31aa91e..58cd1ec 100644 --- a/context/DocumentContextReader.hpp +++ b/context/DocumentContextReader.hpp @@ -23,6 +23,7 @@ #include #include +#include namespace QodeAssist::Context { @@ -72,11 +73,8 @@ public: CopyrightInfo copyrightInfo() const; - LLMCore::ContextData prepareContext(int lineNumber, int cursorPosition) const; - -private: - QString getContextBefore(int lineNumber, int cursorPosition) const; - QString getContextAfter(int lineNumber, int cursorPosition) const; + LLMCore::ContextData prepareContext( + int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const; private: TextEditor::TextDocument *m_textDocument; diff --git a/llmcore/ContextData.hpp b/llmcore/ContextData.hpp index 1515540..1d862a5 100644 --- a/llmcore/ContextData.hpp +++ b/llmcore/ContextData.hpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2024 Petr Mironychev * * This file is part of QodeAssist. @@ -28,6 +28,10 @@ struct Message { QString role; QString content; + + // clang-format off + auto operator<=>(const Message&) const = default; + // clang-format on }; struct ContextData @@ -37,6 +41,8 @@ struct ContextData std::optional suffix; std::optional fileContext; std::optional> history; + + bool operator==(const ContextData &) const = default; }; } // namespace QodeAssist::LLMCore diff --git a/test/DocumentContextReaderTest.cpp b/test/DocumentContextReaderTest.cpp index 8ace66f..fe0379e 100644 --- a/test/DocumentContextReaderTest.cpp +++ b/test/DocumentContextReaderTest.cpp @@ -20,16 +20,59 @@ #include "context/DocumentContextReader.hpp" #include +#include #include using namespace QodeAssist::Context; +using namespace QodeAssist::LLMCore; +using namespace QodeAssist::Settings; std::ostream &operator<<(std::ostream &out, const QString &value) { - out << value.toStdString(); + out << '"' << value.toStdString() << '"'; return out; } +template +std::ostream &operator<<(std::ostream &out, const QVector &value) +{ + out << "["; + for (const auto &el : value) { + out << value << ", "; + } + out << "]"; + return out; +} + +template +std::ostream &operator<<(std::ostream &out, const std::optional &value) +{ + if (value.has_value()) { + out << value.value(); + } else { + out << "(no value)"; + } + return out; +} + +namespace QodeAssist::LLMCore { +std::ostream &operator<<(std::ostream &out, const Message &value) +{ + out << "Message{" + << "role=" << value.role << "content=" << value.content << "}"; + return out; +} + +std::ostream &operator<<(std::ostream &out, const ContextData &value) +{ + out << "ContextData{" + << "\n systemPrompt=" << value.systemPrompt << "\n prefix=" << value.prefix + << "\n suffix=" << value.suffix << "\n fileContext=" << value.fileContext + << "\n history=" << value.history << "\n}"; + return out; +} +} // namespace QodeAssist::LLMCore + class DocumentContextReaderTest : public QObject, public testing::Test { Q_OBJECT @@ -46,6 +89,24 @@ protected: { return DocumentContextReader(createTestDocument(text), "text/python", "/path/to/file"); } + + QSharedPointer createSettingsForWholeFile() + { + // CodeCompletionSettings is noncopyable + auto settings = QSharedPointer::create(); + settings->readFullFile.setValue(true); + return settings; + } + + QSharedPointer createSettingsForLines(int linesBefore, int linesAfter) + { + // CodeCompletionSettings is noncopyable + auto settings = QSharedPointer::create(); + settings->readFullFile.setValue(false); + settings->readStringsBeforeCursor.setValue(linesBefore); + settings->readStringsAfterCursor.setValue(linesAfter); + return settings; + } }; TEST_F(DocumentContextReaderTest, testGetLineText) @@ -208,4 +269,30 @@ TEST_F(DocumentContextReaderTest, testGetContextBetween) EXPECT_EQ(reader.getContextBetween(1, 2, 3, 2), "ne 2\nLine 3\nLi"); } +TEST_F(DocumentContextReaderTest, testPrepareContext) +{ + auto reader = createTestReader("Line 1\nLine 2\nLine 3\nLine 4\nLine 5"); + + EXPECT_EQ( + reader.prepareContext(2, 3, *createSettingsForWholeFile()), + (ContextData{ + .prefix = "Line 1\nLine 2\nLin", + .suffix = "e 3\nLine 4\nLine 5", + .fileContext = "\n Language: (MIME: text/python) filepath: /path/to/file()\n\n\n "})); + + EXPECT_EQ( + reader.prepareContext(2, 3, *createSettingsForLines(1, 1)), + (ContextData{ + .prefix = "Lin", + .suffix = "e 3", + .fileContext = "\n Language: (MIME: text/python) filepath: /path/to/file()\n\n\n "})); + + EXPECT_EQ( + reader.prepareContext(2, 3, *createSettingsForLines(2, 2)), + (ContextData{ + .prefix = "Line 2\nLin", + .suffix = "e 3\nLine 4", + .fileContext = "\n Language: (MIME: text/python) filepath: /path/to/file()\n\n\n "})); +} + #include "DocumentContextReaderTest.moc"