PluginMainServiceNotConfiguredInspection.kt 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /*
  2. * Copyright 2019-2021 Mamoe Technologies and contributors.
  3. *
  4. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  5. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
  6. *
  7. * https://github.com/mamoe/mirai/blob/dev/LICENSE
  8. */
  9. package net.mamoe.mirai.console.intellij.diagnostics
  10. import com.intellij.codeInspection.ProblemHighlightType
  11. import com.intellij.codeInspection.ProblemsHolder
  12. import com.intellij.openapi.progress.impl.CancellationCheck.Companion.runWithCancellationCheck
  13. import com.intellij.psi.PsiClass
  14. import com.intellij.psi.PsiElement
  15. import com.intellij.psi.PsiElementVisitor
  16. import com.intellij.psi.PsiNameIdentifierOwner
  17. import net.mamoe.mirai.console.compiler.common.resolve.AUTO_SERVICE
  18. import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME
  19. import net.mamoe.mirai.console.intellij.diagnostics.fix.ConfigurePluginMainServiceFix
  20. import net.mamoe.mirai.console.intellij.resolve.allSuperNames
  21. import net.mamoe.mirai.console.intellij.resolve.hasAnnotation
  22. import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
  23. import org.jetbrains.kotlin.idea.debugger.readAction
  24. import org.jetbrains.kotlin.idea.util.*
  25. import org.jetbrains.kotlin.psi.KtClassOrObject
  26. import org.jetbrains.kotlin.psi.KtObjectDeclaration
  27. import org.jetbrains.kotlin.psi.classOrObjectVisitor
  28. import java.util.*
  29. /*
  30. private val bundle by lazy {
  31. BundleUtil.loadLanguageBundle(PluginMainServiceNotConfiguredInspection::class.java.classLoader, "messages.InspectionGadgetsBundle")!!
  32. }*/
  33. class PluginMainServiceNotConfiguredInspection : AbstractKotlinInspection() {
  34. companion object {
  35. private val SERVICE_FILE_NAMES = arrayOf(
  36. "net.mamoe.mirai.console.plugin.jvm.JvmPlugin",
  37. "net.mamoe.mirai.console.plugin.jvm.KotlinPlugin",
  38. "net.mamoe.mirai.console.plugin.jvm.JavaPlugin",
  39. )
  40. }
  41. override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
  42. val ktVisitor = classOrObjectVisitor visitor@{ element ->
  43. if (element !is KtObjectDeclaration) return@visitor
  44. if (element.allSuperNames.none { it == PLUGIN_FQ_NAME }) return@visitor
  45. val fqName = element.fqName?.asString() ?: return@visitor
  46. val found = isServiceConfiguredWithAutoService(element)
  47. || isServiceConfiguredWithResource(element, fqName)
  48. if (!found) {
  49. registerProblemImpl(holder, element, fqName)
  50. }
  51. }
  52. val javaVisitor = object : PsiElementVisitor() {
  53. override fun visitElement(element: PsiElement) {
  54. if (element !is PsiClass) return
  55. if (element.allSuperNames.none { it == PLUGIN_FQ_NAME }) return
  56. if (element.hasAnnotation(AUTO_SERVICE.asString())) return
  57. val fqName = element.qualifiedName ?: return
  58. if (isServiceConfiguredWithResource(element, fqName)) return
  59. registerProblemImpl(holder, element, fqName)
  60. }
  61. }
  62. return object : PsiElementVisitor() {
  63. override fun visitElement(element: PsiElement) {
  64. super.visitElement(element)
  65. if (element is KtClassOrObject) ktVisitor.visitClassOrObject(element)
  66. else javaVisitor.visitElement(element)
  67. }
  68. }
  69. }
  70. private fun registerProblemImpl(holder: ProblemsHolder, element: PsiNameIdentifierOwner, fqName: String) {
  71. holder.registerProblem(
  72. element.nameIdentifier ?: element.identifyingElement ?: element,
  73. @Suppress("DialogTitleCapitalization") "插件主类服务未配置",
  74. ProblemHighlightType.WARNING,
  75. ConfigurePluginMainServiceFix(element, fqName)
  76. )
  77. }
  78. private fun isServiceConfiguredWithAutoService(
  79. ktClass: KtClassOrObject,
  80. ): Boolean {
  81. return ktClass.hasAnnotation(AUTO_SERVICE)
  82. }
  83. private fun isServiceConfiguredWithResource(
  84. psiOrKtClass: PsiElement,
  85. fqName: String,
  86. ): Boolean {
  87. return runWithCancellationCheck {
  88. val sourceRoots: Array<com.intellij.openapi.vfs.VirtualFile> =
  89. psiOrKtClass.module?.rootManager?.sourceRoots ?: return@runWithCancellationCheck false
  90. val services = sourceRoots.asSequence().flatMap { file ->
  91. SERVICE_FILE_NAMES.asSequence().mapNotNull { serviceFileName ->
  92. file.findFileByRelativePath("META-INF/services/$serviceFileName")
  93. }
  94. }
  95. return@runWithCancellationCheck services.any { serviceFile ->
  96. serviceFile.readAction { f ->
  97. f.inputStream.bufferedReader().use { reader -> reader.lineSequence().any { it == fqName } }
  98. }
  99. }
  100. }
  101. }
  102. }