summaryrefslogtreecommitdiff
path: root/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt
blob: f368899b617c55b4aaeb48fdfc79f2bd67c6e452 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.

package org.jetbrains.kotlin.idea.search.ideaExtensions

import com.intellij.codeInsight.JavaTargetElementEvaluator
import com.intellij.codeInsight.TargetElementEvaluatorEx
import com.intellij.codeInsight.TargetElementUtil
import com.intellij.codeInsight.TargetElementUtilExtender
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiReference
import com.intellij.util.BitUtil
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinImplicitLambdaParameter.Companion.isAutoCreatedItUsage
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.source.getPsi

class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtilExtender {
    companion object {
        const val DO_NOT_UNWRAP_LABELED_EXPRESSION = 0x100
        const val BYPASS_IMPORT_ALIAS = 0x200

        // Place caret after the open curly brace in lambda for generated 'it'
        fun findLambdaOpenLBraceForGeneratedIt(ref: PsiReference): PsiElement? {
            val element: PsiElement = ref.element
            if (element.text != "it") return null

            if (element !is KtNameReferenceExpression || !isAutoCreatedItUsage(element)) return null

            val itDescriptor = element.resolveMainReferenceToDescriptors().singleOrNull() ?: return null
            val descriptorWithSource = itDescriptor.containingDeclaration as? DeclarationDescriptorWithSource ?: return null
            val lambdaExpression = descriptorWithSource.source.getPsi()?.parent as? KtLambdaExpression ?: return null
            return lambdaExpression.leftCurlyBrace.treeNext?.psi
        }

        // Navigate to receiver element for this in extension declaration
        fun findReceiverForThisInExtensionFunction(ref: PsiReference): PsiElement? {
            val element: PsiElement = ref.element
            if (element.text != "this") return null

            if (element !is KtNameReferenceExpression) return null
            val callableDescriptor = element.resolveMainReferenceToDescriptors().singleOrNull() as? CallableDescriptor ?: return null

            if (!callableDescriptor.isExtension) return null
            val callableDeclaration = callableDescriptor.source.getPsi() as? KtCallableDeclaration ?: return null

            return callableDeclaration.receiverTypeReference
        }
    }

    override fun getAdditionalDefinitionSearchFlags() = 0

    override fun getAdditionalReferenceSearchFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION or BYPASS_IMPORT_ALIAS

    override fun getAllAdditionalFlags() = additionalDefinitionSearchFlags + additionalReferenceSearchFlags

    override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())

    override fun getElementByReference(ref: PsiReference, flags: Int): PsiElement? {
        if (ref is KtSimpleNameReference && ref.expression is KtLabelReferenceExpression) {
            val refTarget = ref.resolve() as? KtExpression ?: return null
            if (!BitUtil.isSet(flags, DO_NOT_UNWRAP_LABELED_EXPRESSION)) {
                return refTarget.getLabeledParent(ref.expression.getReferencedName()) ?: refTarget
            }
            return refTarget
        }

        if (!BitUtil.isSet(flags, BYPASS_IMPORT_ALIAS)) {
            (ref.element as? KtSimpleNameExpression)?.mainReference?.getImportAlias()?.let { return it }
        }

        // prefer destructing declaration entry to its target if element name is accepted
        if (ref is KtDestructuringDeclarationReference && BitUtil.isSet(flags, TargetElementUtil.ELEMENT_NAME_ACCEPTED)) {
            return ref.element
        }

        val refExpression = ref.element as? KtSimpleNameExpression
        val calleeExpression = refExpression?.getParentOfTypeAndBranch<KtCallElement> { calleeExpression }
        if (calleeExpression != null) {
            (ref.resolve() as? KtConstructor<*>)?.let {
                return if (flags and JavaTargetElementEvaluator().additionalReferenceSearchFlags != 0) it else it.containingClassOrObject
            }
        }

        if (BitUtil.isSet(flags, TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED)) {
            return findLambdaOpenLBraceForGeneratedIt(ref)
                ?: findReceiverForThisInExtensionFunction(ref)
        }

        return null
    }

    override fun isIdentifierPart(file: PsiFile, text: CharSequence, offset: Int): Boolean {
        val elementAtCaret = file.findElementAt(offset)

        if (elementAtCaret?.node?.elementType == KtTokens.IDENTIFIER) return true
        // '(' is considered identifier part if it belongs to primary constructor without 'constructor' keyword
        return elementAtCaret?.getNonStrictParentOfType<KtPrimaryConstructor>()?.textOffset == offset
    }
}