Skip to content

Instantly share code, notes, and snippets.

@dkandalov
Last active April 1, 2022 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dkandalov/82f37b0d3a6f8b3e4c6f1f2296a63e41 to your computer and use it in GitHub Desktop.
Save dkandalov/82f37b0d3a6f8b3e4c6f1f2296a63e41 to your computer and use it in GitHub Desktop.
Example of collapsable function/constructor arguments for Kotlin (see https://youtrack.jetbrains.com/issue/KTIJ-14710); use liveplugin to execute https://github.com/dkandalov/live-plugin
import com.intellij.lang.ASTNode
import com.intellij.lang.LanguageExtensionPoint
import com.intellij.lang.folding.CustomFoldingBuilder
import com.intellij.lang.folding.FoldingBuilder
import com.intellij.lang.folding.FoldingDescriptor
import com.intellij.lang.folding.LanguageFolding
import com.intellij.openapi.editor.Document
import com.intellij.openapi.extensions.DefaultPluginDescriptor
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.util.KeyedLazyInstance
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
// depends-on-plugin org.jetbrains.kotlin
val pluginDescriptor = DefaultPluginDescriptor(PluginId.getId("LivePlugin"), MyFolding::class.java.classLoader)
val extensionPoint : KeyedLazyInstance<FoldingBuilder> = LanguageExtensionPoint("kotlin", "Plugin\$MyFolding", pluginDescriptor)
LanguageFolding.EP_NAME.point.registerExtension(extensionPoint, pluginDisposable)
// show(LanguageFolding.INSTANCE.forLanguage(com.intellij.lang.Language.findLanguageByID("kotlin")!!))
// Based on org.jetbrains.kotlin.idea.KotlinFoldingBuilder
class MyFolding : CustomFoldingBuilder(), DumbAware {
override fun buildLanguageFoldRegions(descriptors: MutableList<FoldingDescriptor>, root: PsiElement, document: Document, quick: Boolean) {
if (root !is KtFile) return
appendDescriptors(root.node, document, descriptors)
}
private fun appendDescriptors(node: ASTNode, document: Document, descriptors: MutableList<FoldingDescriptor>) {
if (needFolding(node)) {
val textRange = getRangeToFold(node)
val relativeRange = textRange.shiftRight(-node.textRange.startOffset)
val foldRegionText = node.chars.subSequence(relativeRange.startOffset, relativeRange.endOffset)
if (StringUtil.countNewLines(foldRegionText) > 0) {
descriptors.add(FoldingDescriptor(node, textRange))
}
}
var child = node.firstChildNode
while (child != null) {
appendDescriptors(child, document, descriptors)
child = child.treeNext
}
}
private fun needFolding(node: ASTNode): Boolean {
return node.elementType == KtNodeTypes.CALL_EXPRESSION
}
private fun getRangeToFold(node: ASTNode): TextRange {
if (node.elementType == KtNodeTypes.CALL_EXPRESSION) {
val valueArgumentList = (node.psi as? KtCallExpression)?.valueArgumentList
val leftParenthesis = valueArgumentList?.leftParenthesis
val rightParenthesis = valueArgumentList?.rightParenthesis
if (leftParenthesis != null && rightParenthesis != null) {
return TextRange(leftParenthesis.startOffset, rightParenthesis.endOffset)
}
}
return node.textRange
}
override fun getLanguagePlaceholderText(p0: ASTNode, p1: TextRange): String = "(...)"
override fun isRegionCollapsedByDefault(node: ASTNode) = false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment