Hi guys Any tips on how to resolve a type declarat...
# intellij-plugins
t
Hi guys Any tips on how to resolve a type declaration in the constructor call? i.e. given
Copy code
// foo.kt
package foo
data class Bar(val x: Int)

// foox.kt
package foox
import foo.Bar
data class Buzz(val bar: Bar)
I want to jump from
KtClass
of
Buzz
to the
KtClass
of
Bar
, emulating
jump to declaration
functionality of the IDE. What is a right way to approach this?
i
One way to do it is to override the PsiReference of the PsiElement. But this would need to happen transitively. One could check out how the utilities here are used here https://github.com/JetBrains/intellij-community/blob/a9a90d6ff6437b8ad0625766a579838fe029783b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java or check the Kotlin variant in the plugin
The latter is quite evasive, a more succinct way to go about this is to create a RelatedLineMarkerProvider that stores the PsiReferences and your users can navigate to them.
Or any UI feature that doesn’t involve manipulating the PsiReference, instead it stores the reference you care about as an intermediate.
Here is one example in Meta https://github.com/arrow-kt/arrow-meta/blob/b64fbdd9326f8f25b27bbac452fbb86c6b22df13/idea-plugin/src/main/kotlin/arrow/meta/ide/plugins/proofs/markers/refinement.kt#L108 It displays the targets in line 115. Which would be the PsiReference of
Bar
the cellrenderer and popUptitle are optional. This can be pretty minimalistic if that is desired.
t
Thanks, Imran, I’ll check it out
@Imran/Malic I checked your links, but I think that you misunderstood my question. What I am trying to build is an inspection which would check if all
data class
use either a primitive type or another data class. This is a part of my project to bring transitive immutablility checker. So my question is really: “how can I go from a one data class constructor to declaration of the type of one of its parameters to check if all of them are data classes”
Okay, I figured it out!
Copy code
val context = constructor.analyze()
for (parameter in constructor.parameters) {
  val typeDescriptor = context.get(BindingContext.TYPE, parameter.typeReference)
  val classDescriptor = typeDescriptor?.let { DescriptorUtils.getClassDescriptorForType(it) }
  val psi = classDescriptor?.source?.getPsi()
}
And that’s how we can visit psi of each parameter’s type declaration
i
conceptually whenever someone clicks on an descriptor this is invoked :
Copy code
{element: PsiElement -> PsiNavigateUtil.navigate(element)
}
Or some other variant. The only thing it does it checks the PsiReference and moves the editor and your caret to that place.
So as described above whatever feature fits best to you will get it done. Wether those are implemented as Inspections, Intentions, QuickFixes or else.
In an Inspection you would do the same thing just calling the navigate function on the PsiElement you care about.
Here is an example in Meta using this:
Copy code
val IdeMetaPlugin.fromBuzzToBar: ExtensionPhase
  get() = addApplicableInspection(
    defaultFixText= "Jump to Bar",
    kClass = KtClass::class.java,
    inspectionText= { "Some meaningful text for your users" },
    isApplicable= { it.name == "Buzz" },
    inspectionHighlightType = { ProblemHighlightType.INFORMATION },
    level = HighlightDisplayLevel.WEAK_WARNING,
    applyTo = {
      val bar: KtClass = TODO("Find Bar depending on your use-case")
      PsiNavigateUtil.navigate(bar)
    },
    groupPath = arrayOf("Your Path")
  )
to illustrate what I am saying. The same thing happens when user’s click on GotoRelatedItems or LineMarkers, only that those methods are not called by plugin devs explicitly. Rather it solely asks for the PsiElement and then it calls that internally.