From bf660ea493e5138d25d1fb7b05029231ec66e7a5 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 12 Aug 2024 10:25:41 +0200 Subject: [PATCH] Add initial insight for Bukkit-like plugin.yml --- build.gradle.kts | 26 +- changelog.md | 6 + gradle/libs.versions.toml | 2 + .../kotlin/platform/bukkit/BukkitModule.kt | 8 +- .../platform/bungeecord/BungeeCordModule.kt | 8 +- src/main/kotlin/yaml/PluginYmlInspection.kt | 80 ++ .../yaml/PluginYmlReferenceContributor.kt | 141 ++++ src/main/resources/META-INF/plugin.xml | 743 ++++++++++++------ src/main/resources/META-INF/yaml-support.xml | 34 + .../kotlin/framework/BaseMinecraftTest.kt | 12 +- src/test/kotlin/framework/ProjectBuilder.kt | 6 + src/test/kotlin/framework/test-util.kt | 5 + .../kotlin/platform/bukkit/BaseSpigotTest.kt | 73 ++ .../PluginYmlReferenceContributorTest.kt | 91 +++ .../bukkit/PluginYmlSpigotInspectionTest.kt | 62 ++ .../platform/bungeecord/BaseBungeeCordTest.kt | 73 ++ .../PluginYmlBungeeInspectionTest.kt | 62 ++ .../pluginYmlInspection/SimplePlugin.java | 5 + .../SimplePlugin.java | 5 + .../afterMove/plugin.yml | 1 + .../afterRename/RenamedPlugin.java | 5 + .../afterRename/plugin.yml | 1 + .../pluginYmlReferenceContributor/plugin.yml | 1 + .../pluginYmlInspection/SimplePlugin.java | 5 + 24 files changed, 1186 insertions(+), 269 deletions(-) create mode 100644 src/main/kotlin/yaml/PluginYmlInspection.kt create mode 100644 src/main/kotlin/yaml/PluginYmlReferenceContributor.kt create mode 100644 src/main/resources/META-INF/yaml-support.xml create mode 100644 src/test/kotlin/platform/bukkit/BaseSpigotTest.kt create mode 100644 src/test/kotlin/platform/bukkit/PluginYmlReferenceContributorTest.kt create mode 100644 src/test/kotlin/platform/bukkit/PluginYmlSpigotInspectionTest.kt create mode 100644 src/test/kotlin/platform/bungeecord/BaseBungeeCordTest.kt create mode 100644 src/test/kotlin/platform/bungeecord/PluginYmlBungeeInspectionTest.kt create mode 100644 src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlInspection/SimplePlugin.java create mode 100644 src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/SimplePlugin.java create mode 100644 src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterMove/plugin.yml create mode 100644 src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/RenamedPlugin.java create mode 100644 src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/plugin.yml create mode 100644 src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/plugin.yml create mode 100644 src/test/resources/com/demonwav/mcdev/platform/bungeecord/pluginYmlInspection/SimplePlugin.java diff --git a/build.gradle.kts b/build.gradle.kts index 8dda80673..ab5526d82 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -109,7 +109,21 @@ repositories { } } mavenCentral() - maven("https://repo.spongepowered.org/maven/") + maven("https://repo.spongepowered.org/maven/") { + content { + includeGroup("org.spongepowered") + } + } + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") { + content { + includeGroup("org.spigotmc") + } + } + maven("https://oss.sonatype.org/content/repositories/snapshots/") { + content { + includeGroup("net.md-5") + } + } } dependencies { @@ -141,6 +155,8 @@ dependencies { testLibs(libs.test.mockJdk) testLibs(libs.test.mixin) + testLibs(libs.test.spigotapi) + testLibs(libs.test.bungeecord) testLibs(libs.test.spongeapi) { artifact { classifier = "shaded" @@ -212,6 +228,7 @@ intellij { "ByteCodeViewer", "org.intellij.intelliLang", "properties", + "org.jetbrains.plugins.yaml", // needed dependencies for unit tests "junit" ) @@ -327,7 +344,12 @@ license { exclude("META-INF/plugin.xml") // https://youtrack.jetbrains.com/issue/IDEA-345026 include(endings.map { "**/*.$it" }) - exclude("com/demonwav/mcdev/platform/mixin/invalidInjectorMethodSignature/*.java") + val projectDir = layout.projectDirectory.asFile + exclude { + it.file.toRelativeString(projectDir) + .replace("\\", "/") + .startsWith("src/test/resources") + } this.tasks { register("gradle") { diff --git a/changelog.md b/changelog.md index 70d25386c..00f5aa55e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Minecraft Development for IntelliJ +## [Unreleased] + +### Added + +- `plugin.yml`, `paper-plugin.yml` and `bungee.yml` main class reference and validity inspection + ## [1.8.1] - 2024-08-10 ### Added diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9c1c92308..152fabb47 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,8 @@ fuel-coroutines = { module = "com.github.kittinunf.fuel:fuel-coroutines", versio # Testing test-mockJdk = "org.jetbrains.idea:mock-jdk:1.7-4d76c50" test-mixin = "org.spongepowered:mixin:0.8.5" +test-spigotapi = "org.spigotmc:spigot-api:1.21-R0.1-SNAPSHOT" +test-bungeecord = "net.md-5:bungeecord-api:1.21-R0.1-SNAPSHOT" test-spongeapi = "org.spongepowered:spongeapi:7.4.0" test-fabricloader = "net.fabricmc:fabric-loader:0.15.11" test-nbt = "com.demonwav.mcdev:all-types-nbt:1.0" diff --git a/src/main/kotlin/platform/bukkit/BukkitModule.kt b/src/main/kotlin/platform/bukkit/BukkitModule.kt index 9639e5b6d..74b4539ca 100644 --- a/src/main/kotlin/platform/bukkit/BukkitModule.kt +++ b/src/main/kotlin/platform/bukkit/BukkitModule.kt @@ -42,21 +42,25 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiLiteralExpression import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethodCallExpression +import com.intellij.util.application import org.jetbrains.uast.UClass import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.toUElementOfType class BukkitModule>(facet: MinecraftFacet, type: T) : AbstractModule(facet) { + // Light test cases only support a single source content root, so we mix sources and resources under unit test mode + private val pluginYmlSourceType = if (application.isUnitTestMode) SourceType.SOURCE else SourceType.RESOURCE + var pluginYml by nullable { if (moduleType is PaperModuleType) { - val paperPlugin = facet.findFile("paper-plugin.yml", SourceType.RESOURCE) + val paperPlugin = facet.findFile("paper-plugin.yml", pluginYmlSourceType) if (paperPlugin != null) { return@nullable paperPlugin } } - facet.findFile("plugin.yml", SourceType.RESOURCE) + facet.findFile("plugin.yml", pluginYmlSourceType) } private set diff --git a/src/main/kotlin/platform/bungeecord/BungeeCordModule.kt b/src/main/kotlin/platform/bungeecord/BungeeCordModule.kt index 77710b505..1d2554760 100644 --- a/src/main/kotlin/platform/bungeecord/BungeeCordModule.kt +++ b/src/main/kotlin/platform/bungeecord/BungeeCordModule.kt @@ -37,14 +37,18 @@ import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod +import com.intellij.util.application import org.jetbrains.uast.UClass import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.toUElementOfType class BungeeCordModule>(facet: MinecraftFacet, type: T) : AbstractModule(facet) { + // Light test cases only support a single source content root, so we mix sources and resources under unit test mode + private val pluginYmlSourceType = if (application.isUnitTestMode) SourceType.SOURCE else SourceType.RESOURCE + var pluginYml by nullable { - val file = facet.findFile("bungee.yml", SourceType.RESOURCE) + val file = facet.findFile("bungee.yml", pluginYmlSourceType) if (file != null) { return@nullable file } @@ -53,7 +57,7 @@ class BungeeCordModule>(facet: MinecraftFacet, typ // So we don't check return@nullable null } - return@nullable facet.findFile("plugin.yml", SourceType.RESOURCE) + return@nullable facet.findFile("plugin.yml", pluginYmlSourceType) } private set diff --git a/src/main/kotlin/yaml/PluginYmlInspection.kt b/src/main/kotlin/yaml/PluginYmlInspection.kt new file mode 100644 index 000000000..a48fbb1d7 --- /dev/null +++ b/src/main/kotlin/yaml/PluginYmlInspection.kt @@ -0,0 +1,80 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.yaml + +import com.demonwav.mcdev.facet.MinecraftFacet +import com.demonwav.mcdev.platform.bukkit.PaperModuleType +import com.demonwav.mcdev.platform.bukkit.SpigotModuleType +import com.demonwav.mcdev.platform.bukkit.util.BukkitConstants +import com.demonwav.mcdev.platform.bungeecord.BungeeCordModuleType +import com.demonwav.mcdev.platform.bungeecord.util.BungeeCordConstants +import com.demonwav.mcdev.util.findModule +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.annotations.Nls +import org.jetbrains.yaml.psi.YAMLKeyValue +import org.jetbrains.yaml.psi.YAMLScalar +import org.jetbrains.yaml.psi.YamlPsiElementVisitor + +class PluginYmlInspection : LocalInspectionTool() { + + override fun getStaticDescription(): @Nls String? = "Reports issues in Bukkit-like plugin.yml files" + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + val module = holder.file.findModule() ?: return PsiElementVisitor.EMPTY_VISITOR + val virtualFile = holder.file.virtualFile ?: return PsiElementVisitor.EMPTY_VISITOR + val pluginClassFqn = when { + MinecraftFacet.getInstance(module, SpigotModuleType, PaperModuleType)?.pluginYml == virtualFile -> + BukkitConstants.PLUGIN + MinecraftFacet.getInstance(module, BungeeCordModuleType)?.pluginYml == virtualFile -> + BungeeCordConstants.PLUGIN + else -> return PsiElementVisitor.EMPTY_VISITOR + } + + return Visitor(holder, pluginClassFqn) + } + + private class Visitor(val holder: ProblemsHolder, val pluginClassFqn: String) : YamlPsiElementVisitor() { + + override fun visitScalar(scalar: YAMLScalar) { + super.visitScalar(scalar) + + if ((scalar.parent as? YAMLKeyValue)?.keyText != "main") { + return + } + + val resolved = scalar.references.firstNotNullOfOrNull { it.resolve() as? PsiClass } + if (resolved == null) { + holder.registerProblem(scalar, "Unresolved reference") + return + } + + val pluginClass = JavaPsiFacade.getInstance(holder.project).findClass(pluginClassFqn, scalar.resolveScope) + ?: return + if (!resolved.isInheritor(pluginClass, true)) { + holder.registerProblem(scalar, "Class must implement $pluginClassFqn") + } + } + } +} diff --git a/src/main/kotlin/yaml/PluginYmlReferenceContributor.kt b/src/main/kotlin/yaml/PluginYmlReferenceContributor.kt new file mode 100644 index 000000000..4bb1f76d7 --- /dev/null +++ b/src/main/kotlin/yaml/PluginYmlReferenceContributor.kt @@ -0,0 +1,141 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.yaml + +import com.demonwav.mcdev.facet.MinecraftFacet +import com.demonwav.mcdev.platform.bukkit.PaperModuleType +import com.demonwav.mcdev.platform.bukkit.SpigotModuleType +import com.demonwav.mcdev.platform.bukkit.util.BukkitConstants +import com.demonwav.mcdev.platform.bungeecord.BungeeCordModuleType +import com.demonwav.mcdev.platform.bungeecord.util.BungeeCordConstants +import com.demonwav.mcdev.util.findModule +import com.intellij.codeInsight.completion.JavaLookupElementBuilder +import com.intellij.lang.jvm.JvmModifier +import com.intellij.openapi.util.TextRange +import com.intellij.patterns.ObjectPattern +import com.intellij.patterns.PatternCondition +import com.intellij.patterns.PlatformPatterns +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.psi.PsiReferenceContributor +import com.intellij.psi.PsiReferenceRegistrar +import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference +import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider +import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceSet +import com.intellij.psi.search.searches.ClassInheritorsSearch +import com.intellij.util.ArrayUtilRt +import com.intellij.util.ProcessingContext +import org.jetbrains.yaml.psi.YAMLKeyValue +import org.jetbrains.yaml.psi.YAMLScalar + +private fun

> P.inSpigotOrPaperPluginYml(): P = with( + object : PatternCondition("") { + override fun accepts(t: PsiElement, context: ProcessingContext): Boolean { + val module = t.findModule() ?: return false + val instance = MinecraftFacet.getInstance(module, SpigotModuleType, PaperModuleType) ?: return false + return instance.pluginYml == t.containingFile.originalFile.virtualFile + } + } +) + +private fun

> P.inBungeePluginYml(): P = with( + object : PatternCondition("") { + override fun accepts(t: PsiElement, context: ProcessingContext): Boolean { + val module = t.findModule() ?: return false + val instance = MinecraftFacet.getInstance(module, BungeeCordModuleType) ?: return false + return instance.pluginYml == t.containingFile.originalFile.virtualFile + } + } +) + +class PluginYmlReferenceContributor : PsiReferenceContributor() { + + override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(YAMLScalar::class.java) + .withParent(PlatformPatterns.psiElement(YAMLKeyValue::class.java).withName("main")) + .inSpigotOrPaperPluginYml(), + PluginYmlClassReferenceProvider(BukkitConstants.PLUGIN) + ) + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(YAMLScalar::class.java) + .withParent(PlatformPatterns.psiElement(YAMLKeyValue::class.java).withName("main")) + .inBungeePluginYml(), + PluginYmlClassReferenceProvider(BungeeCordConstants.PLUGIN) + ) + } +} + +class PluginYmlClassReferenceProvider(val superClass: String) : JavaClassReferenceProvider() { + + init { + setOption(ALLOW_DOLLAR_NAMES, true) + setOption(JVM_FORMAT, true) + setOption(CONCRETE, true) + setOption(INSTANTIATABLE, true) + setOption(SUPER_CLASSES, listOf(superClass)) + setOption(ADVANCED_RESOLVE, true) + } + + override fun getReferencesByString( + str: String, + position: PsiElement, + offsetInPosition: Int + ): Array { + return object : JavaClassReferenceSet(str, position, offsetInPosition, true, this) { + override fun isAllowDollarInNames(): Boolean = true + + override fun isAllowSpaces(): Boolean = false + + override fun createReference( + referenceIndex: Int, + referenceText: String, + textRange: TextRange, + staticImport: Boolean + ): JavaClassReference { + return PluginYmlClassReference(this, referenceIndex, referenceText, textRange, staticImport, superClass) + } + }.allReferences + } +} + +class PluginYmlClassReference( + referenceSet: JavaClassReferenceSet, + referenceIndex: Int, + referenceText: String, + textRange: TextRange, + staticImport: Boolean, + val superClass: String +) : JavaClassReference(referenceSet, textRange, referenceIndex, referenceText, staticImport) { + + override fun getVariants(): Array { + val pluginClass = + JavaPsiFacade.getInstance(element.project).findClass(superClass, element.resolveScope) + ?: return ArrayUtilRt.EMPTY_OBJECT_ARRAY + val candidates = + ClassInheritorsSearch.search(pluginClass, element.resolveScope, true) + .filter { !it.hasModifier(JvmModifier.ABSTRACT) } + .map { JavaLookupElementBuilder.forClass(it, it.qualifiedName) } + .toTypedArray() + return candidates + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 27a38c6a3..f10f515d3 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -28,6 +28,7 @@ com.intellij.properties ByteCodeViewer org.toml.lang + org.jetbrains.plugins.yaml com.demonwav.minecraft-dev Minecraft Development @@ -56,42 +57,68 @@ the issue tracker! - ]]> + ]]> - - - - - - - - - + + + + + + + + + - + - - + + - + - - + + - - - + + + - - + + - + @@ -99,112 +126,196 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -226,10 +337,11 @@ - + - + @@ -238,16 +350,20 @@ - - - + + + - + @@ -268,54 +384,81 @@ instance="com.demonwav.mcdev.translations.sorting.TranslationTemplateConfigurable"/> - + - + - + - + - + - - - + + + - - + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - + - - + + UAST com.demonwav.mcdev.translations.intentions.ConvertToTranslationIntention @@ -324,15 +467,21 @@ - - - - + + + - - + + @@ -345,9 +494,11 @@ - + - + @@ -359,10 +510,14 @@ - - - - + + + + @@ -393,42 +548,53 @@ - + - + - + - + - + - + - + - + - + - - - + + + - + @@ -436,39 +602,56 @@ - + - + - - + + - - - - - - - - + + + + + + + + - - - - - + + + + + - - + + - + @@ -479,38 +662,61 @@ - - - - - - + + + + + + - - - - - - - + + + + + + + - - + - + + + - - - - - - + + - + - - - + + + - - + + - - + + - + - + - + @@ -571,7 +787,8 @@ - + @@ -583,8 +800,10 @@ - - + + @@ -799,13 +1018,13 @@ hasStaticDescription="true" implementationClass="com.demonwav.mcdev.platform.mixin.inspection.NonJavaMixinInspection"/> + shortName="UnusedMixin" + groupName="Mixin" + language="JAVA" + enabledByDefault="true" + level="WARNING" + hasStaticDescription="true" + implementationClass="com.demonwav.mcdev.platform.mixin.inspection.UnusedMixinInspection"/> + implementationClass="com.demonwav.mcdev.platform.mixin.inspection.reference.UnqualifiedMemberReferenceInspection"/> - + - + @@ -1289,19 +1510,21 @@ - + - + - - + - @@ -1309,7 +1532,7 @@ - + - + . +--> + + + + + + + + diff --git a/src/test/kotlin/framework/BaseMinecraftTest.kt b/src/test/kotlin/framework/BaseMinecraftTest.kt index 825535497..6e8f14f78 100644 --- a/src/test/kotlin/framework/BaseMinecraftTest.kt +++ b/src/test/kotlin/framework/BaseMinecraftTest.kt @@ -32,15 +32,21 @@ import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.pom.java.LanguageLevel import com.intellij.testFramework.LightProjectDescriptor import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor +import org.junit.jupiter.api.BeforeEach abstract class BaseMinecraftTest( vararg platformTypes: PlatformType, ) : ProjectBuilderTest( getProjectDescriptor(platformTypes), ) { - protected open val resourcePath = "src/test/resources" - protected open val packagePath = "com/demonwav/mcdev" - protected open val dataPath = "" + protected open val testPath = "" + + @BeforeEach + fun setUp() { + if (testPath.isNotBlank()) { + fixture.testDataPath = "$BASE_DATA_PATH/$testPath" + } + } } fun getProjectDescriptor(platformTypes: Array): LightProjectDescriptor { diff --git a/src/test/kotlin/framework/ProjectBuilder.kt b/src/test/kotlin/framework/ProjectBuilder.kt index 07fdc5edf..5919ca032 100644 --- a/src/test/kotlin/framework/ProjectBuilder.kt +++ b/src/test/kotlin/framework/ProjectBuilder.kt @@ -69,6 +69,12 @@ class ProjectBuilder(private val fixture: JavaCodeInsightTestFixture, private va configure: Boolean = true, allowAst: Boolean = false, ) = file(path, code, ".json", configure, allowAst) + fun yml( + path: String, + @Language("yaml") code: String, + configure: Boolean = true, + allowAst: Boolean = false, + ) = file(path, code, ".yml", configure, allowAst) inline fun dir(path: String, block: ProjectBuilder.() -> Unit) { val oldIntermediatePath = intermediatePath diff --git a/src/test/kotlin/framework/test-util.kt b/src/test/kotlin/framework/test-util.kt index 1e0a78dc4..c5dca7d4b 100644 --- a/src/test/kotlin/framework/test-util.kt +++ b/src/test/kotlin/framework/test-util.kt @@ -44,6 +44,11 @@ import org.junit.jupiter.api.Assertions typealias ProjectBuilderFunc = ProjectBuilder.(path: String, code: String, configure: Boolean, allowAst: Boolean) -> VirtualFile +const val RESOURCE_ROOT = "src/test/resources" +const val ROOT_PACKAGE = "com/demonwav/mcdev" +const val BASE_DATA_PATH = "$RESOURCE_ROOT/$ROOT_PACKAGE" +const val BASE_DATA_PATH_2 = "\$PROJECT_ROOT/$BASE_DATA_PATH/" + val mockJdk by lazy { val path = findLibraryPath("mock-jdk") val rtFile = StandardFileSystems.local().findFileByPath(path)!! diff --git a/src/test/kotlin/platform/bukkit/BaseSpigotTest.kt b/src/test/kotlin/platform/bukkit/BaseSpigotTest.kt new file mode 100644 index 000000000..2e1ee6c69 --- /dev/null +++ b/src/test/kotlin/platform/bukkit/BaseSpigotTest.kt @@ -0,0 +1,73 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.bukkit + +import com.demonwav.mcdev.framework.BaseMinecraftTest +import com.demonwav.mcdev.framework.NoEdt +import com.demonwav.mcdev.framework.createLibrary +import com.demonwav.mcdev.platform.PlatformType +import com.demonwav.mcdev.util.runWriteTask +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.roots.libraries.Library +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach + +abstract class BaseSpigotTest : BaseMinecraftTest(PlatformType.SPIGOT) { + + private var library: Library? = null + + @NoEdt + @BeforeEach + fun initSpigot() { + runWriteTask { + library = createLibrary(project, "spigot-api") + } + + ModuleRootModificationUtil.updateModel(module) { model -> + model.addLibraryEntry(library ?: throw IllegalStateException("Library not created")) + val orderEntries = model.orderEntries + val last = orderEntries.last() + System.arraycopy(orderEntries, 0, orderEntries, 1, orderEntries.size - 1) + orderEntries[0] = last + model.rearrangeOrderEntries(orderEntries) + } + } + + @AfterEach + fun cleanupSpigot() { + library?.let { l -> + ModuleRootModificationUtil.updateModel(module) { model -> + model.removeOrderEntry( + model.findLibraryOrderEntry(l) ?: throw IllegalStateException("Library not found"), + ) + } + + runWriteTask { + val table = LibraryTablesRegistrar.getInstance().getLibraryTable(project) + table.modifiableModel.let { model -> + model.removeLibrary(l) + model.commit() + } + } + } + } +} diff --git a/src/test/kotlin/platform/bukkit/PluginYmlReferenceContributorTest.kt b/src/test/kotlin/platform/bukkit/PluginYmlReferenceContributorTest.kt new file mode 100644 index 000000000..8777a9107 --- /dev/null +++ b/src/test/kotlin/platform/bukkit/PluginYmlReferenceContributorTest.kt @@ -0,0 +1,91 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.bukkit + +import com.demonwav.mcdev.framework.BASE_DATA_PATH_2 +import com.demonwav.mcdev.framework.EdtInterceptor +import com.intellij.psi.PsiClass +import com.intellij.testFramework.TestDataPath +import com.intellij.testFramework.requireIs +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +private const val TEST_DATA_PATH = "platform/bukkit/pluginYmlReferenceContributor" + +@ExtendWith(EdtInterceptor::class) +@DisplayName("Plugin Yml Reference Contributor Test") +@TestDataPath(BASE_DATA_PATH_2 + TEST_DATA_PATH) +class PluginYmlReferenceContributorTest : BaseSpigotTest() { + + override val testPath: String = TEST_DATA_PATH + + fun setupProject() { + fixture.configureByFiles("SimplePlugin.java", "plugin.yml") + } + + @Test + @DisplayName("Main Class Reference") + fun mainClassReference() { + setupProject() + val caretRef = fixture.getReferenceAtCaretPositionWithAssertion("plugin.yml") + val resolvedClass = caretRef.resolve().requireIs() + Assertions.assertEquals("simple.plugin.SimplePlugin", resolvedClass.qualifiedName) + } + + @Test + @DisplayName("Main Class Reference Variants") + fun mainClassReferenceVariants() { + setupProject() + fixture.testCompletionVariants("plugin.yml", "simple.plugin.SimplePlugin") + } + + @Test + @DisplayName("Main Class Reference Rename From Plugin Yml") + fun mainClassReferenceRenameFromPluginYml() { + fixture.configureByFiles("plugin.yml", "SimplePlugin.java") // The different order matters + + fixture.renameElementAtCaret("RenamedPlugin") + fixture.checkResultByFile("plugin.yml", "afterRename/plugin.yml", false) + fixture.checkResultByFile("RenamedPlugin.java", "afterRename/RenamedPlugin.java", false) + } + + @Test + @DisplayName("Main Class Reference Rename From Class") + fun mainClassReferenceRenameFromClass() { + setupProject() + + fixture.renameElementAtCaret("RenamedPlugin") + fixture.checkResultByFile("plugin.yml", "afterRename/plugin.yml", false) + fixture.checkResultByFile("RenamedPlugin.java", "afterRename/RenamedPlugin.java", false) + } + + @Test + @DisplayName("Main Class Reference Move") + fun mainClassReferenceMove() { + setupProject() + + fixture.tempDirFixture.findOrCreateDir("pack") + fixture.moveFile("SimplePlugin.java", "pack") + fixture.checkResultByFile("plugin.yml", "afterMove/plugin.yml", false) + } +} diff --git a/src/test/kotlin/platform/bukkit/PluginYmlSpigotInspectionTest.kt b/src/test/kotlin/platform/bukkit/PluginYmlSpigotInspectionTest.kt new file mode 100644 index 000000000..6caedc724 --- /dev/null +++ b/src/test/kotlin/platform/bukkit/PluginYmlSpigotInspectionTest.kt @@ -0,0 +1,62 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.bukkit + +import com.demonwav.mcdev.framework.BASE_DATA_PATH_2 +import com.demonwav.mcdev.yaml.PluginYmlInspection +import com.intellij.testFramework.TestDataPath +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +private const val TEST_DATA_PATH = "platform/bukkit/pluginYmlInspection" + +@DisplayName("Plugin Yml Spigot Inspection Test") +@TestDataPath(BASE_DATA_PATH_2 + TEST_DATA_PATH) +class PluginYmlSpigotInspectionTest : BaseSpigotTest() { + + override val testPath: String = TEST_DATA_PATH + + private fun doTest(@Language("yaml") pluginYml: String) { + fixture.configureByFile("SimplePlugin.java") + fixture.configureByText("plugin.yml", pluginYml) + fixture.enableInspections(PluginYmlInspection::class) + fixture.checkHighlighting() + } + + @Test + @DisplayName("Main Unresolved Reference") + fun mainUnresolvedReference() { + doTest("main: test.BadReference") + } + + @Test + @DisplayName("Main Wrong Type") + fun mainWrongType() { + doTest("main: java.lang.String") + } + + @Test + @DisplayName("Main Good Type") + fun mainGoodType() { + doTest("main: test.SimplePlugin") + } +} diff --git a/src/test/kotlin/platform/bungeecord/BaseBungeeCordTest.kt b/src/test/kotlin/platform/bungeecord/BaseBungeeCordTest.kt new file mode 100644 index 000000000..eff18808c --- /dev/null +++ b/src/test/kotlin/platform/bungeecord/BaseBungeeCordTest.kt @@ -0,0 +1,73 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.bungeecord + +import com.demonwav.mcdev.framework.BaseMinecraftTest +import com.demonwav.mcdev.framework.NoEdt +import com.demonwav.mcdev.framework.createLibrary +import com.demonwav.mcdev.platform.PlatformType +import com.demonwav.mcdev.util.runWriteTask +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.roots.libraries.Library +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach + +abstract class BaseBungeeCordTest : BaseMinecraftTest(PlatformType.BUNGEECORD) { + + private var library: Library? = null + + @NoEdt + @BeforeEach + fun initBungeeCord() { + runWriteTask { + library = createLibrary(project, "bungeecord-api") + } + + ModuleRootModificationUtil.updateModel(module) { model -> + model.addLibraryEntry(library ?: throw IllegalStateException("Library not created")) + val orderEntries = model.orderEntries + val last = orderEntries.last() + System.arraycopy(orderEntries, 0, orderEntries, 1, orderEntries.size - 1) + orderEntries[0] = last + model.rearrangeOrderEntries(orderEntries) + } + } + + @AfterEach + fun cleanupBungeeCord() { + library?.let { l -> + ModuleRootModificationUtil.updateModel(module) { model -> + model.removeOrderEntry( + model.findLibraryOrderEntry(l) ?: throw IllegalStateException("Library not found"), + ) + } + + runWriteTask { + val table = LibraryTablesRegistrar.getInstance().getLibraryTable(project) + table.modifiableModel.let { model -> + model.removeLibrary(l) + model.commit() + } + } + } + } +} diff --git a/src/test/kotlin/platform/bungeecord/PluginYmlBungeeInspectionTest.kt b/src/test/kotlin/platform/bungeecord/PluginYmlBungeeInspectionTest.kt new file mode 100644 index 000000000..f68b82d60 --- /dev/null +++ b/src/test/kotlin/platform/bungeecord/PluginYmlBungeeInspectionTest.kt @@ -0,0 +1,62 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.bungeecord + +import com.demonwav.mcdev.framework.BASE_DATA_PATH_2 +import com.demonwav.mcdev.yaml.PluginYmlInspection +import com.intellij.testFramework.TestDataPath +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +private const val TEST_DATA_PATH = "platform/bungeecord/pluginYmlInspection" + +@DisplayName("Plugin Yml BungeeCord Inspection Test") +@TestDataPath(BASE_DATA_PATH_2 + TEST_DATA_PATH) +class PluginYmlBungeeInspectionTest : BaseBungeeCordTest() { + + override val testPath: String = TEST_DATA_PATH + + private fun doTest(@Language("yaml") pluginYml: String) { + fixture.configureByFile("SimplePlugin.java") + fixture.configureByText("plugin.yml", pluginYml) + fixture.enableInspections(PluginYmlInspection::class) + fixture.checkHighlighting() + } + + @Test + @DisplayName("Main Unresolved Reference") + fun mainUnresolvedReference() { + doTest("main: test.BadReference") + } + + @Test + @DisplayName("Main Wrong Type") + fun mainWrongType() { + doTest("main: java.lang.String") + } + + @Test + @DisplayName("Main Good Type") + fun mainGoodType() { + doTest("main: test.SimplePlugin") + } +} diff --git a/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlInspection/SimplePlugin.java b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlInspection/SimplePlugin.java new file mode 100644 index 000000000..2f6291e42 --- /dev/null +++ b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlInspection/SimplePlugin.java @@ -0,0 +1,5 @@ +package test; + +import org.bukkit.plugin.java.JavaPlugin; + +public class SimplePlugin extends JavaPlugin {} diff --git a/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/SimplePlugin.java b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/SimplePlugin.java new file mode 100644 index 000000000..41013917b --- /dev/null +++ b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/SimplePlugin.java @@ -0,0 +1,5 @@ +package simple.plugin; + +import org.bukkit.plugin.java.JavaPlugin; + +public class SimplePlugin extends JavaPlugin {} diff --git a/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterMove/plugin.yml b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterMove/plugin.yml new file mode 100644 index 000000000..bd624f986 --- /dev/null +++ b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterMove/plugin.yml @@ -0,0 +1 @@ +main: pack.SimplePlugin diff --git a/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/RenamedPlugin.java b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/RenamedPlugin.java new file mode 100644 index 000000000..f78c0db3c --- /dev/null +++ b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/RenamedPlugin.java @@ -0,0 +1,5 @@ +package simple.plugin; + +import org.bukkit.plugin.java.JavaPlugin; + +public class RenamedPlugin extends JavaPlugin {} diff --git a/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/plugin.yml b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/plugin.yml new file mode 100644 index 000000000..f2ceae7f3 --- /dev/null +++ b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/afterRename/plugin.yml @@ -0,0 +1 @@ +main: simple.plugin.RenamedPlugin diff --git a/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/plugin.yml b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/plugin.yml new file mode 100644 index 000000000..41d47ffe8 --- /dev/null +++ b/src/test/resources/com/demonwav/mcdev/platform/bukkit/pluginYmlReferenceContributor/plugin.yml @@ -0,0 +1 @@ +main: simple.plugin.SimplePlugin diff --git a/src/test/resources/com/demonwav/mcdev/platform/bungeecord/pluginYmlInspection/SimplePlugin.java b/src/test/resources/com/demonwav/mcdev/platform/bungeecord/pluginYmlInspection/SimplePlugin.java new file mode 100644 index 000000000..48d334eaf --- /dev/null +++ b/src/test/resources/com/demonwav/mcdev/platform/bungeecord/pluginYmlInspection/SimplePlugin.java @@ -0,0 +1,5 @@ +package test; + +import net.md_5.bungee.api.plugin.Plugin; + +public class SimplePlugin extends Plugin {}