From 254fac246da8eb0536307b5850936ac9d9fbabd2 Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Mon, 20 Oct 2025 18:10:21 +0200 Subject: [PATCH] feat: Add settings for auto apply changes --- ChatView/FileEditItem.cpp | 19 +++++- ChatView/FileEditItem.hpp | 6 ++ ChatView/qml/FileEditChangesItem.qml | 94 ++++++++++++++++++++++------ settings/GeneralSettings.cpp | 10 ++- settings/GeneralSettings.hpp | 1 + settings/SettingsConstants.hpp | 1 + 6 files changed, 109 insertions(+), 22 deletions(-) diff --git a/ChatView/FileEditItem.cpp b/ChatView/FileEditItem.cpp index e5b39ef..798af08 100644 --- a/ChatView/FileEditItem.cpp +++ b/ChatView/FileEditItem.cpp @@ -20,6 +20,7 @@ #include "FileEditItem.hpp" #include "Logger.hpp" +#include "settings/GeneralSettings.hpp" #include #include @@ -89,10 +90,15 @@ void FileEditItem::parseFromContent(const QString &content) emit editModeChanged(); emit originalContentChanged(); emit newContentChanged(); + emit contextBeforeChanged(); + emit contextAfterChanged(); emit addedLinesChanged(); emit removedLinesChanged(); - applyEditInternal(true); + bool autoApplyEnabled = Settings::generalSettings().autoApplyFileEdits.value(); + if (autoApplyEnabled) { + applyEditInternal(true); + } } void FileEditItem::applyEdit() @@ -102,8 +108,15 @@ void FileEditItem::applyEdit() void FileEditItem::applyEditInternal(bool isAutomatic, int retryCount) { - if (!isAutomatic && m_status != EditStatus::Reverted && m_status != EditStatus::Rejected) { - return; + if (isAutomatic) { + if (m_status != EditStatus::Pending) { + return; + } + } else { + if (m_status != EditStatus::Pending && m_status != EditStatus::Reverted + && m_status != EditStatus::Rejected) { + return; + } } if (!acquireFileLock(m_filePath)) { diff --git a/ChatView/FileEditItem.hpp b/ChatView/FileEditItem.hpp index 5c02c19..b75366b 100644 --- a/ChatView/FileEditItem.hpp +++ b/ChatView/FileEditItem.hpp @@ -51,6 +51,8 @@ public: Q_PROPERTY(QString editMode READ editMode NOTIFY editModeChanged FINAL) Q_PROPERTY(QString originalContent READ originalContent NOTIFY originalContentChanged FINAL) Q_PROPERTY(QString newContent READ newContent NOTIFY newContentChanged FINAL) + Q_PROPERTY(QString contextBefore READ contextBefore NOTIFY contextBeforeChanged FINAL) + Q_PROPERTY(QString contextAfter READ contextAfter NOTIFY contextAfterChanged FINAL) Q_PROPERTY(int addedLines READ addedLines NOTIFY addedLinesChanged FINAL) Q_PROPERTY(int removedLines READ removedLines NOTIFY removedLinesChanged FINAL) Q_PROPERTY(EditStatus status READ status NOTIFY statusChanged FINAL) @@ -64,6 +66,8 @@ public: QString editMode() const { return m_editMode; } QString originalContent() const { return m_originalContent; } QString newContent() const { return m_newContent; } + QString contextBefore() const { return m_contextBefore; } + QString contextAfter() const { return m_contextAfter; } int addedLines() const { return m_addedLines; } int removedLines() const { return m_removedLines; } EditStatus status() const { return m_status; } @@ -79,6 +83,8 @@ signals: void editModeChanged(); void originalContentChanged(); void newContentChanged(); + void contextBeforeChanged(); + void contextAfterChanged(); void addedLinesChanged(); void removedLinesChanged(); void statusChanged(); diff --git a/ChatView/qml/FileEditChangesItem.qml b/ChatView/qml/FileEditChangesItem.qml index 8656da6..e559fd7 100644 --- a/ChatView/qml/FileEditChangesItem.qml +++ b/ChatView/qml/FileEditChangesItem.qml @@ -38,13 +38,6 @@ FileEditItem { readonly property int headerPadding: 8 readonly property int statusIndicatorWidth: 4 - readonly property var originalLines: originalContent.split('\n') - readonly property var newLines: newContent.split('\n') - readonly property string firstOriginalLine: originalLines[0] || "" - readonly property string firstNewLine: newLines[0] || "" - readonly property bool hasMultipleOriginalLines: originalLines.length > 1 - readonly property bool hasMultipleNewLines: newLines.length > 1 - readonly property bool isPending: status === FileEditItem.Pending readonly property bool isApplied: status === FileEditItem.Applied readonly property bool isReverted: status === FileEditItem.Reverted @@ -159,10 +152,21 @@ FileEditItem { Text { id: headerText Layout.fillWidth: true - text: qsTr("File Edit: %1 (+%2 -%3)") - .arg(root.filePath) - .arg(root.addedLines) - .arg(root.removedLines) + text: { + var modeText = "" + switch(root.editMode) { + case "replace": modeText = qsTr("Replace"); break; + case "insert_before": modeText = qsTr("Insert Before"); break; + case "insert_after": modeText = qsTr("Insert After"); break; + case "append": modeText = qsTr("Append"); break; + default: modeText = root.editMode; + } + return qsTr("%1: %2 (+%3 -%4)") + .arg(modeText) + .arg(root.filePath) + .arg(root.addedLines) + .arg(root.removedLines) + } font.pixelSize: 12 font.bold: true color: palette.text @@ -194,7 +198,7 @@ FileEditItem { QoAButton { text: qsTr("Apply") - enabled: root.isReverted || root.isRejected + enabled: root.isPending || root.isReverted || root.isRejected visible: !root.isApplied onClicked: root.applyEdit() } @@ -220,22 +224,76 @@ FileEditItem { spacing: 4 visible: opacity > 0 + // Context before (if available) Text { Layout.fillWidth: true - text: "Old: " + root.firstOriginalLine + (root.hasMultipleOriginalLines ? "..." : "") + visible: root.contextBefore.length > 0 + text: root.contextBefore font.family: root.codeFontFamily font.pixelSize: root.codeFontSize - color: Qt.rgba(1, 0.2, 0.2, 0.9) - elide: Text.ElideRight + color: palette.mid + wrapMode: Text.Wrap + opacity: 0.6 + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: oldContentText.implicitHeight + 8 + color: Qt.rgba(1, 0.2, 0.2, 0.1) + radius: 4 + border.width: 1 + border.color: Qt.rgba(1, 0.2, 0.2, 0.3) + visible: root.originalContent.length > 0 + + Text { + id: oldContentText + anchors { + left: parent.left + right: parent.right + top: parent.top + margins: 4 + } + text: "- " + root.originalContent + font.family: root.codeFontFamily + font.pixelSize: root.codeFontSize + color: Qt.rgba(1, 0.2, 0.2, 0.9) + wrapMode: Text.Wrap + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: newContentText.implicitHeight + 8 + color: Qt.rgba(0.2, 0.8, 0.2, 0.1) + radius: 4 + border.width: 1 + border.color: Qt.rgba(0.2, 0.8, 0.2, 0.3) + + Text { + id: newContentText + anchors { + left: parent.left + right: parent.right + top: parent.top + margins: 4 + } + text: "+ " + root.newContent + font.family: root.codeFontFamily + font.pixelSize: root.codeFontSize + color: Qt.rgba(0.2, 0.8, 0.2, 0.9) + wrapMode: Text.Wrap + } } Text { Layout.fillWidth: true - text: "New: " + root.firstNewLine + (root.hasMultipleNewLines ? "..." : "") + visible: root.contextAfter.length > 0 + text: root.contextAfter font.family: root.codeFontFamily font.pixelSize: root.codeFontSize - color: Qt.rgba(0.2, 0.8, 0.2, 0.9) - elide: Text.ElideRight + color: palette.mid + wrapMode: Text.Wrap + opacity: 0.6 } Text { diff --git a/settings/GeneralSettings.cpp b/settings/GeneralSettings.cpp index 2928a01..cfe926a 100644 --- a/settings/GeneralSettings.cpp +++ b/settings/GeneralSettings.cpp @@ -226,6 +226,13 @@ GeneralSettings::GeneralSettings() Tr::tr("Allow tools to write and modify files on disk (WARNING: Use with caution!)")); allowFileSystemWrite.setDefaultValue(false); + autoApplyFileEdits.setSettingsKey(Constants::CA_AUTO_APPLY_FILE_EDITS); + autoApplyFileEdits.setLabelText(Tr::tr("Automatically apply file edits")); + autoApplyFileEdits.setToolTip( + Tr::tr("When enabled, file edits suggested by AI will be applied automatically. " + "When disabled, you will need to manually approve each edit.")); + autoApplyFileEdits.setDefaultValue(false); + readSettings(); Logger::instance().setLoggingEnabled(enableLogging()); @@ -272,7 +279,7 @@ GeneralSettings::GeneralSettings() auto caGroup = Group{ title(TrConstants::CHAT_ASSISTANT), Column{caGrid, - Column{useTools, allowFileSystemRead, allowFileSystemWrite}, + Column{useTools, allowFileSystemRead, allowFileSystemWrite, autoApplyFileEdits}, caTemplateDescription}}; auto rootLayout = Column{ @@ -520,6 +527,7 @@ void GeneralSettings::resetPageToDefaults() resetAspect(useTools); resetAspect(allowFileSystemRead); resetAspect(allowFileSystemWrite); + resetAspect(autoApplyFileEdits); writeSettings(); } } diff --git a/settings/GeneralSettings.hpp b/settings/GeneralSettings.hpp index 604c750..e84b6ef 100644 --- a/settings/GeneralSettings.hpp +++ b/settings/GeneralSettings.hpp @@ -103,6 +103,7 @@ public: Utils::BoolAspect useTools{this}; Utils::BoolAspect allowFileSystemRead{this}; Utils::BoolAspect allowFileSystemWrite{this}; + Utils::BoolAspect autoApplyFileEdits{this}; Utils::StringAspect caTemplateDescription{this}; diff --git a/settings/SettingsConstants.hpp b/settings/SettingsConstants.hpp index d993211..ccffc49 100644 --- a/settings/SettingsConstants.hpp +++ b/settings/SettingsConstants.hpp @@ -77,6 +77,7 @@ const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold"; const char CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion"; const char CC_MODEL_OUTPUT_HANDLER[] = "QodeAssist.ccModelOutputHandler"; const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate"; +const char CA_AUTO_APPLY_FILE_EDITS[] = "QodeAssist.caAutoApplyFileEdits"; const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold"; const char CA_LINK_OPEN_FILES[] = "QodeAssist.caLinkOpenFiles"; const char CA_AUTOSAVE[] = "QodeAssist.caAutosave";