Фаза 6: просмотр Markdown — MdParser (cmark-gfm AST→HTML: таблицы/ссылки/вики/картинки data-URI/задачи-чекбоксы), подсветка кода KSyntaxHighlighting (алиасы «С»/«С++»/python), RichText-просмотр в EditorPane (onLinkActivated); тест mdparser (15/15)

This commit is contained in:
Flora (opencode)
2026-08-17 13:05:19 +03:00
parent 3317aa169a
commit c4708b6be6
8 changed files with 765 additions and 6 deletions
+21 -1
View File
@@ -54,4 +54,24 @@ add_executable(flora_docmgr_test documentmanager_test.cpp
target_include_directories(flora_docmgr_test PRIVATE ../src)
target_link_libraries(flora_docmgr_test PRIVATE
Qt6::Core)
add_test(NAME flora_docmgr_test COMMAND flora_docmgr_test)
add_test(NAME flora_docmgr_test COMMAND flora_docmgr_test)
# Тест MdParser (рендер Markdown → HTML).
add_executable(flora_mdparser_test mdparser_test.cpp
../src/markdown/MdParser.cpp
../src/markdown/MdParser.h
)
target_include_directories(flora_mdparser_test PRIVATE ../src)
target_compile_definitions(flora_mdparser_test PRIVATE
CMARK_GFM_STATIC_DEFINE
CMARK_GFM_EXTENSIONS_STATIC_DEFINE)
target_link_libraries(flora_mdparser_test PRIVATE
Qt6::Core
Qt6::Gui
cmark::cmark
cmark::cmark_extensions
)
if(TARGET KF6SyntaxHighlighting)
target_link_libraries(flora_mdparser_test PRIVATE KF6SyntaxHighlighting)
endif()
add_test(NAME flora_mdparser_test COMMAND flora_mdparser_test)
+101
View File
@@ -0,0 +1,101 @@
// Тест MdParser (Фаза 6): рендер Markdown → HTML
// (заголовки, жирный/курсив/зачёркнутый, таблицы, ссылки, картинки,
// задачи с переключаемыми ссылками, подсветка кода, вики-ссылки).
#include <QCoreApplication>
#include <QTemporaryDir>
#include <QDir>
#include <QFile>
#include <cstdio>
#include "markdown/MdParser.h"
static int fail(const char *msg, const QString &detail = QString())
{
std::fprintf(stderr, "FAIL: %s | %s\n", msg, qPrintable(detail));
return 1;
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QTemporaryDir dir;
if (!dir.isValid())
return fail("temp dir");
const QString vault = dir.path();
QDir(vault).mkpath("sub");
{
QFile f(vault + "/sub/b.md");
f.open(QIODevice::WriteOnly);
f.write("b");
f.close();
QFile g(vault + "/sub/pic.png");
g.open(QIODevice::WriteOnly);
g.write("\x89PNG fake");
g.close();
}
MdParser p;
const QString fileDir = vault + "/sub";
auto probe = [](const QString &html, const char *sub) {
return html.contains(QString::fromUtf8(sub));
};
// Заголовок
QString h = p.render("# Заголовок\n", vault, fileDir);
if (!probe(h, "<h1>Заголовок</h1>"))
return fail("заголовок", h);
// Жирный / курсив / зачёркнутый
QString f = p.render("**ж** *к* ~~з~~\n", vault, fileDir);
if (!probe(f, "<b>ж</b>") || !probe(f, "<i>к</i>") || !probe(f, "<s>з</s>"))
return fail("инлайн-разметка", f);
// Таблица GFM
QString t = p.render("| A | B |\n|---|---|\n| 1 | 2 |\n", vault, fileDir);
if (!probe(t, "<table") || !probe(t, "<th") || !probe(t, "<td"))
return fail("таблица", t);
// Задачи (переключаемые ссылки + галочки)
QString sk = p.render("- [ ] задача\n- [x] готова\n", vault, fileDir);
if (!probe(sk, "flora://task/") || !probe(sk, "\u2610") || !probe(sk, "\u2611"))
return fail("задачи", sk);
// Относительная ссылка на существующий файл → flora://open
QString lk = p.render("[б](b.md)\n", vault, fileDir);
if (!probe(lk, "flora://open/") || !probe(lk, ">б</a>"))
return fail("ссылка", lk);
// Вики-ссылка [[b]]
QString wk = p.render("[[b]]\n", vault, fileDir);
if (!probe(wk, "flora://open/") || !probe(wk, ">b</a>"))
return fail("вики-ссылка", wk);
// Подсветка кода python (должны появиться цветные span'ы)
QString c = p.render("```python\nprint(\"hi\")\n```\n", vault, fileDir);
if (!probe(c, "language-Python") || !probe(c, "style=\"color:"))
return fail("подсветка кода", c);
// Простой код без языка — не должен падать
p.render("```\nпросто\n```\n", vault, fileDir);
// Язык «С++» из кириллицы должен мапиться на C++ (подсветка сработает)
QString cpp = p.render("```С++\nint x = 1;\n```\n", vault, fileDir);
if (!probe(cpp, "language-C++"))
return fail("алиас С++", cpp);
// Экранирование HTML
QString e = p.render("<script>alert(1)</script>\n", vault, fileDir);
if (probe(e, "<script>"))
return fail("экранирование", e);
// Картинка → data: или placeholder (файл невалидный PNG — data не выйдет)
QString im = p.render("![а](pic.png)\n", vault, fileDir);
if (!probe(im, "<img") && !probe(im, "изображение"))
return fail("картинка", im);
std::printf("MDPARSER TEST OK\n");
return 0;
}