diff --git a/qml/dialogs/HistoryDialog.qml b/qml/dialogs/HistoryDialog.qml
index 0fdf06d..0df6c93 100644
--- a/qml/dialogs/HistoryDialog.qml
+++ b/qml/dialogs/HistoryDialog.qml
@@ -28,9 +28,9 @@ Dialog {
property var historyModel: []
property int selectedIndex: -1
property string selectedHash: ""
- property string diffHtml: ""
property string versionContent: ""
property string currentContent: ""
+ property string emptyMessage: qsTr("Нет версии для просмотра")
property bool busy: false
function fileDirOf(p) {
@@ -62,7 +62,7 @@ Dialog {
historyModel = []
versionContent = ""
currentContent = ""
- diffHtml = "
" + qsTr("Файл не выбран") + "
"
+ emptyMessage = qsTr("Файл не выбран")
return
}
busy = true
@@ -79,16 +79,11 @@ Dialog {
selectedHash = ""
versionContent = ""
currentContent = ""
- const hasFile = filePath.length>0
- if (hasFile) {
- const st = gitManager.fileGitStatus(vaultPath, filePath)
- if (st === "неотслеживается" || st === "новый") {
- diffHtml = "" + qsTr("История пуста — файл ещё не сохранён в git. Сохраните файл, и здесь появится первая версия.") + "
"
- } else {
- diffHtml = "" + qsTr("Нет версий для этого файла") + "
"
- }
+ const st = gitManager.fileGitStatus(vaultPath, filePath)
+ if (st === "неотслеживается" || st === "новый") {
+ emptyMessage = qsTr("История пуста — файл ещё не сохранён в git. Сохраните файл, и здесь появится первая версия.")
} else {
- diffHtml = ""
+ emptyMessage = qsTr("Нет версий для этого файла")
}
}
busy = false
@@ -96,16 +91,12 @@ Dialog {
function updateDiff() {
if (selectedHash.length===0) {
- diffHtml = ""
versionContent = ""
currentContent = ""
return
}
// свежее содержимое с диска — diff всегда актуален, даже если файл правили при открытом диалоге
currentContent = gitManager.workdirContent(filePath)
- const html = gitManager.fileDiffHtml(vaultPath, filePath, selectedHash)
- if (html.length===0) diffHtml = "" + qsTr("Нет изменений") + "
"
- else diffHtml = html
// содержимое версии — сырой markdown, отрисуется через MarkdownView как в «Просмотре»,
// с подсветкой отличий от текущего файла прямо там
versionContent = gitManager.fileContentAt(vaultPath, filePath, selectedHash)
@@ -292,87 +283,48 @@ Dialog {
}
}
- // Правая панель — diff и превью
+ // Правая панель — версия как кнопка «Просмотр», с подсветкой отличий прямо там
ColumnLayout {
SplitView.fillWidth: true
SplitView.minimumWidth: 300
spacing: 0
- TabBar {
- id: tabBar
- Layout.fillWidth: true
- TabButton { text: qsTr("Изменения") }
- TabButton { text: qsTr("Просмотр версии") }
- }
-
- StackLayout {
+ Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
- currentIndex: tabBar.currentIndex
-
- // Diff вкладка
- Rectangle {
- color: Theme.editorBg
- border.color: Theme.divider
- ScrollView {
- anchors.fill: parent
- anchors.margins: 6
- clip: true
- ScrollBar.vertical: ScrollBar {}
- TextArea {
- id: diffArea
- readOnly: true
- textFormat: Text.RichText
- wrapMode: Text.Wrap
- color: Theme.text
- selectionColor: Theme.selectionBg
- text: root.diffHtml
- background: Rectangle { color: Theme.editorBg }
- font.family: "Menlo, monospace"
- font.pixelSize: 12
- }
- }
- }
-
- // Просмотр версии — как кнопка «Просмотр» в редакторе
- Rectangle {
- color: Theme.editorBg
- border.color: Theme.divider
- ScrollView {
- id: versionScroll
- anchors.fill: parent
- anchors.margins: 6
- clip: true
- ScrollBar.vertical: ScrollBar {}
- Column {
- width: versionScroll.availableWidth
- MarkdownView {
- id: versionPreview
- width: parent.width
- markdown: root.versionContent
- compareMarkdown: root.currentContent
- vaultRoot: root.vaultPath
- fileDir: root.fileDirOf(root.filePath)
- emptyText: qsTr("Нет версии для просмотра")
- onLinkActivated: (href) => root.handleHistoryLink(href)
- }
+ color: Theme.editorBg
+ border.color: Theme.divider
+ ScrollView {
+ id: versionScroll
+ anchors.fill: parent
+ anchors.margins: 6
+ clip: true
+ ScrollBar.vertical: ScrollBar {}
+ Column {
+ width: versionScroll.availableWidth
+ MarkdownView {
+ id: versionPreview
+ width: parent.width
+ markdown: root.versionContent
+ compareMarkdown: root.currentContent
+ vaultRoot: root.vaultPath
+ fileDir: root.fileDirOf(root.filePath)
+ emptyText: root.emptyMessage
+ onLinkActivated: (href) => root.handleHistoryLink(href)
}
}
}
}
- // Легенда — цвета отличий от текущего файла (обе вкладки)
+ // Легенда — цвета отличий от текущего файла
Row {
Layout.fillWidth: true
Layout.margins: 6
spacing: 12
Row { spacing: 4; Rectangle { width:12; height:12; color:"#7322C55E"; border.color:"#1a7f37"; radius:2; anchors.verticalCenter: parent.verticalCenter } Label { text: qsTr("добавлено"); color: Theme.textMuted; font.pixelSize:10 } }
Row { spacing: 4; Rectangle { width:12; height:12; color:"#73F43F5E"; border.color:"#e5484d"; radius:2; anchors.verticalCenter: parent.verticalCenter } Label { text: qsTr("удалено"); color: Theme.textMuted; font.pixelSize:10 } }
- Row { visible: tabBar.currentIndex === 0; spacing: 4; Rectangle { width:12; height:12; color:"#ddf4ff"; border.color:"#0969da"; radius:2; anchors.verticalCenter: parent.verticalCenter } Label { text: qsTr("заголовок"); color: Theme.textMuted; font.pixelSize:10 } }
Label {
- text: tabBar.currentIndex === 0
- ? qsTr("Сравнение выбранной версии с текущим файлом")
- : qsTr("Версия с подсветкой отличий от текущего файла")
+ text: qsTr("Версия с подсветкой отличий от текущего файла")
color: Theme.textFaint
font.pixelSize: 10
Layout.fillWidth: true
diff --git a/src/git/GitManager.cpp b/src/git/GitManager.cpp
index 351da96..07e0956 100644
--- a/src/git/GitManager.cpp
+++ b/src/git/GitManager.cpp
@@ -41,23 +41,6 @@ QString timeAgoString(const QDateTime &dt)
return dt.toString(QStringLiteral("dd.MM.yyyy hh:mm"));
}
-QString escHtml(const QString &s)
-{
- QString out;
- out.reserve(s.size());
- for (const QChar &c : s) {
- switch (c.unicode()) {
- case '&': out += "&"; break;
- case '<': out += "<"; break;
- case '>': out += ">"; break;
- case '"': out += """; break;
- case '\'': out += "'"; break;
- default: out += c; break;
- }
- }
- return out;
-}
-
} // namespace
bool GitManager::s_gitInitialized = false;
@@ -505,90 +488,6 @@ QString GitManager::fileContentAt(const QString &vaultPath, const QString &fileP
return result;
}
-QString GitManager::fileDiffText(const QString &vaultPath, const QString &filePath, const QString &commitHash) const
-{
- const QString oldContent = fileContentAt(vaultPath, filePath, commitHash);
- QFile f(filePath);
- QString newContent;
- if (f.open(QIODevice::ReadOnly))
- newContent = QString::fromUtf8(f.readAll());
- else
- newContent = QString();
-
- if (oldContent == newContent)
- return QStringLiteral("Нет изменений");
-
- // Используем git_patch_from_buffers для диффа
- git_patch *patch = nullptr;
- const QByteArray oldBytes = oldContent.toUtf8();
- const QByteArray newBytes = newContent.toUtf8();
- const QString rel = relativePath(vaultPath, filePath);
- const QByteArray relBytes = rel.toUtf8();
- git_patch_from_buffers(&patch,
- oldBytes.constData(), (size_t)oldBytes.size(), relBytes.constData(),
- newBytes.constData(), (size_t)newBytes.size(), relBytes.constData(),
- nullptr);
- if (!patch)
- return QStringLiteral("Не удалось построить diff");
-
- QString out;
- // Заголовок
- out += QStringLiteral("--- a/%1\n").arg(rel);
- out += QStringLiteral("+++ b/%1\n").arg(rel);
-
- size_t hunks = git_patch_num_hunks(patch);
- for (size_t h = 0; h < hunks; ++h) {
- const git_diff_hunk *hk = nullptr;
- size_t hunkLines = 0;
- if (git_patch_get_hunk(&hk, &hunkLines, patch, h) < 0) continue;
- out += QString::fromUtf8(hk->header, (int)hk->header_len);
- if (!out.endsWith('\n')) out += '\n';
- for (size_t l = 0; l < hunkLines; ++l) {
- const git_diff_line *line = nullptr;
- if (git_patch_get_line_in_hunk(&line, patch, h, l) < 0) continue;
- QChar origin = QChar(line->origin);
- QString text = QString::fromUtf8(line->content, (int)line->content_len);
- // line->content уже содержит \n в конце
- out += origin + text;
- if (!text.endsWith('\n') && line->content_len > 0) out += '\n';
- }
- }
- git_patch_free(patch);
- if (out.trimmed().isEmpty())
- return QStringLiteral("Нет изменений");
- return out;
-}
-
-QString GitManager::fileDiffHtml(const QString &vaultPath, const QString &filePath, const QString &commitHash) const
-{
- const QString txt = fileDiffText(vaultPath, filePath, commitHash);
- if (txt == QStringLiteral("Нет изменений") || txt.startsWith(QStringLiteral("Не удалось")))
- return QStringLiteral("%1
").arg(escHtml(txt));
-
- QString html;
- html.reserve(txt.size()*2);
- html += "";
- const QStringList lines = txt.split('\n');
- for (const QString &rawLine : lines) {
- if (rawLine.isEmpty()) { html += "\n"; continue; }
- QChar first = rawLine.at(0);
- QString esc = escHtml(rawLine);
- if (first == '+') {
- html += QStringLiteral("%1
").arg(esc);
- } else if (first == '-') {
- html += QStringLiteral("%1
").arg(esc);
- } else if (first == '@') {
- html += QStringLiteral("%1
").arg(esc);
- } else if (first == ' ') {
- html += QStringLiteral("%1
").arg(esc);
- } else {
- html += QStringLiteral("%1
").arg(esc);
- }
- }
- html += "";
- return html;
-}
-
QString GitManager::restoreFile(const QString &vaultPath, const QString &filePath, const QString &commitHash)
{
const QString vault = QDir::cleanPath(vaultPath);
diff --git a/src/git/GitManager.h b/src/git/GitManager.h
index 3de0810..a1dee87 100644
--- a/src/git/GitManager.h
+++ b/src/git/GitManager.h
@@ -41,11 +41,6 @@ public:
// Текущее содержимое файла на диске (для сравнения с версией). Пусто при ошибке/лимите 2 МБ.
Q_INVOKABLE QString workdirContent(const QString &filePath) const;
- // Diff между версией в коммите и текущим файлом (рабочее дерево). Возвращает HTML с цветами.
- Q_INVOKABLE QString fileDiffHtml(const QString &vaultPath, const QString &filePath, const QString &commitHash) const;
- // Текстовый diff (unified)
- Q_INVOKABLE QString fileDiffText(const QString &vaultPath, const QString &filePath, const QString &commitHash) const;
-
// Восстановить файл из коммита (перезаписать и закоммитить). Возвращает "" при успехе или текст ошибки.
Q_INVOKABLE QString restoreFile(const QString &vaultPath, const QString &filePath, const QString &commitHash);
diff --git a/src/markdown/MdParser.cpp b/src/markdown/MdParser.cpp
index bbd4608..0afa8e3 100644
--- a/src/markdown/MdParser.cpp
+++ b/src/markdown/MdParser.cpp
@@ -217,37 +217,6 @@ QString tintDiffHtml(const QString &inner, bool isDel)
return QStringLiteral("%1
").arg(inner);
}
-// Токенизация на слова/пробелы для словного diff (границы слов не рвём)
-QStringList tokenizeWords(const QString &s)
-{
- QStringList toks;
- QString cur;
- bool curSpace = false;
- bool first = true;
- for (const QChar &c : s) {
- const bool sp = c.isSpace();
- if (first) { cur += c; curSpace = sp; first = false; continue; }
- if (sp == curSpace)
- cur += c;
- else {
- toks.append(cur);
- cur = QString(c);
- curSpace = sp;
- }
- }
- if (!cur.isEmpty())
- toks.append(cur);
- return toks;
-}
-
-// Блоки, где встроенный словный diff опасен (ссылки/URL/код — плейсхолдеры
-// порвут разметку): для них оставляем два отдельных tinted-блока
-bool riskyInlineMerge(const QString &block)
-{
- return block.contains(QLatin1Char('`')) || block.contains(QLatin1String("]("))
- || block.contains(QLatin1String("://"));
-}
-
QString fileHrefFor(const QString &path)
{
const QByteArray enc = QUrl::toPercentEncoding(QDir::cleanPath(path));
@@ -876,6 +845,17 @@ private:
QString MdParser::render(const QString &markdown, const QString &vaultRoot,
const QString &fileDir)
+{
+ // Публичный вызов начинает нумерацию кодовых блоков заново (code_0, code_1, ...)
+ if (m) {
+ m->codeBlocks.clear();
+ m->codeCounter = 0;
+ }
+ return renderInner(markdown, vaultRoot, fileDir);
+}
+
+QString MdParser::renderInner(const QString &markdown, const QString &vaultRoot,
+ const QString &fileDir)
{
cmark_gfm_core_extensions_ensure_registered();
cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
@@ -888,10 +868,6 @@ QString MdParser::render(const QString &markdown, const QString &vaultRoot,
cmark_parser_feed(parser, utf8.constData(), static_cast(utf8.size()));
cmark_node *doc = cmark_parser_finish(parser);
- if (m) {
- m->codeBlocks.clear();
- m->codeCounter = 0;
- }
Renderer r(m, vaultRoot, fileDir);
QString html = r.renderTree(doc);
@@ -923,12 +899,7 @@ QVariantList MdParser::parseBlocks(const QString &markdown, const QString &vault
if (start > lastPos) {
QString htmlPart = markdown.mid(lastPos, start - lastPos);
if (!htmlPart.trimmed().isEmpty()) {
- // Сохраняем codeBlocks, т.к. render() их очищает
- QHash savedBlocks;
- int savedCounter = 0;
- if (m) { savedBlocks = m->codeBlocks; savedCounter = m->codeCounter; }
- QString html = render(htmlPart, vaultRoot, fileDir);
- if (m) { m->codeBlocks = savedBlocks; m->codeCounter = savedCounter; }
+ QString html = renderInner(htmlPart, vaultRoot, fileDir);
if (!html.trimmed().isEmpty()) {
QVariantMap block;
block["type"] = "html";
@@ -989,11 +960,7 @@ QVariantList MdParser::parseBlocks(const QString &markdown, const QString &vault
}
// Если вообще не нашли блоки, но markdown не пустой — вернём как html
if (blocks.isEmpty() && !markdown.trimmed().isEmpty()) {
- QHash savedBlocks;
- int savedCounter = 0;
- if (m) { savedBlocks = m->codeBlocks; savedCounter = m->codeCounter; }
- QString html = render(markdown, vaultRoot, fileDir);
- if (m) { m->codeBlocks = savedBlocks; m->codeCounter = savedCounter; }
+ QString html = renderInner(markdown, vaultRoot, fileDir);
QVariantMap block;
block["type"] = "html";
block["html"] = html;
@@ -1025,16 +992,6 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
if (oldMd.isEmpty() && newMd.isEmpty())
return blocks;
- // render() внутри очищает codeBlocks — сохраняем/восстанавливаем вокруг вызовов
- auto renderKeep = [&](const QString &part) -> QString {
- QHash savedBlocks;
- int savedCounter = 0;
- if (m) { savedBlocks = m->codeBlocks; savedCounter = m->codeCounter; }
- QString html = render(part, vaultRoot, fileDir);
- if (m) { m->codeBlocks = savedBlocks; m->codeCounter = savedCounter; }
- return html;
- };
-
auto appendHtmlBlock = [&](const QString &html, const char *change) {
QVariantMap block;
block["type"] = "html";
@@ -1131,73 +1088,6 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
return true;
};
- // Обычный текст (del+add парой): один блок со словным diff внутри через
- // плейсхолдеры — разметка не рвётся, подстановка только в текстовых узлах
- auto appendMergedText = [&](const QString &oldT, const QString &newT) -> bool {
- if (riskyInlineMerge(oldT) || riskyInlineMerge(newT))
- return false;
- const QStringList a = tokenizeWords(oldT);
- const QStringList b = tokenizeWords(newT);
- if (a.isEmpty() || b.isEmpty())
- return false;
- if ((long long)a.size() * b.size() > 250000)
- return false;
- const QVector wops = diffMdBlocks(a, b);
- struct Ph { QString token; QString word; bool isDel; };
- QVector phs;
- QString merged;
- int phId = 0;
- bool hasChange = false;
- for (const DiffOp &w : wops) {
- if (w.kind == '=') {
- merged += w.text;
- } else {
- hasChange = true;
- const QString ph = QString(QChar(0xE000)) + QString::number(phId++)
- + QString(QChar(0xE001));
- merged += ph;
- phs.append({ph, w.text, w.kind == '-'});
- }
- }
- if (!hasChange)
- return false;
- QString html = renderKeep(merged);
- if (html.trimmed().isEmpty())
- return false;
- // подстановка плейсхолдеров: в текстовых узлах — цветной span,
- // внутри тегов (ссылки/атрибуты) — просто слово, чтобы не ломать разметку
- for (const Ph &ph : phs) {
- const QString styled = QStringLiteral("%2")
- .arg(ph.isDel ? QStringLiteral("rgba(244,63,94,0.45)")
- : QStringLiteral("rgba(34,197,94,0.45)"),
- esc(ph.word));
- const QString plain = esc(ph.word);
- QString out;
- out.reserve(html.size() + styled.size());
- int pos = 0;
- bool found = false;
- while (true) {
- const int f = html.indexOf(ph.token, pos);
- if (f < 0) {
- out += html.mid(pos);
- break;
- }
- found = true;
- const int lt = html.lastIndexOf(QLatin1Char('<'), f);
- const int gt = (lt >= 0) ? html.indexOf(QLatin1Char('>'), lt) : -1;
- const bool inTag = (lt >= 0 && (gt < 0 || gt > f));
- out += html.mid(pos, f - pos);
- out += inTag ? plain : styled;
- pos = f + ph.token.length();
- }
- if (!found)
- return false;
- html = out;
- }
- appendHtmlBlock(html, "mod");
- return true;
- };
-
const QVector ops = diffMdBlocks(splitMdBlocks(oldMd), splitMdBlocks(newMd));
for (int k = 0; k < ops.size(); ++k) {
const DiffOp &op = ops[k];
@@ -1206,16 +1096,12 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
const char *change = op.kind == '=' ? "same" : (isDel ? "del" : "add");
QString lang, code;
const bool isCode = isFenceBlock(op.text) && parseFenceBlock(op.text, &lang, &code);
- if (isDel && k + 1 < ops.size() && ops[k + 1].kind == '+') {
- if (isCode) {
- QString nlang, ncode;
- if (isFenceBlock(ops[k + 1].text) && parseFenceBlock(ops[k + 1].text, &nlang, &ncode)
- && appendMergedCode(lang, code, nlang, ncode)) {
- ++k;
- continue;
- }
- } else if (!isFenceBlock(ops[k + 1].text)
- && appendMergedText(op.text, ops[k + 1].text)) {
+ // Изменённый код — один блок с построчным diff внутри; обычный текст —
+ // отдельными tinted-блоками (del, затем add), как стандартный diff
+ 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;
}
@@ -1223,7 +1109,7 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
if (isCode) {
appendCodeBlock(lang, code, change, changed);
} else {
- QString html = renderKeep(op.text);
+ QString html = renderInner(op.text, vaultRoot, fileDir);
if (html.trimmed().isEmpty())
continue;
if (changed)
@@ -1235,7 +1121,7 @@ QVariantList MdParser::parseBlocksWithDiff(const QString &oldMd, const QString &
// Пустой результат при непустом вводе — отдать как один html-блок
if (blocks.isEmpty() && (!oldMd.trimmed().isEmpty() || !newMd.trimmed().isEmpty())) {
const QString src = !oldMd.trimmed().isEmpty() ? oldMd : newMd;
- const QString html = renderKeep(src);
+ const QString html = renderInner(src, vaultRoot, fileDir);
if (!html.trimmed().isEmpty())
appendHtmlBlock(html, "same");
}
diff --git a/src/markdown/MdParser.h b/src/markdown/MdParser.h
index 3ac6c03..edac4e3 100644
--- a/src/markdown/MdParser.h
+++ b/src/markdown/MdParser.h
@@ -42,5 +42,11 @@ public:
struct Impl;
private:
+ // Внутренний рендер без сброса codeBlocks/codeCounter (для parseBlocks*,
+ // которые сами управляют состоянием и вызывают его многократно)
+ QString renderInner(const QString &markdown,
+ const QString &vaultRoot,
+ const QString &fileDir);
+
Impl *m = nullptr;
};
\ No newline at end of file
diff --git a/tests/git_history_test.cpp b/tests/git_history_test.cpp
index 0694985..4431461 100644
--- a/tests/git_history_test.cpp
+++ b/tests/git_history_test.cpp
@@ -66,14 +66,8 @@ int main(int argc, char *argv[])
// content at
QString c1 = git.fileContentAt(vault, file, hash1);
if(!c1.contains("Версия 1")) return fail("content v1", c1);
- // diff с текущим (одинаково) должен быть "Нет изменений" или пусто?
- QString diffSame = git.fileDiffText(vault, file, hash1);
- if(diffSame != "Нет изменений" && !diffSame.isEmpty()) {
- // если файл не менялся, diff должен быть "Нет изменений"
- // Но мы ещё не меняли, так что должно быть нет изменений
- if(!diffSame.contains("Нет изменений"))
- return fail("diff same should be нет изменений", diffSame);
- }
+ // workdirContent читает текущее с диска
+ if(git.workdirContent(file) != "Версия 1\nСтрока A\n") return fail("workdirContent", git.workdirContent(file));
// Изменяем файл → v2
QThread::msleep(1100); // чтобы время коммита отличалось
@@ -95,12 +89,6 @@ int main(int argc, char *argv[])
// content at hash1 всё ещё v1
QString c1again = git.fileContentAt(vault, file, hash1);
if(!c1again.contains("Версия 1") || c1again.contains("Строка B")) return fail("c1again", c1again);
- // diff hash1 vs current (v2): должен показать + строка B и -Версия1 +Версия2
- QString diff = git.fileDiffText(vault, file, hash1);
- if(!diff.contains("+") || !diff.contains("-")) return fail("diff should have + -", diff);
- if(!diff.contains("Версия 1") || !diff.contains("Версия 2")) return fail("diff contains versions", diff);
- QString diffHtml = git.fileDiffHtml(vault, file, hash1);
- if(!diffHtml.contains("34,197,94") || !diffHtml.contains("244,63,94")) return fail("diffHtml colors", diffHtml.left(500));
// forward/backward: добавляем ещё файл other.md, коммитим, история note.md не должна вырасти
QString other = vault+"/other.md";
@@ -140,18 +128,10 @@ int main(int argc, char *argv[])
if(hist5.size()!=5) return fail("hist 5", QString::number(hist5.size()));
if(hist5.first().toMap().value("message").toString()!="v3") return fail("top v3");
- // Проверка что diff между v1 и текущим (v3) показывает изменения
- QString diffV1Now = git.fileDiffText(vault, file, hash1);
- if(!diffV1Now.contains("Версия 1") || !diffV1Now.contains("Версия 3")) return fail("diff v1->v3", diffV1Now);
-
// Проверка несуществующего коммита
QString badContent = git.fileContentAt(vault, file, "0000000000000000000000000000000000000000");
if(!badContent.isEmpty()) return fail("bad hash should return empty");
- QString badDiff = git.fileDiffText(vault, file, "0000000000000000000000000000000000000000");
- // должен либо вернуть ошибку diff, либо содержать old empty
- // не проверяем строго
-
QString badRestore = git.restoreFile(vault, file, "0000000000000000000000000000000000000000");
if(badRestore.isEmpty()) return fail("bad restore should fail");
diff --git a/tests/mdparser_test.cpp b/tests/mdparser_test.cpp
index f222ff1..d1b7b10 100644
--- a/tests/mdparser_test.cpp
+++ b/tests/mdparser_test.cpp
@@ -184,32 +184,21 @@ int main(int argc, char *argv[])
}
}
- // parseBlocksWithDiff: правка в обычном тексте — ОДИН блок со словным diff внутри
+ // parseBlocksWithDiff: правка в обычном тексте — два отдельных tinted-блока
+ // (del, затем add), как стандартный diff; словного уровня нет
{
const QString oldMd = "# T\n\nOld words here\n";
const QString newMd = "# T\n\nNew words here\n";
auto d = p.parseBlocksWithDiff(oldMd, newMd, vault, fileDir);
- if (d.size() != 2) return fail("text merge размер", QString::number(d.size()));
- if (d[0].toMap().value("change").toString() != "same") return fail("text merge[0] same");
- if (d[1].toMap().value("type").toString() != "html"
- || d[1].toMap().value("change").toString() != "mod")
- return fail("text merge[1] должен быть html/mod", d[1].toMap().value("change").toString());
- const QString html = d[1].toMap().value("html").toString();
- if (!html.contains("244,63,94")) return fail("text merge без розового");
- if (!html.contains("34,197,94")) return fail("text merge без зелёного");
- if (!html.contains("Old") || !html.contains("New")) return fail("text merge потерял слова", html.left(300));
- if (!html.contains("words here")) return fail("text merge потерял общее", html.left(300));
- }
-
- // parseBlocksWithDiff: ссылка поменялась — fallback на два отдельных блока (разметку не рвём)
- {
- const QString oldMd = "# T\n\n[a](http://old.com/x)\n";
- const QString newMd = "# T\n\n[a](http://new.com/x)\n";
- auto d = p.parseBlocksWithDiff(oldMd, newMd, vault, fileDir);
- if (d.size() != 3) return fail("link fallback размер", QString::number(d.size()));
+ if (d.size() != 3) return fail("text diff размер", QString::number(d.size()));
+ if (d[0].toMap().value("change").toString() != "same") return fail("text diff[0] same");
if (d[1].toMap().value("change").toString() != "del"
|| d[2].toMap().value("change").toString() != "add")
- return fail("link fallback del/add");
+ return fail("text diff del/add");
+ if (!d[1].toMap().value("html").toString().contains("244,63,94"))
+ return fail("text del без розового");
+ if (!d[2].toMap().value("html").toString().contains("34,197,94"))
+ return fail("text add без зелёного");
}
// parseBlocksWithDiff: правка внутри кода — ОДИН блок с построчным diff внутри