/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.formatting; import com.intellij.formatting.Spacing; import com.intellij.patterns.ElementPattern; import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.impl.source.tree.JavaDocElementType; import gw.plugin.ij.lang.GosuLanguage; import gw.plugin.ij.lang.parser.GosuElementTypes; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.patterns.PlatformPatterns.psiElement; import static com.intellij.patterns.StandardPatterns.or; import static gw.plugin.ij.formatting.GosuSpaces.LINE_FEED; public class GosuBlankLinesProcessor extends GosuElementTypes { private static final ElementPattern<PsiElement> IS_METHOD = psiElement(METHOD_DEFINITION); private static final ElementPattern<PsiElement> IS_INTERFACE_METHOD = psiElement(METHOD_DEFINITION).withParent(psiElement(INTERFACE_DEFINITION)); private static final ElementPattern<PsiElement> IS_FIELD = psiElement(FIELD); private static final ElementPattern<PsiElement> IS_INTERFACE_FIELD = psiElement(FIELD).withParent(psiElement(INTERFACE_DEFINITION)); private static final ElementPattern<PsiElement> IS_ANONYMOUS_CLASS_DEFINITION = psiElement(ANONYMOUS_CLASS_DEFINITION); private static final ElementPattern<PsiElement> IS_NON_ANONYMOUS_CLASS_DEFINITION = or( psiElement(CLASS_DEFINITION), psiElement(INTERFACE_DEFINITION), psiElement(ENUM_DEFINITION), psiElement(ENHANCEMENT_DEFINITION), psiElement(ANNOTATION_DEFINITION)); private static final ElementPattern<PsiElement> IS_NAMESPACE_STATEMENT = psiElement(ELEM_TYPE_NamespaceStatement); private static final ElementPattern<PsiElement> IS_USES_STATEMENT_LIST = psiElement(ELEM_TYPE_UsesStatementList); @Nullable private static Spacing getSpacingAroundPackage(PsiElement psi1, PsiElement psi2, @NotNull CommonCodeStyleSettings settings, GosuCodeStyleSettings gosuSettings) { // [LINES] // package Identifier if (IS_NAMESPACE_STATEMENT.accepts(psi2)) { return GosuSpaces.blankLines(settings.BLANK_LINES_BEFORE_PACKAGE); } // package Identifier // [LINES] if (IS_NAMESPACE_STATEMENT.accepts(psi1)) { return GosuSpaces.blankLines(settings.BLANK_LINES_AFTER_PACKAGE); } return null; } @Nullable private static Spacing getSpacingAroundUses(PsiElement psi1, PsiElement psi2, @NotNull CommonCodeStyleSettings settings, GosuCodeStyleSettings gosuSettings) { // [LINES] // uses a // uses b // uses c if (IS_USES_STATEMENT_LIST.accepts(psi2)) { return GosuSpaces.blankLines(settings.BLANK_LINES_BEFORE_IMPORTS); } // uses a // uses b // uses c // [LINES] if (IS_USES_STATEMENT_LIST.accepts(psi1)) { return GosuSpaces.blankLines(settings.BLANK_LINES_AFTER_IMPORTS); } return null; } @Nullable private static Spacing getSpacingAroundClass(PsiElement psi1, PsiElement psi2, @NotNull CommonCodeStyleSettings settings, GosuCodeStyleSettings gosuSettings) { if (IS_NON_ANONYMOUS_CLASS_DEFINITION.accepts(psi2)) { return psiElement(TT_OP_brace_left).accepts(psi1) ? LINE_FEED : GosuSpaces.blankLines(settings.BLANK_LINES_AROUND_CLASS); } if (IS_NON_ANONYMOUS_CLASS_DEFINITION.accepts(psi1)) { return psiElement(TT_OP_brace_right).accepts(psi2) ? LINE_FEED : GosuSpaces.blankLines(settings.BLANK_LINES_AROUND_CLASS); } return null; } @Nullable private static Spacing getSpacingAfterClassHeader(PsiElement psi1, PsiElement psi2, @NotNull CommonCodeStyleSettings settings, GosuCodeStyleSettings gosuSettings) { // class/interface/enum/enhancement/annotation Identifier { // [LINES] if (psiElement(TT_OP_brace_left).withParent(IS_NON_ANONYMOUS_CLASS_DEFINITION) .accepts(psi1)) { return GosuSpaces.blankLines(settings.BLANK_LINES_AFTER_CLASS_HEADER); } // new Identifier() { // [LINES] if (psiElement(TT_OP_brace_left).withParent(IS_ANONYMOUS_CLASS_DEFINITION) .accepts(psi1)) { return GosuSpaces.blankLines(settings.BLANK_LINES_AFTER_ANONYMOUS_CLASS_HEADER); } return null; } @Nullable private static Spacing getSpacingAroundHelper(@NotNull ElementPattern<PsiElement> pattern, PsiElement psi1, PsiElement psi2, int blankLines) { // Before if (pattern.accepts(psi2)) { return psiElement(TT_OP_brace_left).accepts(psi1) ? LINE_FEED : GosuSpaces.blankLines(blankLines); } // After if (pattern.accepts(psi1)) { return psiElement(TT_OP_brace_right).accepts(psi2) ? LINE_FEED : GosuSpaces.blankLines(blankLines); } return null; } @Nullable private static Spacing getSpacingAroundFields(PsiElement psi1, PsiElement psi2, @NotNull CommonCodeStyleSettings settings, GosuCodeStyleSettings gosuSettings) { Spacing spacing; // interface Identifier { // var a: Type // [LINES] // var b: Type // [LINES] // var c: Type // } spacing = getSpacingAroundHelper(IS_INTERFACE_FIELD, psi1, psi2, settings.BLANK_LINES_AROUND_FIELD_IN_INTERFACE); if (spacing != null) { return spacing; } // class/enum/enhancement/annotation Identifier { // function a() {} // [LINES] // function b() {} // [LINES] // function c() {} // } spacing = getSpacingAroundHelper(IS_FIELD, psi1, psi2, settings.BLANK_LINES_AROUND_FIELD); if (spacing != null) { return spacing; } return null; } @Nullable private static Spacing getSpacingAroundMethods(PsiElement psi1, PsiElement psi2, @NotNull CommonCodeStyleSettings settings, GosuCodeStyleSettings gosuSettings) { Spacing spacing; // interface Identifier { // function a() // [LINES] // function b() // [LINES] // function c() // } spacing = getSpacingAroundHelper(IS_INTERFACE_METHOD, psi1, psi2, settings.BLANK_LINES_AROUND_METHOD_IN_INTERFACE); if (spacing != null) { return spacing; } // class/enum/enhancement/annotation Identifier { // function a() {} // [LINES] // function b() {} // [LINES] // function c() {} // } spacing = getSpacingAroundHelper(IS_METHOD, psi1, psi2, settings.BLANK_LINES_AROUND_METHOD); if (spacing != null) { return spacing; } return spacing; } @Nullable private static Spacing getSpacingBeforeMethodBody(PsiElement psi1, PsiElement psi2, @NotNull CommonCodeStyleSettings settings, GosuCodeStyleSettings gosuSettings) { // function Identifier() { // [LINES] if (psiElement(TT_OP_brace_left) .withParent(psiElement(ELEM_TYPE_StatementList) .withParent(psiElement(METHOD_DEFINITION))) .accepts(psi1)) { return GosuSpaces.blankLines(settings.BLANK_LINES_BEFORE_METHOD_BODY); } if (psiElement(TT_OP_brace_right) .withParent(psiElement(ELEM_TYPE_StatementList)) .accepts(psi2)) { return GosuSpaces.LINE_FEED; } return null; } @Nullable public static Spacing getSpacing(@NotNull GosuBlock child1, @NotNull GosuBlock child2, @NotNull CodeStyleSettings settings) { final PsiElement psi1 = child1.getNode().getPsi(); final PsiElement psi2 = child2.getNode().getPsi(); // No space before java doc if (psiElement(JavaDocElementType.DOC_COMMENT) .accepts(psi1)) { return GosuSpaces.LINE_FEED; } final CommonCodeStyleSettings commonSettings = settings.getCommonSettings(GosuLanguage.instance()); final GosuCodeStyleSettings gosuSettings = settings.getCustomSettings(GosuCodeStyleSettings.class); Spacing spacing; if ((spacing = getSpacingAroundPackage(psi1, psi2, commonSettings, gosuSettings)) != null || (spacing = getSpacingAroundUses(psi1, psi2, commonSettings, gosuSettings)) != null || (spacing = getSpacingAroundClass(psi1, psi2, commonSettings, gosuSettings)) != null || (spacing = getSpacingAfterClassHeader(psi1, psi2, commonSettings, gosuSettings)) != null || (spacing = getSpacingAroundFields(psi1, psi2, commonSettings, gosuSettings)) != null || (spacing = getSpacingAroundMethods(psi1, psi2, commonSettings, gosuSettings)) != null || (spacing = getSpacingBeforeMethodBody(psi1, psi2, commonSettings, gosuSettings)) != null) { return spacing; } return null; } }