Skip to content

Commit 603b082

Browse files
committed
feat(folding): implement Shire folding builder for code structure
1 parent 9344bb4 commit 603b082

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package cc.unitmesh.devti.language.folding
2+
3+
import cc.unitmesh.devti.command.dataprovider.BuiltinCommand
4+
import cc.unitmesh.devti.language.psi.DevInCaseBody
5+
import cc.unitmesh.devti.language.psi.DevInQueryStatement
6+
import cc.unitmesh.devti.language.psi.DevInTypes
7+
import cc.unitmesh.devti.language.psi.DevInUsed
8+
import cc.unitmesh.devti.language.psi.DevInVisitor
9+
import cc.unitmesh.devti.language.utils.lookupFile
10+
import com.intellij.lang.ASTNode
11+
import com.intellij.lang.folding.FoldingBuilderEx
12+
import com.intellij.lang.folding.FoldingDescriptor
13+
import com.intellij.openapi.editor.Document
14+
import com.intellij.openapi.project.Project
15+
import com.intellij.openapi.util.TextRange
16+
import com.intellij.openapi.util.text.StringUtil
17+
import com.intellij.openapi.vfs.VirtualFile
18+
import com.intellij.psi.PsiElement
19+
import com.intellij.psi.util.PsiUtilCore
20+
import com.intellij.psi.util.elementType
21+
22+
class ShireFoldingBuilder : FoldingBuilderEx() {
23+
override fun isCollapsedByDefault(node: ASTNode): Boolean = true
24+
override fun getPlaceholderText(node: ASTNode): String = node.text
25+
26+
override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array<FoldingDescriptor> {
27+
val descriptors = mutableListOf<FoldingDescriptor>()
28+
root.accept(DevInFoldingVisitor(descriptors))
29+
return descriptors.toTypedArray()
30+
}
31+
32+
override fun getPlaceholderText(node: ASTNode, range: TextRange): String {
33+
val elementType = PsiUtilCore.getElementType(node)
34+
when (elementType) {
35+
DevInTypes.USED -> {
36+
val commandId = (node.psi as DevInUsed).commandId
37+
when (commandId?.text) {
38+
BuiltinCommand.FILE.commandName -> {
39+
val prop = (node.psi as DevInUsed).commandProp?.text ?: return ""
40+
val virtualFile = file((node.psi as DevInUsed).project, prop)
41+
return "/${BuiltinCommand.FILE.commandName}:${virtualFile?.name}"
42+
}
43+
BuiltinCommand.STRUCTURE.commandName -> {
44+
val prop = (node.psi as DevInUsed).commandProp?.text ?: return ""
45+
val virtualFile = file((node.psi as DevInUsed).project, prop)
46+
return "/${BuiltinCommand.STRUCTURE.commandName}:${virtualFile?.name}"
47+
}
48+
}
49+
}
50+
}
51+
52+
val explicitName = foldedElementsPresentations[elementType]
53+
val elementText = StringUtil.shortenTextWithEllipsis(node.text, 30, 5)
54+
return explicitName?.let { "$it: $elementText" } ?: elementText
55+
}
56+
57+
private val foldedElementsPresentations = hashMapOf(
58+
DevInTypes.FRONT_MATTER_HEADER to "Hobbit Hole",
59+
DevInTypes.CODE to "Code Block",
60+
DevInTypes.QUERY_STATEMENT to "DevIn AstQL",
61+
DevInTypes.BLOCK_COMMENT to "/* ... */",
62+
)
63+
64+
override fun isCollapsedByDefault(foldingDescriptor: FoldingDescriptor): Boolean {
65+
return when (foldingDescriptor.element.elementType) {
66+
DevInTypes.FRONT_MATTER_HEADER -> true
67+
DevInTypes.CODE -> false
68+
DevInTypes.USED -> true
69+
else -> false
70+
}
71+
}
72+
}
73+
74+
fun file(project: Project, path: String): VirtualFile? {
75+
val filename = path.split("#")[0]
76+
val virtualFile = project.lookupFile(filename)
77+
return virtualFile
78+
}
79+
80+
81+
class DevInFoldingVisitor(private val descriptors: MutableList<FoldingDescriptor>) : DevInVisitor() {
82+
override fun visitElement(element: PsiElement) {
83+
when (element.elementType) {
84+
DevInTypes.FRONT_MATTER_HEADER -> {
85+
descriptors.add(FoldingDescriptor(element.node, element.textRange))
86+
}
87+
88+
DevInTypes.CODE -> {
89+
descriptors.add(FoldingDescriptor(element.node, element.textRange))
90+
}
91+
92+
DevInTypes.USED -> {
93+
val commandId = (element as? DevInUsed)?.commandId
94+
if (commandId?.text == BuiltinCommand.FILE.commandName) {
95+
descriptors.add(FoldingDescriptor(element.node, element.textRange))
96+
}
97+
}
98+
}
99+
100+
element.acceptChildren(this)
101+
}
102+
103+
override fun visitQueryStatement(o: DevInQueryStatement) {
104+
descriptors.add(FoldingDescriptor(o.node, o.textRange))
105+
}
106+
107+
override fun visitCaseBody(o: DevInCaseBody) {
108+
descriptors.add(FoldingDescriptor(o.node, o.textRange))
109+
}
110+
}

exts/devins-lang/src/main/resources/cc.unitmesh.devti.language.xml

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565

6666
<lang.commenter language="DevIn" implementationClass="cc.unitmesh.devti.language.commenter.DevInsCommenter"/>
6767

68+
<lang.foldingBuilder language="Shire"
69+
implementationClass="cc.unitmesh.devti.language.folding.ShireFoldingBuilder"/>
6870
<lang.documentationProvider language="DevIn"
6971
id="devinsDocumentationProvider"
7072
implementationClass="cc.unitmesh.devti.language.documentation.DevInsDocumentationProvider"/>

0 commit comments

Comments
 (0)