From a4418b235a6cf2afd33a1c7c476326da01b50ca0 Mon Sep 17 00:00:00 2001 From: Alexandr Suhinin Date: Wed, 8 Mar 2023 17:33:15 +0200 Subject: [PATCH] Revert "Revert "[code vision] IDEA-308383: cache number of external usages"" This reverts commit f44d8a707041e25db91ec1653fcf0aa886f5eef3. GitOrigin-RevId: 66a729da42e3f40478f1bba3aebc2759a451df1a --- .../daemon/impl/JavaTelescope.java | 31 ++++----- .../daemon/impl/UnusedSymbolUtil.java | 26 +++++-- .../daemon/impl/UsagesCountManager.kt | 69 +++++++++++++++++++ java/java-impl/src/META-INF/JavaPlugin.xml | 1 + 4 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UsagesCountManager.kt diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaTelescope.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaTelescope.java index c7dca003a943..4371b2761203 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaTelescope.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaTelescope.java @@ -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 members, SearchScope scope) { + Project project = file.getProject(); ProgressIndicator progress = ObjectUtils.notNull(ProgressIndicatorProvider.getGlobalProgressIndicator(), /*todo remove*/new EmptyProgressIndicator()); - List 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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.java index efb7dcb82f46..3565a202908c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.java @@ -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 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 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 diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UsagesCountManager.kt b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UsagesCountManager.kt new file mode 100644 index 000000000000..52226864d0f1 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UsagesCountManager.kt @@ -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 = 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 = 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 + } +} \ No newline at end of file diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 7323521f220e..6101ad39da6f 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -2343,6 +2343,7 @@ +