Expose local scopes to use in completion
This commit is contained in:
committed by
Ilya Kirillov
parent
6129f4bcef
commit
97d4918ed3
@@ -34,6 +34,8 @@ class PersistentMultimap<K, V> private constructor(private val map: PersistentMa
|
|||||||
operator fun get(key: K): List<V> {
|
operator fun get(key: K): List<V> {
|
||||||
return map[key] ?: emptyList()
|
return map[key] ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val keys: ImmutableSet<K> get() = map.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
class PersistentSetMultimap<K, V> private constructor(private val map: PersistentMap<K, PersistentSet<V>>) {
|
class PersistentSetMultimap<K, V> private constructor(private val map: PersistentMap<K, PersistentSet<V>>) {
|
||||||
@@ -63,4 +65,4 @@ class PersistentSetMultimap<K, V> private constructor(private val map: Persisten
|
|||||||
operator fun get(key: K): Set<V> {
|
operator fun get(key: K): Set<V> {
|
||||||
return map[key] ?: emptySet()
|
return map[key] ?: emptySet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -83,6 +83,8 @@ open class FirBodyResolveTransformer(
|
|||||||
return data.expectedTypeRef.compose()
|
return data.expectedTypeRef.compose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun onBeforeStatementResolution(statement: FirStatement) {}
|
||||||
|
|
||||||
// ------------------------------------- Expressions -------------------------------------
|
// ------------------------------------- Expressions -------------------------------------
|
||||||
|
|
||||||
override fun transformExpression(expression: FirExpression, data: ResolutionMode): CompositeTransformResult<FirStatement> {
|
override fun transformExpression(expression: FirExpression, data: ResolutionMode): CompositeTransformResult<FirStatement> {
|
||||||
|
|||||||
+3
@@ -291,6 +291,9 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
|
|
||||||
block.transformStatementsIndexed(transformer) { index ->
|
block.transformStatementsIndexed(transformer) { index ->
|
||||||
val value = if (index == numberOfStatements - 1) data else ResolutionMode.ContextIndependent
|
val value = if (index == numberOfStatements - 1) data else ResolutionMode.ContextIndependent
|
||||||
|
|
||||||
|
transformer.onBeforeStatementResolution(block.statements[index])
|
||||||
|
|
||||||
TransformData.Data(value)
|
TransformData.Data(value)
|
||||||
}
|
}
|
||||||
if (data == ResolutionMode.ContextIndependent) {
|
if (data == ResolutionMode.ContextIndependent) {
|
||||||
|
|||||||
@@ -70,4 +70,6 @@ class FirLocalScope private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun mayContainName(name: Name) = properties.containsKey(name) || functions[name].isNotEmpty() || classes.containsKey(name)
|
override fun mayContainName(name: Name) = properties.containsKey(name) || functions[name].isNotEmpty() || classes.containsKey(name)
|
||||||
|
|
||||||
|
override fun getCallableNames(): Set<Name> = properties.keys + functions.keys + classes.keys
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-4
@@ -9,8 +9,12 @@ import com.intellij.openapi.progress.ProgressIndicatorProvider
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.fir.references.*
|
import org.jetbrains.kotlin.fir.references.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.*
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredCallableSymbols
|
import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredCallableSymbols
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
|
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
|
||||||
@@ -162,7 +166,8 @@ internal fun FirDeclaration.runResolve(
|
|||||||
file: FirFile,
|
file: FirFile,
|
||||||
firProvider: FirIdeProvider,
|
firProvider: FirIdeProvider,
|
||||||
toPhase: FirResolvePhase,
|
toPhase: FirResolvePhase,
|
||||||
state: FirModuleResolveState
|
state: FirModuleResolveState,
|
||||||
|
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null,
|
||||||
) {
|
) {
|
||||||
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
|
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
|
||||||
file.runResolve(toPhase = nonLazyPhase, fromPhase = this.resolvePhase)
|
file.runResolve(toPhase = nonLazyPhase, fromPhase = this.resolvePhase)
|
||||||
@@ -193,7 +198,8 @@ internal fun FirDeclaration.runResolve(
|
|||||||
val transformer = FirDesignatedBodyResolveTransformerForIDE(
|
val transformer = FirDesignatedBodyResolveTransformerForIDE(
|
||||||
designation.iterator(), state.getSession(psi as KtElement),
|
designation.iterator(), state.getSession(psi as KtElement),
|
||||||
scopeSession,
|
scopeSession,
|
||||||
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE
|
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
||||||
|
towerDataContextForStatement
|
||||||
)
|
)
|
||||||
file.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
|
file.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
|
||||||
}
|
}
|
||||||
@@ -202,7 +208,8 @@ private class FirDesignatedBodyResolveTransformerForIDE(
|
|||||||
private val designation: Iterator<FirElement>,
|
private val designation: Iterator<FirElement>,
|
||||||
session: FirSession,
|
session: FirSession,
|
||||||
scopeSession: ScopeSession,
|
scopeSession: ScopeSession,
|
||||||
implicitTypeOnly: Boolean
|
implicitTypeOnly: Boolean,
|
||||||
|
private val towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null
|
||||||
) : FirBodyResolveTransformer(
|
) : FirBodyResolveTransformer(
|
||||||
session,
|
session,
|
||||||
phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
||||||
@@ -210,6 +217,7 @@ private class FirDesignatedBodyResolveTransformerForIDE(
|
|||||||
scopeSession = scopeSession,
|
scopeSession = scopeSession,
|
||||||
returnTypeCalculator = createReturnTypeCalculatorForIDE(session, scopeSession)
|
returnTypeCalculator = createReturnTypeCalculatorForIDE(session, scopeSession)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
|
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
|
||||||
if (designation.hasNext()) {
|
if (designation.hasNext()) {
|
||||||
designation.next().visitNoTransform(this, data)
|
designation.next().visitNoTransform(this, data)
|
||||||
@@ -218,6 +226,11 @@ private class FirDesignatedBodyResolveTransformerForIDE(
|
|||||||
|
|
||||||
return super.transformDeclarationContent(declaration, data)
|
return super.transformDeclarationContent(declaration, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBeforeStatementResolution(statement: FirStatement) {
|
||||||
|
if (towerDataContextForStatement == null) return
|
||||||
|
towerDataContextForStatement[statement] = context.towerDataContext
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+35
-5
@@ -5,12 +5,15 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api
|
package org.jetbrains.kotlin.idea.fir.low.level.api
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
|
||||||
@@ -31,21 +34,48 @@ object LowLevelFirApiFacade {
|
|||||||
|
|
||||||
fun getFirOfClosestParent(element: KtElement): FirElement? = element.getFirOfClosestParent(element.firResolveState())?.second
|
fun getFirOfClosestParent(element: KtElement): FirElement? = element.getFirOfClosestParent(element.firResolveState())?.second
|
||||||
|
|
||||||
fun buildFunctionWithResolvedBody(
|
class FirCompletionContext internal constructor(
|
||||||
|
val session: FirSession,
|
||||||
|
private val towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>,
|
||||||
|
private val state: FirModuleResolveState,
|
||||||
|
) {
|
||||||
|
fun getTowerDataContext(element: KtElement): FirTowerDataContext {
|
||||||
|
var current: PsiElement? = element
|
||||||
|
while (current is KtElement) {
|
||||||
|
val mappedFir = state.getCachedMapping(current)
|
||||||
|
|
||||||
|
if (mappedFir is FirStatement) {
|
||||||
|
towerDataContextForStatement[mappedFir]?.let { return it }
|
||||||
|
}
|
||||||
|
current = current.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
error("No context for $element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildCompletionContextForFunction(
|
||||||
firFile: FirFile,
|
firFile: FirFile,
|
||||||
element: KtNamedFunction,
|
element: KtNamedFunction,
|
||||||
phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE
|
phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE
|
||||||
): FirFunction<*> {
|
): FirCompletionContext {
|
||||||
val state = element.firResolveState()
|
val state = element.firResolveState()
|
||||||
val firIdeProvider = firFile.session.firIdeProvider
|
val firIdeProvider = firFile.session.firIdeProvider
|
||||||
val builtFunction = firIdeProvider.buildFunctionWithBody(element)
|
val builtFunction = firIdeProvider.buildFunctionWithBody(element)
|
||||||
|
val towerDataContextForStatement = mutableMapOf<FirStatement, FirTowerDataContext>()
|
||||||
|
|
||||||
return builtFunction.apply {
|
val function = builtFunction.apply {
|
||||||
runResolve(firFile, firIdeProvider, phase, state)
|
runResolve(firFile, firIdeProvider, phase, state, towerDataContextForStatement)
|
||||||
|
|
||||||
// TODO this PSI caching should be somewhere else
|
// TODO this PSI caching should be somewhere else
|
||||||
state.recordElementsFrom(this)
|
state.recordElementsFrom(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return FirCompletionContext(
|
||||||
|
function.session,
|
||||||
|
towerDataContextForStatement,
|
||||||
|
state
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDiagnosticsFor(element: KtElement, resolveState: FirModuleResolveState): Collection<Diagnostic> {
|
fun getDiagnosticsFor(element: KtElement, resolveState: FirModuleResolveState): Collection<Diagnostic> {
|
||||||
@@ -53,4 +83,4 @@ object LowLevelFirApiFacade {
|
|||||||
file.getOrBuildFirWithDiagnostics(resolveState)
|
file.getOrBuildFirWithDiagnostics(resolveState)
|
||||||
return resolveState.getDiagnostics(element)
|
return resolveState.getDiagnostics(element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-7
@@ -12,18 +12,19 @@ import com.intellij.util.ProcessingContext
|
|||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||||
import org.jetbrains.kotlin.fir.resolve.*
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.getSymbolByTypeRef
|
import org.jetbrains.kotlin.fir.resolve.scope
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
||||||
import org.jetbrains.kotlin.idea.fir.getFirOfClosestParent
|
import org.jetbrains.kotlin.idea.fir.getFirOfClosestParent
|
||||||
import org.jetbrains.kotlin.idea.fir.getOrBuildFir
|
|
||||||
import org.jetbrains.kotlin.idea.fir.getOrBuildFirSafe
|
import org.jetbrains.kotlin.idea.fir.getOrBuildFirSafe
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
|
|
||||||
class KotlinFirCompletionContributor : CompletionContributor() {
|
class KotlinFirCompletionContributor : CompletionContributor() {
|
||||||
@@ -46,16 +47,25 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
|||||||
|
|
||||||
val parentFunction = nameExpression.getNonStrictParentOfType<KtNamedFunction>() ?: return
|
val parentFunction = nameExpression.getNonStrictParentOfType<KtNamedFunction>() ?: return
|
||||||
|
|
||||||
val firFunction = LowLevelFirApiFacade.buildFunctionWithResolvedBody(originalFileFir, parentFunction)
|
val completionContext = LowLevelFirApiFacade.buildCompletionContextForFunction(originalFileFir, parentFunction)
|
||||||
|
|
||||||
val element = nameExpression.getFirOfClosestParent() as? FirQualifiedAccessExpression ?: return
|
val element = nameExpression.getFirOfClosestParent() as? FirQualifiedAccessExpression ?: return
|
||||||
|
|
||||||
for (scope in getScopes(element, firFunction.session)) {
|
for (scope in getScopes(element, completionContext.session)) {
|
||||||
for (name in scope.availableNames()) {
|
for (name in scope.availableNames()) {
|
||||||
result.addElement(LookupElementBuilder.create(name.asString()))
|
result.addElement(LookupElementBuilder.create(name.asString()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val towerDataContext = completionContext.getTowerDataContext(nameExpression)
|
||||||
|
|
||||||
|
for (localScope in towerDataContext.localScopes) {
|
||||||
|
for (name in localScope.getCallableNames()) {
|
||||||
|
result.addElement(LookupElementBuilder.create(name.asString()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// val receiver = element.explicitReceiver
|
// val receiver = element.explicitReceiver
|
||||||
// if (receiver != null) {
|
// if (receiver != null) {
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user