From 0d0b1b794569982e5cd3d48932d339d6705735bb Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Tue, 18 Apr 2023 08:35:00 +0200 Subject: [PATCH] [Analysis API] add ability for a custom element navigation for a KtResolveExtensionFile ^KT-57930 --- .../api/fir/references/KtFirReference.kt | 17 ++++++++++++++ .../test/util/KtResolveExtensionForTest.kt | 12 ++++++++++ .../extensions/KtResolveExtensionFile.kt | 6 ++--- ...lveExtensionReferencePsiTargetsProvider.kt | 22 +++++++++++++++++++ .../extensions/LLFirResolveExtensionTool.kt | 21 ++++++++++++++---- 5 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionReferencePsiTargetsProvider.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirReference.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirReference.kt index 6e57a9c6e38..18e2d520f3e 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirReference.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KtFirReference.kt @@ -13,11 +13,15 @@ import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin +import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.psiTargetsProvider +import org.jetbrains.kotlin.analysis.project.structure.KtModuleStructureInternals +import org.jetbrains.kotlin.psi.KtFile interface KtFirReference : KtReference, KtSymbolBasedReference { fun getResolvedToPsi(analysisSession: KtAnalysisSession, referenceTargetSymbols: Collection): Collection = with(analysisSession) { referenceTargetSymbols.flatMap { symbol -> + symbol.getPsiForGeneratedExtensionSymbol()?.let { return@flatMap it } when (symbol) { is KtFirSymbol<*> -> getPsiDeclarations(symbol) else -> listOfNotNull(symbol.psi) @@ -25,6 +29,19 @@ interface KtFirReference : KtReference, KtSymbolBasedReference { } } + context(KtAnalysisSession) + @OptIn(KtModuleStructureInternals::class) + private fun KtSymbol.getPsiForGeneratedExtensionSymbol(): Collection? { + // TODO symbols generated by KtResolveExtension should probably have their own KtSymbolOrigin, now it's KtSymbolOrigin.SOURCE + if (origin != KtSymbolOrigin.SOURCE) return null + val psi = psi ?: return null + val ktFile = psi.containingFile as? KtFile ?: return null + val psiTargetsProvider = ktFile.virtualFile?.psiTargetsProvider ?: return null + return with(psiTargetsProvider) { + getReferenceTargetsForSymbol(this@getPsiForGeneratedExtensionSymbol) + } + } + fun getResolvedToPsi(analysisSession: KtAnalysisSession): Collection = with(analysisSession) { getResolvedToPsi(analysisSession, resolveToSymbols()) diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/util/KtResolveExtensionForTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/util/KtResolveExtensionForTest.kt index 252af85f3f0..bdf6013115c 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/util/KtResolveExtensionForTest.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/util/KtResolveExtensionForTest.kt @@ -7,9 +7,13 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.util import com.intellij.mock.MockProject import com.intellij.openapi.util.ModificationTracker +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider +import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionReferencePsiTargetsProvider +import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager import org.jetbrains.kotlin.name.FqName @@ -66,4 +70,12 @@ class KtResolveExtensionFileForTests( override fun getTopLevelCallableNames(): Set = topLevelCallableNames override fun buildFileText(): String = fileText + + override fun createPsiTargetsProvider(): KtResolveExtensionReferencePsiTargetsProvider { + return object : KtResolveExtensionReferencePsiTargetsProvider() { + override fun KtAnalysisSession.getReferenceTargetsForSymbol(symbol: KtSymbol): Collection { + return emptyList() + } + } + } } \ No newline at end of file diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionFile.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionFile.kt index 4b037fc5536..ce631679443 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionFile.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionFile.kt @@ -77,11 +77,11 @@ abstract class KtResolveExtensionFile { abstract fun buildFileText(): String /** - * Creates a [KtResolveExtensionReferenceTargetsPsiProvider] for this [KtResolveExtensionFile]. + * Creates a [KtResolveExtensionReferencePsiTargetsProvider] for this [KtResolveExtensionFile]. * - * @see KtResolveExtensionReferenceTargetsPsiProvider + * @see KtResolveExtensionReferencePsiTargetsProvider * @see KtResolveExtensionFile */ - abstract fun createTargetPsiProvider(): KtResolveExtensionReferenceTargetsPsiProvider + abstract fun createPsiTargetsProvider(): KtResolveExtensionReferencePsiTargetsProvider } \ No newline at end of file diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionReferencePsiTargetsProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionReferencePsiTargetsProvider.kt new file mode 100644 index 00000000000..114809b6e77 --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/resolve/extensions/KtResolveExtensionReferencePsiTargetsProvider.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.resolve.extensions + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol + +abstract class KtResolveExtensionReferencePsiTargetsProvider { + /** + * Provides a [PsiElement] where `reference.resolveTo` will lead for a [symbol] + * + * Usually returns a single result. Might return an empty collection if there is no navigation target. + * Also, might multiple targets in a case of ambiguity or multiple targets for a [symbol] + * + * Returned [PsiElement] will be used as a navigation target for a reference inside the IDE. + */ + abstract fun KtAnalysisSession.getReferenceTargetsForSymbol(symbol: KtSymbol): Collection +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt index 6d8da2cae36..8562cd99d24 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions +import com.intellij.openapi.util.Key import com.intellij.openapi.util.ModificationTracker +import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile -import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionReferenceTargetsPsiProvider +import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionReferencePsiTargetsProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.FileBasedKotlinDeclarationProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirSymbolProviderNameCache @@ -192,16 +194,24 @@ private class LLFirResolveExtensionToolDeclarationProvider( eventSystemEnabled = true // so every generated KtFile backed by some VirtualFile ) val text = file.buildFileText() - val ktFile = createKtFile(factory, file.getFileName(), text) + val psiTargetsProvider = file.createPsiTargetsProvider() + val ktFile = createKtFile(factory, file.getFileName(), text, psiTargetsProvider) FileBasedKotlinDeclarationProvider(ktFile) } } @OptIn(KtModuleStructureInternals::class) - private fun createKtFile(factory: KtPsiFactory, fileName: String, fileText: String): KtFile { + private fun createKtFile( + factory: KtPsiFactory, + fileName: String, + fileText: String, + psiTargetsProvider: KtResolveExtensionReferencePsiTargetsProvider + ): KtFile { val ktFile = factory.createFile(fileName, fileText) - ktFile.virtualFile.analysisExtensionFileContextModule = ktModule + val virtualFile = ktFile.virtualFile + virtualFile.analysisExtensionFileContextModule = ktModule + virtualFile.psiTargetsProvider = psiTargetsProvider return ktFile } } @@ -274,3 +284,6 @@ private fun KtResolveExtensionFile.mayHaveTopLevelClassifier(name: Name): Boolea private fun KtResolveExtensionFile.mayHaveTopLevelCallable(name: Name): Boolean { return name in getTopLevelCallableNames() } + +@KtModuleStructureInternals +public var VirtualFile.psiTargetsProvider: KtResolveExtensionReferencePsiTargetsProvider? by UserDataProperty(Key.create("KT_RESOLVE_EXTENSION_PSI_TARGETS_PROVIDER"))