/* * Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.goide.editor; import com.goide.GoParserDefinition; import com.goide.GoTypes; import com.goide.psi.*; import com.intellij.codeInsight.folding.CodeFoldingSettings; import com.intellij.lang.ASTNode; import com.intellij.lang.folding.CustomFoldingBuilder; import com.intellij.lang.folding.FoldingDescriptor; import com.intellij.lang.folding.NamedFoldingDescriptor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Set; public class GoFoldingBuilder extends CustomFoldingBuilder implements DumbAware { private static void foldTypes(@Nullable PsiElement e, @NotNull List<FoldingDescriptor> result) { if (e instanceof GoStructType) { if (((GoStructType)e).getFieldDeclarationList().isEmpty()) return; fold(e, ((GoStructType)e).getLbrace(), ((GoStructType)e).getRbrace(), "{...}", result); } if (e instanceof GoInterfaceType) { if (e.getChildren().length == 0) return; fold(e, ((GoInterfaceType)e).getLbrace(), ((GoInterfaceType)e).getRbrace(), "{...}", result); } } private static void fold(@NotNull PsiElement e, @Nullable PsiElement l, @Nullable PsiElement r, @NotNull String placeholderText, @NotNull List<FoldingDescriptor> result) { if (l != null && r != null) { result.add(new NamedFoldingDescriptor(e, l.getTextRange().getStartOffset(), r.getTextRange().getEndOffset(), null, placeholderText)); } } // com.intellij.codeInsight.folding.impl.JavaFoldingBuilderBase.addCodeBlockFolds() private static void addCommentFolds(@NotNull PsiElement comment, @NotNull Set<PsiElement> processedComments, @NotNull List<FoldingDescriptor> result) { if (processedComments.contains(comment)) return; PsiElement end = null; for (PsiElement current = comment.getNextSibling(); current != null; current = current.getNextSibling()) { ASTNode node = current.getNode(); if (node == null) break; IElementType elementType = node.getElementType(); if (elementType == GoParserDefinition.LINE_COMMENT) { end = current; processedComments.add(current); continue; } if (elementType == TokenType.WHITE_SPACE) continue; break; } if (end != null) { int startOffset = comment.getTextRange().getStartOffset(); int endOffset = end.getTextRange().getEndOffset(); result.add(new NamedFoldingDescriptor(comment, startOffset, endOffset, null, "/.../")); } } @Override protected void buildLanguageFoldRegions(@NotNull List<FoldingDescriptor> result, @NotNull PsiElement root, @NotNull Document document, boolean quick) { if (!(root instanceof GoFile)) return; GoFile file = (GoFile)root; if (!file.isContentsLoaded()) return; GoImportList importList = ((GoFile)root).getImportList(); if (importList != null) { GoImportDeclaration firstImport = ContainerUtil.getFirstItem(importList.getImportDeclarationList()); if (firstImport != null) { PsiElement importKeyword = firstImport.getImport(); int offset = importKeyword.getTextRange().getEndOffset(); int startOffset = importKeyword.getNextSibling() instanceof PsiWhiteSpace ? offset + 1 : offset; int endOffset = importList.getTextRange().getEndOffset(); if (endOffset - startOffset > 3) { result.add(new NamedFoldingDescriptor(importList, startOffset, endOffset, null, "...")); } } } for (GoBlock block : PsiTreeUtil.findChildrenOfType(file, GoBlock.class)) { if (block.getTextRange().getLength() > 1) { result.add(new NamedFoldingDescriptor(block.getNode(), block.getTextRange(), null, "{...}")); } } for (GoExprSwitchStatement switchStatement : PsiTreeUtil.findChildrenOfType(file, GoExprSwitchStatement.class)) { fold(switchStatement, switchStatement.getLbrace(), switchStatement.getRbrace(), "{...}", result); } for (GoSelectStatement selectStatement : PsiTreeUtil.findChildrenOfType(file, GoSelectStatement.class)) { fold(selectStatement, selectStatement.getLbrace(), selectStatement.getRbrace(), "{...}", result); } for (GoTypeSpec type : file.getTypes()) { foldTypes(type.getSpecType().getType(), result); } for (GoCaseClause caseClause : PsiTreeUtil.findChildrenOfType(file, GoCaseClause.class)) { PsiElement colon = caseClause.getColon(); if (colon != null && !caseClause.getStatementList().isEmpty()) { fold(caseClause, colon.getNextSibling(), caseClause, "...", result); } } for (GoCommClause commClause : PsiTreeUtil.findChildrenOfType(file, GoCommClause.class)) { PsiElement colon = commClause.getColon(); if (colon != null && !commClause.getStatementList().isEmpty()) { fold(commClause, colon.getNextSibling(), commClause, "...", result); } } for (GoVarDeclaration varDeclaration : PsiTreeUtil.findChildrenOfType(file, GoVarDeclaration.class)) { if (varDeclaration.getVarSpecList().size() > 1) { fold(varDeclaration, varDeclaration.getLparen(), varDeclaration.getRparen(), "(...)", result); } } for (GoConstDeclaration constDeclaration : PsiTreeUtil.findChildrenOfType(file, GoConstDeclaration.class)) { if (constDeclaration.getConstSpecList().size() > 1) { fold(constDeclaration, constDeclaration.getLparen(), constDeclaration.getRparen(), "(...)", result); } } for (GoTypeDeclaration typeDeclaration : PsiTreeUtil.findChildrenOfType(file, GoTypeDeclaration.class)) { if (typeDeclaration.getTypeSpecList().size() > 1) { fold(typeDeclaration, typeDeclaration.getLparen(), typeDeclaration.getRparen(), "(...)", result); } } for (GoCompositeLit compositeLit : PsiTreeUtil.findChildrenOfType(file, GoCompositeLit.class)) { GoLiteralValue literalValue = compositeLit.getLiteralValue(); if (literalValue != null && literalValue.getElementList().size() > 1) { fold(literalValue, literalValue.getLbrace(), literalValue.getRbrace(), "{...}", result); } } if (!quick) { Set<PsiElement> processedComments = ContainerUtil.newHashSet(); PsiTreeUtil.processElements(file, element -> { ASTNode node = element.getNode(); IElementType type = node.getElementType(); TextRange range = element.getTextRange(); if (type == GoParserDefinition.MULTILINE_COMMENT && range.getLength() > 2) { result.add(new NamedFoldingDescriptor(node, range, null, "/*...*/")); } if (type == GoParserDefinition.LINE_COMMENT) { addCommentFolds(element, processedComments, result); } foldTypes(element, result); // folding for inner types return true; }); } } @Nullable @Override protected String getLanguagePlaceholderText(@NotNull ASTNode node, @NotNull TextRange range) { return "..."; } @Override protected boolean isRegionCollapsedByDefault(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type == GoParserDefinition.LINE_COMMENT || type == GoParserDefinition.MULTILINE_COMMENT) { return CodeFoldingSettings.getInstance().COLLAPSE_DOC_COMMENTS; } if (type == GoTypes.BLOCK && CodeFoldingSettings.getInstance().COLLAPSE_METHODS) { ASTNode parent = node.getTreeParent(); return parent != null && parent.getPsi() instanceof GoFunctionOrMethodDeclaration; } return CodeFoldingSettings.getInstance().COLLAPSE_IMPORTS && node.getElementType() == GoTypes.IMPORT_LIST; } }