Added names for Adressing Modes.

Added Documentation Provider for symbol definitions (shows assigned declaration).
Added Documentation Provider for mnemonics (simple version, generated out of ISA information).
Work in progress for Register Flow Documentation Provider.
New minimum required version of IDE is now 2020.3.
This commit is contained in:
Chris Hodges 2021-08-01 19:51:42 +02:00
parent 09a4e62c7c
commit 44a4721d77
13 changed files with 416 additions and 32 deletions

View File

@ -19,12 +19,13 @@ awesome features and is pretty advanced. Check it out. You can install both plug
Big kudos to Yann -- a few features were _inspired_ by his code.
My plugin, on the other hand, is still pretty basic and is the result of about a week's effort. I released a really early first version it because I think
My plugin, on the other hand, is still pretty basic and is the result of about two weeks of work. I released a really early first version it because I think
it's "good enough" to get started, and I can return to demo coding with its current state.
## Features
- Parser / Lexer for MC68000 (yes, only 68000 right now!) assembly language files in VAsm / DevPac style
- Inspection for validating the syntax the 68000 ISA.
- Syntax highlighting and Color Settings Page (you should really modify the color settings to your likings!)
- Mnemonics code completion
- Symbols / Labels / Macros code completion
@ -33,6 +34,7 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- Quote handler
- Goto Symbol support
- Structure view
- Documentation provider for symbol definitions and mnemonics (listing available addressing modes etc.).
## Known issues
@ -42,7 +44,6 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- No support for register replacement (e.g. registers replaced by `EQUR` or `EQURL` will cause syntax errors)
- While the Lexer supports the -spaces option (where a space introduces a comment), this cannot be configured yet (default is off).
- No support for other processor instructions, FPU or 68020+ address modes.
- For versions < V0.4: No semantic checking for allowed address modes or data widths yet.
- Unit Test coverage is not as good as it could be (ahem).
- Missing but planned features:
- Macro evaluation on invocation
@ -70,11 +71,14 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.
### V0.4 (unreleased)
- Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
- Enhancement: Added Structure View filters.
- New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
- Bugfix: Added several missing assembler directives (`opt`, `machine`, etc.).
- Bugfix: Uppercase hexadecimal literals were not parsed (JFlex bug?).
- Bugfix: Interpretation of register lists was wrong in BNF.
- New: Added Documentation Provider for symbol definitions (shows assigned declaration).
- New: Added Documentation Provider for mnemonics (simple version, generated out of ISA information).
### V0.3 (28-Jul-21)

View File

@ -48,10 +48,7 @@ intellij {
}
runPluginVerifier {
ideVersions = ["IC-193.5662.53", "IC-212.4746.92", // 2019.3.1 - 2021.2
"CL-193.5233.103", "CL-193.7288.25", // 2019.3
"CL-201.6668.126", "CL-201.8743.17", // 2020.1
"CL-202.6397.106", "CL-202.8194.17", // 2020.2
ideVersions = ["IC-203.6682.168", "IC-212.4746.92", // 2020.3 - 2021.2
"CL-203.5981.166", "CL-203.8084.11", // 2020.3
"CL-211.6693.114", "CL-211.7628.27", // 2021.1
"CL-212.4746.93"] // 2021.2
@ -62,11 +59,14 @@ patchPluginXml {
setChangeNotes("""
<h4>V0.4 (unreleased)</h4>
<ul>
<li>Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
<li>Enhancement: Added Structure View filters.
<li>New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
<li>Bugfix: Added several missing assembler directives (opt, machine, etc.).
<li>Bugfix: Uppercase hexadecimal literals were not parsed (JFlex bug?).
<li>Bugfix: Interpretation of register lists was wrong in BNF.
<li>New: Added Documentation Provider for symbol definitions (shows assigned declaration).
<li>New: Added Documentation Provider for mnemonics (simple version, generated out of ISA information).
</ul>
<h4>V0.3 (28-Jul-21)</h4>
<ul>

View File

@ -34,20 +34,20 @@ enum class Register(val regname: String, val num: Int) {
}
}
enum class AddressMode {
IMMEDIATE_DATA,
ADDRESS_REGISTER_INDIRECT_PRE_DEC,
ADDRESS_REGISTER_INDIRECT_POST_INC,
ADDRESS_REGISTER_INDIRECT,
ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
SPECIAL_REGISTER_DIRECT,
DATA_REGISTER_DIRECT,
ADDRESS_REGISTER_DIRECT,
REGISTER_LIST,
ABSOLUTE_ADDRESS
enum class AddressMode(val description: String, val syntax: String) {
DATA_REGISTER_DIRECT("data register direct", "Dn"),
ADDRESS_REGISTER_DIRECT("address register direct", "An"),
ADDRESS_REGISTER_INDIRECT("address register indirect", "(An)"),
ADDRESS_REGISTER_INDIRECT_POST_INC("address register indirect with postincrement", "(An)+"),
ADDRESS_REGISTER_INDIRECT_PRE_DEC("address register indirect with predecrement", "-(An)"),
ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT("address register indirect with displacement", "(d16,An)"),
PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT("program counter indirect with displacement", "(d16,PC)"),
ADDRESS_REGISTER_INDIRECT_WITH_INDEX("address register indirect with index", "(d8,An,Xn)"),
PROGRAM_COUNTER_INDIRECT_WITH_INDEX("program counter indirect with index", "(d8,PC,Xn)"),
SPECIAL_REGISTER_DIRECT("special register", "ccr|usp|vbr"),
REGISTER_LIST("register list", "list"),
IMMEDIATE_DATA("immediate", "#<xxx>"),
ABSOLUTE_ADDRESS("absolute short/long", "(xxx).w|l")
}
const val OP_UNSIZED = 0
@ -731,7 +731,7 @@ object M68kIsa {
}
.toSet()
fun findMatchingInstruction(mnemonic: String): List<IsaData> {
fun findMatchingInstructions(mnemonic: String): List<IsaData> {
val lowerMnemonic = mnemonic.lowercase()
return isaData
.filter {
@ -755,6 +755,13 @@ object M68kIsa {
}
}
fun findMatchingAddressMode(modes: List<AllowedAdrMode>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<AllowedAdrMode> {
return modes.filter { am ->
isAddressModeMatching(am, op1, op2, specialReg)
&& ((opSize == null) || ((opSize and am.size) == opSize))
}
}
fun findMatchingOpModeIgnoringSize(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, specialReg: String?): List<IsaData> {
return candidates.filter {
it.modes.any { am -> isAddressModeMatching(am, op1, op2, specialReg) }

View File

@ -0,0 +1,102 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.lang.documentation.AbstractDocumentationProvider
import com.intellij.lang.documentation.DocumentationMarkup
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.text.HtmlBuilder
import com.intellij.openapi.util.text.HtmlChunk
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import de.platon42.intellij.plugins.m68k.asm.AddressMode
import de.platon42.intellij.plugins.m68k.asm.IsaData
import de.platon42.intellij.plugins.m68k.asm.M68kIsa
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp
import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataForInstruction
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findOpSizeDescriptions
class M68kInstructionDocumentationProvider : AbstractDocumentationProvider() {
override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
if (element is M68kAsmInstruction) {
val builder = HtmlBuilder()
val isaData = findExactIsaDataForInstruction(element)
if (isaData == null) {
M68kIsa.findMatchingInstructions(element.asmOp.mnemonic).forEach { buildDocForIsaData(it, builder) }
} else {
buildDocForIsaData(isaData, builder)
}
return builder.toString()
}
return null
}
private fun buildDocForIsaData(isaData: IsaData, builder: HtmlBuilder) {
val defBuilder = createDefinition(isaData)
builder.append(defBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
val bindingRows = HtmlBuilder()
val headerCells = listOf(
HtmlChunk.text("Mnemonic").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
HtmlChunk.text("Op1").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
HtmlChunk.text("Op2").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL)
)
bindingRows.append(HtmlChunk.tag("tr").children(headerCells))
isaData.modes.forEach { allowedAdrMode ->
val mnemonics = findOpSizeDescriptions(allowedAdrMode.size)
.map { HtmlChunk.text(isaData.mnemonic + it).wrapWith(HtmlChunk.p()) }
bindingRows.append(
HtmlChunk.tag("tr").children(
DocumentationMarkup.SECTION_CONTENT_CELL.children(mnemonics),
DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op1)),
DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op2))
)
)
}
val contentBuilder = HtmlBuilder()
contentBuilder.append(bindingRows.br().wrapWith(DocumentationMarkup.SECTIONS_TABLE))
builder.append(contentBuilder.wrapWith(DocumentationMarkup.CONTENT_ELEMENT))
}
override fun generateHoverDoc(element: PsiElement, originalElement: PsiElement?): String? {
if (element is M68kAsmInstruction) {
val isaData = findExactIsaDataForInstruction(element) ?: return null
val defBuilder = createDefinition(isaData)
val builder = HtmlBuilder()
builder.append(defBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
return builder.toString()
}
return null
}
private fun createDefinition(isaData: IsaData): HtmlBuilder {
val defBuilder = HtmlBuilder()
defBuilder.append(HtmlChunk.text(isaData.description).bold().wrapWith("pre"))
if (isaData.isPrivileged) {
defBuilder.append(HtmlChunk.font("red").addText("(privileged)").wrapWith("p"))
}
return defBuilder
}
private fun collectAddressModes(addressModes: Set<AddressMode>?): HtmlChunk {
if (addressModes == null) return HtmlChunk.text("")
val modes = HtmlBuilder()
addressModes.sortedBy(AddressMode::ordinal)
.forEach { modes.append(HtmlChunk.text(it.syntax).wrapWith(HtmlChunk.p())) }
return modes.toFragment()
}
override fun getCustomDocumentationElement(editor: Editor, file: PsiFile, contextElement: PsiElement?, targetOffset: Int): PsiElement? {
if (contextElement == null) return null
if (contextElement is M68kAsmInstruction) return contextElement
val parent = contextElement.parent ?: return null
if (parent is M68kAsmInstruction) return parent
if (parent is M68kAsmOp) return parent.parent
if (parent is M68kOperandSize) return parent.parent.parent
return null
}
}

View File

@ -0,0 +1,39 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.lang.documentation.AbstractDocumentationProvider
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.m68k.asm.RWM_OP1_SHIFT
import de.platon42.intellij.plugins.m68k.asm.RWM_OP2_SHIFT
import de.platon42.intellij.plugins.m68k.asm.Register.Companion.getRegFromName
import de.platon42.intellij.plugins.m68k.psi.M68kAddressRegister
import de.platon42.intellij.plugins.m68k.psi.M68kAddressingMode
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
import de.platon42.intellij.plugins.m68k.psi.M68kDataRegister
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction
class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
if (element is M68kDataRegister || element is M68kAddressRegister) {
val register = getRegFromName(element.text)
val addressingMode = PsiTreeUtil.getParentOfType(element, M68kAddressingMode::class.java) ?: return null
val asmInstruction = PsiTreeUtil.getParentOfType(addressingMode, M68kAsmInstruction::class.java) ?: return null
val (isaData, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return "unknown instruction"
val opShift = if (asmInstruction.addressingModeList[0] == addressingMode) RWM_OP1_SHIFT else RWM_OP2_SHIFT
return register.regname
}
return null
}
override fun getCustomDocumentationElement(editor: Editor, file: PsiFile, contextElement: PsiElement?, targetOffset: Int): PsiElement? {
if (contextElement == null) return null
if (contextElement is M68kDataRegister || contextElement is M68kAddressRegister) return contextElement
val parent = contextElement.parent ?: return null
if (parent is M68kDataRegister || parent is M68kAddressRegister) return parent
return null
}
}

View File

@ -0,0 +1,24 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.lang.documentation.AbstractDocumentationProvider
import com.intellij.lang.documentation.DocumentationMarkup
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import de.platon42.intellij.plugins.m68k.psi.M68kAssignment
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolDefinition
class M68kSymbolDefinitionDocumentationProvider : AbstractDocumentationProvider() {
override fun getQuickNavigateInfo(element: PsiElement, originalElement: PsiElement?): String? {
return generateDoc(element, originalElement)
}
override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
return if (element is M68kSymbolDefinition) {
// TODO find out how we can generate inner links for more symbol references inside the expression (DocumentationManagerUtil)
val value = (element.getParent() as M68kAssignment).expr.text
DocumentationMarkup.DEFINITION_START + StringUtil.escapeXmlEntities(element.name!!) + DocumentationMarkup.DEFINITION_END +
DocumentationMarkup.CONTENT_START + StringUtil.escapeXmlEntities(value) + DocumentationMarkup.CONTENT_END
} else null
}
}

View File

@ -5,7 +5,7 @@ import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingInstruction
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingInstructions
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingOpMode
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingOpModeIgnoringSize
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findSupportedOpSizes
@ -38,7 +38,7 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
override fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
val asmOp = asmInstruction.asmOp
val mnemonicWithSize = asmOp.text
val isaData = findMatchingInstruction(asmOp.mnemonic)
val isaData = findMatchingInstructions(asmOp.mnemonic)
if (isaData.isEmpty()) {
return arrayOf(
manager.createProblemDescriptor(
@ -61,16 +61,19 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
)
)
}
val op1 = getAddressModeForType(asmInstruction.addressingModeList.getOrNull(0))
val op2 = getAddressModeForType(asmInstruction.addressingModeList.getOrNull(1))
val specialReg1 = (asmInstruction.addressingModeList.getOrNull(0) as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val specialReg2 = (asmInstruction.addressingModeList.getOrNull(1) as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val amOp1 = asmInstruction.addressingModeList.getOrNull(0)
val amOp2 = asmInstruction.addressingModeList.getOrNull(1)
val op1 = getAddressModeForType(amOp1)
val op2 = getAddressModeForType(amOp2)
val specialReg1 = (amOp1 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val specialReg2 = (amOp2 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val specialReg = specialReg1 ?: specialReg2
val opSize = asmOp.opSize
val matchingModeIsaData = findMatchingOpMode(isaData, op1, op2, opSize, specialReg1 ?: specialReg2)
val matchingModeIsaData = findMatchingOpMode(isaData, op1, op2, opSize, specialReg)
if (matchingModeIsaData.isEmpty()) {
val matchingModeIsaDataIgnoringSize = findMatchingOpModeIgnoringSize(isaData, op1, op2, specialReg1 ?: specialReg2)
val matchingModeIsaDataIgnoringSize = findMatchingOpModeIgnoringSize(isaData, op1, op2, specialReg)
if (matchingModeIsaDataIgnoringSize.isEmpty()) {
val matchingModeIsaDataSwapped = findMatchingOpModeIgnoringSize(isaData, op2, op1, specialReg1 ?: specialReg2)
val matchingModeIsaDataSwapped = findMatchingOpModeIgnoringSize(isaData, op2, op1, specialReg)
val supportedModesOp1 = isaData.flatMap { it.modes.flatMap { am -> am.op1 ?: emptySet() } }.toSet()
val supportedModesOp2 = isaData.flatMap { it.modes.flatMap { am -> am.op2 ?: emptySet() } }.toSet()
@ -133,7 +136,7 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
}
val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg1 ?: specialReg2)
val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg)
return arrayOf(
when (supportedOpSizes) {
OP_UNSIZED ->

View File

@ -0,0 +1,64 @@
package de.platon42.intellij.plugins.m68k.utils
import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
import de.platon42.intellij.plugins.m68k.psi.M68kSpecialRegisterDirectAddressingMode
object M68kIsaUtil {
// TODO if more than one result, do a check and consolidate
fun findExactIsaDataForInstruction(asmInstruction: M68kAsmInstruction): IsaData? =
findIsaDataForInstruction(asmInstruction).firstOrNull()?.first
// TODO if more than one result, do a check and consolidate
fun findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction: M68kAsmInstruction): Pair<IsaData, AllowedAdrMode>? {
val (isaData, adrModeList) = findIsaDataForInstruction(asmInstruction).firstOrNull() ?: return null
return isaData to adrModeList.first()
}
fun findIsaDataForInstruction(asmInstruction: M68kAsmInstruction): List<Pair<IsaData, List<AllowedAdrMode>>> {
val isaDataCandidates = M68kIsa.findMatchingInstructions(asmInstruction.asmOp.mnemonic)
if (isaDataCandidates.isEmpty()) return emptyList()
val amOp1 = asmInstruction.addressingModeList.getOrNull(0)
val amOp2 = asmInstruction.addressingModeList.getOrNull(1)
val op1 = M68kAddressModeUtil.getAddressModeForType(amOp1)
val op2 = M68kAddressModeUtil.getAddressModeForType(amOp2)
val specialReg1 = (amOp1 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val specialReg2 = (amOp2 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val specialReg = specialReg1 ?: specialReg2
val opSize = asmInstruction.asmOp.opSize
val matchedIsaData =
M68kIsa.findMatchingOpMode(isaDataCandidates, op1, op2, opSize, specialReg)
return matchedIsaData.map { it to M68kIsa.findMatchingAddressMode(it.modes, op1, op2, opSize, specialReg) }
}
fun findOpSizeDescription(opSize: Int): String {
return when (opSize) {
OP_UNSIZED -> ""
OP_SIZE_B -> ".b"
OP_SIZE_W -> ".w"
OP_SIZE_L -> ".l"
OP_SIZE_S -> ".s"
OP_SIZE_BWL -> ".b|.w|.l"
OP_SIZE_WL -> ".w|.l"
OP_SIZE_SBW -> ".s|.b|.w"
else -> "?"
}
}
fun findOpSizeDescriptions(opSize: Int): Set<String> {
return when (opSize) {
OP_UNSIZED -> setOf("")
OP_SIZE_B -> setOf(".b")
OP_SIZE_W -> setOf(".w")
OP_SIZE_L -> setOf(".l")
OP_SIZE_S -> setOf(".s")
OP_SIZE_BWL -> setOf(".b", ".w", ".l")
OP_SIZE_WL -> setOf(".w", ".l")
OP_SIZE_SBW -> setOf(".s", ".b", ".w")
else -> setOf("?")
}
}
}

View File

@ -10,7 +10,7 @@
<p>
<a href="https://github.com/chrisly42/mc68000-asm-plugin/blob/main/README.md">Full documentation here...</a>
]]></description>
<idea-version since-build="193.5233.103"/>
<idea-version since-build="203.5981.166"/>
<depends>com.intellij.modules.lang</depends>
@ -33,6 +33,12 @@
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall"
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallElementManipulator"/>
<lang.documentationProvider language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kSymbolDefinitionDocumentationProvider"/>
<lang.documentationProvider language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kRegisterFlowDocumentationProvider"/>
<lang.documentationProvider language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kInstructionDocumentationProvider"/>
<stubElementTypeHolder class="de.platon42.intellij.plugins.m68k.stubs.M68kElementTypes"/>
<stubIndex implementation="de.platon42.intellij.plugins.m68k.stubs.M68kGlobalLabelStubIndex"/>
<stubIndex implementation="de.platon42.intellij.plugins.m68k.stubs.M68kSymbolDefinitionStubIndex"/>
@ -42,6 +48,7 @@
<renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection"
displayName="Assembly instruction validity" groupName="M68k"
enabledByDefault="true" level="ERROR"/>
</extensions>

View File

@ -0,0 +1,20 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.codeInsight.documentation.DocumentationManager
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.TestDataPath
import de.platon42.intellij.plugins.m68k.AbstractM68kTest
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
@TestDataPath("src/test/resources/documentation")
abstract class AbstractDocumentationProviderTest : AbstractM68kTest() {
fun generateDocumentation(myFixture: CodeInsightTestFixture): String? {
val docElement = DocumentationManager.getInstance(myFixture.project).findTargetElement(myFixture.editor, myFixture.file)
val provider = DocumentationManager.getProviderFromElement(docElement)
return provider.generateDoc(docElement, myFixture.file.findElementAt(myFixture.caretOffset))
}
}

View File

@ -0,0 +1,59 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationProviderTest() {
@Test
internal fun check_documentation_inside_mnemonic(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
mo<caret>veq.l #40,d0
"""
)
assertThat(generateDocumentation(myFixture)).isEqualTo(
"""<div class="definition"><pre><b>Move Quick</b></pre></div><div class="content"><table class="sections"><tr><td class="section" valign="top">Mnemonic</td><td class="section" valign="top">Op1</td><td class="section" valign="top">Op2</td></tr><tr><td valign="top"><p>moveq.l</p></td><td valign="top"><p>#&lt;xxx&gt;</p></td><td valign="top"><p>Dn</p></td></tr><br/></table></div>"""
)
}
@Test
internal fun check_documentation_inside_op_size(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
bra.<caret>s label
"""
)
assertThat(generateDocumentation(myFixture)).isEqualTo(
"""<div class="definition"><pre><b>Branch</b></pre></div><div class="content"><table class="sections"><tr><td class="section" valign="top">Mnemonic</td><td class="section" valign="top">Op1</td><td class="section" valign="top">Op2</td></tr><tr><td valign="top"><p>bra.s</p><p>bra.b</p><p>bra.w</p></td><td valign="top"><p>(xxx).w|l</p></td><td valign="top"></td></tr><br/></table></div>"""
)
}
@Test
internal fun check_documentation_for_privileged_instruction(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
<caret>reset
"""
)
assertThat(generateDocumentation(myFixture)).isEqualTo(
"""<div class="definition"><pre><b>Reset External Devices</b></pre><p><font color="red">(privileged)</font></p></div><div class="content"><table class="sections"><tr><td class="section" valign="top">Mnemonic</td><td class="section" valign="top">Op1</td><td class="section" valign="top">Op2</td></tr><tr><td valign="top"><p>reset</p></td><td valign="top"></td><td valign="top"></td></tr><br/></table></div>"""
)
}
@Test
internal fun check_documentation_if_there_is_no_concrete_match(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
<caret>cmp
"""
)
assertThat(generateDocumentation(myFixture))
.contains("<b>Compare</b>", "<b>Compare Address</b>", "<b>Compare Immediate</b>", "<b>Compare Memory to Memory</b>")
}
}

View File

@ -0,0 +1,31 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kRegisterFlowDocumentationProviderTest : AbstractDocumentationProviderTest() {
@Test
internal fun check_documentation_for_a_symbol_definition(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
label
moveq.l #0,d0
add.w #1,d0
move.w d0,d1
move.b d2,d0
addq.b #1,d0
move.w d<caret>0,d1
move.l d0,d2
move.w d1,d2
rts
"""
)
assertThat(generateDocumentation(myFixture)).isEqualTo("d0")
}
}

View File

@ -0,0 +1,24 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kSymbolDefinitionDocumentationProviderTest : AbstractDocumentationProviderTest() {
@Test
internal fun check_documentation_for_a_symbol_definition(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
PIC_WIDTH = 320
move.w #PIC_WIDT<caret>H,d0
"""
)
assertThat(generateDocumentation(myFixture))
.isEqualTo("<div class='definition'><pre>PIC_WIDTH</pre></div><div class='content'>320</div>")
}
}