|
| 1 | +package org.ice1000.tt.editing.cubicaltt |
| 2 | + |
| 3 | +import com.intellij.lang.ASTNode |
| 4 | +import com.intellij.lang.folding.FoldingBuilderEx |
| 5 | +import com.intellij.lang.folding.FoldingDescriptor |
| 6 | +import com.intellij.openapi.editor.Document |
| 7 | +import com.intellij.openapi.project.DumbAware |
| 8 | +import com.intellij.openapi.util.TextRange |
| 9 | +import com.intellij.psi.PsiComment |
| 10 | +import com.intellij.psi.PsiElement |
| 11 | +import com.intellij.psi.TokenType |
| 12 | +import org.ice1000.tt.FOLDING_PLACEHOLDER |
| 13 | +import org.ice1000.tt.editing.collectFoldRegions |
| 14 | +import org.ice1000.tt.psi.* |
| 15 | +import org.ice1000.tt.psi.cubicaltt.* |
| 16 | + |
| 17 | +class CubicalTTFoldingBuilder : FoldingBuilderEx(), DumbAware { |
| 18 | + override fun getPlaceholderText(node: ASTNode) = when (node.elementType) { |
| 19 | + CubicalTTTokenType.BLOCK_COMMENT -> "{---}" |
| 20 | + CubicalTTTypes.SYSTEM -> "[$FOLDING_PLACEHOLDER]" |
| 21 | + else -> FOLDING_PLACEHOLDER |
| 22 | + } |
| 23 | + |
| 24 | + override fun isCollapsedByDefault(node: ASTNode) = false |
| 25 | + |
| 26 | + override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array<FoldingDescriptor> { |
| 27 | + if (root !is CubicalTTFileImpl) return emptyArray() |
| 28 | + return collectFoldRegions(root) { FoldingVisitor(it, document) } |
| 29 | + } |
| 30 | +} |
| 31 | + |
| 32 | +private class FoldingVisitor( |
| 33 | + private val descriptors: MutableList<FoldingDescriptor>, |
| 34 | + private val document: Document |
| 35 | +) : CubicalTTVisitor() { |
| 36 | + override fun visitData(o: CubicalTTData) { |
| 37 | + val labels = o.labelList.takeIf { it.size > 1 } ?: return |
| 38 | + descriptors += FoldingDescriptor(o, TextRange(labels.first().startOffset, labels.last().endOffset)) |
| 39 | + } |
| 40 | + |
| 41 | + override fun visitSystem(o: CubicalTTSystem) { |
| 42 | + val startLine = document.getLineNumber(o.startOffset) |
| 43 | + val endLine = document.getLineNumber(o.endOffset) |
| 44 | + if (startLine != endLine) descriptors.add(FoldingDescriptor(o, o.textRange)) |
| 45 | + } |
| 46 | + |
| 47 | + override fun visitComment(comment: PsiComment?) { |
| 48 | + if (comment?.elementType == CubicalTTTokenType.BLOCK_COMMENT) |
| 49 | + descriptors += FoldingDescriptor(comment, comment.textRange) |
| 50 | + } |
| 51 | + |
| 52 | + override fun visitSplitBody(o: CubicalTTSplitBody) = layout(o) |
| 53 | + override fun visitExpWhere(o: CubicalTTExpWhere) = layout(o) |
| 54 | + override fun visitMutual(o: CubicalTTMutual) = layout(o) |
| 55 | + override fun visitLetExp(o: CubicalTTLetExp) { |
| 56 | + val layoutStart = o.childrenWithLeaves.firstOrNull { it.elementType == CubicalTTTypes.LAYOUT_START } ?: return |
| 57 | + val layoutEnd = layoutStart.rightSiblings.firstOrNull { |
| 58 | + it.elementType == CubicalTTTypes.KW_IN |
| 59 | + }?.prevSiblingIgnoring<PsiElement>(TokenType.WHITE_SPACE) ?: return |
| 60 | + descriptors += FoldingDescriptor(o, TextRange(layoutStart.startOffset, layoutEnd.endOffset)) |
| 61 | + } |
| 62 | + |
| 63 | + private fun layout(o: PsiElement) { |
| 64 | + val layoutStart = o.childrenWithLeaves.firstOrNull { it.elementType == CubicalTTTypes.LAYOUT_START } ?: return |
| 65 | + val layoutEnd = layoutStart.rightSiblings.firstOrNull { it.elementType == CubicalTTTypes.LAYOUT_END } ?: o.lastChild |
| 66 | + ?: return |
| 67 | + descriptors += FoldingDescriptor(o, TextRange(layoutStart.startOffset, layoutEnd.endOffset)) |
| 68 | + } |
| 69 | + |
| 70 | + override fun visitModule(o: CubicalTTModule) { |
| 71 | + val imports = o.importList.takeIf { it.size > 1 } ?: return |
| 72 | + val startOffset = imports.first().moduleUsage?.startOffset ?: return |
| 73 | + descriptors += FoldingDescriptor(o, TextRange(startOffset, imports.last().endOffset)) |
| 74 | + } |
| 75 | +} |
0 commit comments