From a3f7ddb4f7860da28d874a17824af6d25776ace0 Mon Sep 17 00:00:00 2001 From: chrisly42 Date: Mon, 9 Aug 2021 10:08:41 +0200 Subject: [PATCH] New: Added inspection suppression possibility and quickfix. --- README.md | 4 + build.gradle | 1 + .../inspections/M68kInspectionSuppressor.kt | 83 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 1 + .../inspections/AbstractInspectionTest.kt | 13 +++ .../M68kInspectionSuppressorTest.kt | 70 ++++++++++++++++ 6 files changed, 172 insertions(+) create mode 100644 src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressor.kt create mode 100644 src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressorTest.kt diff --git a/README.md b/README.md index bb257f3..ab505cc 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,9 @@ good enough" to get started, and I can return to demo coding with its current st ### Inspections +The plugin provides a few inspections for code analysis. An error or warning can be suppressed by placing a `; suppress ` comment either on an +end of line comment behind the statement or in a full line comment above the statement. + #### M68kSyntaxInspection - Assembly instruction validity Checks the validity of the current instruction. If an instruction is not recognized, you may get one of the following errors: @@ -150,6 +153,7 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42. - Enhancement: `include`, `incdir` and `incbin` and `output` with `` quotes no longer cause syntax error. - New: Files in `include` directives can be referenced and renamed/refactored. - New: Code completion for local label definitions, suggesting undefined labels already referenced. +- New: Added inspection suppression possibility and quickfix. ### V0.5 (06-Aug-21) diff --git a/build.gradle b/build.gradle index bbacc73..a9e3243 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,7 @@ patchPluginXml {
  • Enhancement: 'include', 'incdir' and 'incbin' and 'output' with '<pathname>' quotes no longer cause syntax error.
  • New: Files in 'include' directives can be referenced and renamed/refactored.
  • New: Code completion for local label definitions, suggesting undefined labels already referenced. +
  • New: Added inspection suppression possibility and quickfix.

    V0.5 (06-Aug-21)

      diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressor.kt b/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressor.kt new file mode 100644 index 0000000..c4f2b98 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressor.kt @@ -0,0 +1,83 @@ +package de.platon42.intellij.plugins.m68k.inspections + +import com.intellij.codeInspection.InspectionSuppressor +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.SuppressQuickFix +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiComment +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import de.platon42.intellij.plugins.m68k.psi.M68kPsiElement +import de.platon42.intellij.plugins.m68k.psi.M68kStatement + +class M68kInspectionSuppressor : InspectionSuppressor { + + companion object { + const val MARKER = "suppress " + } + + override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean { + if (element !is M68kPsiElement) return false + val statement = PsiTreeUtil.getParentOfType(element, M68kStatement::class.java) ?: return false + val nextToken = PsiTreeUtil.skipWhitespacesForward(statement) + return isSuppressedWithComment(nextToken, toolId) || isSuppressedWithComment(PsiTreeUtil.skipWhitespacesBackward(statement), toolId) + } + + override fun getSuppressActions(element: PsiElement?, toolId: String): Array { + return arrayOf(LineSuppressQuickFix(toolId)) + } + + private fun isSuppressedWithComment(nextToken: PsiElement?, toolId: String): Boolean { + return if (nextToken is PsiComment) { + val comment = nextToken.text + comment.contains(MARKER) && comment.contains(toolId) + } else { + false + } + } + + class LineSuppressQuickFix(private val toolId: String) : SuppressQuickFix { + + override fun getFamilyName() = "Suppress for statement" + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val statement = PsiTreeUtil.getParentOfType(descriptor.startElement, M68kStatement::class.java) ?: return + + val document = PsiDocumentManager.getInstance(project).getDocument(statement.containingFile) ?: return + val lineNumber = document.getLineNumber(statement.textOffset) + var usePrevLine = false + val nextToken = PsiTreeUtil.skipWhitespacesForward(statement) + if (nextToken is PsiComment) { + val comment = nextToken.text + if (comment.contains(MARKER)) { + if (!comment.contains(toolId)) { + document.insertString(document.getLineEndOffset(lineNumber), " $toolId") + } + return + } else { + usePrevLine = true + } + } + val prevToken = PsiTreeUtil.skipWhitespacesBackward(statement) + if (prevToken is PsiComment) { + val comment = prevToken.text + if (comment.contains(MARKER)) { + if (!comment.contains(toolId)) { + document.insertString(document.getLineEndOffset(document.getLineNumber(prevToken.textOffset)), " $toolId") + } + return + } + } + if (usePrevLine) { + document.insertString(document.getLineStartOffset(lineNumber), "; $MARKER$toolId\n") + } else { + document.insertString(document.getLineEndOffset(lineNumber), "\t; $MARKER$toolId") + } + } + + override fun isAvailable(project: Project, context: PsiElement) = true + + override fun isSuppressAll() = false + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 057cfff..c4c4c83 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -30,6 +30,7 @@ + { + val quickfixes = myFixture.getAllQuickFixes().filter { it.text.matches(regex) } + assertThat(quickfixes).`as`("Fixes matched by $regex: ${myFixture.getAllQuickFixes().map { it.text }}").hasSize(expectedFixes) + return quickfixes + } } \ No newline at end of file diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressorTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressorTest.kt new file mode 100644 index 0000000..f2ca9be --- /dev/null +++ b/src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kInspectionSuppressorTest.kt @@ -0,0 +1,70 @@ +package de.platon42.intellij.plugins.m68k.inspections + +import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import de.platon42.intellij.jupiter.MyFixture +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class M68kInspectionSuppressorTest : AbstractInspectionTest() { + + companion object { + private const val SUPPRESS_FOR_STATEMENT = "Suppress for statement" + } + + @Test + internal fun suppresses_inspection_with_end_of_line_comment(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kSyntaxInspection::class.java) + myFixture.configureByText("syntax.asm", " rts d0 ; suppress M68kSyntax") + assertThat(myFixture.doHighlighting()).isEmpty() + } + + @Test + internal fun suppresses_inspection_with_full_line_comment_on_line_before(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kSyntaxInspection::class.java) + myFixture.configureByText("syntax.asm", "; suppress M68kSyntax\n rts d0") + assertThat(myFixture.doHighlighting()).isEmpty() + } + + @Test + internal fun suppression_works_without_prior_comment(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kSyntaxInspection::class.java) + myFixture.configureByText("syntax.asm", " rts d0") + executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT) + myFixture.checkResult(" rts d0\t; suppress M68kSyntax") + } + + @Test + internal fun suppression_works_with_prior_suppression_comment(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kSyntaxInspection::class.java) + myFixture.configureByText("syntax.asm", " rts d0; suppress foobar") + executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT) + myFixture.checkResult(" rts d0; suppress foobar M68kSyntax") + } + + @Test + internal fun suppression_works_with_prior_suppression_comment_in_line_above(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kSyntaxInspection::class.java) + myFixture.configureByText("syntax.asm", "; suppress foobar\n rts d0") + executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT) + myFixture.checkResult("; suppress foobar M68kSyntax\n rts d0") + } + + @Test + internal fun suppression_works_by_adding_suppression_comment_in_line_above(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kSyntaxInspection::class.java) + myFixture.configureByText("syntax.asm", " rts d0 ; unrelated comment") + executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT) + myFixture.checkResult("; suppress M68kSyntax\n rts d0 ; unrelated comment") + } + + private fun executeSuppressAction(myFixture: CodeInsightTestFixture, suppressAction: String) { + val highlightInfos = myFixture.doHighlighting() + assertThat(highlightInfos).hasSize(1) + val quickFixPair = highlightInfos[0].quickFixActionRanges[0] + val intentionActionDescriptor = quickFixPair.first + val element = myFixture.file.findElementAt(quickFixPair.second.startOffset)!! + val suppressQuickFix = intentionActionDescriptor.getOptions(element, myFixture.editor)!! + .first { it.text == suppressAction } + myFixture.launchAction(suppressQuickFix) + } +} \ No newline at end of file