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

- 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
+20
View File
@@ -129,6 +129,26 @@ int main(int argc, char *argv[])
|| dm.isTextForEditor("/x/a.pdf"))
return fail("isTextForEditor");
// --- autosaveEnabled (ручной режим настроек) ---
if (!dm.autosaveEnabled())
return fail("autosave enabled by default");
{
QTimer *t = dm.findChild<QTimer*>();
if (!t || !t->isActive())
return fail("autosave timer active");
}
dm.setAutosaveEnabled(false);
if (dm.autosaveEnabled())
return fail("autosave disabled");
{
QTimer *t = dm.findChild<QTimer*>();
if (!t || t->isActive())
return fail("autosave timer stopped");
}
dm.setAutosaveEnabled(true);
if (!dm.autosaveEnabled())
return fail("autosave re-enabled");
std::printf("DOCUMENT MANAGER TEST OK\n");
return 0;
}
+3 -3
View File
@@ -138,11 +138,11 @@ int main(int argc, char *argv[])
// Подсветка: переносы строк — явные <br/>, иначе RichText склеит в одну строку
{
QString hl = p.highlightedCode("int a = 1;\nint b = 2;\n", "cpp");
auto b = p.parseBlocks("```cpp\nint a = 1;\nint b = 2;\n```\n", vault, fileDir);
if (b.size() != 1) return fail("br блок");
const QString hl = b[0].toMap().value("highlightedText").toString();
if (!hl.contains("<br/>")) return fail("highlighted без <br/>", hl.left(200));
if (hl.contains("\n")) return fail("highlighted с сырым \\n", hl.left(200));
QString plain = p.highlightedCode("a\nb", "неизвестный-язык-xyz");
if (!plain.contains("<br/>")) return fail("plain highlight без <br/>", plain);
}
// parseBlocksWithDiff: версия old vs текущее new
+22
View File
@@ -124,6 +124,28 @@ int main(int argc, char *argv[])
if (after.contains(vault + "|" + "old.md")) return fail("old key not migrated");
}
// autosave presets
if (s.autosavePreset() != "balanced") return fail("default preset");
if (s.autosaveDiskMs() != 5000) return fail("balanced disk");
if (s.autocommitIdleMs() != 15000) return fail("balanced idle");
if (!s.autosaveEnabled() || !s.autocommitEnabled()) return fail("balanced enabled");
s.setAutosavePreset("safe");
if (s.autosavePreset() != "safe") return fail("safe preset");
if (s.autosaveDiskMs() != 2000 || s.autocommitIdleMs() != 5000) return fail("safe values");
s.setAutosavePreset("eco");
if (s.autosaveDiskMs() != 10000 || s.autocommitIdleMs() != 60000) return fail("eco values");
s.setAutosavePreset("manual");
if (s.autosaveEnabled() || s.autocommitEnabled()) return fail("manual disabled");
if (s.autosaveDiskMs() != 0 || s.autocommitIdleMs() != 0) return fail("manual zeros");
s.setAutosavePreset(" wat? ");
if (s.autosavePreset() != "balanced") return fail("invalid preset fallback");
s.setAutosavePreset("eco");
{
SettingsStore s2;
if (s2.autosavePreset() != "eco") return fail("preset persistence");
if (s2.autosaveDiskMs() != 10000) return fail("preset values persistence");
}
std::printf("SETTINGS TEST OK\n");
return 0;
}
+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;
}