#include "CodeController.h" #include #include "Application.h" #include "ApplicationSettings.h" #include "FileInfo.h" #include "MessageMoveIDECursor.h" #include "MessageShowError.h" #include "MessageStatus.h" #include "SourceLocation.h" #include "SourceLocationCollection.h" #include "SourceLocationFile.h" #include "StorageAccess.h" #include "TextAccess.h" #include "logging.h" #include "tracing.h" #include "utility.h" #include "utilityString.h" CodeController::CodeController(StorageAccess* storageAccess): m_storageAccess(storageAccess) {} Id CodeController::getSchedulerId() const { return Controller::getTabId(); } void CodeController::handleMessage(MessageActivateErrors* message) { TRACE("code errors"); saveOrRestoreViewMode(message); CodeView* view = getView(); std::vector errors; if (message->file.empty()) { errors = m_storageAccess->getErrorsLimited(message->filter); } else { errors = m_storageAccess->getErrorsForFileLimited(message->filter, message->file); } m_collection = m_storageAccess->getErrorSourceLocations(errors); m_files = getFilesForCollection(m_collection); std::sort(m_files.begin(), m_files.end(), CodeFileParams::sortById); CodeView::CodeParams params; params.clearSnippets = true; params.useSingleFileCache = false; params.errorInfos = errors; createReferences(); expandVisibleFiles(params.useSingleFileCache); showFiles(params, firstReferenceScrollParams(), !message->isReplayed()); } void CodeController::handleMessage(MessageActivateFullTextSearch* message) { TRACE("code fulltext"); saveOrRestoreViewMode(message); m_collection = m_storageAccess->getFullTextSearchLocations( message->searchTerm, message->caseSensitive); CodeView::CodeParams params; params.clearSnippets = true; params.useSingleFileCache = false; m_files = getFilesForCollection(m_collection); createReferences(); expandVisibleFiles(params.useSingleFileCache); showFiles(params, firstReferenceScrollParams(), !message->isReplayed()); } void CodeController::handleMessage(MessageActivateLegend* message) { clear(); } void CodeController::handleMessage(MessageActivateLocalSymbols* message) { createLocalReferences(utility::toSet(message->symbolIds)); m_codeParams.activeLocalSymbolIds = message->symbolIds; m_codeParams.activeLocalSymbolType = LOCATION_LOCAL_SYMBOL; m_codeParams.currentActiveLocalLocationIds.clear(); showFiles(m_codeParams, CodeScrollParams(), !message->isReplayed()); } void CodeController::handleMessage(MessageActivateOverview* message) { TRACE("code all"); saveOrRestoreViewMode(message); clearReferences(); std::shared_ptr currentProject = Application::getInstance()->getCurrentProject(); if (!currentProject || message->acceptedNodeTypes != NodeTypeSet::all()) { clear(); return; } CodeSnippetParams statsSnippet; statsSnippet.hasAllSourceLocations = true; statsSnippet.title = currentProject->getProjectSettingsFilePath().withoutExtension().fileName(); statsSnippet.isOverview = true; statsSnippet.startLineNumber = 1; statsSnippet.endLineNumber = 1; statsSnippet.locationFile = std::make_shared( FilePath(), L"", true, true, true); std::vector description = getProjectDescription(statsSnippet.locationFile.get()); std::stringstream ss; if (description.size()) { ss << "\n"; for (const std::string& line: description) { ss << line + "\n"; } } StorageStats stats = m_storageAccess->getStorageStats(); ErrorCountInfo errorCount = m_storageAccess->getErrorCount(); ss << "\n"; if (stats.timestamp.isValid()) { ss << "\tlast indexed: " + stats.timestamp.toString() + "\n"; ss << "\n"; } ss << "\t" + std::to_string(stats.fileCount) + " files"; ss << (stats.completedFileCount != stats.fileCount ? " (" + std::to_string(stats.completedFileCount) + " complete)" : "") + "\n"; ss << "\t" + std::to_string(stats.fileLOCCount) + " lines of code\n"; ss << "\n"; ss << "\t" + std::to_string(stats.nodeCount) + " symbols\n"; ss << "\t" + std::to_string(stats.edgeCount) + " references\n"; ss << "\n"; ss << "\t" + std::to_string(errorCount.total) + " errors (" + std::to_string(errorCount.fatal) + " fatal)\n"; ss << "\n"; if (errorCount.fatal) { ss << "\tWarning: Your project has fatal errors, which cause\n"; ss << "\t a lot of missing information in affected files.\n"; ss << "\t Try to resolve them!\n"; ss << "\n"; } statsSnippet.code = ss.str(); CodeFileParams file; file.isMinimized = false; file.locationFile = statsSnippet.locationFile; file.snippetParams.push_back(statsSnippet); file.fileParams = std::make_shared(statsSnippet); m_currentFilePath = file.locationFile->getFilePath(); m_files = {file}; CodeView::CodeParams params; params.clearSnippets = true; params.useSingleFileCache = false; showFiles(params, CodeScrollParams(), !message->isReplayed()); } void CodeController::handleMessage(MessageActivateTokens* message) { TRACE("code activate"); saveOrRestoreViewMode(message); CodeView* view = getView(); if (!message->tokenIds.size()) { view->clear(); return; } CodeView::CodeParams params; params.activeTokenIds = message->tokenIds; params.clearSnippets = true; Id declarationId = 0; // 0 means that no token is found. if (!message->isAggregation) { std::vector activeTokenIds; for (Id tokenId: params.activeTokenIds) { utility::append( activeTokenIds, m_storageAccess->getActiveTokenIdsForId(tokenId, &declarationId)); } params.activeTokenIds = activeTokenIds; } if (message->isEdge && params.activeTokenIds.size() == 1) { showFirstActiveReference(params.activeTokenIds[0], !message->isReplayed()); return; } m_collection = m_storageAccess->getSourceLocationsForTokenIds(params.activeTokenIds); m_files = getFilesForActiveSourceLocations(m_collection.get(), declarationId); createReferences(); expandVisibleFiles(params.useSingleFileCache); showFiles(params, definitionReferenceScrollParams(params.activeTokenIds), !message->isReplayed()); // send status message { size_t fileCount = m_collection->getSourceLocationFileCount(); size_t referenceCount = m_collection->getSourceLocationCount(); std::wstring status; for (const SearchMatch& match: message->getSearchMatches()) { status += L"Activate \"" + match.name + L"\": "; break; } status += std::to_wstring(message->tokenIds.size()) + L" "; status += (message->tokenIds.size() == 1 ? L"result" : L"results"); if (fileCount > 0) { status += L" with " + std::to_wstring(referenceCount) + L" "; status += (referenceCount == 1 ? L"reference" : L"references"); status += L" in " + std::to_wstring(fileCount) + L" "; status += (fileCount == 1 ? L"file" : L"files"); } MessageStatus(status).dispatch(); } } void CodeController::handleMessage(MessageActivateTrail* message) { if (message->custom) { getView()->clear(); Id nodeId = {message->originId ? message->originId : message->targetId}; MessageCodeShowDefinition msg(nodeId); handleMessage(&msg); } } void CodeController::handleMessage(MessageActivateTrailEdge* message) { TRACE("trail edge activate"); saveOrRestoreViewMode(message); m_codeParams.activeTokenIds = message->edgeIds; m_collection = m_storageAccess->getSourceLocationsForTokenIds(m_codeParams.activeTokenIds); m_files = getFilesForActiveSourceLocations(m_collection.get(), 0); createReferences(); expandVisibleFiles(m_codeParams.useSingleFileCache); showFiles(m_codeParams, firstReferenceScrollParams(), !message->isReplayed()); } void CodeController::handleMessage(MessageChangeFileView* message) { TRACE("code change file"); saveOrRestoreViewMode(message); for (CodeFileParams& file: m_files) { if (file.locationFile->getFilePath() == message->filePath) { setFileState(file, message->state, m_codeParams.useSingleFileCache); // maximize within snippet list if (message->viewMode == MessageChangeFileView::VIEW_LIST && message->state == MessageChangeFileView::FILE_MAXIMIZED) { file.snippetParams = {*file.fileParams.get()}; } break; } } showFiles(m_codeParams, message->scrollParams, !message->isReplayed()); } void CodeController::handleMessage(MessageCodeReference* message) { bool next = (message->type == MessageCodeReference::REFERENCE_NEXT); bool local = message->localReference; if (local) { iterateLocalReference(next, true); } else { iterateReference(next); } } void CodeController::handleMessage(MessageCodeShowDefinition* message) { TRACE("code show definition"); Id nodeId = message->nodeId; std::shared_ptr collection = m_storageAccess->getSourceLocationsForTokenIds({nodeId}); if (!collection->getSourceLocationFileCount()) { LOG_ERROR("MessageCodeShowDefinition did not contain a nodeId with location files."); return; } size_t lineNumber = 1; size_t columnNumber = 1; FilePath filePath; // use first scope location for nodeId, otherwise first location if (collection->getSourceLocationCount()) { std::shared_ptr filteredCollection = std::make_shared(); bool addedLocation = false; collection->forEachSourceLocation([&](SourceLocation* location) { if (addedLocation || !location->isStartLocation()) { return; } if (location->isScopeLocation()) { filteredCollection->addSourceLocationCopy(location); filteredCollection->addSourceLocationCopy(location->getEndLocation()); filePath = location->getFilePath(); lineNumber = location->getLineNumber(); columnNumber = location->getColumnNumber(); addedLocation = true; return; } }); if (!addedLocation) { SourceLocation* location = collection->getSourceLocationFiles().begin()->second->getSourceLocations().begin()->get(); filteredCollection->addSourceLocationCopy(location); filteredCollection->addSourceLocationCopy(location->getOtherLocation()); filePath = location->getFilePath(); lineNumber = location->getStartLocation()->getLineNumber(); columnNumber = location->getStartLocation()->getColumnNumber(); } collection = filteredCollection; } else // otherwise first file { filePath = collection->getSourceLocationFiles().begin()->second->getFilePath(); } if (message->inIDE) { MessageMoveIDECursor( filePath, static_cast(lineNumber), static_cast(columnNumber)) .dispatch(); return; } CodeFileParams* file = addSourceLocations(collection->getSourceLocationFiles().begin()->second); if (!file) { LOG_ERROR("MessageCodeShowDefinition did not create a file"); return; } saveOrRestoreViewMode(message); showFiles( m_codeParams, CodeScrollParams::toLine(filePath, lineNumber, CodeScrollParams::Target::TOP), !message->isReplayed()); } void CodeController::handleMessage(MessageDeactivateEdge* message) { CodeScrollParams scrollParams; m_codeParams.activeLocationIds.clear(); m_codeParams.activeLocalSymbolIds.clear(); m_codeParams.currentActiveLocalLocationIds.clear(); m_referenceIndex = -1; clearLocalReferences(); if (message->scrollToDefinition) { scrollParams = definitionReferenceScrollParams(m_codeParams.activeTokenIds); } showFiles(m_codeParams, scrollParams, !message->isReplayed()); } void CodeController::handleMessage(MessageErrorCountClear* message) { if (getView()->showsErrors()) { clear(); } } void CodeController::handleMessage(MessageFlushUpdates* message) { showFiles(m_codeParams, m_scrollParams, true); } void CodeController::handleMessage(MessageFocusIn* message) { getView()->focusTokenIds(message->tokenIds); } void CodeController::handleMessage(MessageFocusOut* message) { getView()->defocusTokenIds(); } void CodeController::handleMessage(MessageScrollToLine* message) { getView()->scrollTo( CodeScrollParams::toLine(message->filePath, message->line, CodeScrollParams::Target::TOP), false); MessageStatus( L"Showing source location: " + message->filePath.wstr() + L" : " + std::to_wstring(message->line)) .dispatch(); } void CodeController::handleMessage(MessageScrollCode* message) { if (message->isReplayed()) { m_scrollParams = CodeScrollParams::toValue(message->value, message->inListMode); } } void CodeController::handleMessage(MessageShowError* message) { CodeView* view = getView(); if (view->showsErrors()) { showFirstActiveReference(message->errorId, !message->isReplayed()); } } void CodeController::handleMessage(MessageShowReference* message) { m_referenceIndex = static_cast(message->refIndex); bool replayed = message->isReplayed(); if (m_referenceIndex >= 0 && m_referenceIndex < static_cast(m_references.size())) { const Reference& ref = m_references[m_referenceIndex]; m_codeParams.activeLocationIds = {ref.locationId}; setFileState( ref.filePath, getView()->isInListMode() ? MessageChangeFileView::FILE_SNIPPETS : MessageChangeFileView::FILE_MAXIMIZED, m_codeParams.useSingleFileCache); showFiles(m_codeParams, toReferenceScrollParams(ref), !message->isReplayed()); if (ref.locationType == LOCATION_ERROR) { MessageShowError(ref.tokenId).dispatch(); } } } void CodeController::handleMessage(MessageShowScope* message) { TRACE("code scope"); std::shared_ptr collection = m_storageAccess->getSourceLocationsForLocationIds({message->scopeLocationId}); SourceLocation* location = collection->getSourceLocationById(message->scopeLocationId); if (!location || !location->isScopeLocation() || !location->getOtherLocation()) { LOG_ERROR("MessageShowScope did not contain a valid scope location id"); return; } addSourceLocations(collection->getSourceLocationFiles().begin()->second); if (m_localReferences.size()) { addAllSourceLocations(); createLocalReferences(utility::toSet(m_codeParams.activeLocalSymbolIds)); m_codeParams.currentActiveLocalLocationIds.clear(); } showFiles(m_codeParams, CodeScrollParams(), !message->isReplayed()); } CodeView* CodeController::getView() const { return Controller::getView(); } void CodeController::clear() { getView()->clear(); m_collection = std::make_shared(); m_currentFilePath = FilePath(); clearReferences(); } std::vector CodeController::getFilesForActiveSourceLocations( const SourceLocationCollection* collection, Id declarationId) const { TRACE(); std::vector files; collection->forEachSourceLocationFile([&](std::shared_ptr file) -> void { bool isDeclarationFile = false; bool isDefinitionFile = false; file->forEachSourceLocation([&](SourceLocation* location) { for (Id i: location->getTokenIds()) { if (i == declarationId) { isDeclarationFile = true; if (location->getType() == LOCATION_SCOPE) { isDefinitionFile = true; } break; } } }); CodeFileParams params; params.locationFile = file; params.isDeclaration = isDeclarationFile; params.isDefinition = isDefinitionFile; files.push_back(params); }); std::sort(files.begin(), files.end(), CodeFileParams::sort); return files; } std::vector CodeController::getFilesForCollection( std::shared_ptr collection) const { TRACE(); std::vector files; collection->forEachSourceLocationFile([&](std::shared_ptr file) -> void { CodeFileParams params; params.locationFile = file; files.push_back(params); }); return files; } CodeSnippetParams CodeController::getSnippetParamsForWholeFile( std::shared_ptr locationFile, bool useSingleFileCache) const { CodeSnippetParams snippet; snippet.startLineNumber = 1; if (useSingleFileCache && !getView()->isInListMode() && getView()->hasSingleFileCached(locationFile->getFilePath())) { snippet.locationFile = locationFile; return snippet; } bool showsErrors = false; if (m_collection->getSourceLocationFiles().size()) { std::shared_ptr file = m_collection->getSourceLocationFiles().begin()->second; if (file->getSourceLocations().size()) { showsErrors = (*file->getSourceLocations().begin())->getType() == LOCATION_ERROR; } } std::shared_ptr textAccess = m_storageAccess->getFileContent( locationFile->getFilePath(), showsErrors); snippet.code = textAccess->getText(); // make a copy of SourceLocationFile so that isWhole flag is different for first snippet adding // the file and second snippet adding the content snippet.locationFile = std::make_shared(*locationFile.get()); snippet.locationFile->setIsWhole(true); return snippet; } std::vector CodeController::getSnippetsForFile( std::shared_ptr activeSourceLocations) const { TRACE(); bool showsErrors = false; if (activeSourceLocations->getSourceLocations().size()) { showsErrors = (*activeSourceLocations->getSourceLocations().begin())->getType() == LOCATION_ERROR; } std::shared_ptr textAccess = m_storageAccess->getFileContent( activeSourceLocations->getFilePath(), showsErrors); size_t lineCount = textAccess->getLineCount(); SnippetMerger fileScopedMerger(1, static_cast(lineCount)); std::map> mergers; std::shared_ptr scopeLocations = m_storageAccess->getSourceLocationsOfTypeInFile( activeSourceLocations->getFilePath(), LOCATION_SCOPE); activeSourceLocations->forEachStartSourceLocation([&](SourceLocation* location) { buildMergerHierarchy(location, scopeLocations.get(), fileScopedMerger, mergers); }); std::vector atomicRanges; std::shared_ptr commentLocations = m_storageAccess->getSourceLocationsOfTypeInFile( activeSourceLocations->getFilePath(), LOCATION_COMMENT); commentLocations->forEachStartSourceLocation([&](SourceLocation* location) { atomicRanges.push_back(SnippetMerger::Range( SnippetMerger::Border(static_cast(location->getLineNumber()), false), SnippetMerger::Border( static_cast(location->getOtherLocation()->getLineNumber()), false))); }); atomicRanges = SnippetMerger::Range::mergeAdjacent(atomicRanges); std::deque ranges = fileScopedMerger.merge(atomicRanges); const int snippetExpandRange = ApplicationSettings::getInstance()->getCodeSnippetExpandRange(); std::vector snippets; for (const SnippetMerger::Range& range: ranges) { CodeSnippetParams params; params.startLineNumber = std::max( 1, range.start.row - (range.start.strong ? 0 : snippetExpandRange)); params.endLineNumber = std::min( static_cast(lineCount), range.end.row + (range.end.strong ? 0 : snippetExpandRange)); params.locationFile = activeSourceLocations->getFilteredByLines( params.startLineNumber, params.endLineNumber); if (params.startLineNumber > 1) { const SourceLocation* location = getSourceLocationOfParentScope( params.startLineNumber, scopeLocations.get()); if (location && location->getTokenIds().size()) { params.title = m_storageAccess->getNameHierarchyForNodeId(location->getTokenIds()[0]) .getQualifiedName(); params.titleId = location->getLocationId(); } } if (params.endLineNumber < lineCount) { const SourceLocation* location = getSourceLocationOfParentScope( params.endLineNumber + 1, scopeLocations.get()); if (location && location->getTokenIds().size()) { params.footer = m_storageAccess ->getNameHierarchyForNodeId(location->getTokenIds()[0]) .getQualifiedName(); params.footerId = location->getLocationId(); } } if (params.titleId == 0 && params.startLineNumber > 1) { params.title = activeSourceLocations->getFilePath().wstr(); } else if (params.footerId == 0 && params.endLineNumber < lineCount) { params.footer = activeSourceLocations->getFilePath().wstr(); } for (const std::string& line: textAccess->getLines( static_cast(params.startLineNumber), static_cast(params.endLineNumber))) { params.code += line; } snippets.push_back(params); } return snippets; } std::shared_ptr CodeController::buildMergerHierarchy( const SourceLocation* location, const SourceLocationFile* scopeLocations, SnippetMerger& fileScopedMerger, std::map>& mergers) const { std::shared_ptr currentMerger = std::make_shared( static_cast(location->getStartLocation()->getLineNumber()), static_cast(location->getEndLocation()->getLineNumber())); const SourceLocation* scopeLocation = getSourceLocationOfParentScope( location->getLineNumber(), scopeLocations); if (!scopeLocation) { fileScopedMerger.addChild(currentMerger); return currentMerger; } std::shared_ptr nextMerger; std::map>::iterator it = mergers.find( static_cast(scopeLocation->getLocationId())); if (it == mergers.end()) { nextMerger = buildMergerHierarchy(scopeLocation, scopeLocations, fileScopedMerger, mergers); mergers[static_cast(scopeLocation->getLocationId())] = nextMerger; } else { nextMerger = it->second; } nextMerger->addChild(currentMerger); return currentMerger; } const SourceLocation* CodeController::getSourceLocationOfParentScope( size_t lineNumber, const SourceLocationFile* scopeLocations) const { const SourceLocation* location = nullptr; scopeLocations->forEachStartSourceLocation([&location, lineNumber](SourceLocation* scopeLocation) { if (scopeLocation->getLineNumber() < lineNumber && scopeLocation->getEndLocation()->getLineNumber() >= lineNumber && (!location || *location < *scopeLocation)) { location = scopeLocation; } }); return location; } std::vector CodeController::getProjectDescription(SourceLocationFile* locationFile) const { std::shared_ptr currentProject = Application::getInstance()->getCurrentProject(); if (!currentProject) { return std::vector(); } std::string description = currentProject->getDescription(); if (!description.size()) { return std::vector(); } // todo fixme: this split currently prevents the next step from recognizing multi level name hierarchies. std::vector lines = utility::splitToVector(description, "\\n"); size_t startLineNumber = 2; for (size_t i = 0; i < lines.size(); i++) { std::string line = "\t" + utility::trim(lines[i]); line = utility::replace(line, "\\t", "\t"); line = utility::replace(line, "\\r", "\r"); size_t pos = 0; while (pos != std::string::npos) { size_t posA = line.find('[', pos); size_t posB = line.find(']', posA); if (posA == std::string::npos || posB == std::string::npos) { break; } std::wstring serializedName = utility::decodeFromUtf8( line.substr(posA + 1, posB - posA - 1)); NameHierarchy nameHierarchy = NameHierarchy::deserialize(serializedName); Id tokenId = m_storageAccess->getNodeIdForNameHierarchy(nameHierarchy); std::string nameString = utility::encodeToUtf8(nameHierarchy.getQualifiedName()); if (tokenId > 0) { line.replace(posA, posB - posA + 1, nameString); locationFile->addSourceLocation( LOCATION_TOKEN, 0, {tokenId}, startLineNumber + i, posA + 1, startLineNumber + i, posA + nameString.size()); } else { line.replace(posA, posB - posA + 1, "[symbol not found (" + nameString + ")]"); } pos = posA + serializedName.size(); } lines[i] = line; } return lines; } void CodeController::clearReferences() { m_references.clear(); m_referenceIndex = -1; clearLocalReferences(); } void CodeController::createReferences() { clearReferences(); for (CodeFileParams& file: m_files) { size_t referenceCountBefore = m_references.size(); if (file.locationFile->isWhole()) { Reference ref; ref.filePath = file.locationFile->getFilePath(); m_references.push_back(ref); } else { std::map scopeLocationIds; file.locationFile->forEachStartSourceLocation([&](SourceLocation* location) { if (location->isScopeLocation()) { for (Id tokenId: location->getTokenIds()) { scopeLocationIds.emplace(tokenId, location->getLocationId()); } } }); file.locationFile->forEachStartSourceLocation([&](SourceLocation* location) { if (location->isScopeLocation() || location->getType() == LOCATION_SIGNATURE || location->getType() == LOCATION_COMMENT || location->getType() == LOCATION_QUALIFIER) { return; } if (!location->getTokenIds().size()) { Reference ref; ref.filePath = location->getFilePath(); ref.tokenId = 0; ref.locationId = location->getLocationId(); ref.locationType = location->getType(); m_references.push_back(ref); return; } for (Id i: location->getTokenIds()) { Reference ref; ref.filePath = location->getFilePath(); ref.tokenId = i; ref.locationId = location->getLocationId(); ref.locationType = location->getType(); std::map::const_iterator it = scopeLocationIds.find(i); if (it != scopeLocationIds.end()) { ref.scopeLocationId = it->second; } m_references.push_back(ref); } }); } file.referenceCount = m_references.size() - referenceCountBefore; } } void CodeController::clearLocalReferences() { m_localReferences.clear(); m_localReferenceIndex = -1; } void CodeController::createLocalReferences(const std::set& localSymbolIds) { clearLocalReferences(); if (localSymbolIds.size()) { auto func = [&localSymbolIds, this](const SourceLocation* location) { for (Id tokenId: location->getTokenIds()) { if (localSymbolIds.find(tokenId) != localSymbolIds.end()) { Reference ref; ref.filePath = location->getFilePath(); ref.locationId = location->getLocationId(); ref.locationType = location->getType(); m_localReferences.push_back(ref); return; } } }; bool inListMode = getView()->isInListMode(); for (const CodeFileParams& file: m_files) { if (inListMode) { for (const CodeSnippetParams& snippet: file.snippetParams) { snippet.locationFile->forEachStartSourceLocation(func); } } else if (file.fileParams) { file.fileParams->locationFile->forEachStartSourceLocation(func); } } } } void CodeController::iterateReference(bool next) { if (!m_references.size()) { return; } if (next) { m_referenceIndex++; if (m_referenceIndex == m_references.size()) { m_referenceIndex = 0; } } else { if (m_referenceIndex < 1) { m_referenceIndex = static_cast(m_references.size()) - 1; } else { m_referenceIndex--; } } const Reference& ref = m_references[m_referenceIndex - 1]; MessageShowReference(m_referenceIndex, ref.tokenId, ref.locationId, true).dispatch(); } void CodeController::iterateLocalReference(bool next, bool updateView) { if (!m_localReferences.size()) { return; } if (next) { m_localReferenceIndex++; if (m_localReferenceIndex == static_cast(m_localReferences.size())) { m_localReferenceIndex = 0; } } else { if (m_localReferenceIndex < 1) { m_localReferenceIndex = static_cast(m_localReferences.size()) - 1; } else { m_localReferenceIndex--; } } const Reference& ref = m_localReferences[m_localReferenceIndex]; m_codeParams.currentActiveLocalLocationIds = {ref.locationId}; // synchronise reference navigation with local reference navigation if (ref.locationType == LOCATION_TOKEN) { for (size_t i = 0; i < m_references.size(); i++) { if (m_references[i].locationId == ref.locationId) { m_referenceIndex = static_cast(i); } } } showFiles(m_codeParams, toReferenceScrollParams(ref), updateView); } void CodeController::expandVisibleFiles(bool useSingleFileCache) { TRACE(); if (!m_files.size()) { return; } bool inListMode = getView()->isInListMode(); size_t filesToExpand = inListMode ? std::min(int(m_files.size()), 3) : 1; MessageChangeFileView::FileState state = inListMode ? MessageChangeFileView::FILE_SNIPPETS : MessageChangeFileView::FILE_MAXIMIZED; for (size_t i = 0; i < filesToExpand; i++) { setFileState(m_files[i], state, useSingleFileCache); } } CodeFileParams* CodeController::addSourceLocations(std::shared_ptr locationFile) { if (!m_collection) { m_collection = std::make_shared(); } m_collection->addSourceLocationCopies(locationFile.get()); CodeFileParams* file = nullptr; for (CodeFileParams& f: m_files) { if (f.locationFile->getFilePath() == locationFile->getFilePath()) { file = &f; break; } } if (!file) { CodeFileParams f; f.locationFile = locationFile; m_files.push_back(f); file = &m_files.back(); } if (file->snippetParams.size()) { std::vector snippets = getSnippetsForFile(locationFile); if (snippets.size() != 1) { LOG_ERROR("addSourceLocations() didn't result in one single snippet to be created"); return nullptr; } CodeSnippetParams newSnippet = snippets[0]; size_t i = 0; while (i < file->snippetParams.size()) { size_t start = newSnippet.startLineNumber; size_t end = newSnippet.endLineNumber; const CodeSnippetParams& oldSnippet = file->snippetParams[i]; if (oldSnippet.endLineNumber + 1 < start) // before { i++; continue; } else if (oldSnippet.startLineNumber > end + 1) // after { break; } else if (oldSnippet.startLineNumber <= start && oldSnippet.endLineNumber >= end) // containing { return nullptr; } else if (oldSnippet.startLineNumber < start || oldSnippet.endLineNumber > end) // overlaping { newSnippet = CodeSnippetParams::merge(newSnippet, oldSnippet); } else if (oldSnippet.startLineNumber >= start && oldSnippet.endLineNumber <= end) // enclosing { // copy all source locations from old to new snippet: fulltext locations got lost newSnippet.locationFile->copySourceLocations(oldSnippet.locationFile); } file->snippetParams.erase(file->snippetParams.begin() + i); } file->snippetParams.insert(file->snippetParams.begin() + i, newSnippet); } setFileState( *file, getView()->isInListMode() ? MessageChangeFileView::FILE_SNIPPETS : MessageChangeFileView::FILE_MAXIMIZED, m_codeParams.useSingleFileCache); return file; } void CodeController::setFileState( const FilePath& filePath, MessageChangeFileView::FileState state, bool useSingleFileCache) { for (CodeFileParams& file: m_files) { if (file.locationFile->getFilePath() == filePath) { setFileState(file, state, useSingleFileCache); return; } } } void CodeController::setFileState( CodeFileParams& file, MessageChangeFileView::FileState state, bool useSingleFileCache) { TRACE(); switch (state) { case MessageChangeFileView::FILE_MINIMIZED: file.isMinimized = true; break; case MessageChangeFileView::FILE_SNIPPETS: file.isMinimized = false; if (!file.snippetParams.size()) { if (file.locationFile->isWhole()) { file.snippetParams = { getSnippetParamsForWholeFile(file.locationFile, useSingleFileCache)}; } else { file.snippetParams = getSnippetsForFile(file.locationFile); } } break; case MessageChangeFileView::FILE_MAXIMIZED: if (!file.fileParams) { file.fileParams = std::make_shared( getSnippetParamsForWholeFile(file.locationFile, useSingleFileCache)); } if (file.locationFile) { m_currentFilePath = file.locationFile->getFilePath(); } break; } } bool CodeController::addAllSourceLocations() { TRACE(); bool addedNewLocations = false; for (CodeFileParams& file: m_files) { for (CodeSnippetParams& snippet: file.snippetParams) { if (snippet.hasAllSourceLocations) { continue; } if (snippet.locationFile->isWhole()) { snippet.locationFile = m_storageAccess->getSourceLocationsForFile( snippet.locationFile->getFilePath()); if (snippet.locationFile) { snippet.locationFile->copySourceLocations(file.locationFile); } } else { std::shared_ptr file = m_storageAccess->getSourceLocationsForLinesInFile( snippet.locationFile->getFilePath(), snippet.startLineNumber, snippet.endLineNumber); if (file) { file->copySourceLocations(snippet.locationFile); snippet.locationFile = file; } } addedNewLocations = true; snippet.hasAllSourceLocations = true; } if (file.fileParams) { if (file.fileParams->hasAllSourceLocations) { continue; } file.fileParams->locationFile = m_storageAccess->getSourceLocationsForFile( file.locationFile->getFilePath()); if (file.fileParams->locationFile) { file.fileParams->locationFile->copySourceLocations(file.locationFile); } addedNewLocations = true; file.fileParams->hasAllSourceLocations = true; } } return addedNewLocations; } void CodeController::addModificationTimes() { TRACE(); std::vector filePaths; for (const CodeFileParams& file: m_files) { filePaths.push_back(file.locationFile->getFilePath()); } std::vector fileInfos = m_storageAccess->getFileInfosForFilePaths(filePaths); std::map fileInfoMap; for (FileInfo& fileInfo: fileInfos) { fileInfoMap.emplace(fileInfo.path, fileInfo); } for (CodeFileParams& file: m_files) { file.modificationTime = fileInfoMap[file.locationFile->getFilePath()].lastWriteTime; } } CodeScrollParams CodeController::firstReferenceScrollParams() const { if (m_references.size()) { const Reference& ref = m_references.front(); return CodeScrollParams::toReference( ref.filePath, ref.scopeLocationId ? ref.scopeLocationId : ref.locationId, CodeScrollParams::Target::TOP); } return CodeScrollParams(); } CodeScrollParams CodeController::definitionReferenceScrollParams(const std::vector& activeTokenIds) const { Id activeTokenId = activeTokenIds.size() ? activeTokenIds.front() : 0; if (activeTokenId) { for (const Reference& ref: m_references) { if (ref.scopeLocationId && ref.tokenId == activeTokenId) { return CodeScrollParams::toReference( ref.filePath, ref.scopeLocationId, CodeScrollParams::Target::TOP); } } for (const Reference& ref: m_references) { if (ref.tokenId == activeTokenId) { return CodeScrollParams::toReference( ref.filePath, ref.locationId, CodeScrollParams::Target::TOP); } } } return firstReferenceScrollParams(); } CodeScrollParams CodeController::toReferenceScrollParams(const Reference& ref) const { return CodeScrollParams::toReference( ref.filePath, ref.scopeLocationId ? ref.scopeLocationId : ref.locationId, CodeScrollParams::Target::CENTER); } void CodeController::saveOrRestoreViewMode(MessageBase* message) { if (message->isReplayed()) { auto it = m_messageIdToViewModeMap.find(message->getId()); if (it != m_messageIdToViewModeMap.end()) { getView()->setMode(it->second); } } else { m_messageIdToViewModeMap.emplace(message->getId(), getView()->isInListMode()); } } void CodeController::showFirstActiveReference(Id tokenId, bool updateView) { // iterate local references when same tokenId get reactivated (consecutive edge clicks) if (m_codeParams.activeLocalSymbolIds.size() == 1 && m_codeParams.activeLocalSymbolIds[0] == tokenId && m_localReferences.size()) { iterateLocalReference(true, updateView); return; } std::vector locationIds; int referenceIndex = -1; Reference firstReference; std::set filePathsToExpand; std::map filePathOrder; for (size_t i = 0; i < m_references.size(); i++) { const Reference& ref = m_references[i]; if (ref.tokenId == tokenId) { filePathsToExpand.insert(ref.filePath); locationIds.push_back(ref.locationId); if (!firstReference.tokenId) { firstReference = ref; referenceIndex = static_cast(i); } } filePathOrder.emplace(ref.filePath, filePathOrder.size()); } if (!locationIds.size()) { std::shared_ptr collection = m_storageAccess->getSourceLocationsForTokenIds({tokenId}); collection->forEachSourceLocation([&](SourceLocation* location) { if (!location->isStartLocation()) { return; } for (Id i: location->getTokenIds()) { if (i == tokenId) { locationIds.push_back(location->getLocationId()); filePathsToExpand.insert(location->getFilePath()); if (!firstReference.tokenId || filePathOrder[location->getFilePath()] < filePathOrder[firstReference.filePath]) { firstReference.tokenId = tokenId; firstReference.locationId = location->getLocationId(); firstReference.filePath = location->getFilePath(); } return; } } }); } if (getView()->isInListMode()) { for (const FilePath& filePath: filePathsToExpand) { setFileState( filePath, MessageChangeFileView::FILE_SNIPPETS, m_codeParams.useSingleFileCache); } } else if (firstReference.tokenId) { setFileState( firstReference.filePath, MessageChangeFileView::FILE_MAXIMIZED, m_codeParams.useSingleFileCache); } createLocalReferences({tokenId}); m_codeParams.activeLocalSymbolIds = {tokenId}; m_codeParams.activeLocalSymbolType = LOCATION_TOKEN; m_codeParams.currentActiveLocalLocationIds.clear(); CodeScrollParams scrollParams; if (firstReference.tokenId) { scrollParams = toReferenceScrollParams(firstReference); m_referenceIndex = referenceIndex; } showFiles(m_codeParams, scrollParams, updateView); } void CodeController::showFiles(CodeView::CodeParams params, CodeScrollParams scrollParams, bool updateView) { if (updateView) { addModificationTimes(); params.referenceCount = m_references.size(); params.referenceIndex = m_referenceIndex >= 0 ? m_referenceIndex : m_references.size(); params.localReferenceCount = m_localReferences.size(); params.localReferenceIndex = m_localReferenceIndex >= 0 ? m_localReferenceIndex : m_localReferences.size(); if (getView()->isInListMode()) { getView()->showSnippets(m_files, params, scrollParams); } else { bool updated = false; for (const CodeFileParams& file: m_files) { if (file.locationFile->getFilePath() == m_currentFilePath) { getView()->showSingleFile(file, params, scrollParams); updated = true; break; } } if (!updated) { getView()->showSingleFile(CodeFileParams(), params, scrollParams); } } params.clearSnippets = false; m_scrollParams = CodeScrollParams(); } else if (scrollParams.type != CodeScrollParams::Type::NONE) { m_scrollParams = scrollParams; } m_codeParams = params; if (addAllSourceLocations() && updateView) { getView()->updateSourceLocations(m_files); } }