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
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
- 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)
|
||||
|
||||
|
@ -63,6 +63,7 @@ patchPluginXml {
|
||||
<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: Code completion for local label definitions, suggesting undefined labels already referenced.
|
||||
<li>New: Added inspection suppression possibility and quickfix.
|
||||
</ul>
|
||||
<h4>V0.5 (06-Aug-21)</h4>
|
||||
<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.findUsagesProvider language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.scanner.M68kFindUsagesProvider"/>
|
||||
<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"
|
||||
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
|
||||
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall"
|
||||
|
@ -1,5 +1,6 @@
|
||||
package de.platon42.intellij.plugins.m68k.inspections
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
|
||||
import de.platon42.intellij.jupiter.LightCodeInsightExtension
|
||||
import de.platon42.intellij.jupiter.TestDataPath
|
||||
@ -16,4 +17,16 @@ abstract class AbstractInspectionTest : AbstractM68kTest() {
|
||||
assertThat(myFixture.doHighlighting())
|
||||
.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