6 Commits
32 changed files with 848 additions and 112 deletions
+34 -4
View File
@@ -1,4 +1,4 @@
# MC68000 Assembly Language Plugin [![Build Status](https://app.travis-ci.com/chrisly42/mc68000-asm-plugin.svg?branch=master)](https://app.travis-ci.com/chrisly42/mc68000-asm-plugin) [![Coverage Status](https://coveralls.io/repos/github/chrisly42/mc68000-asm-plugin/badge.svg?branch=main)](https://coveralls.io/github/chrisly42/mc68000-asm-plugin?branch=main) # MC68000 Assembly Language Plugin
_MC68000 Assembly Language Plugin_ is plugin for Jetbrains IDEs (CLion, IntelliJ, etc.). _MC68000 Assembly Language Plugin_ is plugin for Jetbrains IDEs (CLion, IntelliJ, etc.).
@@ -84,6 +84,17 @@ optimize the order of execution.
Points out unresolved references such for global and local labels, macros or symbols. Right now, missing symbol and global label references are shown only as Points out unresolved references such for global and local labels, macros or symbols. Right now, missing symbol and global label references are shown only as
weak warnings as missing macro evaluation will not resolve symbols defined via `STRUCT` macros. weak warnings as missing macro evaluation will not resolve symbols defined via `STRUCT` macros.
#### M68kAddressRegisterWordAccessInspection - Suspicious word access to address register
Warns on `movea.w` to address registers which is often a typo and takes ages to find and fix.
Of course there are legal intentional uses of this instruction that sign extends the word
to a long word, or using it as a temporary register or for index purposes.
Also warns on `cmpa.w` with address register as second operand (there are some clever ways
using this, such as using `cmpa.w ax,ax` to see if a value is between $ffff8000 and $7fff).
Third instruction checked is `tst.w` on address registers for 68020+.
### Documentation provider ### Documentation provider
#### M68kSymbolDefinition #### M68kSymbolDefinition
@@ -133,6 +144,7 @@ If the current statement has no valid syntax, the instruction details of all mat
- Switching the spaces option usually needs the caches to be invalidated. Find Usages word scanner always uses default settings, as it is not configurable per - Switching the spaces option usually needs the caches to be invalidated. Find Usages word scanner always uses default settings, as it is not configurable per
project :-/ project :-/
- Unit Test coverage is not as good as it could be (ahem). - Unit Test coverage is not as good as it could be (ahem).
- Code flow sometimes outputs "uhm?"
- Missing but planned features: - Missing but planned features:
- Macro evaluation on invocation - Macro evaluation on invocation
- Folding - Folding
@@ -149,9 +161,10 @@ to highlight the same address and data registers while editing (see new `View ->
## Development notice ## Development notice
This plugin has been written in Kotlin 1.5 using Grammar-Kit. This plugin has been written in Kotlin 1.9 using Grammar-Kit.
It is probably the only plugin (besides [Cajon](https://github.com/chrisly42/cajon-plugin) from the same author) that uses JUnit 5 Jupiter for unit testing so It is probably the only plugin (besides [Cajon](https://git.platon42.de/chrisly42/cajon-plugin) from the same author) that uses JUnit 5 Jupiter for unit testing
so
far (or at least the only one I'm aware of ;) ). The IntelliJ framework actually uses the JUnit 3 TestCase for plugin testing, and it took me quite a while to far (or at least the only one I'm aware of ;) ). The IntelliJ framework actually uses the JUnit 3 TestCase for plugin testing, and it took me quite a while to
make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.intellij.jupiter```) for your projects (with attribution). make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.intellij.jupiter```) for your projects (with attribution).
@@ -164,6 +177,23 @@ are appreciated. It really is keeping me motivated to continue development.
## Changelog ## Changelog
### V0.11 (unreleased)
- New: Added M68kAddressRegisterWordAccessInspection to find suspicious uses of address registers.
### V0.10 (20-Feb-24)
- Decided to release some features that have been sitting on my harddrive for almost two years, but never got released,
because I was unable to get that damn formatter working in a way that was acceptable (the API and the docs are easily one
the worst abominations I've come across).
- Maintenance. Updated all dependencies to the latest versions.
- New: Added semantic highlighting. Currently available for data and address registers and local labels.
- Bugfix: addq/subq for address register stated it would affect the condition codes, which it in fact doesn't.
- New: Added simple custom navigation bar.
- New: Added folding support for functions and macro definitions.
- New: Added assembler directives to code completion (only lower-case except for other directives like IF
and MACRO, which are only suggested for upper-case).
### V0.9 (16-Aug-22) ### V0.9 (16-Aug-22)
- Maintenance. Updated all dependencies to the latest versions. - Maintenance. Updated all dependencies to the latest versions.
@@ -249,4 +279,4 @@ are appreciated. It really is keeping me motivated to continue development.
### V0.1 (20-Jul-21) ### V0.1 (20-Jul-21)
- Initial public release. - Initial public release.
+20 -36
View File
@@ -1,68 +1,56 @@
plugins { plugins {
id 'java' id 'java'
id 'org.jetbrains.intellij' version '1.9.0' id 'org.jetbrains.intellij' version '1.17.1'
id 'org.jetbrains.kotlin.jvm' version '1.7.20' id 'org.jetbrains.kotlin.jvm' version '1.9.22'
id 'jacoco' id 'jacoco'
id 'com.github.kt3k.coveralls' version '2.12.0'
} }
group = 'de.platon42' group = 'de.platon42'
version = '0.10' version = '0.10'
sourceCompatibility = "1.8" sourceCompatibility = 17
targetCompatibility = "1.8" targetCompatibility = 17
repositories { repositories {
mavenCentral() mavenCentral()
} }
/* /*
To run tests in IntelliJ use these VM Options for run configuration To run tests in IntelliJ use these VM Options for run configuration
-ea -Didea.system.path=build/idea-sandbox/system-test -Didea.config.path=build/idea-sandbox/config-test -Didea.plugins.path=build/idea-sandbox/plugins-test -ea -Didea.system.path=build/idea-sandbox/system-test -Didea.config.path=build/idea-sandbox/config-test -Didea.plugins.path=build/idea-sandbox/plugins-test
*/ */
dependencies { dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.assertj:assertj-core:3.26.3'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.2'
testImplementation "org.jetbrains.kotlin:kotlin-test" testImplementation "org.jetbrains.kotlin:kotlin-test"
testImplementation "org.jetbrains.kotlin:kotlin-reflect" testImplementation "org.jetbrains.kotlin:kotlin-reflect"
testImplementation 'org.junit.platform:junit-platform-launcher:1.9.0' testImplementation 'org.junit.platform:junit-platform-launcher:1.11.2'
// testImplementation "org.jetbrains.kotlin:kotlin-test-junit" // testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
} }
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += "-Xjvm-default=all"
}
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
intellij { intellij {
setVersion("2022.2.3") // LATEST-EAP-SNAPSHOT setVersion("2022.3") // LATEST-EAP-SNAPSHOT
setUpdateSinceUntilBuild(false) setUpdateSinceUntilBuild(false)
// setPlugins(["com.intellij.java"]) // setPlugins(["com.intellij.java"])
} }
runPluginVerifier { runPluginVerifier {
ideVersions = ["IC-203.6682.168", "IC-222.4345.14", // 2020.3 - 2022.2.3 ideVersions = ["IC-203.6682.168", "IC-233.14015.106", // 2020.3 - 2023.3
"CL-203.8084.11", // 2020.3 "CL-203.8084.11", "CL-233.14015.92"] // 2020.3 - 2023.3
"CL-211.7628.27", // 2021.1
"CL-212.5712.21", // 2021.2
"CL-213.7172.20", // 2021.3.4
"CL-221.5921.22", // 2022.1.3
"CL-222.4345.21"] // 2022.2.4
downloadDir = System.getProperty("user.home") + "/.gradle/caches/modules-2/files-2.1/com.jetbrains.intellij.idea/verifier" downloadDir = System.getProperty("user.home") + "/.gradle/caches/modules-2/files-2.1/com.jetbrains.intellij.idea/verifier"
} }
patchPluginXml { patchPluginXml {
setChangeNotes(""" setChangeNotes("""
<h4>V0.10 (undefined)</h4> <h4>V0.10 (20-Feb-24)</h4>
<ul> <ul>
<li>Decided to release some features that have been sitting on my harddrive for almost two years, but never got released,
because I was unable to get that damn formatter working in a way that was acceptable (the API and the docs are easily one
the worst abominations I've come across).
<li>Maintenance. Updated all dependencies to the latest versions.
<li>New: Added semantic highlighting. Currently available for data and address registers and local labels. <li>New: Added semantic highlighting. Currently available for data and address registers and local labels.
<li>Bugfix: addq/subq for address register stated it would affect the condition codes, which it in fact doesn't. <li>Bugfix: addq/subq for address register stated it would affect the condition codes, which it in fact doesn't.
<li>New: Added simple custom navigation bar. <li>New: Added simple custom navigation bar.
@@ -70,7 +58,7 @@ patchPluginXml {
<li>New: Added assembler directives to code completion (only lower-case except for other directives like IF <li>New: Added assembler directives to code completion (only lower-case except for other directives like IF
and MACRO, which are only suggested for upper-case). and MACRO, which are only suggested for upper-case).
</ul> </ul>
<p>Full changelog available at <a href="https://github.com/chrisly42/mc68000-asm-plugin#changelog">Github project site</a>.</p> <p>Full changelog available at <a href="https://git.platon42.de/chrisly42/mc68000-asm-plugin#changelog">Gitea project site</a>.</p>
""") """)
} }
@@ -86,10 +74,6 @@ test {
} }
} }
tasks.coveralls {
dependsOn jacocoTestReport
}
jacoco { jacoco {
toolVersion = '0.8.8' toolVersion = '0.8.8'
} }
@@ -103,4 +87,4 @@ jacocoTestReport {
publishPlugin { publishPlugin {
setToken(intellijPublishToken) setToken(intellijPublishToken)
} }
+1 -1
View File
@@ -1,4 +1,4 @@
kotlin.code.style=official kotlin.code.style=official
kotlin.incremental=true kotlin.incremental=true
intellijPublishToken=perm:dummy intellijPublishToken=perm:dummy
systemProp.jdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2" systemProp.jdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2"
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
+17
View File
@@ -0,0 +1,17 @@
Formatting:
- dc.w statements: Pressing return will automatically add dc.w in next line (plus tabs)
Showing of used registers in selection
Actions:
- Replace constant by symbol (with suggestion)
- Converting of hex values into decimal values and vice versa
- convert new style to old style
- convert old style to new style
- simplify explicit mnemonics (e.g. movea)
Marker:
- go to source register setter
- go to target register setter
- Can we add "find declaration to go for registers"
@@ -649,22 +649,12 @@ public class M68kParser implements PsiParser, LightPsiParser {
} }
/* ********************************************************** */ /* ********************************************************** */
// AsmInstruction | MacroCall // AsmInstruction | MacroCall | PreprocessorDirective
static boolean Instruction(PsiBuilder b, int l) { static boolean Instruction(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "Instruction")) return false; if (!recursion_guard_(b, l, "Instruction")) return false;
if (!nextTokenIs(b, "", MACRO_INVOCATION, MNEMONIC)) return false;
boolean r; boolean r;
r = AsmInstruction(b, l + 1); r = AsmInstruction(b, l + 1);
if (!r) r = MacroCall(b, l + 1); if (!r) r = MacroCall(b, l + 1);
return r;
}
/* ********************************************************** */
// Instruction|PreprocessorDirective
static boolean InstructionOnly(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "InstructionOnly")) return false;
boolean r;
r = Instruction(b, l + 1);
if (!r) r = PreprocessorDirective(b, l + 1); if (!r) r = PreprocessorDirective(b, l + 1);
return r; return r;
} }
@@ -681,13 +671,13 @@ public class M68kParser implements PsiParser, LightPsiParser {
} }
/* ********************************************************** */ /* ********************************************************** */
// LabelWithInstruction | LabelOnly | InstructionOnly // LabelWithInstruction | LabelOnly | Instruction
static boolean LabelInsts(PsiBuilder b, int l) { static boolean LabelInsts(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "LabelInsts")) return false; if (!recursion_guard_(b, l, "LabelInsts")) return false;
boolean r; boolean r;
r = LabelWithInstruction(b, l + 1); r = LabelWithInstruction(b, l + 1);
if (!r) r = LabelOnly(b, l + 1); if (!r) r = LabelOnly(b, l + 1);
if (!r) r = InstructionOnly(b, l + 1); if (!r) r = Instruction(b, l + 1);
return r; return r;
} }
@@ -698,27 +688,18 @@ public class M68kParser implements PsiParser, LightPsiParser {
} }
/* ********************************************************** */ /* ********************************************************** */
// Label (Instruction|PreprocessorDirective) // Label Instruction
static boolean LabelWithInstruction(PsiBuilder b, int l) { static boolean LabelWithInstruction(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "LabelWithInstruction")) return false; if (!recursion_guard_(b, l, "LabelWithInstruction")) return false;
if (!nextTokenIs(b, "", GLOBAL_LABEL_DEF, LOCAL_LABEL_DEF)) return false; if (!nextTokenIs(b, "", GLOBAL_LABEL_DEF, LOCAL_LABEL_DEF)) return false;
boolean r; boolean r;
Marker m = enter_section_(b); Marker m = enter_section_(b);
r = Label(b, l + 1); r = Label(b, l + 1);
r = r && LabelWithInstruction_1(b, l + 1); r = r && Instruction(b, l + 1);
exit_section_(b, m, null, r); exit_section_(b, m, null, r);
return r; return r;
} }
// Instruction|PreprocessorDirective
private static boolean LabelWithInstruction_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "LabelWithInstruction_1")) return false;
boolean r;
r = Instruction(b, l + 1);
if (!r) r = PreprocessorDirective(b, l + 1);
return r;
}
/* ********************************************************** */ /* ********************************************************** */
// LOCAL_LABEL_DEF COLON? // LOCAL_LABEL_DEF COLON?
public static boolean LocalLabel(PsiBuilder b, int l) { public static boolean LocalLabel(PsiBuilder b, int l) {
@@ -10,7 +10,7 @@ class M68kFileElementType private constructor() : ILightStubFileElementType<PsiF
@JvmField @JvmField
val INSTANCE = M68kFileElementType() val INSTANCE = M68kFileElementType()
const val STUB_VERSION = 7 const val STUB_VERSION = 8
const val STUB_EXTERNAL_ID_PREFIX = "MC68000." const val STUB_EXTERNAL_ID_PREFIX = "MC68000."
const val EXTERNAL_ID = STUB_EXTERNAL_ID_PREFIX + "FILE" const val EXTERNAL_ID = STUB_EXTERNAL_ID_PREFIX + "FILE"
} }
@@ -19,7 +19,9 @@ object AssemblerDirectives {
"align", "even", "odd", "cnop", "long", "dphrase", "phrase", "qphrase", "align", "even", "odd", "cnop", "long", "dphrase", "phrase", "qphrase",
"cargs", "comm", "comment", "cargs", "comm", "comment",
"rsset", "clrfo", "clrso", "setfo", "setso" "rsset", "clrfo", "clrso", "setfo", "setso",
"rsreset", "rs.b", "rs.w", "rs.l"
) )
val plainDirectives: Set<String> = setOf( val plainDirectives: Set<String> = setOf(
@@ -88,13 +88,13 @@ fun getCcInfo(cc: Int): Map<String, Pair<String, String>> {
CC_V_OR -> "*" to CC_OR_STR CC_V_OR -> "*" to CC_OR_STR
else -> "*" to "$CC_RES_STR (usually for overflows)" else -> "*" to "$CC_RES_STR (usually for overflows)"
} }
xnzvcMap["C"] = when (cc and CC_V_TST) { xnzvcMap["C"] = when (cc and CC_C_TST) {
0 -> "-" to CC_NOT_AFFECTED_STR 0 -> "-" to CC_NOT_AFFECTED_STR
CC_V_SET -> "1" to CC_ALWAYS_SET_STR CC_C_SET -> "1" to CC_ALWAYS_SET_STR
CC_V_CLEAR -> "0" to CC_ALWAYS_CLEAR_STR CC_C_CLEAR -> "0" to CC_ALWAYS_CLEAR_STR
CC_V_UNDEF -> "U" to CC_UNDEFINED_STR CC_C_UNDEF -> "U" to CC_UNDEFINED_STR
CC_V_AND -> "*" to CC_AND_STR CC_C_AND -> "*" to CC_AND_STR
CC_V_OR -> "*" to CC_OR_STR CC_C_OR -> "*" to CC_OR_STR
else -> "*" to "$CC_RES_STR (usually carry/borrow)" else -> "*" to "$CC_RES_STR (usually carry/borrow)"
} }
@@ -1085,7 +1085,7 @@ object M68kIsa {
IsaData( IsaData(
"stop", "Stop", "stop", "Stop",
machine = ALL_MACHINES, isPrivileged = true, machine = ALL_MACHINES, isPrivileged = true,
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), null, OP_UNSIZED)) modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), null, OP_UNSIZED, affectedCc = cc("*****")))
), ),
*autoExpandForOtherCpus( *autoExpandForOtherCpus(
@@ -1208,10 +1208,10 @@ object M68kIsa {
private val mnemonicLookupMap = isaData.asSequence() private val mnemonicLookupMap = isaData.asSequence()
.flatMap { .flatMap {
(if (it.conditionCodes.isEmpty()) it.altMnemonics.plus(it.mnemonic) else it.altMnemonics.plus(it.conditionCodes (if (it.conditionCodes.isEmpty()) it.altMnemonics.plus(it.mnemonic) else it.altMnemonics.plus(
.map { cc -> it.conditionCodes
it.mnemonic.replace("CC", cc) .map { cc -> it.mnemonic.replace("CC", cc) }))
})).map { mnemonic -> mnemonic to it } .map { mnemonic -> mnemonic to it }
} }
.groupBy({ it.first }) { it.second } .groupBy({ it.first }) { it.second }
@@ -1223,24 +1223,16 @@ object M68kIsa {
fun findMatchingOpMode(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<IsaData> { fun findMatchingOpMode(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<IsaData> {
return candidates.filter { return candidates.filter {
it.modes.any { am -> it.modes.any { am -> isAddressModeMatching(am, op1, op2, specialReg) && ((opSize == null) || ((opSize and am.size) == opSize)) }
isAddressModeMatching(am, op1, op2, specialReg)
&& ((opSize == null) || ((opSize and am.size) == opSize))
}
} }
} }
fun findMatchingAddressMode(modes: List<AllowedAdrMode>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<AllowedAdrMode> { fun findMatchingAddressMode(modes: List<AllowedAdrMode>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<AllowedAdrMode> {
return modes.filter { am -> return modes.filter { am -> isAddressModeMatching(am, op1, op2, specialReg) && ((opSize == null) || ((opSize and am.size) == opSize)) }
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> { fun findMatchingOpModeIgnoringSize(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, specialReg: String?): List<IsaData> {
return candidates.filter { return candidates.filter { it.modes.any { am -> isAddressModeMatching(am, op1, op2, specialReg) } }
it.modes.any { am -> isAddressModeMatching(am, op1, op2, specialReg) }
}
} }
fun findSupportedOpSizes(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, specialReg: String?): Int { fun findSupportedOpSizes(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, specialReg: String?): Int {
@@ -1253,6 +1245,6 @@ object M68kIsa {
private fun isAddressModeMatching(am: AllowedAdrMode, op1: AddressMode?, op2: AddressMode?, specialReg: String?) = private fun isAddressModeMatching(am: AllowedAdrMode, op1: AddressMode?, op2: AddressMode?, specialReg: String?) =
((((op1 == null) && (am.op1 == null)) || am.op1?.contains(op1) ?: false) ((((op1 == null) && (am.op1 == null)) || am.op1?.contains(op1) ?: false)
&& (((op2 == null) && (am.op2 == null)) || am.op2?.contains(op2) ?: false) && (((op2 == null) && (am.op2 == null)) || am.op2?.contains(op2) ?: false)
&& ((specialReg == null) || (am.specialReg?.split('|')?.any { it.equals(specialReg, true) } ?: false))) && ((specialReg == null) || (am.specialReg?.split('|')?.any { it.equals(specialReg, true) } ?: false)))
} }
@@ -0,0 +1,23 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Alignment
import com.intellij.formatting.Block
import com.intellij.formatting.Spacing
import com.intellij.formatting.Wrap
import com.intellij.lang.ASTNode
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
class M68kAsmAssignmentBlock(
node: ASTNode, wrap: Wrap?, alignment: Alignment?,
private val codeStyleSettings: CodeStyleSettings
) : AbstractBlock(node, wrap, alignment) {
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
return Spacing.getReadOnlySpacing()
}
override fun isLeaf() = true
override fun buildChildren() = emptyList<Block>()
}
@@ -0,0 +1,62 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Alignment
import com.intellij.formatting.Block
import com.intellij.formatting.Spacing
import com.intellij.formatting.Wrap
import com.intellij.lang.ASTNode
import com.intellij.psi.TokenType
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
import de.platon42.intellij.plugins.m68k.psi.M68kTypes
class M68kAsmBlock(
node: ASTNode, wrap: Wrap?, alignment: Alignment?,
val column: Int,
private val codeStyleSettings: CodeStyleSettings
) : AbstractBlock(node, wrap, alignment) {
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
if (child1 is M68kAsmBlock && child2 is M68kAsmBlock) {
val columnDiff = child2.column - child1.column
if (columnDiff > 0) {
var minSpaces = columnDiff * codeStyleSettings.indentOptions.INDENT_SIZE - child1.node.textLength
while (minSpaces < 1) {
minSpaces += codeStyleSettings.indentOptions.INDENT_SIZE
}
return Spacing.createSpacing(minSpaces, minSpaces, 0, false, 0)
}
}
return null
}
override fun isLeaf() = (node.firstChildNode == null)
override fun buildChildren(): List<Block> {
val subBlocks = ArrayList<Block>()
if (myNode.elementType == M68kTypes.ASM_OP) {
return subBlocks
}
var child = myNode.firstChildNode
var newColumn = column
while (child != null) {
if (child.elementType != TokenType.WHITE_SPACE) {
subBlocks.add(
M68kAsmBlock(
child,
null,
null,
newColumn,
codeStyleSettings
)
)
if (child.elementType == M68kTypes.ASM_OP) {
newColumn += 2
}
}
child = child.treeNext
}
return subBlocks
}
}
@@ -0,0 +1,7 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CustomCodeStyleSettings
class M68kAsmCodeStyleSettings(container: CodeStyleSettings) : CustomCodeStyleSettings("MC68000AsmCodeStyleSettings", container) {
}
@@ -0,0 +1,31 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.application.options.CodeStyleAbstractConfigurable
import com.intellij.application.options.CodeStyleAbstractPanel
import com.intellij.application.options.TabbedLanguageCodeStylePanel
import com.intellij.psi.codeStyle.CodeStyleConfigurable
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CodeStyleSettingsProvider
import com.intellij.psi.codeStyle.CustomCodeStyleSettings
import de.platon42.intellij.plugins.m68k.MC68000Language
class M68kAsmCodeStyleSettingsProvider : CodeStyleSettingsProvider() {
override fun createCustomSettings(settings: CodeStyleSettings): CustomCodeStyleSettings {
return M68kAsmCodeStyleSettings(settings)
}
override fun createConfigurable(settings: CodeStyleSettings, modelSettings: CodeStyleSettings): CodeStyleConfigurable {
return object : CodeStyleAbstractConfigurable(settings, modelSettings, configurableDisplayName) {
override fun createPanel(settings: CodeStyleSettings): CodeStyleAbstractPanel {
return M68kAsmCodeStyleMainPanel(currentSettings, settings)
}
}
}
override fun getConfigurableDisplayName() = "M68k"
private class M68kAsmCodeStyleMainPanel(currentSettings: CodeStyleSettings, settings: CodeStyleSettings) :
TabbedLanguageCodeStylePanel(MC68000Language.INSTANCE, currentSettings, settings)
}
@@ -0,0 +1,21 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Block
import com.intellij.formatting.Spacing
import com.intellij.lang.ASTNode
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
class M68kAsmCommentBlock(
node: ASTNode,
private val codeStyleSettings: CodeStyleSettings
) : AbstractBlock(node, null, null) {
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
return Spacing.getReadOnlySpacing()
}
override fun isLeaf() = true
override fun buildChildren() = emptyList<Block>()
}
@@ -0,0 +1,14 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Block
import com.intellij.lang.ASTNode
import com.intellij.psi.formatter.common.AbstractBlock
class M68kAsmEolBlock(node: ASTNode) : AbstractBlock(node, null, null) {
override fun getSpacing(child1: Block?, child2: Block) = null
override fun isLeaf() = true
override fun buildChildren() = emptyList<Block>()
}
@@ -0,0 +1,19 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.FormattingContext
import com.intellij.formatting.FormattingModel
import com.intellij.formatting.FormattingModelBuilder
import com.intellij.formatting.FormattingModelProvider
class M68kAsmFormattingModelBuilder : FormattingModelBuilder {
override fun createModel(formattingContext: FormattingContext): FormattingModel {
val codeStyleSettings = formattingContext.codeStyleSettings
return FormattingModelProvider.createFormattingModelForPsiFile(
formattingContext.containingFile,
M68kAsmRootBlock(formattingContext.node, codeStyleSettings),
codeStyleSettings
)
}
}
@@ -0,0 +1,23 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Alignment
import com.intellij.formatting.Block
import com.intellij.formatting.Spacing
import com.intellij.formatting.Wrap
import com.intellij.lang.ASTNode
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
class M68kAsmLabelBlock(
node: ASTNode, wrap: Wrap?, alignment: Alignment?,
private val codeStyleSettings: CodeStyleSettings
) : AbstractBlock(node, wrap, alignment) {
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
return Spacing.getReadOnlySpacing()
}
override fun isLeaf() = true
override fun buildChildren() = emptyList<Block>()
}
@@ -0,0 +1,21 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Block
import com.intellij.formatting.Spacing
import com.intellij.lang.ASTNode
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
class M68kAsmMacroDefBlock(
node: ASTNode,
private val codeStyleSettings: CodeStyleSettings
) : AbstractBlock(node, null, null) {
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
return Spacing.getReadOnlySpacing()
}
override fun isLeaf() = true
override fun buildChildren() = emptyList<Block>()
}
@@ -0,0 +1,70 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Block
import com.intellij.formatting.Indent
import com.intellij.formatting.Spacing
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiComment
import com.intellij.psi.TokenType
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
import de.platon42.intellij.plugins.m68k.psi.M68kMacroDefinition
import de.platon42.intellij.plugins.m68k.psi.M68kStatement
import de.platon42.intellij.plugins.m68k.psi.M68kTypes
class M68kAsmRootBlock(node: ASTNode, private val codeStyleSettings: CodeStyleSettings) : AbstractBlock(node, null, null) {
override fun getIndent(): Indent? {
return Indent.getAbsoluteNoneIndent()
}
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
if (child2 is M68kAsmEolBlock) {
return Spacing.createSpacing(0, 0, 0, false, 0)
//return null
}
if (child2 is M68kAsmMacroDefBlock) {
return child2.getSpacing(child1, child2)
}
if (child2 is M68kAsmCommentBlock) {
if (child1 is M68kAsmStatementBlock) {
val indentSize = codeStyleSettings.indentOptions.INDENT_SIZE
val oddIdent = indentSize - (child1.node.textLength % indentSize)
return Spacing.createSpacing(oddIdent, Integer.MAX_VALUE, 0, false, 0)
} else {
return Spacing.getReadOnlySpacing()
}
}
if (child2.subBlocks.isNotEmpty()) {
return child2.getSpacing(child1, child2.subBlocks.first())
}
return null
}
override fun isLeaf(): Boolean {
return false
}
override fun buildChildren(): List<Block> {
val subBlocks = ArrayList<Block>()
var child = myNode.firstChildNode
while (child != null) {
if (child.elementType == M68kTypes.EOL) {
subBlocks.add(M68kAsmEolBlock(child))
} else if (child.elementType != TokenType.WHITE_SPACE) {
if (child.psi is M68kStatement) {
subBlocks.add(M68kAsmStatementBlock(child, codeStyleSettings))
} else if (child.psi is M68kMacroDefinition) {
subBlocks.add(M68kAsmMacroDefBlock(child, codeStyleSettings))
} else if (child.psi is PsiComment) {
subBlocks.add(M68kAsmCommentBlock(child, codeStyleSettings))
} else {
subBlocks.add(M68kAsmBlock(child, null, null, 0, codeStyleSettings))
}
}
child = child.treeNext
}
return subBlocks
}
}
@@ -0,0 +1,74 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.formatting.Block
import com.intellij.formatting.Indent
import com.intellij.formatting.Spacing
import com.intellij.lang.ASTNode
import com.intellij.psi.TokenType
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
import de.platon42.intellij.plugins.m68k.psi.*
class M68kAsmStatementBlock(node: ASTNode, private val codeStyleSettings: CodeStyleSettings) :
AbstractBlock(node, null, null) {
override fun getIndent(): Indent? {
val statement = myNode.psi as M68kStatement
if (((statement.asmInstruction != null) || (statement.asmInstruction != null) || (statement.preprocessorDirective != null)
|| (statement.macroCall != null))
&& ((statement.localLabel == null) && (statement.globalLabel == null))
) {
return null
} else {
return Indent.getAbsoluteNoneIndent()
}
}
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
if (child2 is M68kAsmBlock) {
val indentSize = codeStyleSettings.indentOptions.INDENT_SIZE * child2.column
if (child1 is M68kAsmEolBlock || child1 is M68kAsmCommentBlock) {
return Spacing.createSpacing(indentSize, indentSize, 0, true, 0)
}
if (child1 is M68kAsmLabelBlock) {
val spacesLeft = indentSize - child1.node.textLength
if (spacesLeft <= 0) {
return Spacing.createSpacing(0, 0, 1, true, 0)
} else {
return Spacing.createSpacing(spacesLeft, spacesLeft, 0, true, 0)
}
}
return Spacing.createSpacing(indentSize, indentSize, 0, true, 0)
}
if (child2 is M68kAsmLabelBlock || child2 is M68kAsmAssignmentBlock) {
return Spacing.createSpacing(0, 0, 0, true, 0)
}
return null
}
override fun isLeaf() = false
override fun buildChildren(): List<Block> {
val subBlocks = ArrayList<Block>()
var child = myNode.firstChildNode
while (child != null) {
if (child.elementType != TokenType.WHITE_SPACE) {
val element = child.psi
when (element) {
is M68kAssignment -> subBlocks.add(M68kAsmAssignmentBlock(child, null, null, codeStyleSettings))
is M68kAsmInstruction, is M68kPreprocessorDirective, is M68kMacroCall ->
subBlocks.add(M68kAsmBlock(child, null, null, 2, codeStyleSettings))
is M68kGlobalLabel, is M68kLocalLabel ->
subBlocks.add(M68kAsmLabelBlock(child, null, null, codeStyleSettings))
else -> subBlocks.add(M68kAsmBlock(child, null, null, 1, codeStyleSettings))
}
}
child = child.treeNext
}
return subBlocks
}
}
@@ -0,0 +1,41 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider
import de.platon42.intellij.plugins.m68k.MC68000Language
class M68kLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvider() {
override fun getLanguage() = MC68000Language.INSTANCE
override fun getCodeSample(settingsType: SettingsType) = """; This is an example assembly language program
PIC_HEIGHT = 256
include "../includes/hardware/custom.i"
BLTHOGON MACRO ; macro definition
move.w #DMAF_SETCLR|DMAF_BLITHOG,dmacon(a5) ; hog!
ENDM
demo_init ; global label
tst.w d1
beq.s .skip
PUSHM d0-d7/a0-a6 ; this is a macro call
lea hello(pc),a1
lea pd_ModViewTable(a4,d1.w),a0
moveq.l #0,d0
move.w #PIC_HEIGHT-1,d7
.loop move.l d0,(a0)+ ; local label
dbra d7,.loop
POPM
.skip rts
irq: move.l a0,-(sp)
move usp,a0
move.l a0,${'$'}400.w
move.l (sp)+,a0
rte
hello: dc.b 'Hello World!',10,0
even
dc.w *-hello ; length of string
"""
}
@@ -0,0 +1,69 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import de.platon42.intellij.plugins.m68k.asm.AddressMode
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingInstructions
import de.platon42.intellij.plugins.m68k.asm.OP_SIZE_W
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction
class M68kAddressRegisterWordAccessInspection : AbstractBaseM68kLocalInspectionTool() {
companion object {
private const val DISPLAY_NAME = "Word access to address register"
private const val ADDR_REG_WORD_ACCESS_MSG_TEMPLATE = "Suspicious %s %s address register"
}
override fun getDisplayName() = DISPLAY_NAME
override fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
val asmOp = asmInstruction.asmOp
if (asmOp.opSize != OP_SIZE_W) return emptyArray()
val isaDataCandidates = findMatchingInstructions(asmOp.mnemonic)
if (isaDataCandidates.isEmpty()) return emptyArray()
val (isaData, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return emptyArray()
return when (isaData.mnemonic) {
"movea" -> arrayOf(
manager.createProblemDescriptor(
asmInstruction,
asmInstruction,
ADDR_REG_WORD_ACCESS_MSG_TEMPLATE.format(isaData.mnemonic, "word write to"),
ProblemHighlightType.WARNING,
isOnTheFly
)
)
"cmpa" -> arrayOf(
manager.createProblemDescriptor(
asmInstruction,
asmInstruction,
ADDR_REG_WORD_ACCESS_MSG_TEMPLATE.format(isaData.mnemonic, "comparing with word-sized"),
ProblemHighlightType.WARNING,
isOnTheFly
)
)
"tst" -> if (M68kAddressModeUtil.getAddressModeForType(asmInstruction.addressingModeList.getOrNull(0)) == AddressMode.ADDRESS_REGISTER_DIRECT) {
arrayOf(
manager.createProblemDescriptor(
asmInstruction,
asmInstruction,
ADDR_REG_WORD_ACCESS_MSG_TEMPLATE.format(isaData.mnemonic, "with word-sized"),
ProblemHighlightType.WARNING,
isOnTheFly
)
)
} else {
emptyArray()
}
else -> emptyArray()
}
}
}
@@ -136,11 +136,10 @@ SymbolDefinition ::= SYMBOLDEF {
Assignment ::= SymbolDefinition COLON? (OP_ASSIGN|EQU) expr Assignment ::= SymbolDefinition COLON? (OP_ASSIGN|EQU) expr
private LabelInsts ::= LabelWithInstruction | LabelOnly | InstructionOnly private LabelInsts ::= LabelWithInstruction | LabelOnly | Instruction
private LabelOnly ::= Label private LabelOnly ::= Label
private LabelWithInstruction ::= Label (Instruction|PreprocessorDirective) private LabelWithInstruction ::= Label Instruction
private InstructionOnly ::= (Instruction|PreprocessorDirective)
LocalLabel ::= LOCAL_LABEL_DEF COLON? { LocalLabel ::= LOCAL_LABEL_DEF COLON? {
name = "local label" name = "local label"
@@ -199,7 +198,7 @@ MacroCall ::= MACRO_INVOCATION PlainOperands? {
} }
AsmInstruction ::= AsmOp AsmOperands? AsmInstruction ::= AsmOp AsmOperands?
private Instruction ::= AsmInstruction | MacroCall private Instruction ::= AsmInstruction | MacroCall | PreprocessorDirective
//external Instruction ::= parseMacroCallOrAsmInstruction //external Instruction ::= parseMacroCallOrAsmInstruction
private AsmOperands ::= AddressingMode (SEPARATOR AddressingMode)? private AsmOperands ::= AddressingMode (SEPARATOR AddressingMode)?
@@ -12,22 +12,26 @@ import de.platon42.intellij.plugins.m68k.psi.utils.M68kLookupUtil
class M68kGlobalLabelSymbolCompletionContributor : CompletionContributor() { class M68kGlobalLabelSymbolCompletionContributor : CompletionContributor() {
companion object { companion object {
val REGS = listOf(
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "sp",
"pc"
)
val REGSET = REGS.toSet()
val REGISTER_SUGGESTIONS: List<LookupElement> = val REGISTER_SUGGESTIONS: List<LookupElement> =
listOf( REGS.map { PrioritizedLookupElement.withPriority(LookupElementBuilder.create(it).withIcon(M68kIcons.REGISTER).withBoldness(true), 2.0) }
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "sp",
"pc"
)
.map { PrioritizedLookupElement.withPriority(LookupElementBuilder.create(it).withIcon(M68kIcons.REGISTER).withBoldness(true), 2.0) }
} }
init { init {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(M68kTypes.SYMBOL), object : CompletionProvider<CompletionParameters>() { extend(CompletionType.BASIC, PlatformPatterns.psiElement(M68kTypes.SYMBOL), object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, resultSet: CompletionResultSet) { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, resultSet: CompletionResultSet) {
resultSet.addAllElements(REGISTER_SUGGESTIONS) resultSet.addAllElements(REGISTER_SUGGESTIONS)
resultSet.addAllElements(M68kLookupUtil.findAllGlobalLabels(parameters.originalFile.project).map(LookupElementBuilder::createWithIcon)) val prefix = resultSet.prefixMatcher.prefix.lowercase()
resultSet.addAllElements(M68kLookupUtil.findAllSymbolDefinitions(parameters.originalFile.project).map(LookupElementBuilder::createWithIcon)) if (!(prefix == "a" || prefix == "d" || REGSET.contains(prefix))) {
resultSet.addAllElements(M68kLookupUtil.findAllGlobalLabels(parameters.originalFile.project).map(LookupElementBuilder::createWithIcon))
resultSet.addAllElements(M68kLookupUtil.findAllSymbolDefinitions(parameters.originalFile.project).map(LookupElementBuilder::createWithIcon))
}
} }
}) })
} }
} }
+12 -6
View File
@@ -1,14 +1,14 @@
<idea-plugin> <idea-plugin>
<id>de.platon42.m68k</id> <id>de.platon42.m68k</id>
<name>MC68000 Assembly Language Support</name> <name>MC68000 Assembly Language Support</name>
<vendor email="chrisly@platon42.de" url="https://github.com/chrisly42/mc68000-asm-plugin">Chris 'platon42' Hodges <vendor email="chrisly@platon42.de" url="https://git.platon42.de/chrisly42/mc68000-asm-plugin">Chris 'platon42' Hodges
</vendor> </vendor>
<description><![CDATA[ <description><![CDATA[
MC68000 Assembly Language Plugin adds language support for Motorola 68000 (M68k) assembly language files (asm). MC68000 Assembly Language Plugin adds language support for Motorola 68000 (M68k) assembly language files (asm).
It provides syntax highlighting and refactoring possibilities among other things. It provides syntax highlighting and refactoring possibilities among other things.
<p> <p>
<a href="https://github.com/chrisly42/mc68000-asm-plugin/blob/main/README.md">Full documentation here...</a> <a href="https://git.platon42.de/chrisly42/mc68000-asm-plugin/blob/main/README.md">Full documentation here...</a>
]]></description> ]]></description>
<idea-version since-build="203.5981.166"/> <idea-version since-build="203.5981.166"/>
@@ -21,9 +21,12 @@
implementationClass="de.platon42.intellij.plugins.m68k.parser.M68kParserDefinition"/> implementationClass="de.platon42.intellij.plugins.m68k.parser.M68kParserDefinition"/>
<lang.syntaxHighlighterFactory language="MC68000" <lang.syntaxHighlighterFactory language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.syntax.M68kSyntaxHighlighterFactory"/> implementationClass="de.platon42.intellij.plugins.m68k.syntax.M68kSyntaxHighlighterFactory"/>
<lang.foldingBuilder <lang.foldingBuilder language="MC68000"
language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.folding.M68kFoldingBuilder"/>
implementationClass="de.platon42.intellij.plugins.m68k.folding.M68kFoldingBuilder"/> <!--lang.formatter language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.formatter.M68kAsmFormattingModelBuilder"/-->
<codeStyleSettingsProvider implementation="de.platon42.intellij.plugins.m68k.formatter.M68kAsmCodeStyleSettingsProvider"/>
<langCodeStyleSettingsProvider implementation="de.platon42.intellij.plugins.m68k.formatter.M68kLanguageCodeStyleSettingsProvider"/>
<navbar implementation="de.platon42.intellij.plugins.m68k.navigation.M68kStructureAwareNavbar"/> <navbar implementation="de.platon42.intellij.plugins.m68k.navigation.M68kStructureAwareNavbar"/>
<highlightVisitor implementation="de.platon42.intellij.plugins.m68k.syntax.M68kRainbowVisitor"/> <highlightVisitor implementation="de.platon42.intellij.plugins.m68k.syntax.M68kRainbowVisitor"/>
<colorSettingsPage implementation="de.platon42.intellij.plugins.m68k.syntax.M68kColorSettingsPage"/> <colorSettingsPage implementation="de.platon42.intellij.plugins.m68k.syntax.M68kColorSettingsPage"/>
@@ -74,6 +77,9 @@
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kUnresolvedReferenceInspection" <localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kUnresolvedReferenceInspection"
displayName="Unresolved label/symbol/macro reference" groupName="M68k" displayName="Unresolved label/symbol/macro reference" groupName="M68k"
enabledByDefault="true" level="WARNING"/> enabledByDefault="true" level="WARNING"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kAddressRegisterWordAccessInspection"
displayName="Suspicious word access to address register" groupName="M68k"
enabledByDefault="true" level="WARNING"/>
<projectService serviceImplementation="de.platon42.intellij.plugins.m68k.settings.M68kProjectSettings"/> <projectService serviceImplementation="de.platon42.intellij.plugins.m68k.settings.M68kProjectSettings"/>
@@ -84,4 +90,4 @@
<actions> <actions>
</actions> </actions>
</idea-plugin> </idea-plugin>
@@ -0,0 +1,52 @@
package de.platon42.intellij.plugins.m68k.formatter
import com.intellij.application.options.CodeStyle
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import com.intellij.util.containers.ContainerUtil
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataPath
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@TestDataPath("src/test/resources/formatting")
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kAsmFormatterTest {
@Test
@Disabled
internal fun check_if_formatting_works_as_expected(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByFile("basic_before.asm")
val settings = CodeStyle.getLanguageSettings(myFixture.file)
settings.indentOptions?.INDENT_SIZE = 6
settings.indentOptions?.CONTINUATION_INDENT_SIZE = 9
WriteCommandAction.runWriteCommandAction(myFixture.project)
{
CodeStyleManager.getInstance(myFixture.project).reformatText(
myFixture.file,
ContainerUtil.newArrayList(myFixture.file.textRange)
)
}
myFixture.checkResultByFile("basic_after.asm")
}
@Test
internal fun check_formatting_for_mnemonics(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByFile("mnemonics_before.asm")
val settings = CodeStyle.getLanguageSettings(myFixture.file)
settings.indentOptions?.INDENT_SIZE = 8
WriteCommandAction.runWriteCommandAction(myFixture.project)
{
CodeStyleManager.getInstance(myFixture.project).reformatText(
myFixture.file,
ContainerUtil.newArrayList(myFixture.file.textRange)
)
}
myFixture.checkResultByFile("mnemonics_after.asm")
}
}
@@ -0,0 +1,29 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import org.junit.jupiter.api.Test
internal class M68kAddressRegisterWordAccessInspectionTest : AbstractInspectionTest() {
@Test
internal fun shows_warning_on_movea_word_sized(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kAddressRegisterWordAccessInspection::class.java)
myFixture.configureByText("wordaccess.asm", " move.w d0,a0")
assertHighlightings(myFixture, 1, "Suspicious movea word write to address register")
}
@Test
internal fun shows_warning_on_cmpa_word_sized(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kAddressRegisterWordAccessInspection::class.java)
myFixture.configureByText("wordaccess.asm", " cmp.w d0,a0")
assertHighlightings(myFixture, 1, "Suspicious cmpa comparing with word-sized address register")
}
@Test
internal fun shows_warning_on_tst_with_word_sized_address_register(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kAddressRegisterWordAccessInspection::class.java)
myFixture.configureByText("wordaccess.asm", " tst.w a0")
assertHighlightings(myFixture, 1, "Suspicious tst with word-sized address register")
}
}
@@ -0,0 +1,51 @@
; this is a test
FOO = 1
; this is the main demo routine
demo_main:
moveq.l #0,d0 ; end of line comment
rts ; some more comments
; data area starts here
.data dc.w 10,0
even
this_is_a_very_long_label: moveq.l #0,d0 ; and an end of line comment, too
; this is another folding area
; could be anything.
intro_part1
; What is this doing here?
dc.w $47fe
illegal
rts
; this comment is two lines away
intro_part2:
moveq.l #0,d1
rts
; standard macros
BLTWAIT MACRO
.bw\@
btst #DMAB_BLTDONE-8,dmaconr(a5)
bne.s .bw\@
ENDM
MACRO BLTHOGOFF
moveq.l #0,d0
ENDM
********************** foobar
some_more_data:
dc.w $123
dc.w $345
dc.w $333
dc.w $222
CUBE_SIZE = 100
@@ -0,0 +1,51 @@
; this is a test
FOO = 1
; this is the main demo routine
demo_main:
moveq.l #0,d0 ; end of line comment
rts ; some more comments
; data area starts here
.data dc.w 10,0
even
this_is_a_very_long_label: moveq.l #0,d0 ; and an end of line comment, too
; this is another folding area
; could be anything.
intro_part1
; What is this doing here?
dc.w $47fe
illegal
rts
; this comment is two lines away
intro_part2:
moveq.l #0,d1
rts
; standard macros
BLTWAIT MACRO
.bw\@
btst #DMAB_BLTDONE-8,dmaconr(a5)
bne.s .bw\@
ENDM
MACRO BLTHOGOFF
moveq.l #0,d0
ENDM
********************** foobar
some_more_data:
dc.w $123
dc.w $345
dc.w $333
dc.w $222
CUBE_SIZE = 100
@@ -0,0 +1,32 @@
FOO = 1234
BAR = 1235
COUNTER SET COUNTER+1
FISH MACRO
.fish\@ rts
ENDM
MACRO SOUP
moveq.l #0,d0
ENDM
; with space
; standalone comment
moveq.l #10,d1 ; end of line comment
FISH
add.l #10,d1
SOUP
subq.b #2,d2 ; end of line comment
bra.s .foo
nop
.foo move.l d2,d1
rts
.verylonglabel:
stop #$2000
.narf
moveq.l #2,d0
globallabel:
moveq.l #0,d0
@@ -0,0 +1,31 @@
FOO = 1234
BAR = 1235
COUNTER SET COUNTER+1
FISH MACRO
.fish\@ rts
ENDM
MACRO SOUP
moveq.l #0,d0
ENDM
; with space
; standalone comment
moveq.l #10,d1; end of line comment
FISH
add.l #10,d1
SOUP
subq.b #2,d2 ; end of line comment
bra.s .foo
nop
.foo move.l d2,d1
rts
.verylonglabel: stop #$2000
.narf
moveq.l #2,d0
globallabel: moveq.l #0,d0