J2K SourceNavigationHelper: convert to Kotlin and prettify
This commit is contained in:
+245
-392
@@ -14,501 +14,354 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.decompiler.navigation;
|
package org.jetbrains.kotlin.idea.decompiler.navigation
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.google.common.collect.Sets;
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.project.DumbService
|
||||||
import com.intellij.openapi.module.Module;
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.project.DumbService;
|
import com.intellij.openapi.roots.OrderEntry
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
import com.intellij.openapi.roots.OrderEntry;
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.openapi.roots.ProjectRootManager;
|
import com.intellij.psi.PsiClass
|
||||||
import com.intellij.openapi.util.Condition;
|
import com.intellij.psi.search.EverythingGlobalScope
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.JavaPsiFacade;
|
import com.intellij.psi.stubs.StringStubIndexExtension
|
||||||
import com.intellij.psi.PsiClass;
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import com.intellij.psi.PsiElement;
|
import gnu.trove.THashSet
|
||||||
import com.intellij.psi.PsiFile;
|
import org.jetbrains.annotations.TestOnly
|
||||||
import com.intellij.psi.search.EverythingGlobalScope;
|
import org.jetbrains.kotlin.asJava.toLightClass
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||||
import com.intellij.psi.stubs.StringStubIndexExtension;
|
import org.jetbrains.kotlin.context.ContextForNewModule
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import org.jetbrains.kotlin.context.ProjectContext
|
||||||
import gnu.trove.THashSet;
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import kotlin.collections.CollectionsKt;
|
import org.jetbrains.kotlin.frontend.di.createLazyResolveSession
|
||||||
import kotlin.jvm.functions.Function1;
|
import org.jetbrains.kotlin.idea.decompiler.navigation.MemberMatching.*
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.kotlin.idea.stubindex.*
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
import org.jetbrains.annotations.TestOnly;
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns;
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.context.ContextKt;
|
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||||
import org.jetbrains.kotlin.context.MutableModuleContext;
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
import org.jetbrains.kotlin.psi.debugText.getDebugText
|
||||||
import org.jetbrains.kotlin.frontend.di.InjectionKt;
|
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
|
||||||
import org.jetbrains.kotlin.idea.stubindex.*;
|
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
|
||||||
import org.jetbrains.kotlin.name.ClassId;
|
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
|
||||||
import org.jetbrains.kotlin.name.Name;
|
|
||||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap;
|
|
||||||
import org.jetbrains.kotlin.psi.*;
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer;
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
object SourceNavigationHelper {
|
||||||
import java.util.Collections;
|
private val LOG = Logger.getInstance(SourceNavigationHelper::class.java)
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass;
|
enum class NavigationKind {
|
||||||
import static org.jetbrains.kotlin.idea.decompiler.navigation.MemberMatching.*;
|
|
||||||
|
|
||||||
public class SourceNavigationHelper {
|
|
||||||
private static final Logger LOG = Logger.getInstance(SourceNavigationHelper.class);
|
|
||||||
|
|
||||||
public enum NavigationKind {
|
|
||||||
CLASS_FILES_TO_SOURCES,
|
CLASS_FILES_TO_SOURCES,
|
||||||
SOURCES_TO_CLASS_FILES
|
SOURCES_TO_CLASS_FILES
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean forceResolve = false;
|
private var forceResolve = false
|
||||||
|
|
||||||
private SourceNavigationHelper() {
|
@TestOnly
|
||||||
|
fun setForceResolve(forceResolve: Boolean) {
|
||||||
|
SourceNavigationHelper.forceResolve = forceResolve
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun createLibraryOrSourcesScope(
|
||||||
private static GlobalSearchScope createLibraryOrSourcesScope(
|
declaration: KtNamedDeclaration,
|
||||||
@NotNull KtNamedDeclaration declaration,
|
navigationKind: NavigationKind
|
||||||
@NotNull NavigationKind navigationKind
|
): GlobalSearchScope {
|
||||||
) {
|
val containingFile = declaration.containingKtFile
|
||||||
KtFile containingFile = declaration.getContainingKtFile();
|
containingFile.virtualFile ?: return GlobalSearchScope.EMPTY_SCOPE
|
||||||
VirtualFile libraryFile = containingFile.getVirtualFile();
|
|
||||||
if (libraryFile == null) return GlobalSearchScope.EMPTY_SCOPE;
|
|
||||||
|
|
||||||
boolean includeLibrarySources = navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES;
|
val includeLibrarySources = navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES
|
||||||
if (ProjectRootsUtil.isInContent(declaration, false, includeLibrarySources, !includeLibrarySources, true)) {
|
if (ProjectRootsUtil.isInContent(declaration, false, includeLibrarySources, !includeLibrarySources, true)) {
|
||||||
return GlobalSearchScope.EMPTY_SCOPE;
|
return GlobalSearchScope.EMPTY_SCOPE
|
||||||
}
|
}
|
||||||
|
|
||||||
Project project = declaration.getProject();
|
val project = declaration.project
|
||||||
return includeLibrarySources
|
return if (includeLibrarySources)
|
||||||
? KotlinSourceFilterScope.librarySources(new EverythingGlobalScope(project), project)
|
KotlinSourceFilterScope.librarySources(EverythingGlobalScope(project), project)
|
||||||
: KotlinSourceFilterScope.libraryClassFiles(new EverythingGlobalScope(project), project);
|
else
|
||||||
|
KotlinSourceFilterScope.libraryClassFiles(EverythingGlobalScope(project), project)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<KtFile> getContainingFiles(@NotNull Iterable<KtNamedDeclaration> declarations) {
|
private fun haveRenamesInImports(files: Collection<KtFile>) = files.any { it.importDirectives.any { it.aliasName != null } }
|
||||||
Set<KtFile> result = Sets.newHashSet();
|
|
||||||
for (KtNamedDeclaration declaration : declarations) {
|
|
||||||
PsiFile containingFile = declaration.getContainingFile();
|
|
||||||
if (containingFile instanceof KtFile) {
|
|
||||||
result.add((KtFile) containingFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Lists.newArrayList(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean haveRenamesInImports(@NotNull List<KtFile> files) {
|
private fun findSpecialProperty(memberName: Name, containingClass: KtClass): KtNamedDeclaration? {
|
||||||
for (KtFile file : files) {
|
|
||||||
for (KtImportDirective importDirective : file.getImportDirectives()) {
|
|
||||||
if (importDirective.getAliasName() != null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static KtNamedDeclaration findSpecialProperty(@NotNull Name memberName, @NotNull KtClass containingClass) {
|
|
||||||
// property constructor parameters
|
// property constructor parameters
|
||||||
List<KtParameter> constructorParameters = containingClass.getPrimaryConstructorParameters();
|
val constructorParameters = containingClass.primaryConstructorParameters
|
||||||
for (KtParameter constructorParameter : constructorParameters) {
|
for (constructorParameter in constructorParameters) {
|
||||||
if (memberName.equals(constructorParameter.getNameAsName()) && constructorParameter.hasValOrVar()) {
|
if (memberName == constructorParameter.nameAsName && constructorParameter.hasValOrVar()) {
|
||||||
return constructorParameter;
|
return constructorParameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// enum entries
|
// enum entries
|
||||||
if (containingClass.hasModifier(KtTokens.ENUM_KEYWORD)) {
|
if (containingClass.hasModifier(KtTokens.ENUM_KEYWORD)) {
|
||||||
for (KtEnumEntry enumEntry : ContainerUtil.findAll(containingClass.getDeclarations(), KtEnumEntry.class)) {
|
for (enumEntry in ContainerUtil.findAll<KtDeclaration, KtEnumEntry>(containingClass.declarations, KtEnumEntry::class.java)) {
|
||||||
if (memberName.equals(enumEntry.getNameAsName())) {
|
if (memberName == enumEntry.nameAsName) {
|
||||||
return enumEntry;
|
return enumEntry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private fun convertPropertyOrFunction(
|
||||||
private static KtNamedDeclaration convertPropertyOrFunction(
|
declaration: KtNamedDeclaration,
|
||||||
@NotNull KtNamedDeclaration declaration,
|
navigationKind: NavigationKind
|
||||||
@NotNull NavigationKind navigationKind
|
): KtNamedDeclaration? {
|
||||||
) {
|
if (declaration is KtPrimaryConstructor) {
|
||||||
if (declaration instanceof KtPrimaryConstructor) {
|
val sourceClassOrObject = findClassOrObject(declaration.getContainingClassOrObject(), navigationKind)
|
||||||
KtClassOrObject sourceClassOrObject =
|
return sourceClassOrObject?.primaryConstructor ?: sourceClassOrObject
|
||||||
findClassOrObject(((KtPrimaryConstructor) declaration).getContainingClassOrObject(), navigationKind);
|
|
||||||
KtPrimaryConstructor primaryConstructor = sourceClassOrObject != null ? sourceClassOrObject.getPrimaryConstructor() : null;
|
|
||||||
return primaryConstructor != null ? primaryConstructor : sourceClassOrObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String memberNameAsString = declaration.getName();
|
val memberNameAsString = declaration.name
|
||||||
if (memberNameAsString == null) {
|
if (memberNameAsString == null) {
|
||||||
LOG.debug("JetSourceNavigationHelper.convertPropertyOrFunction(): null name for declaration " + declaration);
|
LOG.debug("Declaration with null name:" + declaration.getDebugText())
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
Name memberName = Name.identifier(memberNameAsString);
|
val memberName = Name.identifier(memberNameAsString)
|
||||||
|
|
||||||
PsiElement decompiledContainer = declaration.getParent();
|
val decompiledContainer = declaration.parent
|
||||||
|
|
||||||
Collection<KtNamedDeclaration> candidates;
|
var candidates: Collection<KtNamedDeclaration>
|
||||||
if (decompiledContainer instanceof KtFile) {
|
when (decompiledContainer) {
|
||||||
candidates = getInitialTopLevelCandidates(declaration, navigationKind);
|
is KtFile -> candidates = getInitialTopLevelCandidates(declaration, navigationKind)
|
||||||
}
|
is KtClassBody -> {
|
||||||
else if (decompiledContainer instanceof KtClassBody) {
|
val decompiledClassOrObject = decompiledContainer.getParent() as KtClassOrObject
|
||||||
KtClassOrObject decompiledClassOrObject = (KtClassOrObject) decompiledContainer.getParent();
|
val sourceClassOrObject = findClassOrObject(decompiledClassOrObject, navigationKind)
|
||||||
KtClassOrObject sourceClassOrObject = findClassOrObject(decompiledClassOrObject, navigationKind);
|
|
||||||
|
|
||||||
//noinspection unchecked
|
candidates = sourceClassOrObject?.let {
|
||||||
candidates = sourceClassOrObject == null
|
getInitialMemberCandidates(sourceClassOrObject, memberName, declaration::class.java)
|
||||||
? Collections.<KtNamedDeclaration>emptyList()
|
}.orEmpty()
|
||||||
: getInitialMemberCandidates(sourceClassOrObject, memberName,
|
|
||||||
(Class<KtNamedDeclaration>) declaration.getClass());
|
|
||||||
|
|
||||||
if (candidates.isEmpty()) {
|
if (candidates.isEmpty()) {
|
||||||
if (declaration instanceof KtProperty && sourceClassOrObject instanceof KtClass) {
|
if (declaration is KtProperty && sourceClassOrObject is KtClass) {
|
||||||
return findSpecialProperty(memberName, (KtClass) sourceClassOrObject);
|
return findSpecialProperty(memberName, sourceClassOrObject)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else -> throw IllegalStateException("Unexpected container of " +
|
||||||
else {
|
(if (navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES) "decompiled" else "source") +
|
||||||
throw new IllegalStateException("Unexpected container of " +
|
" declaration: " +
|
||||||
(navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES ? "decompiled" : "source") +
|
decompiledContainer::class.java.simpleName)
|
||||||
" declaration: " +
|
|
||||||
decompiledContainer.getClass().getSimpleName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (candidates.isEmpty()) {
|
if (candidates.isEmpty()) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
candidates = filterByOrderEntries(declaration, candidates);
|
candidates = filterByOrderEntries(declaration, candidates)
|
||||||
|
|
||||||
if (!forceResolve) {
|
if (!forceResolve) {
|
||||||
candidates = filterByReceiverPresenceAndParametersCount(declaration, candidates);
|
candidates = candidates.filter { sameReceiverPresenceAndParametersCount(it, declaration) }
|
||||||
|
|
||||||
if (candidates.size() <= 1) {
|
if (candidates.size <= 1) {
|
||||||
return candidates.isEmpty() ? null : candidates.iterator().next();
|
return candidates.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!haveRenamesInImports(getContainingFiles(candidates))) {
|
if (!haveRenamesInImports(candidates.getContainingFiles())) {
|
||||||
candidates = filterByReceiverAndParameterTypes(declaration, candidates);
|
candidates = candidates.filter { receiverAndParametersShortTypesMatch(it, declaration) }
|
||||||
|
|
||||||
if (candidates.size() <= 1) {
|
|
||||||
return candidates.isEmpty() ? null : candidates.iterator().next();
|
if (candidates.size <= 1) {
|
||||||
|
return candidates.firstOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KotlinCodeAnalyzer analyzer = createAnalyzer(candidates, declaration.getProject());
|
val analyzer = createAnalyzer(candidates, declaration.project)
|
||||||
|
|
||||||
for (KtNamedDeclaration candidate : candidates) {
|
for (candidate in candidates) {
|
||||||
//noinspection unchecked
|
val candidateDescriptor = analyzer.resolveToDescriptor(candidate) as CallableDescriptor
|
||||||
CallableDescriptor candidateDescriptor = (CallableDescriptor) analyzer.resolveToDescriptor(candidate);
|
|
||||||
if (receiversMatch(declaration, candidateDescriptor)
|
if (receiversMatch(declaration, candidateDescriptor)
|
||||||
&& valueParametersTypesMatch(declaration, candidateDescriptor)
|
&& valueParametersTypesMatch(declaration, candidateDescriptor)
|
||||||
&& typeParametersMatch((KtTypeParameterListOwner) declaration, candidateDescriptor.getTypeParameters())) {
|
&& typeParametersMatch(declaration as KtTypeParameterListOwner, candidateDescriptor.typeParameters)) {
|
||||||
return candidate;
|
return candidate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun createAnalyzer(
|
||||||
private static KotlinCodeAnalyzer createAnalyzer(
|
candidates: Collection<KtNamedDeclaration>,
|
||||||
@NotNull Collection<KtNamedDeclaration> candidates,
|
project: Project
|
||||||
@NotNull Project project
|
): KotlinCodeAnalyzer {
|
||||||
) {
|
val context = ContextForNewModule(
|
||||||
MutableModuleContext context = ContextKt.ContextForNewModule(
|
ProjectContext(project), Name.special("<library module>"), DefaultBuiltIns.Instance, null
|
||||||
ContextKt.ProjectContext(project), Name.special("<library module>"), DefaultBuiltIns.getInstance(), null
|
)
|
||||||
);
|
context.setDependencies(context.module, context.module.builtIns.builtInsModule)
|
||||||
context.setDependencies(context.getModule(), context.getModule().getBuiltIns().getBuiltInsModule());
|
val resolveSession = createLazyResolveSession(context, candidates.getContainingFiles())
|
||||||
ResolveSession resolveSession = InjectionKt.createLazyResolveSession(context, getContainingFiles(candidates));
|
context.initializeModuleContents(resolveSession.packageFragmentProvider)
|
||||||
context.initializeModuleContents(resolveSession.getPackageFragmentProvider());
|
return resolveSession
|
||||||
return resolveSession;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private fun <T : KtNamedDeclaration> findFirstMatchingInIndex(
|
||||||
private static <T extends KtNamedDeclaration> T findFirstMatchingInIndex(
|
entity: T,
|
||||||
@NotNull T entity,
|
navigationKind: NavigationKind,
|
||||||
@NotNull NavigationKind navigationKind,
|
index: StringStubIndexExtension<T>
|
||||||
@NotNull StringStubIndexExtension<T> index
|
): T? {
|
||||||
) {
|
val classFqName = entity.fqName!!
|
||||||
FqName classFqName = entity.getFqName();
|
|
||||||
assert classFqName != null;
|
|
||||||
|
|
||||||
GlobalSearchScope librarySourcesScope = createLibraryOrSourcesScope(entity, navigationKind);
|
val librarySourcesScope = createLibraryOrSourcesScope(entity, navigationKind)
|
||||||
if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
|
if (librarySourcesScope === GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
Collection<T> classes = index.get(classFqName.asString(), entity.getProject(), librarySourcesScope);
|
return index.get(classFqName.asString(), entity.project, librarySourcesScope).firstOrNull()
|
||||||
if (classes.isEmpty()) {
|
}
|
||||||
return null;
|
|
||||||
|
private fun findClassOrObject(decompiledClassOrObject: KtClassOrObject, navigationKind: NavigationKind): KtClassOrObject? {
|
||||||
|
return findFirstMatchingInIndex<KtClassOrObject>(decompiledClassOrObject, navigationKind, KotlinFullClassNameIndex.getInstance())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInitialTopLevelCandidates(
|
||||||
|
declaration: KtNamedDeclaration,
|
||||||
|
navigationKind: NavigationKind
|
||||||
|
): Collection<KtNamedDeclaration> {
|
||||||
|
val librarySourcesScope = createLibraryOrSourcesScope(declaration, navigationKind)
|
||||||
|
if (librarySourcesScope === GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
|
||||||
|
return emptyList()
|
||||||
}
|
}
|
||||||
return classes.iterator().next(); // if there are more than one with this FQ, find first of them
|
|
||||||
|
val index = getIndexForTopLevelPropertyOrFunction(declaration)
|
||||||
|
return index.get(declaration.fqName!!.asString(), declaration.project, librarySourcesScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private fun getIndexForTopLevelPropertyOrFunction(
|
||||||
private static KtClassOrObject findClassOrObject(
|
decompiledDeclaration: KtNamedDeclaration
|
||||||
@NotNull KtClassOrObject decompiledClassOrObject,
|
): StringStubIndexExtension<out KtNamedDeclaration> {
|
||||||
@NotNull NavigationKind navigationKind
|
when (decompiledDeclaration) {
|
||||||
) {
|
is KtNamedFunction -> return KotlinTopLevelFunctionFqnNameIndex.getInstance()
|
||||||
return findFirstMatchingInIndex(decompiledClassOrObject, navigationKind, KotlinFullClassNameIndex.getInstance());
|
is KtProperty -> return KotlinTopLevelPropertyFqnNameIndex.getInstance()
|
||||||
}
|
else -> throw IllegalArgumentException("Neither function nor declaration: " + decompiledDeclaration::class.java.name)
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static Collection<KtNamedDeclaration> getInitialTopLevelCandidates(
|
|
||||||
@NotNull KtNamedDeclaration declaration,
|
|
||||||
@NotNull NavigationKind navigationKind
|
|
||||||
) {
|
|
||||||
FqName memberFqName = declaration.getFqName();
|
|
||||||
assert memberFqName != null;
|
|
||||||
|
|
||||||
GlobalSearchScope librarySourcesScope = createLibraryOrSourcesScope(declaration, navigationKind);
|
|
||||||
if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
//noinspection unchecked
|
|
||||||
StringStubIndexExtension<KtNamedDeclaration> index =
|
|
||||||
(StringStubIndexExtension<KtNamedDeclaration>) getIndexForTopLevelPropertyOrFunction(declaration);
|
|
||||||
return index.get(memberFqName.asString(), declaration.getProject(), librarySourcesScope);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StringStubIndexExtension<? extends KtNamedDeclaration> getIndexForTopLevelPropertyOrFunction(
|
private fun getInitialMemberCandidates(
|
||||||
@NotNull KtNamedDeclaration decompiledDeclaration
|
sourceClassOrObject: KtClassOrObject,
|
||||||
) {
|
name: Name,
|
||||||
if (decompiledDeclaration instanceof KtNamedFunction) {
|
declarationClass: Class<out KtNamedDeclaration>
|
||||||
return KotlinTopLevelFunctionFqnNameIndex.getInstance();
|
) = sourceClassOrObject.declarations.filterIsInstance(declarationClass).filter {
|
||||||
|
declaration ->
|
||||||
|
name == declaration.nameAsSafeName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterByOrderEntries(
|
||||||
|
declaration: KtNamedDeclaration,
|
||||||
|
candidates: Collection<KtNamedDeclaration>
|
||||||
|
): List<KtNamedDeclaration> {
|
||||||
|
val fileIndex = ProjectRootManager.getInstance(declaration.project).fileIndex
|
||||||
|
val orderEntries = fileIndex.getOrderEntriesForFile(declaration.containingFile.virtualFile)
|
||||||
|
|
||||||
|
return candidates.filter {
|
||||||
|
val candidateOrderEntries = fileIndex.getOrderEntriesForFile(it.containingFile.virtualFile)
|
||||||
|
ContainerUtil.intersects<OrderEntry>(orderEntries, candidateOrderEntries)
|
||||||
}
|
}
|
||||||
if (decompiledDeclaration instanceof KtProperty) {
|
|
||||||
return KotlinTopLevelPropertyFqnNameIndex.getInstance();
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Neither function nor declaration: " + decompiledDeclaration.getClass().getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
fun getOriginalPsiClassOrCreateLightClass(classOrObject: KtClassOrObject): PsiClass? {
|
||||||
private static List<KtNamedDeclaration> getInitialMemberCandidates(
|
val fqName = classOrObject.fqName
|
||||||
@NotNull KtClassOrObject sourceClassOrObject,
|
|
||||||
@NotNull final Name name,
|
|
||||||
@NotNull Class<KtNamedDeclaration> declarationClass
|
|
||||||
) {
|
|
||||||
List<KtNamedDeclaration> allByClass = ContainerUtil.findAll(sourceClassOrObject.getDeclarations(), declarationClass);
|
|
||||||
return ContainerUtil.filter(allByClass, new Condition<KtNamedDeclaration>() {
|
|
||||||
@Override
|
|
||||||
public boolean value(KtNamedDeclaration declaration) {
|
|
||||||
return name.equals(declaration.getNameAsSafeName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static List<KtNamedDeclaration> filterByOrderEntries(
|
|
||||||
@NotNull KtNamedDeclaration declaration,
|
|
||||||
@NotNull Collection<KtNamedDeclaration> candidates
|
|
||||||
) {
|
|
||||||
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(declaration.getProject()).getFileIndex();
|
|
||||||
final List<OrderEntry> orderEntries = fileIndex.getOrderEntriesForFile(declaration.getContainingFile().getVirtualFile());
|
|
||||||
|
|
||||||
return CollectionsKt.filter(
|
|
||||||
candidates,
|
|
||||||
new Function1<KtNamedDeclaration, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean invoke(KtNamedDeclaration candidate) {
|
|
||||||
List<OrderEntry> candidateOrderEntries = fileIndex.getOrderEntriesForFile(candidate.getContainingFile().getVirtualFile());
|
|
||||||
return ContainerUtil.intersects(orderEntries, candidateOrderEntries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static List<KtNamedDeclaration> filterByReceiverPresenceAndParametersCount(
|
|
||||||
final @NotNull KtNamedDeclaration decompiledDeclaration,
|
|
||||||
@NotNull Collection<KtNamedDeclaration> candidates
|
|
||||||
) {
|
|
||||||
return ContainerUtil.filter(candidates, new Condition<KtNamedDeclaration>() {
|
|
||||||
@Override
|
|
||||||
public boolean value(KtNamedDeclaration candidate) {
|
|
||||||
return sameReceiverPresenceAndParametersCount(candidate, decompiledDeclaration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static List<KtNamedDeclaration> filterByReceiverAndParameterTypes(
|
|
||||||
final @NotNull KtNamedDeclaration decompiledDeclaration,
|
|
||||||
@NotNull Collection<KtNamedDeclaration> candidates
|
|
||||||
) {
|
|
||||||
return ContainerUtil.filter(candidates, new Condition<KtNamedDeclaration>() {
|
|
||||||
@Override
|
|
||||||
public boolean value(KtNamedDeclaration candidate) {
|
|
||||||
return receiverAndParametersShortTypesMatch(candidate, decompiledDeclaration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly
|
|
||||||
public static void setForceResolve(boolean forceResolve) {
|
|
||||||
SourceNavigationHelper.forceResolve = forceResolve;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static PsiClass getOriginalPsiClassOrCreateLightClass(@NotNull KtClassOrObject classOrObject) {
|
|
||||||
FqName fqName = classOrObject.getFqName();
|
|
||||||
if (fqName != null) {
|
if (fqName != null) {
|
||||||
ClassId javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName.toUnsafe());
|
val javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName.toUnsafe())
|
||||||
if (javaClassId != null) {
|
if (javaClassId != null) {
|
||||||
return JavaPsiFacade.getInstance(classOrObject.getProject()).findClass(
|
return JavaPsiFacade.getInstance(classOrObject.project).findClass(
|
||||||
javaClassId.asSingleFqName().asString(),
|
javaClassId.asSingleFqName().asString(),
|
||||||
GlobalSearchScope.allScope(classOrObject.getProject())
|
GlobalSearchScope.allScope(classOrObject.project)
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return toLightClass(classOrObject);
|
return classOrObject.toLightClass()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
fun getOriginalClass(classOrObject: KtClassOrObject): PsiClass? {
|
||||||
public static PsiClass getOriginalClass(@NotNull KtClassOrObject classOrObject) {
|
|
||||||
// Copied from JavaPsiImplementationHelperImpl:getOriginalClass()
|
// Copied from JavaPsiImplementationHelperImpl:getOriginalClass()
|
||||||
FqName fqName = classOrObject.getFqName();
|
val fqName = classOrObject.fqName ?: return null
|
||||||
if (fqName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
KtFile file = classOrObject.getContainingKtFile();
|
val file = classOrObject.containingKtFile
|
||||||
|
|
||||||
VirtualFile vFile = file.getVirtualFile();
|
val vFile = file.virtualFile
|
||||||
Project project = file.getProject();
|
val project = file.project
|
||||||
|
|
||||||
final ProjectFileIndex idx = ProjectRootManager.getInstance(project).getFileIndex();
|
val idx = ProjectRootManager.getInstance(project).fileIndex
|
||||||
|
|
||||||
if (vFile == null || !idx.isInLibrarySource(vFile)) return null;
|
if (vFile == null || !idx.isInLibrarySource(vFile)) return null
|
||||||
final Set<OrderEntry> orderEntries = new THashSet<OrderEntry>(idx.getOrderEntriesForFile(vFile));
|
val orderEntries = THashSet<OrderEntry>(idx.getOrderEntriesForFile(vFile))
|
||||||
|
|
||||||
return JavaPsiFacade.getInstance(project).findClass(fqName.asString(), new GlobalSearchScope(project) {
|
return JavaPsiFacade.getInstance(project).findClass(fqName.asString(), object : GlobalSearchScope(project) {
|
||||||
@Override
|
override fun compare(file1: VirtualFile, file2: VirtualFile): Int {
|
||||||
public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
|
return 0
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun contains(file: VirtualFile): Boolean {
|
||||||
public boolean contains(@NotNull VirtualFile file) {
|
val entries = idx.getOrderEntriesForFile(file)
|
||||||
List<OrderEntry> entries = idx.getOrderEntriesForFile(file);
|
for (entry in entries) {
|
||||||
for (OrderEntry entry : entries) {
|
if (orderEntries.contains(entry)) return true
|
||||||
if (orderEntries.contains(entry)) return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun isSearchInModuleContent(aModule: Module): Boolean {
|
||||||
public boolean isSearchInModuleContent(@NotNull Module aModule) {
|
return false
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun isSearchInLibraries(): Boolean {
|
||||||
public boolean isSearchInLibraries() {
|
return true
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
fun getNavigationElement(declaration: KtDeclaration) = navigateToDeclaration(declaration, NavigationKind.CLASS_FILES_TO_SOURCES)
|
||||||
public static KtDeclaration getNavigationElement(@NotNull KtDeclaration declaration) {
|
|
||||||
return navigateToDeclaration(declaration, NavigationKind.CLASS_FILES_TO_SOURCES);
|
fun getOriginalElement(declaration: KtDeclaration) = navigateToDeclaration(declaration, NavigationKind.SOURCES_TO_CLASS_FILES)
|
||||||
|
|
||||||
|
private fun navigateToDeclaration(
|
||||||
|
from: KtDeclaration,
|
||||||
|
navigationKind: NavigationKind
|
||||||
|
): KtDeclaration {
|
||||||
|
if (DumbService.isDumb(from.project)) return from
|
||||||
|
|
||||||
|
when (navigationKind) {
|
||||||
|
SourceNavigationHelper.NavigationKind.CLASS_FILES_TO_SOURCES -> if (!from.containingKtFile.isCompiled) return from
|
||||||
|
SourceNavigationHelper.NavigationKind.SOURCES_TO_CLASS_FILES -> {
|
||||||
|
if (from.containingKtFile.isCompiled) return from
|
||||||
|
if (!ProjectRootsUtil.isInContent(from, false, true, false, true)) return from
|
||||||
|
if (KtPsiUtil.isLocal(from)) return from
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return from.accept(SourceAndDecompiledConversionVisitor(navigationKind), Unit) ?: from
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private class SourceAndDecompiledConversionVisitor(private val navigationKind: NavigationKind) : KtVisitor<KtDeclaration?, Unit>() {
|
||||||
public static KtDeclaration getOriginalElement(@NotNull KtDeclaration declaration) {
|
|
||||||
return navigateToDeclaration(declaration, NavigationKind.SOURCES_TO_CLASS_FILES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
override fun visitNamedFunction(function: KtNamedFunction, data: Unit) = convertPropertyOrFunction(function, navigationKind)
|
||||||
private static KtDeclaration navigateToDeclaration(
|
|
||||||
@NotNull KtDeclaration from,
|
|
||||||
@NotNull NavigationKind navigationKind
|
|
||||||
) {
|
|
||||||
if (DumbService.isDumb(from.getProject())) return from;
|
|
||||||
|
|
||||||
switch (navigationKind) {
|
override fun visitProperty(property: KtProperty, data: Unit) = convertPropertyOrFunction(property, navigationKind)
|
||||||
case CLASS_FILES_TO_SOURCES:
|
|
||||||
if (!from.getContainingKtFile().isCompiled()) return from;
|
override fun visitObjectDeclaration(declaration: KtObjectDeclaration, data: Unit) = findClassOrObject(declaration, navigationKind)
|
||||||
break;
|
|
||||||
case SOURCES_TO_CLASS_FILES:
|
override fun visitClass(klass: KtClass, data: Unit) = findClassOrObject(klass, navigationKind)
|
||||||
if (from.getContainingKtFile().isCompiled()) return from;
|
|
||||||
if (!ProjectRootsUtil.isInContent(from, false, true, false, true)) return from;
|
override fun visitTypeAlias(typeAlias: KtTypeAlias, data: Unit)
|
||||||
if (KtPsiUtil.isLocal(from)) return from;
|
= findFirstMatchingInIndex(typeAlias, navigationKind, KotlinTopLevelTypeAliasFqNameIndex.getInstance())
|
||||||
break;
|
|
||||||
|
override fun visitParameter(parameter: KtParameter, data: Unit): KtDeclaration? {
|
||||||
|
val callableDeclaration = parameter.parent.parent as KtCallableDeclaration
|
||||||
|
val parameters = callableDeclaration.valueParameters
|
||||||
|
val index = parameters.indexOf(parameter)
|
||||||
|
|
||||||
|
val sourceCallable = callableDeclaration.accept(this, Unit) as? KtCallableDeclaration ?: return null
|
||||||
|
val sourceParameters = sourceCallable.valueParameters
|
||||||
|
if (sourceParameters.size != parameters.size) return null
|
||||||
|
return sourceParameters.get(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
KtDeclaration result = from.accept(new SourceAndDecompiledConversionVisitor(navigationKind), null);
|
override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor, data: Unit)
|
||||||
return result != null ? result : from;
|
= convertPropertyOrFunction(constructor, navigationKind)
|
||||||
}
|
|
||||||
|
|
||||||
private static class SourceAndDecompiledConversionVisitor extends KtVisitor<KtDeclaration, Void> {
|
override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor, data: Unit)
|
||||||
private final NavigationKind navigationKind;
|
= convertPropertyOrFunction(constructor, navigationKind)
|
||||||
|
|
||||||
public SourceAndDecompiledConversionVisitor(@NotNull NavigationKind navigationKind) {
|
|
||||||
this.navigationKind = navigationKind;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitNamedFunction(@NotNull KtNamedFunction function, Void data) {
|
|
||||||
return convertPropertyOrFunction(function, navigationKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitProperty(@NotNull KtProperty property, Void data) {
|
|
||||||
return convertPropertyOrFunction(property, navigationKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) {
|
|
||||||
return findClassOrObject(declaration, navigationKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitClass(@NotNull KtClass klass, Void data) {
|
|
||||||
return findClassOrObject(klass, navigationKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitTypeAlias(@NotNull KtTypeAlias typeAlias, Void data) {
|
|
||||||
return findFirstMatchingInIndex(typeAlias, navigationKind, KotlinTopLevelTypeAliasFqNameIndex.getInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitParameter(@NotNull KtParameter parameter, Void data) {
|
|
||||||
KtCallableDeclaration callableDeclaration = (KtCallableDeclaration) parameter.getParent().getParent();
|
|
||||||
List<KtParameter> parameters = callableDeclaration.getValueParameters();
|
|
||||||
int index = parameters.indexOf(parameter);
|
|
||||||
|
|
||||||
KtCallableDeclaration sourceCallable = (KtCallableDeclaration) callableDeclaration.accept(this, null);
|
|
||||||
if (sourceCallable == null) return null;
|
|
||||||
List<KtParameter> sourceParameters = sourceCallable.getValueParameters();
|
|
||||||
if (sourceParameters.size() != parameters.size()) return null;
|
|
||||||
return sourceParameters.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitPrimaryConstructor(@NotNull KtPrimaryConstructor constructor, Void data) {
|
|
||||||
return convertPropertyOrFunction(constructor, navigationKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KtDeclaration visitSecondaryConstructor(@NotNull KtSecondaryConstructor constructor, Void data) {
|
|
||||||
return convertPropertyOrFunction(constructor, navigationKind);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Collection<KtNamedDeclaration>.getContainingFiles(): Collection<KtFile> = mapNotNullTo(LinkedHashSet()) {
|
||||||
|
it.containingFile as? KtFile
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user