mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-05 21:44:43 +08:00
116 lines
4.9 KiB
Kotlin
116 lines
4.9 KiB
Kotlin
/*
|
|
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
*
|
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link.
|
|
*
|
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
*/
|
|
|
|
package net.mamoe.mirai.console.intellij.diagnostics
|
|
|
|
import com.intellij.codeInspection.ProblemHighlightType
|
|
import com.intellij.codeInspection.ProblemsHolder
|
|
import com.intellij.openapi.progress.impl.CancellationCheck.Companion.runWithCancellationCheck
|
|
import com.intellij.psi.PsiClass
|
|
import com.intellij.psi.PsiElement
|
|
import com.intellij.psi.PsiElementVisitor
|
|
import com.intellij.psi.PsiNameIdentifierOwner
|
|
import net.mamoe.mirai.console.compiler.common.resolve.AUTO_SERVICE
|
|
import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME
|
|
import net.mamoe.mirai.console.intellij.diagnostics.fix.ConfigurePluginMainServiceFix
|
|
import net.mamoe.mirai.console.intellij.resolve.allSuperNames
|
|
import net.mamoe.mirai.console.intellij.resolve.hasAnnotation
|
|
import org.jetbrains.kotlin.idea.debugger.readAction
|
|
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
|
import org.jetbrains.kotlin.idea.util.module
|
|
import org.jetbrains.kotlin.idea.util.rootManager
|
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
|
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
|
import org.jetbrains.kotlin.psi.classOrObjectVisitor
|
|
import java.util.*
|
|
|
|
/*
|
|
private val bundle by lazy {
|
|
BundleUtil.loadLanguageBundle(PluginMainServiceNotConfiguredInspection::class.java.classLoader, "messages.InspectionGadgetsBundle")!!
|
|
}*/
|
|
|
|
class PluginMainServiceNotConfiguredInspection : AbstractKotlinInspection() {
|
|
companion object {
|
|
private val SERVICE_FILE_NAMES = arrayOf(
|
|
"net.mamoe.mirai.console.plugin.jvm.JvmPlugin",
|
|
"net.mamoe.mirai.console.plugin.jvm.KotlinPlugin",
|
|
"net.mamoe.mirai.console.plugin.jvm.JavaPlugin",
|
|
)
|
|
}
|
|
|
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
|
val ktVisitor = classOrObjectVisitor visitor@{ element ->
|
|
if (element !is KtObjectDeclaration) return@visitor
|
|
if (element.allSuperNames.none { it == PLUGIN_FQ_NAME }) return@visitor
|
|
val fqName = element.fqName?.asString() ?: return@visitor
|
|
|
|
val found = isServiceConfiguredWithAutoService(element)
|
|
|| isServiceConfiguredWithResource(element, fqName)
|
|
|
|
if (!found) {
|
|
registerProblemImpl(holder, element, fqName)
|
|
}
|
|
}
|
|
|
|
val javaVisitor = object : PsiElementVisitor() {
|
|
override fun visitElement(element: PsiElement) {
|
|
if (element !is PsiClass) return
|
|
if (element.allSuperNames.none { it == PLUGIN_FQ_NAME }) return
|
|
|
|
if (element.hasAnnotation(AUTO_SERVICE.asString())) return
|
|
val fqName = element.qualifiedName ?: return
|
|
if (isServiceConfiguredWithResource(element, fqName)) return
|
|
|
|
registerProblemImpl(holder, element, fqName)
|
|
}
|
|
}
|
|
|
|
return object : PsiElementVisitor() {
|
|
override fun visitElement(element: PsiElement) {
|
|
super.visitElement(element)
|
|
if (element is KtClassOrObject) ktVisitor.visitClassOrObject(element)
|
|
else javaVisitor.visitElement(element)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun registerProblemImpl(holder: ProblemsHolder, element: PsiNameIdentifierOwner, fqName: String) {
|
|
holder.registerProblem(
|
|
element.nameIdentifier ?: element.identifyingElement ?: element,
|
|
@Suppress("DialogTitleCapitalization") "插件主类服务未配置",
|
|
ProblemHighlightType.WARNING,
|
|
ConfigurePluginMainServiceFix(element, fqName)
|
|
)
|
|
}
|
|
|
|
private fun isServiceConfiguredWithAutoService(
|
|
ktClass: KtClassOrObject,
|
|
): Boolean {
|
|
return ktClass.hasAnnotation(AUTO_SERVICE)
|
|
}
|
|
|
|
private fun isServiceConfiguredWithResource(
|
|
psiOrKtClass: PsiElement,
|
|
fqName: String,
|
|
): Boolean {
|
|
return runWithCancellationCheck {
|
|
val sourceRoots = psiOrKtClass.module?.rootManager?.sourceRoots ?: return@runWithCancellationCheck false
|
|
val services = sourceRoots.asSequence().flatMap { file ->
|
|
SERVICE_FILE_NAMES.asSequence().mapNotNull { serviceFileName ->
|
|
file.findFileByRelativePath("META-INF/services/$serviceFileName")
|
|
}
|
|
}
|
|
return@runWithCancellationCheck services.any { serviceFile ->
|
|
serviceFile.readAction { f ->
|
|
f.inputStream.bufferedReader().use { reader -> reader.lineSequence().any { it == fqName } }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |