Files
QodeAssist/sources/common/ResponseCleaner.hpp
2026-07-02 22:26:32 +02:00

128 lines
3.6 KiB
C++

// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QRegularExpression>
#include <QString>
#include <QStringList>
namespace QodeAssist {
class ResponseCleaner
{
public:
static QString clean(const QString &response)
{
bool extractedFromFence = false;
QString cleaned = removeCodeBlocks(response, extractedFromFence);
cleaned = trimWhitespace(cleaned);
cleaned = removeExplanations(cleaned, extractedFromFence);
return cleaned;
}
private:
static QString removeCodeBlocks(const QString &text, bool &extractedFromFence)
{
if (!text.contains("```")) {
return text;
}
QRegularExpression codeBlockRegex("```\\w*\\n([\\s\\S]*?)```");
QRegularExpressionMatch match = codeBlockRegex.match(text);
if (match.hasMatch()) {
extractedFromFence = true;
return match.captured(1);
}
int firstFence = text.indexOf("```");
int lastFence = text.lastIndexOf("```");
if (firstFence != -1 && lastFence > firstFence) {
int firstNewLine = text.indexOf('\n', firstFence);
if (firstNewLine != -1) {
extractedFromFence = true;
return text.mid(firstNewLine + 1, lastFence - firstNewLine - 1);
}
}
return text;
}
static QString trimWhitespace(const QString &text)
{
QString result = text;
while (result.startsWith('\n') || result.startsWith('\r')) {
result = result.mid(1);
}
while (result.endsWith('\n') || result.endsWith('\r')) {
result.chop(1);
}
return result;
}
static bool isProseHeader(const QString &line)
{
if (line.length() >= 50 || !line.endsWith(':') || !line.contains(' ')) {
return false;
}
const QString head = line.left(line.length() - 1);
for (const QChar c : head) {
if (!c.isLetter() && c != ' ') {
return false;
}
}
return true;
}
static QString removeExplanations(const QString &text, bool extractedFromFence)
{
static const QStringList explanationPrefixes
= {"here's the",
"here is the",
"here's",
"here is",
"the refactored",
"refactored code:",
"code:",
"i've refactored",
"i refactored",
"i've changed",
"i changed"};
QStringList lines = text.split('\n');
int startLine = 0;
for (int i = 0; i < qMin(3, lines.size()); ++i) {
QString line = lines[i].trimmed().toLower();
bool isExplanation = false;
for (const QString &prefix : explanationPrefixes) {
if (line.startsWith(prefix) || line.contains(prefix + " code")) {
isExplanation = true;
break;
}
}
if (!extractedFromFence && isProseHeader(line)) {
isExplanation = true;
}
if (isExplanation) {
startLine = i + 1;
} else if (!line.isEmpty()) {
break;
}
}
if (startLine > 0 && startLine < lines.size()) {
lines = lines.mid(startLine);
return lines.join('\n');
}
return text;
}
};
} // namespace QodeAssist