История: diff внутри того же блока кода + насыщенные цвета
- parseBlocksWithDiff: пара del/add кодовых блоков сливается в ОДИН блок (change=mod) с построчным LCS-diff внутри: удалённые строки — розовая подложка rgba(244,63,94,.45) с −, добавленные — зелёная rgba(34,197,94,.45) с +, неизменные — как были; копируется новое содержимое; fallback — два блока - Цвета насыщеннее: html тинты #ffd6dd/#b7f5c6 (были бледные), текстовый diff тоже, легенда в тон; на тёмном пузыре светлый текст читается - Тесты: merge в один блок, маркеры, цвета, copy-нового
This commit is contained in:
@@ -208,12 +208,12 @@ QVector<DiffOp> diffMdBlocks(const QStringList &a, const QStringList &b)
|
||||
return ops;
|
||||
}
|
||||
|
||||
// Цветная обёртка изменённого блока (те же цвета, что в fileDiffHtml)
|
||||
// Цветная обёртка изменённого блока: насыщенные розовый/зелёный, текст читается
|
||||
QString tintDiffHtml(const QString &inner, bool isDel)
|
||||
{
|
||||
if (isDel)
|
||||
return QStringLiteral("<div style=\"background-color:#ffebe9;border-left:3px solid #cf222e;border-radius:6px;padding:6px 8px;margin:4px 0\">%1</div>").arg(inner);
|
||||
return QStringLiteral("<div style=\"background-color:#e6ffec;border-left:3px solid #1a7f37;border-radius:6px;padding:6px 8px;margin:4px 0\">%1</div>").arg(inner);
|
||||
return QStringLiteral("<div style=\"background-color:#ffd6dd;border-left:3px solid #e5484d;border-radius:6px;padding:6px 8px;margin:4px 0\">%1</div>").arg(inner);
|
||||
return QStringLiteral("<div style=\"background-color:#b7f5c6;border-left:3px solid #1a7f37;border-radius:6px;padding:6px 8px;margin:4px 0\">%1</div>").arg(inner);
|
||||
}
|
||||
|
||||
QString fileHrefFor(const QString &path)
|
||||
@@ -1015,6 +1015,14 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
|
||||
blocks.append(block);
|
||||
};
|
||||
|
||||
// Насыщенная полупрозрачная подложка для кода: пузырь CodeBlock всегда тёмно-синий,
|
||||
// светлый текст на ней читается, в отличие от пастели
|
||||
auto tintCodeHtml = [&](const QString &inner, bool isDel) -> QString {
|
||||
const QString bg = isDel ? QStringLiteral("rgba(244,63,94,0.45)")
|
||||
: QStringLiteral("rgba(34,197,94,0.45)");
|
||||
return QStringLiteral("<div style=\"background-color:%1;border-radius:6px;padding:6px 8px;margin:4px 0\">%2</div>").arg(bg, inner);
|
||||
};
|
||||
|
||||
auto appendCodeBlock = [&](const QString &lang, const QString &code, const char *change, bool tint) {
|
||||
QString codeId;
|
||||
if (m) {
|
||||
@@ -1025,7 +1033,7 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
|
||||
QString highlighted = (m && m->m_repo) ? hl.highlight(code, lang, true)
|
||||
: plainToRich(code);
|
||||
if (tint)
|
||||
highlighted = tintDiffHtml(highlighted, change[0] == 'd');
|
||||
highlighted = tintCodeHtml(highlighted, change[0] == 'd');
|
||||
QVariantMap block;
|
||||
block["type"] = "code";
|
||||
block["codeText"] = code;
|
||||
@@ -1037,13 +1045,85 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
|
||||
blocks.append(block);
|
||||
};
|
||||
|
||||
// Изменённый код (del-блок сразу за add-блоком): один блок с построчной
|
||||
// подсветкой внутри — изменения видны там, где сделаны. Копируется новое.
|
||||
auto appendMergedCode = [&](const QString &oldLang, const QString &oldCode,
|
||||
const QString &newLang, const QString &newCode) -> bool {
|
||||
CodeHighlighter hl(m ? m->m_repo : nullptr);
|
||||
const QString oldHl = (m && m->m_repo) ? hl.highlight(oldCode, oldLang, true)
|
||||
: plainToRich(oldCode);
|
||||
const QString newHl = (m && m->m_repo) ? hl.highlight(newCode, newLang, true)
|
||||
: plainToRich(newCode);
|
||||
const QStringList oldLines = oldCode.split(QLatin1Char('\n'));
|
||||
const QStringList newLines = newCode.split(QLatin1Char('\n'));
|
||||
const QStringList oldFrag = oldHl.split(QStringLiteral("<br/>"));
|
||||
const QStringList newFrag = newHl.split(QStringLiteral("<br/>"));
|
||||
if (oldFrag.size() != oldLines.size() || newFrag.size() != newLines.size())
|
||||
return false; // fallback: два отдельных блока
|
||||
const QVector<DiffOp> lops = diffMdBlocks(oldLines, newLines);
|
||||
QStringList out;
|
||||
QStringList pending;
|
||||
auto flushPending = [&]() {
|
||||
if (!pending.isEmpty()) {
|
||||
out.append(pending.join(QStringLiteral("<br/>")));
|
||||
pending.clear();
|
||||
}
|
||||
};
|
||||
int oi = 0, ni = 0;
|
||||
for (const DiffOp &lop : lops) {
|
||||
if (lop.kind == '=') {
|
||||
pending.append(oldFrag[oi]);
|
||||
++oi; ++ni;
|
||||
} else {
|
||||
flushPending();
|
||||
const bool isDel = lop.kind == '-';
|
||||
QString frag = isDel ? oldFrag[oi++] : newFrag[ni++];
|
||||
if (frag.isEmpty())
|
||||
frag = QStringLiteral("<br/>");
|
||||
const QString mark = isDel
|
||||
? QStringLiteral("<span style=\"color:#ff8f9e;font-weight:bold;\">\u2212 </span>")
|
||||
: QStringLiteral("<span style=\"color:#4ade80;font-weight:bold;\">+ </span>");
|
||||
const QString bg = isDel ? QStringLiteral("rgba(244,63,94,0.45)")
|
||||
: QStringLiteral("rgba(34,197,94,0.45)");
|
||||
out.append(QStringLiteral("<div style=\"background-color:%1;border-radius:4px;padding:0 6px;margin:1px 0;\">%2%3</div>").arg(bg, mark, frag));
|
||||
}
|
||||
}
|
||||
flushPending();
|
||||
const QString lang = !newLang.isEmpty() ? newLang : oldLang;
|
||||
QString codeId;
|
||||
if (m) {
|
||||
codeId = QStringLiteral("code_%1").arg(m->codeCounter++);
|
||||
m->codeBlocks.insert(codeId, newCode);
|
||||
}
|
||||
QVariantMap block;
|
||||
block["type"] = "code";
|
||||
block["codeText"] = newCode;
|
||||
block["language"] = lang.isEmpty() ? "text" : lang;
|
||||
block["codeId"] = codeId;
|
||||
block["highlightedText"] = out.join(QString());
|
||||
block["html"] = "";
|
||||
block["change"] = "mod";
|
||||
blocks.append(block);
|
||||
return true;
|
||||
};
|
||||
|
||||
const QVector<DiffOp> ops = diffMdBlocks(splitMdBlocks(oldMd), splitMdBlocks(newMd));
|
||||
for (const DiffOp &op : ops) {
|
||||
for (int k = 0; k < ops.size(); ++k) {
|
||||
const DiffOp &op = ops[k];
|
||||
const bool changed = op.kind != '=';
|
||||
const bool isDel = op.kind == '-';
|
||||
const char *change = op.kind == '=' ? "same" : (isDel ? "del" : "add");
|
||||
QString lang, code;
|
||||
if (isFenceBlock(op.text) && parseFenceBlock(op.text, &lang, &code)) {
|
||||
const bool isCode = isFenceBlock(op.text) && parseFenceBlock(op.text, &lang, &code);
|
||||
if (isCode && isDel && k + 1 < ops.size() && ops[k + 1].kind == '+') {
|
||||
QString nlang, ncode;
|
||||
if (isFenceBlock(ops[k + 1].text) && parseFenceBlock(ops[k + 1].text, &nlang, &ncode)
|
||||
&& appendMergedCode(lang, code, nlang, ncode)) {
|
||||
++k;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isCode) {
|
||||
appendCodeBlock(lang, code, change, changed);
|
||||
} else {
|
||||
QString html = renderKeep(op.text);
|
||||
|
||||
Reference in New Issue
Block a user