From bd18cb2e3a60adb4b81d3aadf52200584deb4c51 Mon Sep 17 00:00:00 2001 From: Dan Timofte Date: Sun, 1 Dec 2024 21:51:41 +0200 Subject: [PATCH] #51 Migrate java inspections to kotlin --- .../ReplaceWithDefinitionInspection.java | 107 ------------------ .../quickfixspec/common/spec/XmlUtils.kt | 30 +++++ .../ReplaceWithDefinitionInspection.kt | 82 ++++++++++++++ .../quickfixspec/common/spec/TestXmlUtils.kt | 25 +++- 4 files changed, 134 insertions(+), 110 deletions(-) delete mode 100644 src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java create mode 100644 src/main/kotlin/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.kt diff --git a/src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java b/src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java deleted file mode 100644 index fbd5afb..0000000 --- a/src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java +++ /dev/null @@ -1,107 +0,0 @@ -package ac.quant.quickfixspec.inspections; - -import com.intellij.codeInspection.LocalInspectionTool; -import com.intellij.codeInspection.ProblemsHolder; -import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.codeInspection.LocalQuickFix; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.SmartPointerManager; -import com.intellij.psi.SmartPsiElementPointer; -import com.intellij.psi.xml.XmlTag; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.NotNull; - -import static ac.quant.quickfixspec.common.spec.XmlUtils.*; - -public class ReplaceWithDefinitionInspection extends LocalInspectionTool { - - @Override - public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - return new PsiElementVisitor() { - @Override - public void visitElement(@NotNull PsiElement element) { - if (isNotComponentTagReference(element)) { - return; - } - - final XmlTag rootTag = getRootTag(element); - final XmlTag tag = (XmlTag) element; - if (rootTag == null) { - return; - } - - final String componentName = tag.getAttributeValue("name"); - - - if (componentName == null) { - return; - } - - XmlTag definition = findDefinition(componentName, tag.getName(), rootTag); - if (definition != null) { - holder.registerProblem(tag, "Replace with definition", new ReplaceWithDefinitionQuickFix(definition)); - } - } - }; - } - - private boolean isNotComponentTagReference(@NotNull PsiElement element) { - if (!(element instanceof XmlTag tag)) { - return true; - } - - final XmlTag parentTag = tag.getParentTag(); - if (parentTag == null) { - return true; - } - - if (parentTag.getName().equals("components")) { - return true; - } - - // if the tag is not self-closing that means it is a definition - if (tag.getSubTags().length > 0) { - return true; - } - - return !tag.getName().equals("component"); - } - - private static class ReplaceWithDefinitionQuickFix implements LocalQuickFix { - @SafeFieldForPreview - private final SmartPsiElementPointer definitionPointer; - - ReplaceWithDefinitionQuickFix(XmlTag definition) { - this.definitionPointer = SmartPointerManager.createPointer(definition); - } - - @Override - public @NotNull String getName() { - return "Replace with definition"; - } - - @Override - public @NotNull String getFamilyName() { - return getName(); - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IncorrectOperationException { - // Get the tag that is to be replaced - XmlTag tagToReplace = (XmlTag) descriptor.getPsiElement(); - - // Retrieve the full definition of the from the pointer - XmlTag definition = definitionPointer.getElement(); - - if (definition != null) { - // Create a copy of the definition tag to be inserted - XmlTag newTag = (XmlTag) definition.copy(); - - // Replace the original tag with the full definition - tagToReplace.replace(newTag); - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ac/quant/quickfixspec/common/spec/XmlUtils.kt b/src/main/kotlin/ac/quant/quickfixspec/common/spec/XmlUtils.kt index 3ac7443..2edc4e6 100644 --- a/src/main/kotlin/ac/quant/quickfixspec/common/spec/XmlUtils.kt +++ b/src/main/kotlin/ac/quant/quickfixspec/common/spec/XmlUtils.kt @@ -66,4 +66,34 @@ object XmlUtils { return DEFINITION_GROUP_NAME.containsValue(parentTagName) } + + @JvmStatic + fun getCurrentTag(element: PsiElement): XmlTag { + if (element is XmlTag) { + return element + } + + // iterate up the tree until we find the tag + var current = element + while (current !is XmlTag) { + current = current.parent + } + return current + } + + //Check if the tag is a reference to a component tag + @JvmStatic + fun isComponentReference(tag: XmlTag): Boolean { + val parentTag = tag.parentTag ?: return false + if (parentTag.name == "components") { + return false + } + + // if the tag is not self-closing that means it is a definition + if (tag.subTags.size > 0) { + return false + } + + return true + } } diff --git a/src/main/kotlin/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.kt b/src/main/kotlin/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.kt new file mode 100644 index 0000000..0ae5567 --- /dev/null +++ b/src/main/kotlin/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.kt @@ -0,0 +1,82 @@ +package ac.quant.quickfixspec.inspections + +import ac.quant.quickfixspec.common.spec.XmlUtils.findDefinition +import ac.quant.quickfixspec.common.spec.XmlUtils.getRootTag +import ac.quant.quickfixspec.common.spec.XmlUtils.isComponentReference +import ac.quant.quickfixspec.common.spec.XmlUtils.getCurrentTag + +import ac.quant.quickfixspec.inspections.ReplaceWithDefinitionInspection.ReplaceWithDefinitionQuickFix +import com.intellij.codeInsight.intention.FileModifier.SafeFieldForPreview +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.SmartPointerManager +import com.intellij.psi.SmartPsiElementPointer +import com.intellij.psi.xml.XmlTag +import com.intellij.util.IncorrectOperationException + + +class ReplaceWithDefinitionInspection : LocalInspectionTool() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object : PsiElementVisitor() { + override fun visitElement(element: PsiElement) { + val rootTag = getRootTag(element) ?: return + + val currentTag: XmlTag = getCurrentTag(element) + if (currentTag.name != "component") { + return + } + + + // return if the tag is not a component reference, and it's actually a definition from the "components" group + if (!isComponentReference(currentTag)) { + return + } + + val componentName = currentTag.getAttributeValue("name") ?: return + val definition = findDefinition(componentName, "component", rootTag) ?: return + + // check if we didn't already register a problem for this tag + if (holder.isOnTheFly && holder.results.any { it.psiElement == currentTag }) { + return + } + + holder.registerProblem(currentTag, "Replace with definition", ReplaceWithDefinitionQuickFix(definition)) + } + } + } + + private class ReplaceWithDefinitionQuickFix(definition: XmlTag) : LocalQuickFix { + @SafeFieldForPreview + private val definitionPointer: SmartPsiElementPointer = SmartPointerManager.createPointer(definition) + + override fun getName(): String { + return "Replace component reference with definition" + } + + override fun getFamilyName(): String { + return name + } + + @Throws(IncorrectOperationException::class) + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + // Get the tag that is to be replaced + val tagToReplace = descriptor.psiElement as XmlTag + + // Retrieve the full definition of the from the pointer + val definition = definitionPointer.getElement() + + if (definition != null) { + // Create a copy of the definition tag to be inserted + val newTag = definition.copy() as XmlTag + + // Replace the original tag with the full definition + tagToReplace.replace(newTag) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/ac/quant/quickfixspec/common/spec/TestXmlUtils.kt b/src/test/kotlin/ac/quant/quickfixspec/common/spec/TestXmlUtils.kt index 70a7b44..daf5ddd 100644 --- a/src/test/kotlin/ac/quant/quickfixspec/common/spec/TestXmlUtils.kt +++ b/src/test/kotlin/ac/quant/quickfixspec/common/spec/TestXmlUtils.kt @@ -21,7 +21,6 @@ class TestXmlUtils : BasePlatformTestCase() { } } - fun testFindDefinition() { val psiFile = getXmlFile(myFixture, "FIX44.xml") val rootTag = psiFile.findElementAt(0)!!.parent as XmlTag @@ -111,10 +110,8 @@ class TestXmlUtils : BasePlatformTestCase() { } } - fun testIsTagDeclaration() { val psiFile = getXmlFile(myFixture, "FIX44.xml") - val rootTag = psiFile.findElementAt(0)!!.parent as XmlTag val indexes = arrayOf(2650, 2785, 108734, 167517) val expectedDeclarations = arrayOf(false, false, true, true) @@ -133,6 +130,28 @@ class TestXmlUtils : BasePlatformTestCase() { } + fun testGetCurrentTag() { + val psiFile = getXmlFile(myFixture, "FIX44.xml") + + val expectedTagsMap = mapOf( + 0 to "fix", + 2650 to "field", + 2785 to "component", + 4879 to "message", + 12818 to "group", + 108734 to "component", + 167517 to "field" + ) + + for (positionInFile in expectedTagsMap.keys) { + val expectedTag = expectedTagsMap[positionInFile]!! + val psiElement = psiFile.findElementAt(positionInFile)!! + + val currentTag = XmlUtils.getCurrentTag(psiElement) + assertEquals("Current tag for element at positionInFile $positionInFile should be '$expectedTag' but was '${currentTag.name}'", expectedTag, currentTag.name) + } + } + override fun getTestDataPath() = "src/main/resources/spec" }