From 719065ebfce8a5d23fbe51ea33e9d9a8a56ce814 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 10 Mar 2025 18:42:40 +0200 Subject: [PATCH] refactor: Extract document reading to separate class (#127) This decouples LLMClientInterface from Qt Creator text editor implementation and allows to write tests --- LLMClientInterface.cpp | 12 ++++---- LLMClientInterface.hpp | 3 ++ context/CMakeLists.txt | 2 ++ context/DocumentReaderQtCreator.hpp | 45 +++++++++++++++++++++++++++++ context/IDocumentReader.hpp | 41 ++++++++++++++++++++++++++ qodeassist.cpp | 3 ++ 6 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 context/DocumentReaderQtCreator.hpp create mode 100644 context/IDocumentReader.hpp diff --git a/LLMClientInterface.cpp b/LLMClientInterface.cpp index 48b7bc8..a565d00 100644 --- a/LLMClientInterface.cpp +++ b/LLMClientInterface.cpp @@ -23,8 +23,6 @@ #include #include -#include - #include "CodeHandler.hpp" #include "context/ContextManager.hpp" #include "context/DocumentContextReader.hpp" @@ -44,12 +42,14 @@ LLMClientInterface::LLMClientInterface( LLMCore::IProviderRegistry &providerRegistry, LLMCore::IPromptProvider *promptProvider, LLMCore::RequestHandlerBase &requestHandler, + Context::IDocumentReader &documentReader, IRequestPerformanceLogger &performanceLogger) : m_generalSettings(generalSettings) , m_completeSettings(completeSettings) , m_providerRegistry(providerRegistry) , m_promptProvider(promptProvider) , m_requestHandler(requestHandler) + , m_documentReader(documentReader) , m_performanceLogger(performanceLogger) { connect( @@ -261,12 +261,10 @@ LLMCore::ContextData LLMClientInterface::prepareContext( QJsonObject params = request["params"].toObject(); QJsonObject doc = params["doc"].toObject(); QJsonObject position = doc["position"].toObject(); - auto filePath = Context::extractFilePathFromRequest(request); - TextEditor::TextDocument *textDocument = TextEditor::TextDocument::textDocumentForFilePath( - Utils::FilePath::fromString(filePath)); - if (!textDocument) { + auto documentInfo = m_documentReader.readDocument(filePath); + if (!documentInfo.document) { LOG_MESSAGE("Error: Document is not available for" + filePath); return LLMCore::ContextData{}; } @@ -275,7 +273,7 @@ LLMCore::ContextData LLMClientInterface::prepareContext( int lineNumber = position["line"].toInt(); Context::DocumentContextReader - reader(textDocument->document(), textDocument->mimeType(), filePath); + reader(documentInfo.document, documentInfo.mimeType, documentInfo.filePath); return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings); } diff --git a/LLMClientInterface.hpp b/LLMClientInterface.hpp index 5878ebf..6411dd8 100644 --- a/LLMClientInterface.hpp +++ b/LLMClientInterface.hpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,7 @@ public: LLMCore::IProviderRegistry &providerRegistry, LLMCore::IPromptProvider *promptProvider, LLMCore::RequestHandlerBase &requestHandler, + Context::IDocumentReader &documentReader, IRequestPerformanceLogger &performanceLogger); Utils::FilePath serverDeviceTemplate() const override; @@ -77,6 +79,7 @@ private: LLMCore::IPromptProvider *m_promptProvider = nullptr; LLMCore::IProviderRegistry &m_providerRegistry; LLMCore::RequestHandlerBase &m_requestHandler; + Context::IDocumentReader &m_documentReader; IRequestPerformanceLogger &m_performanceLogger; QElapsedTimer m_completionTimer; }; diff --git a/context/CMakeLists.txt b/context/CMakeLists.txt index 0032f98..8d569ac 100644 --- a/context/CMakeLists.txt +++ b/context/CMakeLists.txt @@ -3,6 +3,8 @@ add_library(Context STATIC ChangesManager.h ChangesManager.cpp ContextManager.hpp ContextManager.cpp ContentFile.hpp + DocumentReaderQtCreator.hpp + IDocumentReader.hpp TokenUtils.hpp TokenUtils.cpp ProgrammingLanguage.hpp ProgrammingLanguage.cpp ) diff --git a/context/DocumentReaderQtCreator.hpp b/context/DocumentReaderQtCreator.hpp new file mode 100644 index 0000000..bdaa8e4 --- /dev/null +++ b/context/DocumentReaderQtCreator.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 Povilas Kanapickas + * + * 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 . + */ + +#pragma once + +#include "IDocumentReader.hpp" + +#include + +namespace QodeAssist::Context { + +class DocumentReaderQtCreator : public IDocumentReader +{ +public: + DocumentInfo readDocument(const QString &path) const override + { + auto *textDocument = TextEditor::TextDocument::textDocumentForFilePath( + Utils::FilePath::fromString(path)); + if (!textDocument) { + return {}; + } + return { + .document = textDocument->document(), + .mimeType = textDocument->mimeType(), + .filePath = path}; + } +}; + +} // namespace QodeAssist::Context diff --git a/context/IDocumentReader.hpp b/context/IDocumentReader.hpp new file mode 100644 index 0000000..458cb5c --- /dev/null +++ b/context/IDocumentReader.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2025 Povilas Kanapickas + * + * 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 . + */ + +#pragma once + +#include + +namespace QodeAssist::Context { + +struct DocumentInfo +{ + QTextDocument *document = nullptr; // not owned + QString mimeType; + QString filePath; +}; + +class IDocumentReader +{ +public: + virtual ~IDocumentReader() = default; + + virtual DocumentInfo readDocument(const QString &path) const = 0; +}; + +} // namespace QodeAssist::Context diff --git a/qodeassist.cpp b/qodeassist.cpp index ed727b0..088d769 100644 --- a/qodeassist.cpp +++ b/qodeassist.cpp @@ -46,6 +46,7 @@ #include "Version.hpp" #include "chat/ChatOutputPane.h" #include "chat/NavigationPanel.hpp" +#include "context/DocumentReaderQtCreator.hpp" #include "llmcore/PromptProviderFim.hpp" #include "llmcore/ProvidersManager.hpp" #include "logger/RequestPerformanceLogger.hpp" @@ -146,6 +147,7 @@ public: LLMCore::ProvidersManager::instance(), &m_promptProvider, m_requestHandler, + m_documentReader, m_performanceLogger)); } @@ -189,6 +191,7 @@ private: QPointer m_qodeAssistClient; LLMCore::PromptProviderFim m_promptProvider; LLMCore::RequestHandler m_requestHandler{this}; + Context::DocumentReaderQtCreator m_documentReader; RequestPerformanceLogger m_performanceLogger; QPointer m_chatOutputPane; QPointer m_navigationPanel;