Skip to content

Instantly share code, notes, and snippets.

@LionZXY
Created July 27, 2025 00:41
Show Gist options
  • Save LionZXY/4ca174c713c9e0df69a63af4ee017806 to your computer and use it in GitHub Desktop.
Save LionZXY/4ca174c713c9e0df69a63af4ee017806 to your computer and use it in GitHub Desktop.
Metro Migration Assisted
// depends-on-plugin org.jetbrains.kotlin
// depends-on-plugin com.intellij.java.ide
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.project.Project
import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiReference
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.elementType
import liveplugin.PluginUtil.show
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtLambdaArgument
import org.jetbrains.kotlin.psi.KtLambdaExpression
fun findInjectAnnotatedKotlinClasses(project: Project): List<KtClass> {
val psiManager = PsiManager.getInstance(project)
val kotlinFileType = FileTypeManager.getInstance().getFileTypeByExtension("kt")
val searchScope = GlobalSearchScope.projectScope(project)
val injectAnnotatedClasses = mutableListOf<KtClass>()
val allKtFiles: Collection<VirtualFile> = FileTypeIndex.getFiles(kotlinFileType, searchScope)
ReadAction.run<RuntimeException> {
for (virtualFile in allKtFiles) {
val psiFile = psiManager.findFile(virtualFile) as? KtFile ?: continue
val ktClasses = psiFile.declarations.filterIsInstance<KtClass>()
for (ktClass in ktClasses) {
val annotationEntries = ktClass.annotationEntries
if (annotationEntries.any { it.shortName?.asString() == "Inject" } && hasAssistedParameter(ktClass)) {
injectAnnotatedClasses.add(ktClass)
}
}
}
}
return injectAnnotatedClasses
}
fun hasAssistedParameter(ktClass: KtClass): Boolean {
val constructor = ktClass.primaryConstructor ?: return false
val parameters = constructor.valueParameters
for (param in parameters) {
if (param.annotationEntries.any {
it.shortName?.asString() == "Assisted"
}
) {
return true
}
}
return false
}
fun isUsedAsLambdaReturnType(ref: PsiReference): Boolean {
val element = ref.element
// Try to find if we are inside a function literal (lambda)
val lambda = element.parent?.parent?.parent?.let { ancestor ->
PsiTreeUtil.getParentOfType(ancestor, KtLambdaExpression::class.java)
} ?: return false
// Not 100% reliable without binding info, but can heuristically check:
return lambda.parent is KtLambdaArgument &&
element.text == ref.canonicalText // ensure reference matches class name used
}
fun processOneClass(ktClass: KtClass, project: Project) {
val usages = ReferencesSearch.search(ktClass, GlobalSearchScope.projectScope(project)).findAll().toList()
show("Process ${ktClass.fqName?.asString()} with ${usages.size} usages")
usages.forEach {
var parent = it.element.parent
while (parent != null) {
if (PsiTreeUtil.getParentOfType(parent, KtLambdaExpression::class.java) != null) {
show("Find!")
}
//show("Parent is ${parent.elementType?.debugName}")
parent = parent.parent
}
}
}
val project = ProjectManager.getInstance().openProjects.firstOrNull()
if (project != null) {
val classes = findInjectAnnotatedKotlinClasses(project)
if (classes.isEmpty()) {
show("No Kotlin classes with @Inject found.")
} else {
processOneClass(classes.first(), project)
}
} else {
show("No open project found.")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment