#pragma once #include "indexer.h" #include "serializer.h" #include #include struct QueryFile; struct QueryType; struct QueryFunc; struct QueryVar; struct QueryDatabase; using QueryFileId = Id; using QueryTypeId = Id; using QueryFuncId = Id; using QueryVarId = Id; struct IdMap; // There are two sources of reindex updates: the (single) definition of a // symbol has changed, or one of many users of the symbol has changed. // // For simplicitly, if the single definition has changed, we update all of the // associated single-owner definition data. See |Update*DefId|. // // If one of the many symbol users submits an update, we store the update such // that it can be merged with other updates before actually being applied to // the main database. See |MergeableUpdate|. template struct MergeableUpdate { // The type/func/var which is getting new usages. Usr usr; // Entries to add and remove. std::vector to_remove; std::vector to_add; MergeableUpdate(Usr usr, std::vector&& to_remove, std::vector&& to_add) : usr(usr), to_remove(std::move(to_remove)), to_add(std::move(to_add)) {} MergeableUpdate(Usr usr, const std::vector& to_remove, const std::vector& to_add) : usr(usr), to_remove(to_remove), to_add(to_add) {} }; template struct WithUsr { Usr usr; T value; WithUsr(Usr usr, const T& value) : usr(usr), value(value) {} WithUsr(Usr usr, T&& value) : usr(usr), value(value) {} }; template struct WithFileContent { T value; std::string file_content; WithFileContent(const T& value, const std::string& file_content) : value(value), file_content(file_content) {} }; struct QueryFile { struct Def { std::string path; std::vector args; // Language identifier std::string language; // Includes in the file. std::vector includes; // Outline of the file (ie, for code lens). std::vector outline; // Every symbol found in the file (ie, for goto definition) std::vector all_symbols; // Parts of the file which are disabled. std::vector inactive_regions; // Used by |$ccls/freshenIndex|. std::vector dependencies; }; using DefUpdate = WithFileContent; int id = -1; std::optional def; int symbol_idx = -1; }; template struct QueryEntity { using Def = QDef; using DefUpdate = WithUsr; Def* AnyDef() { Def* ret = nullptr; for (auto& i : static_cast(this)->def) { ret = &i; if (i.spell) break; } return ret; } const Def* AnyDef() const { return const_cast(this)->AnyDef(); } }; using UsrUpdate = MergeableUpdate; using UseUpdate = MergeableUpdate; struct QueryFunc : QueryEntity { Usr usr; int symbol_idx = -1; std::forward_list def; std::vector declarations; std::vector uses; std::vector derived; }; struct QueryType : QueryEntity { Usr usr; int symbol_idx = -1; std::forward_list def; std::vector declarations; std::vector uses; std::vector derived; std::vector instances; }; struct QueryVar : QueryEntity { Usr usr; int symbol_idx = -1; std::forward_list def; std::vector declarations; std::vector uses; }; struct IndexUpdate { // Creates a new IndexUpdate based on the delta from previous to current. If // no delta computation should be done just pass null for previous. static IndexUpdate CreateDelta(IndexFile* previous, IndexFile* current); // Merge |update| into this update; this can reduce overhead / index update // work can be parallelized. void Merge(IndexUpdate&& update); // Dump the update to a string. std::string ToString(); int file_id; // File updates. std::optional files_removed; std::optional files_def_update; // Function updates. std::vector funcs_removed; std::vector funcs_def_update; std::vector funcs_declarations; std::vector funcs_uses; std::vector funcs_derived; // Type updates. std::vector types_removed; std::vector types_def_update; std::vector types_declarations; std::vector types_uses; std::vector types_derived; std::vector types_instances; // Variable updates. std::vector vars_removed; std::vector vars_def_update; std::vector vars_declarations; std::vector vars_uses; }; // The query database is heavily optimized for fast queries. It is stored // in-memory. struct QueryDatabase { // All File/Func/Type/Var symbols. std::vector symbols; std::vector files; std::unordered_map name2file_id; spp::sparse_hash_map usr2func; spp::sparse_hash_map usr2type; spp::sparse_hash_map usr2var; // Marks the given Usrs as invalid. void RemoveUsrs(SymbolKind usr_kind, const std::vector& to_remove); void RemoveUsrs(SymbolKind usr_kind, int file_id, const std::vector& to_remove); // Insert the contents of |update| into |db|. void ApplyIndexUpdate(IndexUpdate* update); int Update(QueryFile::DefUpdate&& u); void Update(int file_id, std::vector&& updates); void Update(int file_id, std::vector&& updates); void Update(int file_id, std::vector&& updates); void UpdateSymbols(int* symbol_idx, SymbolKind kind, Usr usr); std::string_view GetSymbolName(int symbol_idx, bool qualified); QueryFile& GetFile(SymbolIdx ref) { return files[ref.usr]; } QueryFunc& GetFunc(SymbolIdx ref) { return usr2func[ref.usr]; } QueryType& GetType(SymbolIdx ref) { return usr2type[ref.usr]; } QueryVar& GetVar(SymbolIdx ref) { return usr2var[ref.usr]; } QueryFunc& Func(Usr usr) { return usr2func[usr]; } QueryType& Type(Usr usr) { return usr2type[usr]; } QueryVar& Var(Usr usr) { return usr2var[usr]; } };