void (@NotNull QualifiedName qualifiedNamePattern, @NotNull PsiFile currentFile, @NotNull GlobalSearchScope scope, @NotNull Processor<? super ExportedName> processor) { if (qualifiedNamePattern.getComponentCount() < 2) return; QualifiedNameMatcher matcher = new QualifiedNameMatcher(qualifiedNamePattern); StubIndex stubIndex = StubIndex.getInstance(); Project project = Objects.requireNonNull(scope.getProject()); PsiManager psiManager = PsiManager.getInstance(project); GlobalSearchScope moduleMatchingScope = scope.intersectWith(new ModuleQualifiedNameMatchingScope(matcher, project)); Set<QualifiedName> alreadySuggestedAttributes = new HashSet<>(); IndexLookupStats stats = new IndexLookupStats(); try { List<String> matchingAttributeNames = new ArrayList<>(); stubIndex.processAllKeys(PyExportedModuleAttributeIndex.KEY, attributeName -> { ProgressManager.checkCanceled(); stats.scannedKeys++; if (!matcher.attributeMatches(attributeName)) return true; stats.matchingKeys++; matchingAttributeNames.add(attributeName); return true; }, moduleMatchingScope); for (String attributeName : matchingAttributeNames) { stubIndex.processElements(PyExportedModuleAttributeIndex.KEY, attributeName, project, moduleMatchingScope, null, PyElement.class, element -> { ProgressManager.checkCanceled(); VirtualFile vFile = element.getContainingFile().getVirtualFile(); QualifiedName moduleQualifiedName = findQualifiedNameInClosestRoot(vFile, project); assert moduleQualifiedName != null : vFile; QualifiedName canonicalImportPath = findCanonicalImportPath(element, moduleQualifiedName, currentFile); QualifiedName importPath; if (canonicalImportPath != null && matcher.qualifierMatches(canonicalImportPath)) { importPath = canonicalImportPath; } else { importPath = moduleQualifiedName; } if (ContainerUtil.exists(importPath.getComponents(), c -> c.startsWith("_")) && !psiManager.isInProject(element)) { return true; } QualifiedName attributeQualifiedName = importPath.append(attributeName); if (alreadySuggestedAttributes.add(attributeQualifiedName)) { if (!processor.process(new ExportedName(attributeQualifiedName, element))) { return false; } } return true; }); } } catch (ProcessCanceledException e) { stats.cancelled = true; throw e; } finally { if (LOG.isDebugEnabled()) { LOG.debug("Index lookup stats for '" + qualifiedNamePattern + "':\n" + "Scanned keys: " + stats.scannedKeys + "\n" + "Matched keys: " + stats.matchingKeys + "\n" + (stats.cancelled ? "Cancelled in " : "Completed in ") + stats.getDuration() + " ms"); } } }