Skip to content

Commit

Permalink
#51 Migrate java inspections to kotlin (#61)
Browse files Browse the repository at this point in the history
* #51 Migrate java inspections to kotlin

* tinker with the settings description a bit

* fix test-ui ubuntu pipeline by using maximize build space


---------

Co-authored-by: Dan Timofte <dantimofte@proton.me>
  • Loading branch information
dantimofte and Dan Timofte authored Dec 1, 2024
1 parent c3256f0 commit b3566fd
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 118 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/run-ui-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ jobs:

steps:

# Free GitHub Actions Environment Disk Space
- name: Maximize Build Space
if : ${{ matrix.os == 'ubuntu-latest' }}
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
tool-cache: false
large-packages: false


# Check out the current repository
- name: Fetch Sources
uses: actions/checkout@v4
Expand Down

This file was deleted.

30 changes: 30 additions & 0 deletions src/main/kotlin/ac/quant/quickfixspec/common/spec/XmlUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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<XmlTag?> = SmartPointerManager.createPointer<XmlTag?>(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 <component> tag that is to be replaced
val tagToReplace = descriptor.psiElement as XmlTag

// Retrieve the full definition of the <component> 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 <component> tag with the full definition
tagToReplace.replace(newTag)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<html lang="en">
<body>
<p>
Detects shorthand <code>&lt;component&gt;</code> tags (e.g., <code>&lt;component name="compName1" /&gt;</code>) within
<code>&lt;message&gt;</code> sections and suggests replacing them with their full definition from the
<code>&lt;components&gt;</code> section of the same XML file.
Detects shorthand <code>&lt;component&gt;</code> tags (e.g., <code>&lt;component name="compName1" /&gt;</code>)
and suggests replacing them with their full definition from the <code>&lt;components&gt;</code> section of the same XML file.
</p>

<h3>Example</h3>
<pre><code class="language-xml">
&lt;message&gt;
&lt;field name="field1"/&gt;
&lt;component name="compName1" /&gt;
&lt;field name="field2" /&gt;
&lt;/message&gt;

&lt;components&gt;
Expand All @@ -21,14 +22,19 @@ <h3>Example</h3>
</code></pre>

<p>
Suggestion: Expand <code>&lt;component name="compName1" /&gt;</code> to:
Suggestion: <strong>Replace component reference with definition</strong>
</p>

<p>Result:</p>
<pre><code class="language-xml">
&lt;component name="compName1"&gt;
&lt;field name="aa" /&gt;
&lt;field name="bb" /&gt;
&lt;/component&gt;
&lt;message&gt;
&lt;field name="field1"/&gt;
&lt;component name="compName1"&gt;
&lt;field name="aa" /&gt;
&lt;field name="bb" /&gt;
&lt;/component&gt;
&lt;field name="field2" /&gt;
&lt;/message&gt;
</code></pre>

</body>
Expand Down
25 changes: 22 additions & 3 deletions src/test/kotlin/ac/quant/quickfixspec/common/spec/TestXmlUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class TestXmlUtils : BasePlatformTestCase() {
}
}


fun testFindDefinition() {
val psiFile = getXmlFile(myFixture, "FIX44.xml")
val rootTag = psiFile.findElementAt(0)!!.parent as XmlTag
Expand Down Expand Up @@ -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)
Expand All @@ -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"

}

0 comments on commit b3566fd

Please sign in to comment.