feat: Add settings for auto apply changes

This commit is contained in:
Petr Mironychev
2025-10-20 18:10:21 +02:00
parent 0365018834
commit 254fac246d
6 changed files with 109 additions and 22 deletions

View File

@ -20,6 +20,7 @@
#include "FileEditItem.hpp" #include "FileEditItem.hpp"
#include "Logger.hpp" #include "Logger.hpp"
#include "settings/GeneralSettings.hpp"
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
@ -89,10 +90,15 @@ void FileEditItem::parseFromContent(const QString &content)
emit editModeChanged(); emit editModeChanged();
emit originalContentChanged(); emit originalContentChanged();
emit newContentChanged(); emit newContentChanged();
emit contextBeforeChanged();
emit contextAfterChanged();
emit addedLinesChanged(); emit addedLinesChanged();
emit removedLinesChanged(); emit removedLinesChanged();
applyEditInternal(true); bool autoApplyEnabled = Settings::generalSettings().autoApplyFileEdits.value();
if (autoApplyEnabled) {
applyEditInternal(true);
}
} }
void FileEditItem::applyEdit() void FileEditItem::applyEdit()
@ -102,8 +108,15 @@ void FileEditItem::applyEdit()
void FileEditItem::applyEditInternal(bool isAutomatic, int retryCount) void FileEditItem::applyEditInternal(bool isAutomatic, int retryCount)
{ {
if (!isAutomatic && m_status != EditStatus::Reverted && m_status != EditStatus::Rejected) { if (isAutomatic) {
return; 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)) { if (!acquireFileLock(m_filePath)) {

View File

@ -51,6 +51,8 @@ public:
Q_PROPERTY(QString editMode READ editMode NOTIFY editModeChanged FINAL) Q_PROPERTY(QString editMode READ editMode NOTIFY editModeChanged FINAL)
Q_PROPERTY(QString originalContent READ originalContent NOTIFY originalContentChanged FINAL) Q_PROPERTY(QString originalContent READ originalContent NOTIFY originalContentChanged FINAL)
Q_PROPERTY(QString newContent READ newContent NOTIFY newContentChanged 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 addedLines READ addedLines NOTIFY addedLinesChanged FINAL)
Q_PROPERTY(int removedLines READ removedLines NOTIFY removedLinesChanged FINAL) Q_PROPERTY(int removedLines READ removedLines NOTIFY removedLinesChanged FINAL)
Q_PROPERTY(EditStatus status READ status NOTIFY statusChanged FINAL) Q_PROPERTY(EditStatus status READ status NOTIFY statusChanged FINAL)
@ -64,6 +66,8 @@ public:
QString editMode() const { return m_editMode; } QString editMode() const { return m_editMode; }
QString originalContent() const { return m_originalContent; } QString originalContent() const { return m_originalContent; }
QString newContent() const { return m_newContent; } 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 addedLines() const { return m_addedLines; }
int removedLines() const { return m_removedLines; } int removedLines() const { return m_removedLines; }
EditStatus status() const { return m_status; } EditStatus status() const { return m_status; }
@ -79,6 +83,8 @@ signals:
void editModeChanged(); void editModeChanged();
void originalContentChanged(); void originalContentChanged();
void newContentChanged(); void newContentChanged();
void contextBeforeChanged();
void contextAfterChanged();
void addedLinesChanged(); void addedLinesChanged();
void removedLinesChanged(); void removedLinesChanged();
void statusChanged(); void statusChanged();

View File

@ -38,13 +38,6 @@ FileEditItem {
readonly property int headerPadding: 8 readonly property int headerPadding: 8
readonly property int statusIndicatorWidth: 4 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 isPending: status === FileEditItem.Pending
readonly property bool isApplied: status === FileEditItem.Applied readonly property bool isApplied: status === FileEditItem.Applied
readonly property bool isReverted: status === FileEditItem.Reverted readonly property bool isReverted: status === FileEditItem.Reverted
@ -159,10 +152,21 @@ FileEditItem {
Text { Text {
id: headerText id: headerText
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("File Edit: %1 (+%2 -%3)") text: {
.arg(root.filePath) var modeText = ""
.arg(root.addedLines) switch(root.editMode) {
.arg(root.removedLines) 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.pixelSize: 12
font.bold: true font.bold: true
color: palette.text color: palette.text
@ -194,7 +198,7 @@ FileEditItem {
QoAButton { QoAButton {
text: qsTr("Apply") text: qsTr("Apply")
enabled: root.isReverted || root.isRejected enabled: root.isPending || root.isReverted || root.isRejected
visible: !root.isApplied visible: !root.isApplied
onClicked: root.applyEdit() onClicked: root.applyEdit()
} }
@ -220,22 +224,76 @@ FileEditItem {
spacing: 4 spacing: 4
visible: opacity > 0 visible: opacity > 0
// Context before (if available)
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: "Old: " + root.firstOriginalLine + (root.hasMultipleOriginalLines ? "..." : "") visible: root.contextBefore.length > 0
text: root.contextBefore
font.family: root.codeFontFamily font.family: root.codeFontFamily
font.pixelSize: root.codeFontSize font.pixelSize: root.codeFontSize
color: Qt.rgba(1, 0.2, 0.2, 0.9) color: palette.mid
elide: Text.ElideRight 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 { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: "New: " + root.firstNewLine + (root.hasMultipleNewLines ? "..." : "") visible: root.contextAfter.length > 0
text: root.contextAfter
font.family: root.codeFontFamily font.family: root.codeFontFamily
font.pixelSize: root.codeFontSize font.pixelSize: root.codeFontSize
color: Qt.rgba(0.2, 0.8, 0.2, 0.9) color: palette.mid
elide: Text.ElideRight wrapMode: Text.Wrap
opacity: 0.6
} }
Text { Text {

View File

@ -226,6 +226,13 @@ GeneralSettings::GeneralSettings()
Tr::tr("Allow tools to write and modify files on disk (WARNING: Use with caution!)")); Tr::tr("Allow tools to write and modify files on disk (WARNING: Use with caution!)"));
allowFileSystemWrite.setDefaultValue(false); 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(); readSettings();
Logger::instance().setLoggingEnabled(enableLogging()); Logger::instance().setLoggingEnabled(enableLogging());
@ -272,7 +279,7 @@ GeneralSettings::GeneralSettings()
auto caGroup = Group{ auto caGroup = Group{
title(TrConstants::CHAT_ASSISTANT), title(TrConstants::CHAT_ASSISTANT),
Column{caGrid, Column{caGrid,
Column{useTools, allowFileSystemRead, allowFileSystemWrite}, Column{useTools, allowFileSystemRead, allowFileSystemWrite, autoApplyFileEdits},
caTemplateDescription}}; caTemplateDescription}};
auto rootLayout = Column{ auto rootLayout = Column{
@ -520,6 +527,7 @@ void GeneralSettings::resetPageToDefaults()
resetAspect(useTools); resetAspect(useTools);
resetAspect(allowFileSystemRead); resetAspect(allowFileSystemRead);
resetAspect(allowFileSystemWrite); resetAspect(allowFileSystemWrite);
resetAspect(autoApplyFileEdits);
writeSettings(); writeSettings();
} }
} }

View File

@ -103,6 +103,7 @@ public:
Utils::BoolAspect useTools{this}; Utils::BoolAspect useTools{this};
Utils::BoolAspect allowFileSystemRead{this}; Utils::BoolAspect allowFileSystemRead{this};
Utils::BoolAspect allowFileSystemWrite{this}; Utils::BoolAspect allowFileSystemWrite{this};
Utils::BoolAspect autoApplyFileEdits{this};
Utils::StringAspect caTemplateDescription{this}; Utils::StringAspect caTemplateDescription{this};

View File

@ -77,6 +77,7 @@ const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
const char CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion"; const char CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion";
const char CC_MODEL_OUTPUT_HANDLER[] = "QodeAssist.ccModelOutputHandler"; const char CC_MODEL_OUTPUT_HANDLER[] = "QodeAssist.ccModelOutputHandler";
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate"; 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_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold";
const char CA_LINK_OPEN_FILES[] = "QodeAssist.caLinkOpenFiles"; const char CA_LINK_OPEN_FILES[] = "QodeAssist.caLinkOpenFiles";
const char CA_AUTOSAVE[] = "QodeAssist.caAutosave"; const char CA_AUTOSAVE[] = "QodeAssist.caAutosave";