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
|
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import liveplugin.registerInspection
import liveplugin.show
import org.jetbrains.kotlin.idea.base.utils.fqname.fqName
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.util.AnnotationModificationHelper
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.postProcessing.type
import org.jetbrains.kotlin.psi.*
// depends-on-plugin org.jetbrains.kotlin
val forgeEvent = "SubscribeEvent"
val handleEvent = "HandleEvent"
val skyHanniModule = "SkyHanniModule"
val patternGroup = "at.hannibal2.skyhanni.utils.repopatterns.RepoPatternGroup"
val pattern = "java.util.regex.Pattern"
registerInspection(ModuleInspectionKotlin())
fun isEvent(function: KtNamedFunction): Boolean {
return function.annotationEntries.any {
it.shortName!!.asString() == handleEvent || it.shortName!!.asString() == forgeEvent
}
}
fun isRepoPattern(property: KtProperty): Boolean {
val type = property.type()?.fqName?.asString() ?: return false
if (type == patternGroup) return true
if (type == pattern && property.hasDelegate()) return true
return false
}
class ModuleInspectionKotlin : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
val visitor = object : KtVisitorVoid() {
override fun visitClass(klass: KtClass) {
val hasAnnotation = klass.annotationEntries.any { it.shortName?.asString() == skyHanniModule }
if (hasAnnotation) {
holder.registerProblem(
klass.nameIdentifier!!,
"@SkyHanniModule can only be applied to objects",
ProblemHighlightType.GENERIC_ERROR
)
}
}
override fun visitObjectDeclaration(declaration: KtObjectDeclaration) {
if (declaration.isCompanion()) return
val hasAnnotation = declaration.annotationEntries.any { it.shortName?.asString() == skyHanniModule }
if (hasAnnotation) return
val hasSkyHanniEvents = declaration.body!!.functions.any { function -> isEvent(function) }
val hasRepoPatterns = declaration.body!!.properties.any { property -> isRepoPattern(property) }
if (!hasSkyHanniEvents && !hasRepoPatterns) return
holder.registerProblem(
declaration,
"Module should have a @SkyHanniModule annotation",
ModuleQuickFix()
)
}
}
return visitor
}
override fun getDisplayName() = "Modules should have a @SkyHanniModule annotation"
override fun getShortName() = "SkyHanniModuleInspection"
override fun getGroupDisplayName() = "SkyHanni"
override fun isEnabledByDefault() = true
}
class ModuleQuickFix : LocalQuickFix {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val obj = descriptor.psiElement as KtObjectDeclaration
AnnotationModificationHelper.addAnnotation(
obj,
FqName("at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule"),
null,
null,
{ null },
" ",
null
)
show("Annotation applied, make sure SkyHanniMod isn't still loading this module")
}
override fun getName() = "Annotate with @SkyHanniModule"
override fun getFamilyName() = name
}
|