diff --git a/build.gradle b/build.gradle
index 4e43411..5ee55a4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -66,6 +66,7 @@ patchPluginXml {
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.
Full changelog available at Github project site.
""")
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/folding/M68kFoldingBuilder.kt b/src/main/java/de/platon42/intellij/plugins/m68k/folding/M68kFoldingBuilder.kt
new file mode 100644
index 0000000..a708622
--- /dev/null
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/folding/M68kFoldingBuilder.kt
@@ -0,0 +1,67 @@
+package de.platon42.intellij.plugins.m68k.folding
+
+import com.intellij.lang.ASTNode
+import com.intellij.lang.folding.FoldingBuilderEx
+import com.intellij.lang.folding.FoldingDescriptor
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.project.DumbAware
+import com.intellij.psi.PsiElement
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.refactoring.suggested.endOffset
+import com.intellij.refactoring.suggested.startOffset
+import de.platon42.intellij.plugins.m68k.psi.M68kFile
+import de.platon42.intellij.plugins.m68k.psi.M68kMacroDefinition
+import de.platon42.intellij.plugins.m68k.psi.M68kStatement
+import de.platon42.intellij.plugins.m68k.psi.utils.M68kPsiWalkUtil.getBeginningOfRelatedComment
+import de.platon42.intellij.plugins.m68k.psi.utils.M68kPsiWalkUtil.isSignificantLine
+
+class M68kFoldingBuilder : FoldingBuilderEx(), DumbAware {
+
+ override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array {
+ if (root !is M68kFile) return FoldingDescriptor.EMPTY
+
+ val descriptors = ArrayList()
+ var lineElement = root.firstChild
+ var foldingOpen = false
+ var foldingStart: PsiElement? = null
+ var foldingName: String? = null
+ var lastStatement = lineElement as? M68kStatement
+ while (lineElement != null) {
+ if (lineElement is M68kMacroDefinition) {
+ val fd = FoldingDescriptor(lineElement, lineElement.startOffset, lineElement.endOffset, null, "[[[ MACRO " + lineElement.name!! + " ]]]")
+ descriptors.add(fd)
+ }
+ if (lineElement is M68kStatement) {
+ val label = lineElement.globalLabel
+ if (label != null) {
+ foldingOpen = true
+ foldingStart = getBeginningOfRelatedComment(lineElement)
+ foldingName = "[[[ ${label.name} ]]]"
+ }
+ lastStatement = lineElement
+ }
+ lineElement = PsiTreeUtil.skipWhitespacesAndCommentsForward(lineElement)
+ if (foldingOpen) {
+ val stopIt = (lineElement == null) || isSignificantLine(lineElement)
+ if (stopIt) {
+ val fd = FoldingDescriptor(
+ foldingStart!!.parent!!,
+ foldingStart.startOffsetInParent, lastStatement!!.textRangeInParent.endOffset,
+ null, foldingName!!
+ )
+ descriptors.add(fd)
+ foldingOpen = false
+ }
+ }
+ }
+ return descriptors.toArray(FoldingDescriptor.EMPTY)
+ }
+
+ override fun getPlaceholderText(node: ASTNode): String? {
+ return "..."
+ }
+
+ override fun isCollapsedByDefault(node: ASTNode): Boolean {
+ return false
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/utils/M68kPsiWalkUtil.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/utils/M68kPsiWalkUtil.kt
index 2e48435..b645f7a 100644
--- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/utils/M68kPsiWalkUtil.kt
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/utils/M68kPsiWalkUtil.kt
@@ -2,6 +2,7 @@ package de.platon42.intellij.plugins.m68k.psi.utils
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartList
import com.intellij.util.containers.addIfNotNull
@@ -31,6 +32,31 @@ object M68kPsiWalkUtil {
return comments.reversed()
}
+ fun getBeginningOfRelatedComment(lineElement: PsiElement): PsiElement {
+ // go back to catch comments that are not more than one empty line away
+ var relStart = lineElement
+ var prevLine = relStart.prevSibling ?: return lineElement
+ var eolCount = 2
+ do {
+ if (prevLine is PsiComment) {
+ relStart = prevLine
+ eolCount = 1
+ } else if (prevLine is PsiWhiteSpace) {
+ if (--eolCount < 0) break
+ } else {
+ break
+ }
+ prevLine = prevLine.prevSibling ?: break
+ } while (true)
+ return relStart
+ }
+
+ fun isSignificantLine(element: PsiElement) = when (element) {
+ is M68kStatement -> (element.assignment != null) || (element.globalLabel != null)
+ is M68kMacroDefinition -> true
+ else -> false
+ }
+
fun findStatementForElement(psiElement: PsiElement): M68kStatement? {
if (psiElement is M68kStatement) return psiElement
if (psiElement.parent is M68kFile) {
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index f38a04f..29e6add 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -21,6 +21,9 @@
implementationClass="de.platon42.intellij.plugins.m68k.parser.M68kParserDefinition"/>
+
diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/folding/M68kFoldingBuilderTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/folding/M68kFoldingBuilderTest.kt
new file mode 100644
index 0000000..de90023
--- /dev/null
+++ b/src/test/java/de/platon42/intellij/plugins/m68k/folding/M68kFoldingBuilderTest.kt
@@ -0,0 +1,19 @@
+package de.platon42.intellij.plugins.m68k.folding
+
+import com.intellij.testFramework.fixtures.CodeInsightTestFixture
+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.Test
+import org.junit.jupiter.api.extension.ExtendWith
+
+@TestDataPath("src/test/resources/folding")
+@ExtendWith(LightCodeInsightExtension::class)
+internal class M68kFoldingBuilderTest {
+
+ @Test
+ internal fun check_documentation_for_a_symbol_definition(@MyFixture myFixture: CodeInsightTestFixture) {
+ myFixture.testFolding(myFixture.testDataPath + "/folding.asm")
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/folding/folding.asm b/src/test/resources/folding/folding.asm
new file mode 100644
index 0000000..60c0f9e
--- /dev/null
+++ b/src/test/resources/folding/folding.asm
@@ -0,0 +1,46 @@
+; this is a test
+
+FOO = 1
+; this is the main demo routine
+demo_main:
+ moveq.l #0,d0
+ rts
+
+; data area starts here
+.data dc.w 10,0
+ even
+
+; this is an unrelated comment
+
+; this is another folding area
+; could be anything.
+
+intro_part1
+ 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
+
+********************** foobar
+
+
+some_more_data:
+ dc.w $123
+ dc.w $345
+ dc.w $333
+ dc.w $222
+
+CUBE_SIZE = 100