Revert "Revert "[code vision] IDEA-308383: cache number of external usages""

This reverts commit f44d8a707041e25db91ec1653fcf0aa886f5eef3.

GitOrigin-RevId: 66a729da42e3f40478f1bba3aebc2759a451df1a
This commit is contained in:
Alexandr Suhinin
2023-03-08 17:23:05 +00:00
committed by intellij-monorepo-bot
parent 384e0f55da
commit a4418b235a
4 changed files with 106 additions and 21 deletions
@@ -9,14 +9,12 @@ import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.DeepestSuperMethodsSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -28,31 +26,32 @@ final class JavaTelescope {
private static final int TOO_MANY_USAGES = -1;
static String usagesHint(@NotNull PsiMember member, @NotNull PsiFile file) {
Project project = file.getProject();
int totalUsageCount = UsagesCountManager.getInstance(member.getProject()).countMemberUsages(file, member);
if (totalUsageCount == TOO_MANY_USAGES) return null;
if (!Registry.is("code.lens.java.show.0.usages") && totalUsageCount == 0) return null;
return JavaBundle.message("usages.telescope", totalUsageCount);
}
AtomicInteger totalUsageCount = new AtomicInteger();
public static int usagesCount(@NotNull PsiFile file, List<PsiMember> members, SearchScope scope) {
Project project = file.getProject();
ProgressIndicator progress = ObjectUtils.notNull(ProgressIndicatorProvider.getGlobalProgressIndicator(), /*todo remove*/new EmptyProgressIndicator());
List<PsiMember> things =
member instanceof PsiMethod ? new ArrayList<>(DeepestSuperMethodsSearch.search((PsiMethod)member).findAll()) : Collections.singletonList(member);
if (things.isEmpty()) {
things.add(member);
}
JobLauncher.getInstance().invokeConcurrentlyUnderProgress(things, progress, e -> {
int count = usagesCount(project, file, e, progress);
AtomicInteger totalUsageCount = new AtomicInteger();
JobLauncher.getInstance().invokeConcurrentlyUnderProgress(members, progress, member -> {
int count = usagesCount(project, file, member, scope, progress);
int newCount = totalUsageCount.updateAndGet(old -> count == TOO_MANY_USAGES ? TOO_MANY_USAGES : old + count);
return newCount != TOO_MANY_USAGES;
});
if (totalUsageCount.get() == TOO_MANY_USAGES) return null;
if (!Registry.is("code.lens.java.show.0.usages") && totalUsageCount.get() == 0) return null;
return JavaBundle.message("usages.telescope", totalUsageCount.get());
return totalUsageCount.get();
}
private static int usagesCount(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull final PsiMember member,
@NotNull SearchScope scope,
@NotNull ProgressIndicator progress) {
SearchScope useScope = UnusedSymbolUtil.getUseScope(member);
AtomicInteger count = new AtomicInteger();
boolean ok = UnusedSymbolUtil.processUsages(project, containingFile, member, progress, null, info -> {
boolean ok = UnusedSymbolUtil.processUsages(project, containingFile, useScope.intersectWith(scope), member, progress, null, info -> {
PsiFile psiFile = info.getFile();
if (psiFile == null) {
return true;
@@ -203,6 +203,17 @@ public final class UnusedSymbolUtil {
//System.out.println(s);
}
@NotNull
public static SearchScope getUseScope(@NotNull PsiMember member) {
Project project = member.getProject();
SearchScope useScope = PsiSearchHelper.getInstance(project).getUseScope(member);
// some classes may have references from within XML outside dependent modules, e.g. our actions
if (useScope instanceof GlobalSearchScope globalUseScope && member instanceof PsiClass) {
useScope = GlobalSearchScope.projectScope(project).uniteWith(globalUseScope);
}
return useScope;
}
// return false if can't process usages (weird member of too may usages) or processor returned false
public static boolean processUsages(@NotNull Project project,
@NotNull PsiFile containingFile,
@@ -210,18 +221,23 @@ public final class UnusedSymbolUtil {
@NotNull ProgressIndicator progress,
@Nullable PsiFile ignoreFile,
@NotNull Processor<? super UsageInfo> usageInfoProcessor) {
return processUsages(project, containingFile, getUseScope(member), member, progress, ignoreFile, usageInfoProcessor);
}
public static boolean processUsages(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull final SearchScope useScope,
@NotNull PsiMember member,
@NotNull ProgressIndicator progress,
@Nullable PsiFile ignoreFile,
@NotNull Processor<? super UsageInfo> usageInfoProcessor) {
String name = member.getName();
if (name == null) {
log("* "+member.getName()+" no name; false");
return false;
}
SearchScope useScope = PsiSearchHelper.getInstance(project).getUseScope(member);
PsiSearchHelper searchHelper = PsiSearchHelper.getInstance(project);
if (useScope instanceof GlobalSearchScope) {
// some classes may have references from within XML outside dependent modules, e.g. our actions
if (member instanceof PsiClass) {
useScope = GlobalSearchScope.projectScope(project).uniteWith((GlobalSearchScope)useScope);
}
PsiSearchHelper.SearchCostResult cheapEnough = searchHelper.isCheapEnoughToSearch(name, (GlobalSearchScope)useScope, ignoreFile, progress);
if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES
@@ -0,0 +1,69 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl
import com.intellij.ide.actions.QualifiedNameProviderUtil
import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.DeepestSuperMethodsSearch
import com.intellij.psi.util.PsiUtilCore
import com.intellij.util.containers.ContainerUtil
import java.util.concurrent.ConcurrentMap
class UsagesCountManager(project: Project): Disposable {
companion object {
@JvmStatic
fun getInstance(project: Project): UsagesCountManager {
return project.getService(UsagesCountManager::class.java)
}
}
private val externalUsagesCache: ConcurrentMap<VirtualFile, FileUsagesCache> = ContainerUtil.createConcurrentWeakKeySoftValueMap()
init {
val listener = object : PsiTreeAnyChangeAbstractAdapter() {
override fun onChange(psiFile: PsiFile?) {
val file = psiFile?.virtualFile ?: return
val valueToKeep = externalUsagesCache[file]
externalUsagesCache.clear()
if (valueToKeep != null) {
externalUsagesCache[file] = valueToKeep
}
}
}
PsiManager.getInstance(project).addPsiTreeChangeListener(listener, this)
}
fun countMemberUsages(file: PsiFile, member: PsiMember): Int {
val virtualFile = PsiUtilCore.getVirtualFile(file)
return externalUsagesCache.getOrPut(virtualFile) { FileUsagesCache() }.countMemberUsagesCached(file, member)
}
override fun dispose() {
}
}
private class FileUsagesCache {
private val externalUsagesCache: ConcurrentMap<String, Int> = ContainerUtil.createConcurrentWeakKeySoftValueMap()
fun countMemberUsagesCached(file: PsiFile, member: PsiMember): Int {
val methodMembers = if (member is PsiMethod) DeepestSuperMethodsSearch.search(member).findAll().toList() else emptyList()
val superMembers = methodMembers.ifEmpty { listOf(member) }
val localScope = GlobalSearchScope.fileScope(file)
val externalScope = GlobalSearchScope.notScope(localScope)
val internalUsages = JavaTelescope.usagesCount(file, superMembers, localScope)
val key = QualifiedNameProviderUtil.getQualifiedName(member)
val externalUsages = if (key != null) {
externalUsagesCache.getOrPut(key) { JavaTelescope.usagesCount(file, superMembers, externalScope) }
}
else {
JavaTelescope.usagesCount(file, superMembers, externalScope)
}
return externalUsages + internalUsages
}
}
@@ -2343,6 +2343,7 @@
<vcs.codeVisionLanguageContext language="JAVA" implementationClass="com.intellij.codeInsight.hints.JavaVcsCodeVisionContext"/>
<codeInsight.codeVisionSettingsPreviewLanguage modelId="vcs.code.vision" language="JAVA"/>
<codeInsight.daemonBoundCodeVisionProvider implementation="com.intellij.codeInsight.daemon.impl.JavaReferencesCodeVisionProvider"/>
<projectService serviceImplementation="com.intellij.codeInsight.daemon.impl.UsagesCountManager" />
<codeInsight.codeVisionSettingsPreviewLanguage modelId="references" language="JAVA"/>
<codeInsight.daemonBoundCodeVisionProvider implementation="com.intellij.codeInsight.daemon.impl.JavaInheritorsCodeVisionProvider"/>
<codeInsight.codeVisionSettingsPreviewLanguage modelId="inheritors" language="JAVA"/>