Compare commits

...

8 Commits

Author SHA1 Message Date
b789e42602 chore: Upgrade plugin to 0.4.11
Some checks failed
Build plugin / ${{ matrix.config.name }} (map[artifact:Linux-x64 cc:gcc cxx:g++ name:Ubuntu Latest GCC os:ubuntu-latest platform:linux_x64]) (push) Has been cancelled
Build plugin / ${{ matrix.config.name }} (map[artifact:Linux-x64(Ubuntu-22.04-experimental) cc:gcc cxx:g++ name:Ubuntu 22.04 GCC os:ubuntu-22.04 platform:linux_x64]) (push) Has been cancelled
Build plugin / ${{ matrix.config.name }} (map[artifact:Windows-x64 cc:cl cxx:cl environment_script:C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat name:Windows Latest MSVC os:windows-latest platform:windows_x64]) (push) Has been cancelled
Build plugin / ${{ matrix.config.name }} (map[artifact:macOS-universal cc:clang cxx:clang++ name:macOS Latest Clang os:macos-latest platform:mac_x64]) (push) Has been cancelled
Build plugin / update_json (push) Has been cancelled
Build plugin / release (push) Has been cancelled
2025-01-27 00:55:39 +01:00
4bf955462f feat: Update dialog offer open plugin folder 2025-01-27 00:54:49 +01:00
5b99e68e53 doc: Added file context feature description 2025-01-27 00:14:38 +01:00
0f1b277ef7 chore: Upgrade plugin to 0.4.10 2025-01-26 23:11:58 +01:00
56995c9edf fix: Saving name of chat in native language
- Add button to show chat history folder in system viewer
2025-01-26 23:06:51 +01:00
45aba6b6be fix: Sync editors and chat when sync enable 2025-01-26 22:35:43 +01:00
1dfb3feb96 doc: Update info for QtC 15.0.1 changes
Some checks are pending
Build plugin / ${{ matrix.config.name }} (map[artifact:Linux-x64 cc:gcc cxx:g++ name:Ubuntu Latest GCC os:ubuntu-latest platform:linux_x64]) (push) Waiting to run
Build plugin / ${{ matrix.config.name }} (map[artifact:Linux-x64(Ubuntu-22.04-experimental) cc:gcc cxx:g++ name:Ubuntu 22.04 GCC os:ubuntu-22.04 platform:linux_x64]) (push) Waiting to run
Build plugin / ${{ matrix.config.name }} (map[artifact:Windows-x64 cc:cl cxx:cl environment_script:C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat name:Windows Latest MSVC os:windows-latest platform:windows_x64]) (push) Waiting to run
Build plugin / ${{ matrix.config.name }} (map[artifact:macOS-universal cc:clang cxx:clang++ name:macOS Latest Clang os:macos-latest platform:mac_x64]) (push) Waiting to run
Build plugin / update_json (push) Blocked by required conditions
Build plugin / release (push) Blocked by required conditions
2025-01-26 10:08:03 +01:00
2c49d45297 fix: Keep name of chat after saving 2025-01-26 10:03:34 +01:00
8 changed files with 212 additions and 50 deletions

View File

@ -20,6 +20,7 @@
#include "ChatRootView.hpp"
#include <QClipboard>
#include <QDesktopServices>
#include <QFileDialog>
#include <QMessageBox>
@ -72,7 +73,7 @@ ChatRootView::ChatRootView(QQuickItem *parent)
this,
&ChatRootView::updateInputTokensCount);
connect(m_chatModel, &ChatModel::modelReseted, [this]() { setRecentFilePath(QString{}); });
connect(m_chatModel, &ChatModel::modelReseted, this, [this]() { setRecentFilePath(QString{}); });
connect(this, &ChatRootView::attachmentFilesChanged, &ChatRootView::updateInputTokensCount);
connect(this, &ChatRootView::linkedFilesChanged, &ChatRootView::updateInputTokensCount);
connect(&Settings::chatAssistantSettings().useSystemPrompt, &Utils::BaseAspect::changed,
@ -82,9 +83,20 @@ ChatRootView::ChatRootView(QQuickItem *parent)
auto editors = Core::EditorManager::instance();
connect(editors, &Core::EditorManager::editorOpened, this, &ChatRootView::onEditorOpened);
connect(editors, &Core::EditorManager::editorAboutToClose, this, &ChatRootView::onEditorAboutToClose);
connect(editors, &Core::EditorManager::editorsClosed, this, &ChatRootView::onEditorsClosed);
connect(editors, &Core::EditorManager::editorCreated, this, &ChatRootView::onEditorCreated);
connect(
editors,
&Core::EditorManager::editorAboutToClose,
this,
&ChatRootView::onEditorAboutToClose);
connect(editors, &Core::EditorManager::currentEditorAboutToChange, this, [this]() {
if (m_isSyncOpenFiles) {
for (auto editor : std::as_const(m_currentEditors)) {
onAppendLinkFileFromEditor(editor);
}
}
});
updateInputTokensCount();
}
@ -173,6 +185,8 @@ void ChatRootView::saveHistory(const QString &filePath)
auto result = ChatSerializer::saveToFile(m_chatModel, filePath);
if (!result.success) {
LOG_MESSAGE(QString("Failed to save chat history: %1").arg(result.errorMessage));
} else {
setRecentFilePath(filePath);
}
}
@ -243,17 +257,50 @@ QString ChatRootView::getSuggestedFileName() const
{
QStringList parts;
static const QRegularExpression saitizeSymbols = QRegularExpression("[\\/:*?\"<>|\\s]");
static const QRegularExpression underSymbols = QRegularExpression("_+");
if (m_chatModel->rowCount() > 0) {
QString firstMessage
= m_chatModel->data(m_chatModel->index(0), ChatModel::Content).toString();
QString shortMessage = firstMessage.split('\n').first().simplified().left(30);
shortMessage.replace(QRegularExpression("[^a-zA-Z0-9_-]"), "_");
parts << shortMessage;
QString sanitizedMessage = shortMessage;
sanitizedMessage.replace(saitizeSymbols, "_");
sanitizedMessage.replace(underSymbols, "_");
sanitizedMessage = sanitizedMessage.trimmed();
if (!sanitizedMessage.isEmpty()) {
if (sanitizedMessage.startsWith('_')) {
sanitizedMessage.remove(0, 1);
}
if (sanitizedMessage.endsWith('_')) {
sanitizedMessage.chop(1);
}
QString targetDir = getChatsHistoryDir();
QString fullPath = QDir(targetDir).filePath(sanitizedMessage);
QFileInfo fileInfo(fullPath);
if (!fileInfo.exists() && QFileInfo(fileInfo.path()).isWritable()) {
parts << sanitizedMessage;
}
}
}
parts << QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm");
return parts.join("_");
QString fileName = parts.join("_");
QString fullPath = QDir(getChatsHistoryDir()).filePath(fileName);
QFileInfo finalCheck(fullPath);
if (fileName.isEmpty() || finalCheck.exists() || !QFileInfo(finalCheck.path()).isWritable()) {
fileName = QString("chat_%1").arg(
QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm"));
}
return fileName;
}
void ChatRootView::autosave()
@ -306,7 +353,7 @@ void ChatRootView::showAttachFilesDialog()
QStringList newFilePaths = dialog.selectedFiles();
if (!newFilePaths.isEmpty()) {
bool filesAdded = false;
for (const QString &filePath : newFilePaths) {
for (const QString &filePath : std::as_const(newFilePaths)) {
if (!m_attachmentFiles.contains(filePath)) {
m_attachmentFiles.append(filePath);
filesAdded = true;
@ -340,7 +387,7 @@ void ChatRootView::showLinkFilesDialog()
QStringList newFilePaths = dialog.selectedFiles();
if (!newFilePaths.isEmpty()) {
bool filesAdded = false;
for (const QString &filePath : newFilePaths) {
for (const QString &filePath : std::as_const(newFilePaths)) {
if (!m_linkedFiles.contains(filePath)) {
m_linkedFiles.append(filePath);
filesAdded = true;
@ -373,6 +420,31 @@ void ChatRootView::setIsSyncOpenFiles(bool state)
m_isSyncOpenFiles = state;
emit isSyncOpenFilesChanged();
}
if (m_isSyncOpenFiles) {
for (auto editor : std::as_const(m_currentEditors)) {
onAppendLinkFileFromEditor(editor);
}
}
}
void ChatRootView::openChatHistoryFolder()
{
QString path;
if (auto project = ProjectExplorer::ProjectManager::startupProject()) {
Settings::ProjectSettings projectSettings(project);
path = projectSettings.chatHistoryPath().toString();
} else {
path = QString("%1/qodeassist/chat_history").arg(Core::ICore::userResourcePath().toString());
}
QDir dir(path);
if (!dir.exists()) {
dir.mkpath(".");
}
QUrl url = QUrl::fromLocalFile(dir.absolutePath());
QDesktopServices::openUrl(url);
}
void ChatRootView::updateInputTokensCount()
@ -414,7 +486,20 @@ bool ChatRootView::isSyncOpenFiles() const
return m_isSyncOpenFiles;
}
void ChatRootView::onEditorOpened(Core::IEditor *editor)
void ChatRootView::onEditorAboutToClose(Core::IEditor *editor)
{
if (auto document = editor->document(); document && isSyncOpenFiles()) {
QString filePath = document->filePath().toString();
m_linkedFiles.removeOne(filePath);
emit linkedFilesChanged();
}
if (editor) {
m_currentEditors.removeOne(editor);
}
}
void ChatRootView::onAppendLinkFileFromEditor(Core::IEditor *editor)
{
if (auto document = editor->document(); document && isSyncOpenFiles()) {
QString filePath = document->filePath().toString();
@ -425,25 +510,10 @@ void ChatRootView::onEditorOpened(Core::IEditor *editor)
}
}
void ChatRootView::onEditorAboutToClose(Core::IEditor *editor)
void ChatRootView::onEditorCreated(Core::IEditor *editor, const Utils::FilePath &filePath)
{
if (auto document = editor->document(); document && isSyncOpenFiles()) {
QString filePath = document->filePath().toString();
m_linkedFiles.removeOne(filePath);
emit linkedFilesChanged();
}
}
void ChatRootView::onEditorsClosed(QList<Core::IEditor *> editors)
{
if (isSyncOpenFiles()) {
for (Core::IEditor *editor : editors) {
if (auto document = editor->document()) {
QString filePath = document->filePath().toString();
m_linkedFiles.removeOne(filePath);
}
}
emit linkedFilesChanged();
if (editor && editor->document()) {
m_currentEditors.append(editor);
}
}

View File

@ -64,15 +64,16 @@ public:
Q_INVOKABLE void removeFileFromLinkList(int index);
Q_INVOKABLE void calculateMessageTokensCount(const QString &message);
Q_INVOKABLE void setIsSyncOpenFiles(bool state);
Q_INVOKABLE void openChatHistoryFolder();
Q_INVOKABLE void updateInputTokensCount();
int inputTokensCount() const;
bool isSyncOpenFiles() const;
void onEditorOpened(Core::IEditor *editor);
void onEditorAboutToClose(Core::IEditor *editor);
void onEditorsClosed(QList<Core::IEditor *> editors);
void onAppendLinkFileFromEditor(Core::IEditor *editor);
void onEditorCreated(Core::IEditor *editor, const Utils::FilePath &filePath);
QString chatFileName() const;
void setRecentFilePath(const QString &filePath);
@ -106,6 +107,7 @@ private:
int m_messageTokensCount{0};
int m_inputTokensCount{0};
bool m_isSyncOpenFiles;
QList<Core::IEditor *> m_currentEditors;
};
} // namespace QodeAssist::Chat

View File

@ -75,6 +75,7 @@ ChatRootView {
recentPath {
text: qsTr("Latest chat file name: %1").arg(root.chatFileName.length > 0 ? root.chatFileName : "Unsaved")
}
openChatHistory.onClicked: root.openChatHistoryFolder()
}
ListView {

View File

@ -29,6 +29,7 @@ Rectangle {
property alias clearButton: clearButtonId
property alias tokensBadge: tokensBadgeId
property alias recentPath: recentPathId
property alias openChatHistory: openChatHistoryId
color: palette.window.hslLightness > 0.5 ?
Qt.darker(palette.window, 1.1) :
@ -66,11 +67,16 @@ Rectangle {
Text {
id: recentPathId
Layout.fillWidth: true
elide: Text.ElideMiddle
color: palette.text
}
QoAButton {
id: openChatHistoryId
text: qsTr("Show in system")
}
Item {
Layout.fillWidth: true
}

View File

@ -1,7 +1,7 @@
{
"Id" : "qodeassist",
"Name" : "QodeAssist",
"Version" : "0.4.9",
"Version" : "0.4.11",
"Vendor" : "Petr Mironychev",
"VendorId" : "petrmironychev",
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",

View File

@ -2,7 +2,7 @@
[![Build plugin](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml/badge.svg?branch=main)](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/Palm1r/QodeAssist/total?color=41%2C173%2C71)
![GitHub Tag](https://img.shields.io/github/v/tag/Palm1r/QodeAssist)
![Static Badge](https://img.shields.io/badge/QtCreator-15.0.0-brightgreen)
![Static Badge](https://img.shields.io/badge/QtCreator-15.0.1-brightgreen)
![Static Badge](https://img.shields.io/badge/donations:0-brightgreen)
![qodeassist-icon](https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d) QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
@ -20,19 +20,25 @@
4. [Configure for OpenAI](#configure-for-openai)
5. [Configure for using Ollama](#configure-for-using-ollama)
6. [System Prompt Configuration](#system-prompt-configuration)
7. [Template-Model Compatibility](#template-model-compatibility)
8. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
9. [Development Progress](#development-progress)
10. [Hotkeys](#hotkeys)
11. [Troubleshooting](#troubleshooting)
12. [Support the Development](#support-the-development-of-qodeassist)
13. [How to Build](#how-to-build)
7. [File Context Features](#file-context-features)
8. [Template-Model Compatibility](#template-model-compatibility)
9. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
10. [Development Progress](#development-progress)
11. [Hotkeys](#hotkeys)
12. [Troubleshooting](#troubleshooting)
13. [Support the Development](#support-the-development-of-qodeassist)
14. [How to Build](#how-to-build)
## Overview
- AI-powered code completion
- Chat functionality:
- Side and Bottom panels
- Chat history autosave and restore
- Token usage monitoring and management
- Attach files for one-time code analysis
- Link files for persistent context with auto update in conversations
- Automatic syncing with open editor files (optional)
- Support for multiple LLM providers:
- Ollama
- OpenAI
@ -63,9 +69,15 @@
<img width="326" alt="QodeAssistBottomPanel" src="https://github.com/user-attachments/assets/4cc64c23-a294-4df8-9153-39ad6fdab34b">
</details>
<details>
<summary>Automatic syncing with open editor files: (click to expand)</summary>
<img width="600" alt="OpenedDocumentsSync" src="https://github.com/user-attachments/assets/08efda2f-dc4d-44c3-927c-e6a975090d2f">
</details>
## Install plugin to QtCreator
1. Install Latest Qt Creator
2. Download the QodeAssist plugin for your Qt Creator
- Remove old version plugin if already was installed
3. Launch Qt Creator and install the plugin:
- Go to:
- MacOS: Qt Creator -> About Plugins...
@ -136,25 +148,59 @@ You're all set! QodeAssist is now ready to use in Qt Creator.
The plugin comes with default system prompts optimized for chat and instruct models, as these currently provide better results for code assistance. If you prefer using FIM (Fill-in-Middle) models, you can easily customize the system prompt in the settings.
## File Context Features
QodeAssist provides two powerful ways to include source code files in your chat conversations: Attachments and Linked Files. Each serves a distinct purpose and helps provide better context for the AI assistant.
### Attached Files
Attachments are designed for one-time code analysis and specific queries:
- Files are included only in the current message
- Content is discarded after the message is processed
- Ideal for:
- Getting specific feedback on code changes
- Code review requests
- Analyzing isolated code segments
- Quick implementation questions
- Files can be attached using the paperclip icon in the chat interface
- Multiple files can be attached to a single message
### Linked Files
Linked files provide persistent context throughout the conversation:
- Files remain accessible for the entire chat session
- Content is included in every message exchange
- Files are automatically refreshed - always using latest content from disk
- Perfect for:
- Long-term refactoring discussions
- Complex architectural changes
- Multi-file implementations
- Maintaining context across related questions
- Can be managed using the link icon in the chat interface
- Supports automatic syncing with open editor files (can be enabled in settings)
- Files can be added/removed at any time during the conversation
## Template-Model Compatibility
| Template | Compatible Models | Purpose |
|----------|------------------|----------|
| CodeLlama FIM | `codellama:code` | Code completion |
| DeepSeekCoder FIM | `deepseek-coder-v2`, `deepseek-v2.5` | Code completion |
| Ollama Auto FIM | `Any Ollama base model` | Code completion |
| Qwen FIM | `Qwen 2.5 models` | Code completion |
| Ollama Auto FIM | `Any Ollama base/fim models` | Code completion |
| Qwen FIM | `Qwen 2.5 models(exclude instruct)` | Code completion |
| StarCoder2 FIM | `starcoder2 base model` | Code completion |
| Alpaca | `starcoder2:instruct` | Chat assistance |
| Basic Chat| `Messages without tokens` | Chat assistance |
| ChatML | `Qwen 2.5 models` | Chat assistance |
| ChatML | `Qwen 2.5 models(exclude base models)` | Chat assistance |
| Llama2 | `llama2 model family`, `codellama:instruct` | Chat assistance |
| Llama3 | `llama3 model family` | Chat assistance |
| Ollama Auto Chat | `Any Ollama chat model` | Chat assistance |
| Ollama Auto Chat | `Any Ollama chat/instruct models` | Chat assistance |
## QtCreator Version Compatibility
- QtCreator 15.0.0 - 0.4.x
- QtCreator 15.0.1 - 0.4.8 - 0.4.x
- QtCreator 15.0.0 - 0.4.0 - 0.4.7
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
- QtCreator 14.0.1 - 0.2.2 plugin version and below

View File

@ -159,11 +159,19 @@ void PluginUpdater::handleDownloadFinished()
return;
}
QString downloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
QString downloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)
+ QDir::separator() + "QodeAssist_v" + m_lastUpdateInfo.version;
QDir().mkpath(downloadPath);
QString filePath = downloadPath + QDir::separator() + m_lastUpdateInfo.fileName;
if (QFile::exists(filePath)) {
emit downloadError(tr("Update file already exists: %1").arg(filePath));
reply->deleteLater();
return;
}
QString filePath = downloadPath + "/" + m_lastUpdateInfo.fileName;
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly)) {
emit downloadError(tr("Could not save the update file"));
reply->deleteLater();
@ -174,7 +182,6 @@ void PluginUpdater::handleDownloadFinished()
file.close();
emit downloadFinished(filePath);
reply->deleteLater();
}

View File

@ -19,6 +19,11 @@
#include "UpdateDialog.hpp"
#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <QDesktopServices>
namespace QodeAssist {
UpdateDialog::UpdateDialog(QWidget *parent)
@ -166,7 +171,32 @@ void UpdateDialog::updateProgress(qint64 received, qint64 total)
void UpdateDialog::handleDownloadFinished(const QString &path)
{
m_progress->setVisible(false);
QMessageBox::information(this, tr("Update Successful"), tr("Update has been downloaded."));
QMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Update Downloaded"));
msgBox.setText(tr("The update has been downloaded successfully.\n"
"Would you like to close Qt Creator now and open the plugin folder?"));
msgBox.setInformativeText(tr("To complete the update:\n\n"
"1. Close Qt Creator completely\n"
"2. Navigate to the plugin folder\n"
"3. Remove the old version of QodeAssist plugin\n"
"4. Install plugin as usual via About plugin menu"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.setIcon(QMessageBox::Information);
if (msgBox.exec() == QMessageBox::Yes) {
const auto pluginSpecs = ExtensionSystem::PluginManager::plugins();
for (const ExtensionSystem::PluginSpec *spec : pluginSpecs) {
if (spec->name() == QLatin1String("QodeAssist")) {
const auto pluginPath = spec->filePath().path();
QFileInfo fileInfo(pluginPath);
QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absolutePath()));
Core::ICore::exit();
break;
}
}
}
accept();
}