New: Added inspection suppression possibility and quickfix.
This commit is contained in:
parent
593719043e
commit
a3f7ddb4f7
@ -39,6 +39,9 @@ good enough" to get started, and I can return to demo coding with its current st
|
|||||||
|
|
||||||
### Inspections
|
### Inspections
|
||||||
|
|
||||||
|
The plugin provides a few inspections for code analysis. An error or warning can be suppressed by placing a `; suppress <InspectionName>` comment either on an
|
||||||
|
end of line comment behind the statement or in a full line comment above the statement.
|
||||||
|
|
||||||
#### M68kSyntaxInspection - Assembly instruction validity
|
#### 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:
|
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 `<pathname>` quotes no longer cause syntax error.
|
- 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: Files in `include` directives can be referenced and renamed/refactored.
|
||||||
- New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
- New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
||||||
|
- New: Added inspection suppression possibility and quickfix.
|
||||||
|
|
||||||
### V0.5 (06-Aug-21)
|
### V0.5 (06-Aug-21)
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ patchPluginXml {
|
|||||||
<li>Enhancement: 'include', 'incdir' and 'incbin' and 'output' with '<pathname>' quotes no longer cause syntax error.
|
<li>Enhancement: 'include', 'incdir' and 'incbin' and 'output' with '<pathname>' quotes no longer cause syntax error.
|
||||||
<li>New: Files in 'include' directives can be referenced and renamed/refactored.
|
<li>New: Files in 'include' directives can be referenced and renamed/refactored.
|
||||||
<li>New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
<li>New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
||||||
|
<li>New: Added inspection suppression possibility and quickfix.
|
||||||
</ul>
|
</ul>
|
||||||
<h4>V0.5 (06-Aug-21)</h4>
|
<h4>V0.5 (06-Aug-21)</h4>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -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<SuppressQuickFix> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,7 @@
|
|||||||
<lang.quoteHandler language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.M68kStringQuoteHandler"/>
|
<lang.quoteHandler language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.M68kStringQuoteHandler"/>
|
||||||
<lang.findUsagesProvider language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.scanner.M68kFindUsagesProvider"/>
|
<lang.findUsagesProvider language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.scanner.M68kFindUsagesProvider"/>
|
||||||
<lang.psiStructureViewFactory language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.structureview.M68kStructureViewFactory"/>
|
<lang.psiStructureViewFactory language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.structureview.M68kStructureViewFactory"/>
|
||||||
|
<lang.inspectionSuppressor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kInspectionSuppressor"/>
|
||||||
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference"
|
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference"
|
||||||
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
|
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
|
||||||
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall"
|
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package de.platon42.intellij.plugins.m68k.inspections
|
package de.platon42.intellij.plugins.m68k.inspections
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
|
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
|
||||||
import de.platon42.intellij.jupiter.LightCodeInsightExtension
|
import de.platon42.intellij.jupiter.LightCodeInsightExtension
|
||||||
import de.platon42.intellij.jupiter.TestDataPath
|
import de.platon42.intellij.jupiter.TestDataPath
|
||||||
@ -16,4 +17,16 @@ abstract class AbstractInspectionTest : AbstractM68kTest() {
|
|||||||
assertThat(myFixture.doHighlighting())
|
assertThat(myFixture.doHighlighting())
|
||||||
.areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing"))
|
.areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun executeQuickFixes(myFixture: CodeInsightTestFixture, regex: Regex, expectedFixes: Int) {
|
||||||
|
val quickfixes = getQuickFixes(myFixture, regex, expectedFixes)
|
||||||
|
assertThat(quickfixes.groupBy { it.familyName }).hasSize(1)
|
||||||
|
quickfixes.forEach(myFixture::launchAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getQuickFixes(myFixture: CodeInsightTestFixture, regex: Regex, expectedFixes: Int): List<IntentionAction> {
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
@ -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<caret> 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<caret> 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<caret> 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<caret> 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user