bool QueryJob::filter(const String &value) const { if (mFilters.isEmpty() && !(queryFlags() & QueryMessage::FilterSystemIncludes)) return true; const char *val = value.constData(); while (*val && isspace(*val)) ++val; const char *space = strchr(val, ' '); Path path; uint32_t fileId = 0; if (space) { path.assign(val, space - val); } else { path = val; } if (!path.isFile()) return true; // non-file things go unfiltered if (queryFlags() & QueryMessage::FilterSystemIncludes && Path::isSystem(val)) return false; fileId = Location::fileId(path); if (mFilters.isEmpty()) return true; for (const std::shared_ptr<Filter> &filter : mFilters) { if (filter->match(fileId, path)) return true; } return false; }
int FollowLocationJob::execute() { const Symbol symbol = project()->findSymbol(location); if (symbol.isNull()) return 1; if (queryFlags() & QueryMessage::AllTargets) { const Set<String> usrs = project()->findTargetUsrs(location); for (const String &usr : usrs) { for (const Symbol &s : project()->findByUsr(usr, location.fileId(), Project::ArgDependsOn, location)) { write(s.toString()); } } } const auto target = project()->findTarget(symbol); if (target.isNull()) return 1; if (symbol.usr == target.usr) { write(target.location); return 0; } if (queryFlags() & QueryMessage::DeclarationOnly ? target.isDefinition() : !target.isDefinition()) { const auto other = project()->findTarget(target); if (!other.isNull() && other.usr == target.usr) { write(other.location); return 0; } } write(target.location); return 0; }
void ListSymbolsJob::execute() { Set<String> out; std::shared_ptr<Project> proj = project(); if (proj) { if (queryFlags() & QueryMessage::IMenu) { out = imenu(proj); } else { out = listSymbols(proj); } } const bool elispList = queryFlags() & QueryMessage::ElispList; if (elispList) { write("(list", IgnoreMax|DontQuote); for (Set<String>::const_iterator it = out.begin(); it != out.end(); ++it) { write(*it); } write(")", IgnoreMax|DontQuote); } else { List<String> sorted = out.toList(); if (queryFlags() & QueryMessage::ReverseSort) { std::sort(sorted.begin(), sorted.end(), std::greater<String>()); } else { std::sort(sorted.begin(), sorted.end()); } const int count = sorted.size(); for (int i=0; i<count; ++i) { write(sorted.at(i)); } } }
bool QueryJob::locationToString(Location location, const std::function<void(LocationPiece, const String &)> &cb, Flags<WriteFlag> writeFlags) { if (location.isNull()) return false; Flags<Location::ToStringFlag> kf = locationToStringFlags(); kf &= ~Location::ShowContext; cb(Piece_Location, location.toString(kf, &mContextCache)); if (!(writeFlags & NoContext) && !(queryFlags() & QueryMessage::NoContext)) cb(Piece_Context, location.context(kf, &mContextCache)); const bool containingFunction = queryFlags() & QueryMessage::ContainingFunction; const bool containingFunctionLocation = queryFlags() & QueryMessage::ContainingFunctionLocation; const bool cursorKind = queryFlags() & QueryMessage::CursorKind; const bool displayName = queryFlags() & QueryMessage::DisplayName; if (containingFunction || containingFunctionLocation || cursorKind || displayName || !mKindFilters.isEmpty()) { int idx; Symbol symbol = project()->findSymbol(location, &idx); if (symbol.isNull()) { error() << "Somehow can't find" << location << "in symbols"; } else { if (!mKindFilters.filter(symbol)) return false; if (displayName) cb(Piece_SymbolName, symbol.displayName()); if (cursorKind) cb(Piece_Kind, symbol.kindSpelling()); if (containingFunction || containingFunctionLocation) { const uint32_t fileId = location.fileId(); const unsigned int line = location.line(); const unsigned int column = location.column(); auto fileMap = project()->openSymbols(location.fileId()); if (fileMap) { while (idx > 0) { symbol = fileMap->valueAt(--idx); if (symbol.location.fileId() != fileId) break; if (symbol.isDefinition() && RTags::isContainer(symbol.kind) && comparePosition(line, column, symbol.startLine, symbol.startColumn) >= 0 && comparePosition(line, column, symbol.endLine, symbol.endColumn) <= 0) { if (containingFunction) cb(Piece_ContainingFunctionName, symbol.symbolName); if (containingFunctionLocation) cb(Piece_ContainingFunctionLocation, symbol.location.toString(locationToStringFlags() & ~Location::ShowContext)); break; } } } } } } return true; }
int ListSymbolsJob::execute() { Set<String> out; std::shared_ptr<Project> proj = project(); if (proj) { if (queryFlags() & QueryMessage::WildcardSymbolNames && (string.contains('*') || string.contains('?')) && !string.endsWith('*')) { string += '*'; } List<QueryMessage::PathFilter> filters = pathFilters(); List<Path> paths; for (const auto &filter : filters) { if (filter.mode == QueryMessage::PathFilter::Self) { paths.append(filter.pattern); if (!paths.last().isFile()) { paths.clear(); break; } } else { paths.clear(); break; } } if (!paths.isEmpty()) { out = listSymbolsWithPathFilter(proj, paths); } else { out = listSymbols(proj); } } if (queryFlags() & QueryMessage::Elisp) { write("(list", IgnoreMax | DontQuote); for (Set<String>::const_iterator it = out.begin(); it != out.end(); ++it) { write(*it); } write(")", IgnoreMax | DontQuote); } else { List<String> sorted = out.toList(); if (queryFlags() & QueryMessage::ReverseSort) { std::sort(sorted.begin(), sorted.end(), std::greater<String>()); } else { std::sort(sorted.begin(), sorted.end()); } const int count = sorted.size(); for (int i = 0; i < count; ++i) { write(sorted.at(i)); } } return out.isEmpty() ? 1 : 0; }
Set<String> ListSymbolsJob::listSymbolsWithPathFilter(const std::shared_ptr<Project> &project, const List<Path> &paths) const { Set<String> out; const bool wildcard = queryFlags() & QueryMessage::WildcardSymbolNames && (string.contains('*') || string.contains('?')); const bool stripParentheses = queryFlags() & QueryMessage::StripParentheses; const bool caseInsensitive = queryFlags() & QueryMessage::MatchCaseInsensitive; const String::CaseSensitivity cs = caseInsensitive ? String::CaseInsensitive : String::CaseSensitive; for (size_t i=0; i<paths.size(); ++i) { const Path file = paths.at(i); const uint32_t fileId = Location::fileId(file); if (!fileId) continue; auto symbols = project->openSymbols(fileId); if (!symbols) continue; const int count = symbols->count(); for (int j=0; j<count; ++j) { const Symbol &symbol = symbols->valueAt(j); if (!filterKind(symbol)) { continue; } const String &symbolName = symbol.symbolName; if (symbolName.isEmpty()) continue; if (!string.isEmpty()) { if (wildcard) { if (!Project::matchSymbolName(string, symbolName, cs)) { continue; } } else if (!symbolName.contains(string, cs)) { continue; } } if (stripParentheses) { const int paren = symbolName.indexOf('('); if (paren == -1) { out.insert(symbolName); } else { if (!RTags::isFunctionVariable(symbolName)) out.insert(symbolName.left(paren)); } } else { out.insert(symbolName); } } } return out; }
bool QueryJob::write(const Location &location, Flags<WriteFlag> flags) { if (location.isNull()) return false; if (!(flags & Unfiltered)) { if (!filterLocation(location)) return false; flags |= Unfiltered; } String out = location.key(keyFlags()); const bool containingFunction = queryFlags() & QueryMessage::ContainingFunction; const bool cursorKind = queryFlags() & QueryMessage::CursorKind; const bool displayName = queryFlags() & QueryMessage::DisplayName; if (containingFunction || cursorKind || displayName || !mKindFilters.isEmpty()) { int idx; Symbol symbol = project()->findSymbol(location, &idx); if (symbol.isNull()) { error() << "Somehow can't find" << location << "in symbols"; } else { if (!filterKind(symbol.kind)) return false; if (displayName) out += '\t' + symbol.displayName(); if (cursorKind) out += '\t' + symbol.kindSpelling(); if (containingFunction) { const uint32_t fileId = location.fileId(); const unsigned int line = location.line(); const unsigned int column = location.column(); auto fileMap = project()->openSymbols(location.fileId()); if (fileMap) { while (idx > 0) { symbol = fileMap->valueAt(--idx); if (symbol.location.fileId() != fileId) break; if (symbol.isDefinition() && RTags::isContainer(symbol.kind) && comparePosition(line, column, symbol.startLine, symbol.startColumn) >= 0 && comparePosition(line, column, symbol.endLine, symbol.endColumn) <= 0) { out += "\tfunction: " + symbol.symbolName; break; } } } } } } return write(out, flags); }
void FindSymbolsJob::execute() { Map<Location, bool> out; shared_ptr<Project> proj = project(); if (proj) { Scope<const SymbolNameMap&> scope = proj->lockSymbolNamesForRead(); if (scope.isNull()) return; const SymbolNameMap &map = scope.data(); const SymbolNameMap::const_iterator it = map.find(string); if (it != map.end()) { const Set<Location> &locations = it->second; for (Set<Location>::const_iterator i = locations.begin(); i != locations.end(); ++i) { out[*i] = true; } } } if (out.size()) { Scope<const SymbolMap&> scope = proj->lockSymbolsForRead(); const SymbolMap *map = &scope.data(); List<RTags::SortedCursor> sorted; sorted.reserve(out.size()); for (Map<Location, bool>::const_iterator it = out.begin(); it != out.end(); ++it) { RTags::SortedCursor node(it->first); if (it->second && map) { const SymbolMap::const_iterator found = map->find(it->first); if (found != map->end()) { node.isDefinition = found->second.isDefinition(); node.kind = found->second.kind; } } sorted.push_back(node); } if (queryFlags() & QueryMessage::ReverseSort) { std::sort(sorted.begin(), sorted.end(), std::greater<RTags::SortedCursor>()); } else { std::sort(sorted.begin(), sorted.end()); } const uint32_t keyFlags = QueryMessage::keyFlags(queryFlags()); const int count = sorted.size(); for (int i=0; i<count; ++i) { write(sorted.at(i).location.key(keyFlags)); } } }
void CursorInfoJob::execute() { Scope<const SymbolMap &> scope = project()->lockSymbolsForRead(); if (scope.isNull()) return; const SymbolMap &map = scope.data(); if (map.isEmpty()) return; bool moved = false; SymbolMap::const_iterator it = RTags::findCursorInfo(map, location, queryFlags() & QueryMessage::ValidateSymbol ? &moved : 0); if (moved) { write("Symbol has moved"); return; } unsigned ciFlags = 0; if (!(queryFlags() & QueryMessage::CursorInfoIncludeTargets)) ciFlags |= CursorInfo::IgnoreTargets; if (!(queryFlags() & QueryMessage::CursorInfoIncludeReferences)) ciFlags |= CursorInfo::IgnoreReferences; if (it != map.end()) { write(it->first); write(it->second, ciFlags); } else { it = map.lower_bound(location); if (it == map.end()) --it; } ciFlags |= CursorInfo::IgnoreTargets|CursorInfo::IgnoreReferences; if (it != map.begin() && queryFlags() & QueryMessage::CursorInfoIncludeParents) { const uint32_t fileId = location.fileId(); const int offset = location.offset(); while (true) { --it; if (it->first.fileId() != fileId) break; if (it->second.isDefinition() && RTags::isContainer(it->second.kind) && offset >= it->second.start && offset <= it->second.end) { write("===================="); write(it->first); write(it->second, ciFlags); } if (it == map.begin()) break; } } }
int DependenciesJob::execute() { std::shared_ptr<Project> proj = project(); if (!proj) return 2; write(proj->dumpDependencies(mFileId, mArgs, queryFlags())); return 0; }
int FindSymbolsJob::execute() { int ret = 2; if (std::shared_ptr<Project> proj = project()) { Set<Symbol> symbols; Location filter; const uint32_t filteredFile = fileFilter(); if (filteredFile) filter = Location(filteredFile, 0, 0); auto inserter = [proj, this, &symbols, &filter, filteredFile](Project::SymbolMatchType type, const String &symbolName, const Set<Location> &locations) { if (filter.fileId()) { auto it = locations.lower_bound(filter); if (it == locations.end() || it->fileId() != filteredFile) return; } if (type == Project::StartsWith) { const size_t paren = symbolName.indexOf('('); if (paren == String::npos || paren != string.size() || RTags::isFunctionVariable(symbolName)) return; } for (const auto &it : locations) { const Symbol sym = proj->findSymbol(it); if (!sym.isNull()) symbols.insert(sym); } }; proj->findSymbols(string, inserter, queryFlags()); if (!symbols.isEmpty()) { const List<RTags::SortedSymbol> sorted = proj->sort(symbols, queryFlags()); const Flags<WriteFlag> writeFlags = fileFilter() ? Unfiltered : NoWriteFlags; const int count = sorted.size(); ret = count ? 0 : 1; for (int i=0; i<count; ++i) { write(sorted.at(i).location, writeFlags); } } } return ret; }
Set<String> ListSymbolsJob::listSymbols(const std::shared_ptr<Project> &project) const { const bool hasFilter = QueryJob::hasFilter(); const bool stripParentheses = queryFlags() & QueryMessage::StripParentheses; const bool imenu = queryFlags() & QueryMessage::IMenu; Set<String> out; auto inserter = [this, &project, hasFilter, stripParentheses, imenu, &out](Project::SymbolMatchType, const String &string, const Set<Location> &locations) { if (hasFilter) { bool ok = false; for (const auto &l : locations) { if (filter(l.path())) { ok = true; break; } } if (!ok) return; } if (imenu) { const Symbol sym = project->findSymbol(*locations.begin()); if (!isImenuSymbol(sym)) return; } const int paren = string.indexOf('('); if (paren == -1) { out.insert(string); } else { if (!RTags::isFunctionVariable(string)) out.insert(string.left(paren)); if (!stripParentheses) out.insert(string); } }; project->findSymbols(string, inserter, queryFlags()); return out; }
Set<String> ListSymbolsJob::listSymbols(const std::shared_ptr<Project> &project) { const bool hasFilter = QueryJob::hasFilter(); const bool stripParentheses = queryFlags() & QueryMessage::StripParentheses; Set<String> out; auto inserter = [this, hasFilter, stripParentheses, &out](Project::SymbolMatchType, const String &string, const Set<Location> &locations) { if (hasFilter) { bool ok = false; for (const auto &l : locations) { if (filter(l.path())) { ok = true; break; } } if (!ok) return; } const int paren = string.indexOf('('); if (paren == -1) { out.insert(string); } else { if (!RTags::isFunctionVariable(string)) out.insert(string.left(paren)); if (!stripParentheses) out.insert(string); } }; if (queryFlags() & QueryMessage::WildcardSymbolNames && (string.contains('*') || string.contains('?')) && !string.endsWith('*')) string += '*'; project->findSymbols(string, inserter, queryFlags()); return out; }
Set<String> ListSymbolsJob::listSymbols(const shared_ptr<Project> &project) { Set<String> out; const bool hasFilter = Job::hasFilter(); const bool stripParentheses = queryFlags() & QueryMessage::StripParentheses; Scope<const SymbolNameMap&> symbolNames = project->lockSymbolNamesForRead(); const SymbolNameMap &map = symbolNames.data(); SymbolNameMap::const_iterator it = string.isEmpty() ? map.begin() : map.lower_bound(string); int count = 0; while (it != map.end()) { const String &entry = it->first; ++it; if (!string.isEmpty() && !entry.startsWith(string)) break; bool ok = true; if (hasFilter) { ok = false; const Set<Location> &locations = it->second; for (Set<Location>::const_iterator i = locations.begin(); i != locations.end(); ++i) { if (filter(i->path())) { ok = true; break; } } } if (ok) { const int paren = entry.indexOf('('); if (paren == -1) { out.insert(entry); } else { out.insert(entry.left(paren)); if (!stripParentheses) out.insert(entry); } } if (!(++count % 100) && isAborted()) break; } return out; }
bool QueryJob::write(const Symbol &symbol, Flags<WriteFlag> writeFlags) { Flags<Symbol::ToStringFlag> toStringFlags; if (queryFlags() & QueryMessage::SymbolInfoIncludeTargets) toStringFlags |= Symbol::IncludeTargets; if (queryFlags() & QueryMessage::SymbolInfoIncludeReferences) toStringFlags |= Symbol::IncludeReferences; if (queryFlags() & QueryMessage::SymbolInfoIncludeParents) toStringFlags |= Symbol::IncludeParents; if (queryFlags() & QueryMessage::SymbolInfoIncludeBaseClasses) toStringFlags |= Symbol::IncludeBaseClasses; if (queryFlags() & (QueryMessage::ContainingFunction|QueryMessage::JSON|QueryMessage::Elisp)) toStringFlags |= Symbol::IncludeContainingFunction; if (queryFlags() & (QueryMessage::ContainingFunctionLocation|QueryMessage::JSON|QueryMessage::Elisp)) toStringFlags |= Symbol::IncludeContainingFunctionLocation; if (symbol.isNull()) return false; if (!filterLocation(symbol.location)) return false; if (!mKindFilters.filter(symbol)) return false; String out; if (queryFlags() & (QueryMessage::Elisp|QueryMessage::JSON)) { Value val = symbol.toValue(project(), toStringFlags, locationToStringFlags() | Location::NoColor, mPieceFilters); if (queryFlags() & QueryMessage::Elisp) { out = RTags::toElisp(val); } else { out = val.toJSON(); } } else { out = symbol.toString(project(), toStringFlags, locationToStringFlags(), mPieceFilters); } return write(out, writeFlags|Unfiltered); }
void IndexerJob::execute() { if (isAborted()) return; mTimer.start(); mData.reset(new IndexData); if (mType == Dump) { assert(id() != -1); if (shared_ptr<Project> p = project()) { for (int i=0; i<mSourceInformation.builds.size(); ++i) { parse(i); if (mUnits.at(i).second) { DumpUserData u = { 0, this, !(queryFlags() & QueryMessage::NoContext) }; clang_visitChildren(clang_getTranslationUnitCursor(mUnits.at(i).second), IndexerJob::dumpVisitor, &u); } } } } else { { MutexLocker lock(&mMutex); mStarted = true; } int errorCount = 0; int unitCount = 0; const int buildCount = mSourceInformation.builds.size(); for (int i=0; i<buildCount; ++i) { if (!parse(i)) { goto end; } if (mUnits.at(i).second) ++unitCount; } mParseTime = time(0); for (int i=0; i<buildCount; ++i) { int err = 0; if (!visit(i) || !diagnose(i, &err)) goto end; errorCount += err; } { mData->message += mSourceInformation.sourceFile.toTilde(); if (buildCount > 1) mData->message += String::format<16>(" (%d builds)", buildCount); if (!unitCount) { mData->message += " error"; } else if (unitCount != buildCount) { mData->message += String::format<16>(" (%d errors, %d ok)", buildCount - unitCount, unitCount); } mData->message += String::format<16>(" in %sms. ", String::number(mTimer.elapsed()).constData()); if (unitCount) { mData->message += String::format<1024>("(%d syms, %d symNames, %d refs, %d deps, %d files)", mData->symbols.size(), mData->symbolNames.size(), mData->references.size(), mData->dependencies.size(), mVisitedFiles.size()); } else if (mData->dependencies.size()) { mData->message += String::format<16>("(%d deps)", mData->dependencies.size()); } if (mType == Dirty) mData->message += " (dirty)"; } end: shared_ptr<Project> p = project(); if (p) { shared_ptr<IndexerJob> job = static_pointer_cast<IndexerJob>(shared_from_this()); p->onJobFinished(job); } } for (int i=0; i<mUnits.size(); ++i) { if (mUnits.at(i).first) clang_disposeIndex(mUnits.at(i).first); if (mUnits.at(i).second) clang_disposeTranslationUnit(mUnits.at(i).second); } mUnits.clear(); }
int TokensJob::execute() { std::shared_ptr<Project> proj = project(); if (!proj) return 1; auto map = proj->openTokens(mFileId); if (!map) return 2; const uint32_t count = map->count(); uint32_t i = 0; if (mFrom != 0) { i = map->lowerBound(mFrom); if (i > 0 && i < count) { const Token val = map->valueAt(i - 1); if (val.offset + val.length >= mFrom) --i; } } std::function<bool(const Token &)> writeToken; if (queryFlags() & QueryMessage::Elisp) { const char *elispFormat = "(cons %d (list (cons 'length %d) (cons 'kind \"%s\") (cons 'spelling \"%s\")))"; write("(list"); if (queryFlags() & QueryMessage::TokensIncludeSymbols) { writeToken = [this, &proj, elispFormat](const Token &token) { String out = String::format<1024>(elispFormat, token.offset, token.length, RTags::tokenKindSpelling(token.kind), RTags::elispEscape(token.spelling).constData()); const Symbol sym = proj->findSymbol(token.location); if (!sym.isNull()) { out.chop(2); out << " (cons 'symbol "; } if (!write(out)) return false; if (!sym.isNull()) return write(sym) && write(")))"); return true; }; } else { writeToken = [this, elispFormat](const Token &token) { return write<1024>(elispFormat, token.offset, token.length, RTags::tokenKindSpelling(token.kind), RTags::elispEscape(token.spelling).constData()); }; } } else { writeToken = [this](const Token &token) { return write(token.toString()); }; } while (i < count) { const Token token = map->valueAt(i++); if (token.offset > mTo) break; if (!writeToken(token)) return 4; } if (queryFlags() & QueryMessage::Elisp) { write(")"); } return 0; }
int ReferencesJob::execute() { std::shared_ptr<Project> proj = project(); Location startLocation; Map<Location, std::pair<bool, uint16_t> > references; if (proj) { if (!symbolName.isEmpty()) locations = proj->locations(symbolName); if (!locations.isEmpty()) { const SymbolMap &map = proj->symbols(); for (Set<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it) { Location pos; SymbolMap::const_iterator found; found = RTags::findCursorInfo(map, *it); if (found == map.end()) continue; pos = found->first; if (startLocation.isNull()) startLocation = pos; std::shared_ptr<CursorInfo> cursorInfo = found->second; if (!cursorInfo) continue; if (RTags::isReference(cursorInfo->kind)) { cursorInfo = cursorInfo->bestTarget(map, &pos); if (!cursorInfo) continue; } if (queryFlags() & QueryMessage::AllReferences) { const SymbolMap all = cursorInfo->allReferences(pos, map); bool classRename = false; switch (cursorInfo->kind) { case CXCursor_Constructor: case CXCursor_Destructor: classRename = true; break; default: classRename = cursorInfo->isClass(); break; } for (SymbolMap::const_iterator a = all.begin(); a != all.end(); ++a) { if (!classRename) { references[a->first] = std::make_pair(a->second->isDefinition(), a->second->kind); } else { enum State { FoundConstructor = 0x1, FoundClass = 0x2, FoundReferences = 0x4 }; unsigned state = 0; const SymbolMap targets = a->second->targetInfos(map); for (SymbolMap::const_iterator t = targets.begin(); t != targets.end(); ++t) { if (t->second->kind != a->second->kind) state |= FoundReferences; if (t->second->kind == CXCursor_Constructor) { state |= FoundConstructor; } else if (t->second->isClass()) { state |= FoundClass; } } if ((state & (FoundConstructor|FoundClass)) != FoundConstructor || !(state & FoundReferences)) { references[a->first] = std::make_pair(a->second->isDefinition(), a->second->kind); } } } } else if (queryFlags() & QueryMessage::FindVirtuals) { const SymbolMap virtuals = cursorInfo->virtuals(pos, map); const bool declarationOnly = queryFlags() & QueryMessage::DeclarationOnly; for (SymbolMap::const_iterator v = virtuals.begin(); v != virtuals.end(); ++v) { const bool def = v->second->isDefinition(); if (declarationOnly && def) { const std::shared_ptr<CursorInfo> decl = v->second->bestTarget(map); if (decl && !decl->isNull()) continue; } references[v->first] = std::make_pair(def, v->second->kind); } startLocation.clear(); // since one normally calls this on a declaration it kinda // doesn't work that well do the clever offset thing // underneath } else { const SymbolMap callers = cursorInfo->callers(pos, map); for (SymbolMap::const_iterator c = callers.begin(); c != callers.end(); ++c) { references[c->first] = std::make_pair(false, CXCursor_FirstInvalid); // For find callers we don't want to prefer definitions or do ranks on cursors } } } } } enum { Rename = (QueryMessage::ReverseSort|QueryMessage::AllReferences) }; if ((queryFlags() & Rename) == Rename) { if (!references.isEmpty()) { Map<Location, std::pair<bool, uint16_t> >::const_iterator it = references.end(); do { --it; write(it->first); } while (it != references.begin()); return 0; } } else { List<RTags::SortedCursor> sorted; sorted.reserve(references.size()); for (Map<Location, std::pair<bool, uint16_t> >::const_iterator it = references.begin(); it != references.end(); ++it) { sorted.append(RTags::SortedCursor(it->first, it->second.first, it->second.second)); } if (queryFlags() & QueryMessage::ReverseSort) { std::sort(sorted.begin(), sorted.end(), std::greater<RTags::SortedCursor>()); } else { std::sort(sorted.begin(), sorted.end()); } int startIndex = 0; const int count = sorted.size(); if (!startLocation.isNull() && !(queryFlags() & QueryMessage::NoSortReferencesByInput)) { for (int i=0; i<count; ++i) { if (sorted.at(i).location == startLocation) { startIndex = i + 1; break; } } } for (int i=0; i<count; ++i) { const Location &loc = sorted.at((startIndex + i) % count).location; write(loc); } if (count) return 0; } return 1; }
void ReferencesJob::execute() { shared_ptr<Project> proj = project(); Location startLocation; Set<Location> references; if (proj) { if (!symbolName.isEmpty()) { Scope<const SymbolNameMap&> scope = proj->lockSymbolNamesForRead(); if (scope.isNull()) return; locations = scope.data().value(symbolName); } if (!locations.isEmpty()) { Scope<const SymbolMap&> scope = proj->lockSymbolsForRead(); if (scope.isNull()) return; const SymbolMap &map = scope.data(); for (Set<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it) { Location pos; CursorInfo cursorInfo = RTags::findCursorInfo(map, *it, &pos); if (startLocation.isNull()) startLocation = pos; if (RTags::isReference(cursorInfo.kind)) { cursorInfo = cursorInfo.bestTarget(map, &pos); } if (queryFlags() & QueryMessage::ReferencesForRenameSymbol) { const SymbolMap all = cursorInfo.allReferences(pos, map); bool classRename = false; switch (cursorInfo.kind) { case CXCursor_Constructor: case CXCursor_Destructor: classRename = true; break; default: classRename = cursorInfo.isClass(); break; } for (SymbolMap::const_iterator a = all.begin(); a != all.end(); ++a) { if (!classRename) { references.insert(a->first); } else { enum State { FoundConstructor = 0x1, FoundClass = 0x2, FoundReferences = 0x4 }; unsigned state = 0; const SymbolMap targets = a->second.targetInfos(map); for (SymbolMap::const_iterator t = targets.begin(); t != targets.end(); ++t) { if (t->second.kind != a->second.kind) state |= FoundReferences; if (t->second.kind == CXCursor_Constructor) { state |= FoundConstructor; } else if (t->second.isClass()) { state |= FoundClass; } } if ((state & (FoundConstructor|FoundClass)) != FoundConstructor || !(state & FoundReferences)) { references.insert(a->first); } } } } else if (queryFlags() & QueryMessage::FindVirtuals) { const SymbolMap virtuals = cursorInfo.virtuals(pos, map); for (SymbolMap::const_iterator v = virtuals.begin(); v != virtuals.end(); ++v) { references.insert(v->first); } } else { const SymbolMap callers = cursorInfo.callers(pos, map); for (SymbolMap::const_iterator c = callers.begin(); c != callers.end(); ++c) { references.insert(c->first); } } } } } List<Location> sorted = references.toList(); if (queryFlags() & QueryMessage::ReverseSort) { std::sort(sorted.begin(), sorted.end(), std::greater<Location>()); } else { std::sort(sorted.begin(), sorted.end()); } // We don't want to do the startIndex stuff when renaming. The only way to // tell the difference between rtags-find-all-references and // rtags-rename-symbol is that the latter does a reverse sort. It kinda // doesn't make sense to have this behavior in reverse sort anyway so I // won't formalize the rename parameters to indicate that we're renaming int startIndex = 0; const int count = sorted.size(); if (!(queryFlags() & QueryMessage::ReverseSort) && sorted.size() != 1 && !startLocation.isNull()) { startIndex = sorted.indexOf(startLocation) + 1; } const unsigned keyFlags = Job::keyFlags(); for (int i=0; i<count; ++i) { const Location &loc = sorted.at((startIndex + i) % count); write(loc.key(keyFlags)); } }
int ReferencesJob::execute() { const bool rename = queryFlags() & QueryMessage::Rename; std::shared_ptr<Project> proj = project(); if (!proj) return 1; Set<Symbol> refs; Map<Location, std::pair<bool, CXCursorKind> > references; if (!symbolName.isEmpty()) { const bool hasFilter = QueryJob::hasFilter(); auto inserter = [this, hasFilter](Project::SymbolMatchType type, const String &string, const Set<Location> &locs) { if (type == Project::StartsWith) { const size_t paren = string.indexOf('('); if (paren == String::npos || paren != symbolName.size() || RTags::isFunctionVariable(string)) return; } for (const auto &l : locs) { if (!hasFilter || filter(l.path())) { locations.insert(l); } } }; proj->findSymbols(symbolName, inserter, queryFlags()); } const bool declarationOnly = queryFlags() & QueryMessage::DeclarationOnly; const bool definitionOnly = queryFlags() & QueryMessage::DefinitionOnly; Location startLocation; bool first = true; for (auto it = locations.begin(); it != locations.end(); ++it) { const Location pos = *it; Symbol sym = proj->findSymbol(pos); if (sym.isNull()) continue; if (first && !(queryFlags() & QueryMessage::NoSortReferencesByInput)) { first = false; startLocation = sym.location; } if (sym.isReference()) { const Symbol target = proj->findTarget(sym); if (!target.isNull() && target.kind != CXCursor_MacroExpansion) sym = target; } if (sym.isNull()) continue; if (rename && sym.isConstructorOrDestructor()) { const Location loc = sym.location; sym.clear(); const Set<String> usrs = proj->findTargetUsrs(loc); for (const String &usr : usrs) { for (const Symbol &s : proj->findByUsr(usr, loc.fileId(), Project::ArgDependsOn, loc)) { if (s.isClass()) { sym = s; if (s.isDefinition()) break; } } } if (sym.isNull()) continue; } if (queryFlags() & QueryMessage::AllReferences) { const Set<Symbol> all = proj->findAllReferences(sym); for (const auto &symbol : all) { if (rename) { if (symbol.kind == CXCursor_MacroExpansion && sym.kind != CXCursor_MacroDefinition) continue; if (symbol.flags & Symbol::AutoRef) continue; } else if (sym.isClass() && symbol.isConstructorOrDestructor()) { continue; } const bool def = symbol.isDefinition(); if (def) { if (declarationOnly) continue; } else if (definitionOnly) { continue; } references[symbol.location] = std::make_pair(def, symbol.kind); } } else if (queryFlags() & QueryMessage::FindVirtuals) { const Set<Symbol> virtuals = proj->findVirtuals(sym); for (const auto &symbol : virtuals) { const bool def = symbol.isDefinition(); if (def) { if (declarationOnly) continue; } else if (definitionOnly) { continue; } references[symbol.location] = std::make_pair(def, symbol.kind); } } else { const Set<Symbol> symbols = proj->findCallers(sym); for (const auto &symbol : symbols) { const bool def = symbol.isDefinition(); if (def) { if (declarationOnly) continue; } else if (definitionOnly) { continue; } references[symbol.location] = std::make_pair(false, CXCursor_FirstInvalid); } } } Flags<QueryJob::WriteFlag> writeFlags; Flags<Location::ToStringFlag> kf = locationToStringFlags(); if (queryFlags() & QueryMessage::Elisp) { write("(list ", DontQuote); writeFlags |= QueryJob::NoContext; } else if (queryFlags() & QueryMessage::NoContext) { writeFlags |= QueryJob::NoContext; } auto writeCons = [this](const String &car, const String &cdr) { write("(cons ", DontQuote); write(car, DontQuote); write(cdr); write(")", DontQuote); }; auto writeLoc = [this, writeCons, writeFlags, kf](Location loc) { if (queryFlags() & QueryMessage::Elisp) { if (!filterLocation(loc)) return; write("(list ", DontQuote); locationToString(loc, [writeCons, this](LocationPiece piece, const String &string) { switch (piece) { case Piece_ContainingFunctionLocation: if (queryFlags() & QueryMessage::ContainingFunctionLocation) writeCons("'cfl", string); break; case Piece_ContainingFunctionName: if (queryFlags() & QueryMessage::ContainingFunction) writeCons("'cf", string); break; case Piece_Location: writeCons("'loc", string); break; case Piece_Context: if (!(queryFlags() & QueryMessage::NoContext)) writeCons("'ctx", string); break; case Piece_SymbolName: case Piece_Kind: break; } }); write(")", DontQuote); } else { write(loc, writeFlags); } }; if (rename) { if (!references.isEmpty()) { if (queryFlags() & QueryMessage::ReverseSort) { Map<Location, std::pair<bool, CXCursorKind> >::const_iterator it = references.end(); do { --it; writeLoc(it->first); } while (it != references.begin()); } else { for (const auto &it : references) { writeLoc(it.first); } } } } else { List<RTags::SortedSymbol> sorted; sorted.reserve(references.size()); for (Map<Location, std::pair<bool, CXCursorKind> >::const_iterator it = references.begin(); it != references.end(); ++it) { sorted.append(RTags::SortedSymbol(it->first, it->second.first, it->second.second)); } if (queryFlags() & QueryMessage::ReverseSort) { std::sort(sorted.begin(), sorted.end(), std::greater<RTags::SortedSymbol>()); } else { std::sort(sorted.begin(), sorted.end()); } int startIndex = 0; const int count = sorted.size(); if (!startLocation.isNull()) { for (int i=0; i<count; ++i) { if (sorted.at(i).location == startLocation) { startIndex = i + 1; break; } } } for (int i=0; i<count; ++i) { Location loc = sorted.at((startIndex + i) % count).location; writeLoc(loc); } } if (queryFlags() & QueryMessage::Elisp) write(")", DontQuote); return references.isEmpty() ? 1 : 0; }
int FindFileJob::execute() { std::shared_ptr<Project> proj = project(); if (!proj) { return 1; } if (!proj->fileManager()) return 1; const Path srcRoot = proj->path(); assert(srcRoot.endsWith('/')); enum Mode { All, FilePath, Regex, Pattern, } mode = All; String::CaseSensitivity cs = String::CaseSensitive; if (queryFlags() & QueryMessage::MatchRegex) { mode = Regex; } else if (!mPattern.isEmpty()) { mode = mPattern[0] == '/' ? FilePath : Pattern; } if (queryFlags() & QueryMessage::MatchCaseInsensitive) cs = String::CaseInsensitive; String out; out.reserve(PATH_MAX); const bool absolutePath = queryFlags() & QueryMessage::AbsolutePath; if (absolutePath) out.append(srcRoot); const Files& dirs = proj->files(); assert(proj->fileManager()); if (dirs.isEmpty()) proj->fileManager()->load(FileManager::Synchronous); Files::const_iterator dirit = dirs.begin(); bool foundExact = false; const int patternSize = mPattern.size(); List<String> matches; const bool preferExact = queryFlags() & QueryMessage::FindFilePreferExact; int ret = 1; bool firstElisp = queryFlags() & QueryMessage::Elisp; auto writeFile = [this, &firstElisp](const Path &path) { if (firstElisp) { firstElisp = false; if (!write("(list", DontQuote)) return false; } return write(path); }; while (dirit != dirs.end()) { const Path &dir = dirit->first; if (dir.size() < srcRoot.size()) { continue; } else { out.append(dir.constData() + srcRoot.size(), dir.size() - srcRoot.size()); } const Set<String> &files = dirit->second; for (Set<String>::const_iterator it = files.begin(); it != files.end(); ++it) { const String &key = *it; out.append(key); bool ok; switch (mode) { case All: ok = true; break; case Regex: ok = Rct::contains(out, mRegex); break; case FilePath: case Pattern: if (!preferExact) { ok = out.contains(mPattern, cs); } else { const int outSize = out.size(); const bool exact = (outSize > patternSize && out.endsWith(mPattern) && out.at(outSize - (patternSize + 1)) == '/'); if (exact) { ok = true; if (!foundExact) { matches.clear(); foundExact = true; } } else { ok = !foundExact && out.contains(mPattern, cs); } } if (!ok && mode == FilePath) { Path p(out); if (!absolutePath) p.prepend(srcRoot); p.resolve(); if (p == mPattern) ok = true; } break; } if (ok) { ret = 0; Path matched = out; if (absolutePath) matched.resolve(); if (preferExact && !foundExact) { matches.append(matched); } else { if (!writeFile(matched)) return 1; } } out.chop(key.size()); } out.chop(dir.size() - srcRoot.size()); ++dirit; } for (List<String>::const_iterator it = matches.begin(); it != matches.end(); ++it) { if (!writeFile(*it)) { return 1; } } if (queryFlags() & QueryMessage::Elisp && !firstElisp && !write(")", DontQuote)) return 1; return ret; }
int IncludeFileJob::execute() { if (mSource.isNull()) { return 1; } const Path directory = mSource.sourceFile().parentDir(); Set<Location> last; int matches = 0; auto process = [&directory, this](const Set<Location> &locations) { for (const Location &loc : locations) { bool first = true; for (const Path &path : headersForSymbol(project(), loc)) { bool found = false; const Symbol sym = project()->findSymbol(loc); switch (sym.kind) { case CXCursor_ClassDecl: case CXCursor_StructDecl: case CXCursor_ClassTemplate: case CXCursor_TypedefDecl: if (!sym.isDefinition()) break; case CXCursor_FunctionDecl: case CXCursor_FunctionTemplate: { List<String> alternatives; if (path.startsWith(directory)) alternatives << String::format<256>("#include \"%s\"", path.mid(directory.size()).constData()); for (const Source::Include &inc : mSource.includePaths) { const Path p = inc.path.ensureTrailingSlash(); if (path.startsWith(p)) { alternatives << String::format<256>("#include <%s>", path.mid(p.size()).constData()); } } const int tail = strlen(path.fileName()) + 1; List<String>::iterator it = alternatives.begin(); while (it != alternatives.end()) { bool drop = false; for (List<String>::const_iterator it2 = it + 1; it2 != alternatives.end(); ++it2) { if (it2->size() < it->size()) { if (!strncmp(it2->constData() + 9, it->constData() + 9, it2->size() - tail - 9)) { drop = true; break; } } } if (drop) { it = alternatives.erase(it); } else { ++it; } } std::sort(alternatives.begin(), alternatives.end(), [](const String &a, const String &b) { return a.size() < b.size(); }); for (const auto &a : alternatives) { found = true; write(a); } break; } default: break; } if (first) { if (found) break; first = false; } } } }; project()->findSymbols(mSymbol, [&](Project::SymbolMatchType type, const String &symbolName, const Set<Location> &locations) { ++matches; bool fuzzy = false; if (type == Project::StartsWith) { fuzzy = true; const int paren = symbolName.indexOf('('); if (paren == mSymbol.size() && !RTags::isFunctionVariable(symbolName)) fuzzy = false; } if (!fuzzy) { process(locations); } else if (matches == 1) { last = locations; } }, queryFlags()); if (matches == 1 && !last.isEmpty()) { process(last); } return 0; }
Set<String> ListSymbolsJob::listSymbols(const std::shared_ptr<Project> &project) { Set<String> out; const bool hasFilter = Job::hasFilter(); const bool stripParentheses = queryFlags() & QueryMessage::StripParentheses; const bool wildcard = queryFlags() & QueryMessage::WildcardSymbolNames && (string.contains('*') || string.contains('?')); const bool caseInsensitive = queryFlags() & QueryMessage::MatchCaseInsensitive; const String::CaseSensitivity cs = caseInsensitive ? String::CaseInsensitive : String::CaseSensitive; String lowerBound; // error() << "SHOBA" << wildcard << string; if (wildcard) { if (!string.endsWith('*')) string.append('*'); if (!caseInsensitive) { for (int i=0; i<string.size(); ++i) { if (string.at(i) == '?' || string.at(i) == '*') { lowerBound = string.left(i); break; } } } } else if (!caseInsensitive) { lowerBound = string; } const SymbolNameMap &map = project->symbolNames(); SymbolNameMap::const_iterator it = string.isEmpty() || caseInsensitive ? map.begin() : map.lower_bound(lowerBound); int count = 0; while (it != map.end()) { const String &entry = it->first; ++it; if (!string.isEmpty()) { if (wildcard) { if (!Rct::wildCmp(string.constData(), entry.constData(), cs)) { continue; } } else if (!entry.startsWith(string, cs)) { if (!caseInsensitive) { break; } else { continue; } } } bool ok = true; if (hasFilter) { ok = false; const Set<Location> &locations = it->second; for (Set<Location>::const_iterator i = locations.begin(); i != locations.end(); ++i) { if (filter(i->path())) { ok = true; break; } } } if (ok) { const int paren = entry.indexOf('('); if (paren == -1) { out.insert(entry); } else { out.insert(entry.left(paren)); if (!stripParentheses) out.insert(entry); } } if (!(++count % 100) && isAborted()) break; } return out; }