mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 15:45:08 +00:00
Simplify query_utils
This commit is contained in:
parent
65ba98c3f8
commit
909c2e247a
@ -685,7 +685,6 @@ const int IndexFile::kMinorVersion = 0;
|
||||
IndexFile::IndexFile(const std::string& path, const std::string& contents)
|
||||
: id_cache(path), path(path), file_contents(contents) {}
|
||||
|
||||
// TODO: Optimize for const char*?
|
||||
IndexTypeId IndexFile::ToTypeId(Usr usr) {
|
||||
auto it = id_cache.usr_to_type_id.find(usr);
|
||||
if (it != id_cache.usr_to_type_id.end())
|
||||
@ -1358,7 +1357,7 @@ ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor,
|
||||
sem_parent, Role::Definition);
|
||||
ref_type->def.extent =
|
||||
SetUse(db, ref_cursor.get_extent(), lex_parent, Role::None);
|
||||
#if CINDEX_HAVE_PRETTY
|
||||
#if 0&&CINDEX_HAVE_PRETTY
|
||||
ref_type->def.detailed_name = param->PrettyPrintCursor(ref_cursor.cx_cursor);
|
||||
#else
|
||||
ref_type->def.detailed_name = ref_cursor.get_spell_name();
|
||||
@ -1392,7 +1391,8 @@ ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor,
|
||||
sem_parent, Role::Definition);
|
||||
ref_type->def.extent =
|
||||
SetUse(db, ref_cursor.get_extent(), lex_parent, Role::None);
|
||||
#if CINDEX_HAVE_PRETTY
|
||||
#if 0&&CINDEX_HAVE_PRETTY
|
||||
// template<class T> void f(T t){} // weird, the name is empty
|
||||
ref_type->def.detailed_name = param->PrettyPrintCursor(ref_cursor.cx_cursor);
|
||||
#else
|
||||
ref_type->def.detailed_name = ref_cursor.get_spell_name();
|
||||
|
@ -61,7 +61,7 @@ ExpandNode(QueryDatabase* db, WorkingFiles* working_files, QueryTypeId root) {
|
||||
return {};
|
||||
|
||||
std::vector<Out_CqueryMemberHierarchy::Entry> ret;
|
||||
EachWithGen(db->vars, def->vars, [&](QueryVar& var) {
|
||||
EachDefinedEntity(db->vars, def->vars, [&](QueryVar& var) {
|
||||
const QueryVar::Def* def1 = var.AnyDef();
|
||||
Out_CqueryMemberHierarchy::Entry entry;
|
||||
entry.name = def1->ShortName();
|
||||
|
@ -35,7 +35,7 @@ BuildParentInheritanceHierarchyForType(QueryDatabase* db,
|
||||
const QueryType::Def* def = root_type.AnyDef();
|
||||
parent_entries.reserve(def->parents.size());
|
||||
|
||||
EachWithGen(db->types, def->parents, [&](QueryType& parent_type) {
|
||||
EachDefinedEntity(db->types, def->parents, [&](QueryType& parent_type) {
|
||||
Out_CqueryTypeHierarchyTree::TypeEntry parent_entry;
|
||||
const QueryType::Def* def1 = parent_type.AnyDef();
|
||||
parent_entry.name = def1->detailed_name.c_str();
|
||||
@ -74,7 +74,7 @@ BuildInheritanceHierarchyForType(QueryDatabase* db,
|
||||
entry.children.push_back(base);
|
||||
|
||||
// Add derived.
|
||||
EachWithGen(db->types, root_type.derived, [&](QueryType& type) {
|
||||
EachDefinedEntity(db->types, root_type.derived, [&](QueryType& type) {
|
||||
auto derived_entry =
|
||||
BuildInheritanceHierarchyForType(db, working_files, type);
|
||||
if (derived_entry)
|
||||
|
@ -359,7 +359,7 @@ struct TextDocumentCodeActionHandler
|
||||
// Get implementation file.
|
||||
Out_TextDocumentCodeAction::Command command;
|
||||
|
||||
EachWithGen(db->funcs, def->funcs, [&](QueryFunc& func_def) {
|
||||
EachDefinedEntity(db->funcs, def->funcs, [&](QueryFunc& func_def) {
|
||||
const QueryFunc::Def* def1 = func_def.AnyDef();
|
||||
if (def1->extent)
|
||||
return;
|
||||
|
@ -81,7 +81,7 @@ struct TextDocumentDefinitionHandler
|
||||
// - start at spelling but end at extent for better mouse tooltip
|
||||
// - goto declaration while in definition of recursive type
|
||||
std::vector<Use> uses;
|
||||
EachDef(db, sym, [&](const auto& def) {
|
||||
EachEntityDef(db, sym, [&](const auto& def) {
|
||||
if (def.spell && def.extent) {
|
||||
Use spell = *def.spell;
|
||||
// If on a definition, clear |uses| to find declarations below.
|
||||
|
@ -39,19 +39,13 @@ struct TextDocumentDocumentHighlightHandler
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(working_file, file, request->params.position)) {
|
||||
// Found symbol. Return references to highlight.
|
||||
std::vector<Use> uses = GetUsesOfSymbol(db, sym, true);
|
||||
out.result.reserve(uses.size());
|
||||
for (Use use : uses) {
|
||||
EachUse(db, sym, true, [&](Use use) {
|
||||
if (use.file != file_id)
|
||||
continue;
|
||||
|
||||
optional<lsLocation> ls_location =
|
||||
GetLsLocation(db, working_files, use);
|
||||
if (!ls_location)
|
||||
continue;
|
||||
|
||||
return;
|
||||
if (optional<lsLocation> ls_loc =
|
||||
GetLsLocation(db, working_files, use)) {
|
||||
lsDocumentHighlight highlight;
|
||||
highlight.range = ls_location->range;
|
||||
highlight.range = ls_loc->range;
|
||||
if (use.role & Role::Write)
|
||||
highlight.kind = lsDocumentHighlightKind::Write;
|
||||
else if (use.role & Role::Read)
|
||||
@ -61,6 +55,7 @@ struct TextDocumentDocumentHighlightHandler
|
||||
highlight.role = use.role;
|
||||
out.result.push_back(highlight);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -54,16 +54,13 @@ struct TextDocumentReferencesHandler
|
||||
for (const SymbolRef& sym :
|
||||
FindSymbolsAtLocation(working_file, file, request->params.position)) {
|
||||
// Found symbol. Return references.
|
||||
std::vector<Use> uses = GetUsesOfSymbol(
|
||||
db, sym, request->params.context.includeDeclaration);
|
||||
out.result.reserve(uses.size());
|
||||
for (Use use : uses) {
|
||||
optional<lsLocationEx> ls_loc = GetLsLocationEx(
|
||||
db, working_files, use, config->extension.referenceContainer);
|
||||
if (ls_loc) {
|
||||
EachUse(db, sym, request->params.context.includeDeclaration,
|
||||
[&](Use use) {
|
||||
if (optional<lsLocationEx> ls_loc =
|
||||
GetLsLocationEx(db, working_files, use,
|
||||
config->extension.referenceContainer))
|
||||
out.result.push_back(*ls_loc);
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,14 @@ namespace {
|
||||
|
||||
lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db,
|
||||
WorkingFiles* working_files,
|
||||
const std::vector<Use>& uses,
|
||||
SymbolRef sym,
|
||||
const std::string& new_text) {
|
||||
std::unordered_map<QueryFileId, lsTextDocumentEdit> path_to_edit;
|
||||
|
||||
for (Use use : uses) {
|
||||
optional<lsLocation> ls_location =
|
||||
GetLsLocation(db, working_files, use);
|
||||
EachUse(db, sym, true, [&](Use use) {
|
||||
optional<lsLocation> ls_location = GetLsLocation(db, working_files, use);
|
||||
if (!ls_location)
|
||||
continue;
|
||||
return;
|
||||
|
||||
QueryFileId file_id = use.file;
|
||||
if (path_to_edit.find(file_id) == path_to_edit.end()) {
|
||||
@ -22,16 +21,14 @@ lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db,
|
||||
|
||||
QueryFile& file = db->files[file_id.id];
|
||||
if (!file.def)
|
||||
continue;
|
||||
return;
|
||||
|
||||
const std::string& path = file.def->path;
|
||||
path_to_edit[file_id].textDocument.uri =
|
||||
lsDocumentUri::FromPath(path);
|
||||
path_to_edit[file_id].textDocument.uri = lsDocumentUri::FromPath(path);
|
||||
|
||||
WorkingFile* working_file = working_files->GetFileByFilename(path);
|
||||
if (working_file)
|
||||
path_to_edit[file_id].textDocument.version =
|
||||
working_file->version;
|
||||
path_to_edit[file_id].textDocument.version = working_file->version;
|
||||
}
|
||||
|
||||
lsTextEdit edit;
|
||||
@ -42,7 +39,7 @@ lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db,
|
||||
auto& edits = path_to_edit[file_id].edits;
|
||||
if (std::find(edits.begin(), edits.end(), edit) == edits.end())
|
||||
edits.push_back(edit);
|
||||
}
|
||||
});
|
||||
|
||||
lsWorkspaceEdit edit;
|
||||
for (const auto& changes : path_to_edit)
|
||||
@ -98,9 +95,8 @@ struct TextDocumentRenameHandler : BaseMessageHandler<Ipc_TextDocumentRename> {
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(working_file, file, request->params.position)) {
|
||||
// Found symbol. Return references to rename.
|
||||
out.result = BuildWorkspaceEdit(db, working_files,
|
||||
GetUsesOfSymbol(db, sym, true),
|
||||
request->params.newName);
|
||||
out.result =
|
||||
BuildWorkspaceEdit(db, working_files, sym, request->params.newName);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <climits>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace {
|
||||
|
||||
@ -40,63 +41,19 @@ std::vector<Use> ToUsesHelper(std::vector<Q>& entities,
|
||||
|
||||
Maybe<Use> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
|
||||
SymbolIdx sym) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::File:
|
||||
break;
|
||||
case SymbolKind::Func: {
|
||||
for (auto& def : db->GetFunc(sym).def)
|
||||
if (def.spell)
|
||||
return def.spell;
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Type: {
|
||||
for (auto& def : db->GetType(sym).def)
|
||||
if (def.spell)
|
||||
return def.spell;
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Var: {
|
||||
for (auto& def : db->GetVar(sym).def)
|
||||
if (def.spell)
|
||||
return def.spell;
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Invalid:
|
||||
assert(false && "unexpected");
|
||||
break;
|
||||
}
|
||||
return nullopt;
|
||||
Maybe<Use> ret;
|
||||
EachEntityDef(db, sym, [&](const auto& def) { return !(ret = def.spell); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
Maybe<Use> GetDefinitionExtentOfSymbol(QueryDatabase* db, SymbolIdx sym) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::File:
|
||||
// Used to jump to file.
|
||||
if (sym.kind == SymbolKind::File)
|
||||
return Use(Range(Position(0, 0), Position(0, 0)), sym.id, sym.kind,
|
||||
Role::None, QueryFileId(sym.id));
|
||||
case SymbolKind::Func: {
|
||||
for (auto& def : db->GetFunc(sym).def)
|
||||
if (def.extent)
|
||||
return def.extent;
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Type: {
|
||||
for (auto& def : db->GetType(sym).def)
|
||||
if (def.extent)
|
||||
return def.extent;
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Var: {
|
||||
for (auto& def : db->GetVar(sym).def)
|
||||
if (def.extent)
|
||||
return def.extent;
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Invalid: {
|
||||
assert(false && "unexpected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullopt;
|
||||
Maybe<Use> ret;
|
||||
EachEntityDef(db, sym, [&](const auto& def) { return !(ret = def.extent); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
Maybe<QueryFileId> GetDeclarationFileForSymbol(QueryDatabase* db,
|
||||
@ -144,51 +101,6 @@ std::vector<Use> ToUses(QueryDatabase* db, const std::vector<QueryVarId>& ids) {
|
||||
return ToUsesHelper(db->vars, ids);
|
||||
}
|
||||
|
||||
std::vector<Use> GetUsesOfSymbol(QueryDatabase* db,
|
||||
SymbolIdx sym,
|
||||
bool include_decl) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::Type: {
|
||||
QueryType& type = db->GetType(sym);
|
||||
std::vector<Use> ret = type.uses;
|
||||
if (include_decl) {
|
||||
for (auto& def : type.def)
|
||||
if (def.spell)
|
||||
ret.push_back(*def.spell);
|
||||
AddRange(&ret, type.declarations);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case SymbolKind::Func: {
|
||||
QueryFunc& func = db->GetFunc(sym);
|
||||
std::vector<Use> ret = func.uses;
|
||||
if (include_decl) {
|
||||
for (auto& def : func.def)
|
||||
if (def.spell)
|
||||
ret.push_back(*def.spell);
|
||||
AddRange(&ret, func.declarations);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case SymbolKind::Var: {
|
||||
QueryVar& var = db->GetVar(sym);
|
||||
std::vector<Use> ret = var.uses;
|
||||
if (include_decl) {
|
||||
for (auto& def : var.def)
|
||||
if (def.spell)
|
||||
ret.push_back(*def.spell);
|
||||
ret.insert(ret.end(), var.declarations.begin(), var.declarations.end());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case SymbolKind::File:
|
||||
case SymbolKind::Invalid: {
|
||||
assert(false && "unexpected");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Use> GetDeclarationsOfSymbolForGotoDefinition(
|
||||
QueryDatabase* db,
|
||||
SymbolIdx sym) {
|
||||
@ -205,41 +117,30 @@ std::vector<Use> GetDeclarationsOfSymbolForGotoDefinition(
|
||||
}
|
||||
|
||||
bool HasCallersOnSelfOrBaseOrDerived(QueryDatabase* db, QueryFunc& root) {
|
||||
// Check self.
|
||||
if (!root.uses.empty())
|
||||
return true;
|
||||
const QueryFunc::Def* def = root.AnyDef();
|
||||
|
||||
// Check for base calls.
|
||||
std::unordered_set<Usr> seen;
|
||||
std::queue<QueryFunc*> queue;
|
||||
EachWithGen<QueryFunc>(db->funcs, def->base, [&](QueryFunc& func) {
|
||||
queue.push(&func);
|
||||
});
|
||||
seen.insert(root.usr);
|
||||
queue.push(&root);
|
||||
while (!queue.empty()) {
|
||||
QueryFunc& func = *queue.front();
|
||||
queue.pop();
|
||||
if (!func.uses.empty())
|
||||
return true;
|
||||
if (def)
|
||||
EachWithGen<QueryFunc>(db->funcs, def->base, [&](QueryFunc& func1) {
|
||||
if (auto* def = func.AnyDef()) {
|
||||
EachDefinedEntity(db->funcs, def->base, [&](QueryFunc& func1) {
|
||||
if (!seen.count(func1.usr)) {
|
||||
seen.insert(func1.usr);
|
||||
queue.push(&func1);
|
||||
}
|
||||
});
|
||||
EachDefinedEntity(db->funcs, func.derived, [&](QueryFunc& func1) {
|
||||
if (!seen.count(func1.usr)) {
|
||||
seen.insert(func1.usr);
|
||||
queue.push(&func1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check for derived calls.
|
||||
EachWithGen<QueryFunc>(db->funcs, root.derived, [&](QueryFunc& func1) {
|
||||
queue.push(&func1);
|
||||
});
|
||||
while (!queue.empty()) {
|
||||
QueryFunc& func = *queue.front();
|
||||
queue.pop();
|
||||
if (!func.uses.empty())
|
||||
return true;
|
||||
EachWithGen<QueryFunc>(db->funcs, func.derived, [&](QueryFunc& func1) {
|
||||
queue.push(&func1);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -251,7 +152,7 @@ std::vector<Use> GetCallersForAllBaseFunctions(QueryDatabase* db,
|
||||
return callers;
|
||||
|
||||
std::queue<QueryFunc*> queue;
|
||||
EachWithGen<QueryFunc>(db->funcs, def->base, [&](QueryFunc& func1) {
|
||||
EachDefinedEntity(db->funcs, def->base, [&](QueryFunc& func1) {
|
||||
queue.push(&func1);
|
||||
});
|
||||
while (!queue.empty()) {
|
||||
@ -260,7 +161,7 @@ std::vector<Use> GetCallersForAllBaseFunctions(QueryDatabase* db,
|
||||
|
||||
AddRange(&callers, func.uses);
|
||||
if (const QueryFunc::Def* def1 = func.AnyDef()) {
|
||||
EachWithGen<QueryFunc>(db->funcs, def1->base, [&](QueryFunc& func1) {
|
||||
EachDefinedEntity(db->funcs, def1->base, [&](QueryFunc& func1) {
|
||||
queue.push(&func1);
|
||||
});
|
||||
}
|
||||
@ -274,7 +175,7 @@ std::vector<Use> GetCallersForAllDerivedFunctions(QueryDatabase* db,
|
||||
std::vector<Use> callers;
|
||||
|
||||
std::queue<QueryFunc*> queue;
|
||||
EachWithGen<QueryFunc>(db->funcs, root.derived, [&](QueryFunc& func) {
|
||||
EachDefinedEntity(db->funcs, root.derived, [&](QueryFunc& func) {
|
||||
queue.push(&func);
|
||||
});
|
||||
|
||||
@ -282,7 +183,7 @@ std::vector<Use> GetCallersForAllDerivedFunctions(QueryDatabase* db,
|
||||
QueryFunc& func = *queue.front();
|
||||
queue.pop();
|
||||
|
||||
EachWithGen<QueryFunc>(db->funcs, func.derived, [&](QueryFunc& func1) {
|
||||
EachDefinedEntity(db->funcs, func.derived, [&](QueryFunc& func1) {
|
||||
queue.push(&func1);
|
||||
});
|
||||
AddRange(&callers, func.uses);
|
||||
@ -377,28 +278,11 @@ optional<lsLocationEx> GetLsLocationEx(QueryDatabase* db,
|
||||
return nullopt;
|
||||
lsLocationEx ret;
|
||||
ret.lsLocation::operator=(*ls_loc);
|
||||
if (extension)
|
||||
switch (use.kind) {
|
||||
default:
|
||||
break;
|
||||
case SymbolKind::Func: {
|
||||
const QueryFunc::Def* def = db->GetFunc(use).AnyDef();
|
||||
if (def)
|
||||
ret.containerName = std::string_view(def->detailed_name);
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Type: {
|
||||
const QueryType::Def* def = db->GetType(use).AnyDef();
|
||||
if (def)
|
||||
ret.containerName = std::string_view(def->detailed_name);
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Var: {
|
||||
const QueryVar::Def* def = db->GetVar(use).AnyDef();
|
||||
if (def)
|
||||
ret.containerName = std::string_view(def->detailed_name);
|
||||
break;
|
||||
}
|
||||
if (extension) {
|
||||
EachEntityDef(db, use, [&](const auto& def) {
|
||||
ret.containerName = std::string_view(def.detailed_name);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -18,9 +18,6 @@ std::vector<Use> ToUses(QueryDatabase* db,
|
||||
std::vector<Use> ToUses(QueryDatabase* db,
|
||||
const std::vector<QueryVarId>& ids);
|
||||
|
||||
std::vector<Use> GetUsesOfSymbol(QueryDatabase* db,
|
||||
SymbolIdx sym,
|
||||
bool include_decl);
|
||||
std::vector<Use> GetDeclarationsOfSymbolForGotoDefinition(
|
||||
QueryDatabase* db,
|
||||
SymbolIdx sym);
|
||||
@ -64,43 +61,52 @@ void EmitDiagnostics(WorkingFiles* working_files,
|
||||
std::vector<lsDiagnostic> diagnostics);
|
||||
|
||||
template <typename Fn>
|
||||
void EachDef(QueryDatabase* db, SymbolIdx sym, Fn fn) {
|
||||
void WithEntity(QueryDatabase* db, SymbolIdx sym, Fn&& fn) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::Invalid:
|
||||
case SymbolKind::File:
|
||||
break;
|
||||
case SymbolKind::Func:
|
||||
for (auto& def : db->GetFunc(sym).def)
|
||||
if (!fn(def))
|
||||
break;
|
||||
fn(db->GetFunc(sym));
|
||||
break;
|
||||
case SymbolKind::Type:
|
||||
for (auto& def : db->GetType(sym).def)
|
||||
if (!fn(def))
|
||||
break;
|
||||
fn(db->GetType(sym));
|
||||
break;
|
||||
case SymbolKind::Var:
|
||||
for (auto& def : db->GetVar(sym).def)
|
||||
fn(db->GetVar(sym));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void EachEntityDef(QueryDatabase* db, SymbolIdx sym, Fn&& fn) {
|
||||
WithEntity(db, sym, [&](const auto& entity) {
|
||||
for (auto& def : entity.def)
|
||||
if (!fn(def))
|
||||
break;
|
||||
break;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void EachUse(QueryDatabase* db, SymbolIdx sym, bool include_decl, Fn&& fn) {
|
||||
WithEntity(db, sym, [&](const auto& entity) {
|
||||
for (Use use : entity.uses)
|
||||
fn(use);
|
||||
if (include_decl) {
|
||||
for (auto& def : entity.def)
|
||||
if (def.spell)
|
||||
fn(*def.spell);
|
||||
for (Use use : entity.declarations)
|
||||
fn(use);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Q, typename Fn>
|
||||
void EachWithGen(std::vector<Q>& collection, Id<Q> x, Fn fn) {
|
||||
Q& obj = collection[x.id];
|
||||
// FIXME Deprecate optional<Def> def
|
||||
// if (obj.gen == x.gen && obj.def)
|
||||
if (obj.AnyDef())
|
||||
fn(obj);
|
||||
}
|
||||
|
||||
template <typename Q, typename Fn>
|
||||
void EachWithGen(std::vector<Q>& collection, const std::vector<Id<Q>>& ids, Fn fn) {
|
||||
void EachDefinedEntity(std::vector<Q>& collection, const std::vector<Id<Q>>& ids, Fn&& fn) {
|
||||
for (Id<Q> x : ids) {
|
||||
Q& obj = collection[x.id];
|
||||
if (obj.AnyDef())
|
||||
if (!obj.def.empty())
|
||||
fn(obj);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user