#pragma once #include #include #include #include #include // Индекс связей хранилища: теги, [[перекрёстные ссылки]], md-ссылки между // файлами, а также узлы и рёбра графа. Сканируется синхронно. class VaultIndex : public QObject { Q_OBJECT Q_PROPERTY(QVariantList nodes READ nodes NOTIFY indexChanged) Q_PROPERTY(QVariantList edges READ edges NOTIFY indexChanged) Q_PROPERTY(int fileCount READ fileCount NOTIFY indexChanged) Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) public: explicit VaultIndex(QObject *parent = nullptr); QVariantList nodes() const { return m_nodes; } QVariantList edges() const { return m_edges; } int fileCount() const { return m_fileMeta.size(); } bool busy() const { return m_busy; } // Полное пересканирование хранилища (старт, смена хранилища, вручную, внешние изменения). Q_INVOKABLE void rebuild(const QString &vault); // Точечно перечитать один файл (создание/сохранение/переименование-в). Дёшево. Q_INVOKABLE void updateFile(const QString &path); // Убрать один файл из индекса (удаление/переименование-из). Q_INVOKABLE void removeFile(const QString &path); // Сверить список файлов с диском по mtime/size и дочитать только изменённые. // Для внешних изменений (синхронизация, другие программы). Q_INVOKABLE void syncIncremental(); // Теги файла / ссылки файла (список {text,path} для открытия). Q_INVOKABLE QStringList tagsOfFile(const QString &path) const; Q_INVOKABLE QVariantList linksOfFile(const QString &path) const; // Все файлы с тегом tag. Q_INVOKABLE QStringList filesWithTag(const QString &tag) const; Q_INVOKABLE QString displayName(const QString &nodeKey) const; signals: void indexChanged(); void busyChanged(); private: struct FileMeta { QDateTime mtime; qint64 size = -1; bool operator==(const FileMeta &o) const { return mtime == o.mtime && size == o.size; } bool operator!=(const FileMeta &o) const { return !(*this == o); } }; QString nodeKeyFor(const QString &path, bool isFile) const; void collectFiles(const QString &dir, int depth, QStringList &out) const; void parseFile(const QString &path); QString resolveLink(const QString &fileDir, const QString &target) const; int ensureNode(const QString &key, const QString &name, const QString &type); void addEdge(const QString &from, const QString &to); void deduplicateEdges(); // Файл попадает в индекс? bool shouldIndex(const QString &cleanPath) const; // Снять собственные данные файла (теги/исходящие рёбра/ссылки), узел и входящие — нет. void purgeOutgoing(const QString &cleanPath); // Полностью выкинуть файл: + входящие рёбра/ссылки, узел, bybase, мета. void dropFile(const QString &cleanPath); void dropNode(const QString &key); // Перепарсить файлы, чьи неразрешённые ссылки могли починиться новым файлом. bool relinkForNewFile(const QString &newPath); static FileMeta metaOf(const QString &cleanPath); QString m_vault; QVariantList m_nodes; QVariantList m_edges; QHash m_nodeById; QHash m_tagsOfFile; QHash m_linksOfFile; QHash m_filesWithTag; QHash m_fileByBase; // имя без расширения → путь QHash m_unresolvedByFile; // файл → неразрешённые цели ссылок QHash m_fileMeta; // проиндексированные .md bool m_busy = false; };