Инкрементальный индекс, пресеты автосейва, диалог Настройки

- VaultIndex: updateFile/removeFile/syncIncremental (mtime+size), полный рескан только на старте/смене/внешних изменениях; relink висячих [[ссылок]] при создании файла; FileStore.externalChanged для внешних правок
- QML: сейвы и правки идут точечно, дерево — через syncIncremental
- Настройки: пресеты safe/balanced/eco/manual (диск 2/5/10с/выкл, коммит после паузы 5/15/60с/вручную), диалог с +/-, шкалами и подсказкой; коммит только в простое, в manual — по Ctrl+S
- Удалены highlightedCode, словный merge, fileDiffText/Html, 2-arg fileHistory; renderInner без побочек
- Тесты: инкремент индекса, пресеты, autosaveEnabled, ~~~-блоки
This commit is contained in:
2026-09-06 23:02:05 +03:00
parent 1f140d3433
commit 417d0273b7
20 changed files with 802 additions and 80 deletions
+69
View File
@@ -87,6 +87,75 @@ int main(int argc, char *argv[])
if (files2.size() != 2)
return fail("filesWithTag(тег2)", QString::number(files2.size()));
// --- инкремент: updateFile меняет только один файл (убираем теги из a.md) ---
put("a.md", "# Заголовок\n\nСсылка на [[b]].\n");
idx.updateFile(a);
if (idx.fileCount() != 3)
return fail("updateFile fileCount", QString::number(idx.fileCount()));
// a.md теперь без тегов — тег1 остался только... нигде: filesWithTag пуст
if (!idx.filesWithTag("тег1").isEmpty())
return fail("updateFile снял тег1", idx.filesWithTag("тег1").join(","));
if (idx.tagsOfFile(a).size() != 0)
return fail("updateFile tagsOfFile", idx.tagsOfFile(a).join(","));
// b.md не трогали — его теги на месте
if (!idx.tagsOfFile(b).contains("тег2"))
return fail("updateFile не тронул b");
// --- инкремент: новый файл чинит висячую ссылку (relink) ---
put("d.md", "Ссылка на [[future]].\n");
const QString d = dir.path() + "/d.md";
idx.updateFile(d);
if (idx.fileCount() != 4)
return fail("updateFile new fileCount", QString::number(idx.fileCount()));
if (!idx.linksOfFile(d).isEmpty())
return fail("висячая ссылка должна быть пустой");
put("sub/future.md", "# Future\n");
const QString future = dir.path() + "/sub/future.md";
idx.updateFile(future);
{
bool foundFuture = false;
for (const auto &l : idx.linksOfFile(d))
if (l.toMap().value("path").toString() == future)
foundFuture = true;
if (!foundFuture)
return fail("relink не починил [[future]]");
}
// --- инкремент: removeFile чистит теги/ссылки/узел ---
// (в реальности вызывается потому что файл удалён — удаляем и с диска)
QFile::remove(future);
idx.removeFile(future);
if (idx.fileCount() != 4)
return fail("removeFile fileCount", QString::number(idx.fileCount()));
{
bool foundFuture = false;
for (const auto &l : idx.linksOfFile(d))
if (l.toMap().value("path").toString() == future)
foundFuture = true;
if (foundFuture)
return fail("removeFile не убрал входящую ссылку");
}
// removeFile несуществующего — тихо, без падения
idx.removeFile(dir.path() + "/нет-такого.md");
idx.updateFile(dir.path() + "/нет-такого.md");
// --- инкремент: syncIncremental догоняет внешние изменения ---
put("ext.md", "Внешний #внешний\n");
const QString ext = dir.path() + "/ext.md";
if (idx.fileCount() != 4)
return fail("до sync файл не виден", QString::number(idx.fileCount()));
idx.syncIncremental();
if (idx.fileCount() != 5)
return fail("sync подхватил файл", QString::number(idx.fileCount()));
if (!idx.tagsOfFile(ext).contains("внешний"))
return fail("sync теги");
QFile::remove(ext);
idx.syncIncremental();
if (idx.fileCount() != 4)
return fail("sync убрал файл", QString::number(idx.fileCount()));
if (idx.tagsOfFile(ext).size() != 0)
return fail("sync теги удалены");
std::printf("VAULTINDEX TEST OK\n");
return 0;
}