Search in sources :

Example 1 with PsiPerlStatementModifier

use of com.perl5.lang.perl.psi.PsiPerlStatementModifier in project Perl5-IDEA by Camelcade.

the class PerlLoopControlInspection method buildVisitor.

@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
    PerlSubDefinitionElement breakDefinition = PerlBuiltInSubsService.getInstance(holder.getProject()).findSub("break");
    return new PerlVisitor() {

        @Override
        public void visitNextExpr(@NotNull PsiPerlNextExpr o) {
            processLoopsControl(o);
        }

        @Override
        public void visitRedoExpr(@NotNull PsiPerlRedoExpr o) {
            processLoopsControl(o);
        }

        @Override
        public void visitLastExpr(@NotNull PsiPerlLastExpr o) {
            processLoopsControl(o);
        }

        @Override
        public void visitContinueExpr(@NotNull PsiPerlContinueExpr o) {
            PsiElement position = o;
            boolean isInsideTheLoop = false;
            while (true) {
                PsiElement closestBlockContainer = getClosestBlockContainer(position);
                if (closestBlockContainer == null) {
                    break;
                }
                IElementType blockContainerElementType = PsiUtilCore.getElementType(closestBlockContainer);
                if (blockContainerElementType == WHEN_COMPOUND || blockContainerElementType == DEFAULT_COMPOUND) {
                    return;
                } else if (LOOPS_CONTAINERS.contains(blockContainerElementType)) {
                    isInsideTheLoop = true;
                } else if (blockContainerElementType == NAMED_BLOCK) {
                    break;
                } else if (MAP_GREP.contains(blockContainerElementType)) {
                    break;
                } else if (BLOCKS_WITH_RETURN_VALUE.contains(blockContainerElementType)) {
                    break;
                } else if (blockContainerElementType == GIVEN_COMPOUND) {
                    break;
                }
                position = closestBlockContainer;
            }
            if (isInsideTheLoop) {
                holder.registerProblem(o, PerlBundle.message("perl.inspection.loop.control.continue.instead.of.next"), new ReplaceWithExpressionQuickFix("next"));
            } else {
                problem(o, "perl.inspection.loop.control.continue");
            }
        }

        @Override
        public void visitSubNameElement(@NotNull PerlSubNameElement o) {
            PsiReference reference = o.getReference();
            if (reference == null) {
                return;
            }
            if (reference.resolve() != breakDefinition) {
                return;
            }
            PsiElement position = o;
            boolean isInsideTheLoop = false;
            while (true) {
                PsiElement closestBlockContainer = getClosestBlockContainer(position);
                if (closestBlockContainer == null) {
                    break;
                }
                IElementType blockContainerElementType = PsiUtilCore.getElementType(closestBlockContainer);
                if (LOOPS_CONTAINERS.contains(blockContainerElementType)) {
                    isInsideTheLoop = true;
                } else if (blockContainerElementType == NAMED_BLOCK) {
                    break;
                } else if (MAP_GREP.contains(blockContainerElementType)) {
                    break;
                } else if (BLOCKS_WITH_RETURN_VALUE.contains(blockContainerElementType)) {
                    break;
                } else if (blockContainerElementType == GIVEN_COMPOUND) {
                    return;
                }
                position = closestBlockContainer;
            }
            if (isInsideTheLoop) {
                holder.registerProblem(o, PerlBundle.message("perl.inspection.loop.control.break.instead.of.last"), new ReplaceWithExpressionQuickFix("last"));
            } else {
                problem(o, "perl.inspection.loop.control.break");
            }
        }

        private void problem(@NotNull PsiElement anchor, @NotNull String key, @NotNull String... args) {
            registerProblem(holder, anchor, PerlBundle.message(key, (Object[]) args));
        }

        /**
         * @return parent psi element for closest parent block element
         */
        @Nullable
        private PsiElement getClosestBlockContainer(@NotNull PsiElement position) {
            PsiPerlBlock enclosingBlock = PsiTreeUtil.getParentOfType(position, PsiPerlBlock.class);
            if (enclosingBlock == null) {
                return null;
            }
            PsiElement blockContainer = enclosingBlock.getParent();
            return PsiUtilCore.getElementType(blockContainer) == LP_CODE_BLOCK ? blockContainer.getParent() : blockContainer;
        }

        /**
         * Traversing blocks up, trying to figure out if last/next/redo are in right place.
         *
         * @param expr last/next/redo expression
         */
        private void processLoopsControl(@NotNull PsiElement expr) {
            PsiElement keyword = expr.getFirstChild();
            if (keyword == null) {
                return;
            }
            String keywordText = keyword.getText();
            // checks modifier
            PsiPerlStatementImpl containingStatement = PsiTreeUtil.getParentOfType(expr, PsiPerlStatementImpl.class);
            PsiPerlStatementModifier modifier = containingStatement == null ? null : containingStatement.getModifier();
            if (modifier instanceof PsiPerlForStatementModifier) {
                return;
            }
            // traversing up
            PsiElement position = expr;
            boolean isInsideGiven = false;
            boolean isInsideWhenOrDefault = false;
            while (true) {
                PsiElement closestBlockContainer = getClosestBlockContainer(position);
                if (closestBlockContainer == null) {
                    break;
                }
                IElementType blockContainerType = PsiUtilCore.getElementType(closestBlockContainer);
                if (LOOPS_CONTAINERS.contains(blockContainerType)) {
                    return;
                } else if (blockContainerType == NAMED_BLOCK) {
                    problem(expr, "perl.inspection.loop.control.in.named.block", keywordText);
                    return;
                } else if (MAP_GREP.contains(blockContainerType)) {
                    problem(expr, "perl.inspection.loop.control.in.map", keywordText);
                    return;
                } else if (BLOCKS_WITH_RETURN_VALUE.contains(blockContainerType)) {
                    problem(expr, "perl.inspection.loop.control.in.do", keywordText);
                    return;
                } else if (blockContainerType == GIVEN_COMPOUND) {
                    isInsideGiven = true;
                } else if (blockContainerType == WHEN_COMPOUND || blockContainerType == DEFAULT_COMPOUND) {
                    isInsideWhenOrDefault = true;
                }
                position = closestBlockContainer;
            }
            if (expr instanceof PsiPerlNextExpr && isInsideWhenOrDefault) {
                holder.registerProblem(expr, PerlBundle.message("perl.inspection.loop.control.next.instead.of.continue"), new ReplaceWithExpressionQuickFix("continue"));
            } else if (expr instanceof PsiPerlLastExpr && isInsideGiven) {
                holder.registerProblem(expr, PerlBundle.message("perl.inspection.loop.control.last.instead.of.break"), new ReplaceWithExpressionQuickFix("break"));
            } else {
                problem(expr, "perl.inspection.loop.control.outside", keywordText);
            }
        }
    };
}
Also used : PsiReference(com.intellij.psi.PsiReference) PsiPerlStatementImpl(com.perl5.lang.perl.psi.impl.PsiPerlStatementImpl) NotNull(org.jetbrains.annotations.NotNull) IElementType(com.intellij.psi.tree.IElementType) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 2 with PsiPerlStatementModifier

use of com.perl5.lang.perl.psi.PsiPerlStatementModifier in project Perl5-IDEA by Camelcade.

the class StatementToCompoundIntention method invoke.

@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
    PsiPerlStatementImpl statement = getStatement(element);
    if (statement == null) {
        return;
    }
    PsiPerlStatementModifier modifier = statement.getModifier();
    if (modifier == null) {
        return;
    }
    PsiElement keyword = modifier.getFirstChild();
    if (keyword == null) {
        return;
    }
    PsiPerlExpr modifierExpression = PsiTreeUtil.getChildOfType(modifier, PsiPerlExpr.class);
    PsiPerlExpr valueExpression = modifierExpression instanceof PsiPerlParenthesisedExpr ? ((PsiPerlParenthesisedExpr) modifierExpression).getExpr() : modifierExpression;
    String conditionText = valueExpression == null ? "" : valueExpression.getText();
    TextRange modifierRangeInStatement = TextRange.from(modifier.getStartOffsetInParent(), modifier.getTextLength());
    String statementText = modifierRangeInStatement.replace(statement.getText(), "");
    StringBuilder newCode = new StringBuilder();
    newCode.append(keyword.getText()).append("(").append(conditionText).append("){\n").append(statementText).append("\n}");
    PerlFileImpl fakeFile = PerlElementFactory.createFile(project, newCode.toString());
    PsiElement[] children = fakeFile.getChildren();
    assert children.length == 1;
    statement.replace(children[0]);
}
Also used : PerlFileImpl(com.perl5.lang.perl.psi.impl.PerlFileImpl) PsiPerlStatementImpl(com.perl5.lang.perl.psi.impl.PsiPerlStatementImpl) PsiPerlStatementModifier(com.perl5.lang.perl.psi.PsiPerlStatementModifier) PsiPerlParenthesisedExpr(com.perl5.lang.perl.psi.PsiPerlParenthesisedExpr) TextRange(com.intellij.openapi.util.TextRange) PsiPerlExpr(com.perl5.lang.perl.psi.PsiPerlExpr) PsiElement(com.intellij.psi.PsiElement)

Example 3 with PsiPerlStatementModifier

use of com.perl5.lang.perl.psi.PsiPerlStatementModifier in project Perl5-IDEA by Camelcade.

the class PerlFormattingContext method getSpacing.

@Nullable
public Spacing getSpacing(@NotNull ASTBlock parent, @Nullable Block child1, @NotNull Block child2) {
    if (parent instanceof PerlSyntheticBlock) {
        parent = ((PerlSyntheticBlock) parent).getRealBlock();
    }
    if (child1 instanceof PerlSyntheticBlock) {
        child1 = ((PerlSyntheticBlock) child1).getLastRealBlock();
    }
    if (child2 instanceof PerlSyntheticBlock) {
        child2 = ((PerlSyntheticBlock) child2).getFirstRealBlock();
    }
    if (child1 instanceof ASTBlock && child2 instanceof ASTBlock) {
        ASTNode parentNode = parent.getNode();
        IElementType parentNodeType = PsiUtilCore.getElementType(parentNode);
        ASTNode child1Node = ((ASTBlock) child1).getNode();
        IElementType child1Type = child1Node.getElementType();
        ASTNode child2Node = ((ASTBlock) child2).getNode();
        IElementType child2Type = child2Node.getElementType();
        if (parentNodeType == PARENTHESISED_EXPR && (child1Type == LEFT_PAREN || child2Type == RIGHT_PAREN) && parentNode.getPsi().getParent() instanceof PsiPerlStatementModifier) {
            return getSettings().SPACE_WITHIN_IF_PARENTHESES ? Spacing.createSpacing(1, 1, 0, true, 1) : Spacing.createSpacing(0, 0, 0, true, 1);
        }
        // fix for number/concat
        if (child2Type == OPERATOR_CONCAT) {
            ASTNode run = child1Node;
            while (run instanceof CompositeElement) {
                run = run.getLastChildNode();
            }
            if (run != null) {
                IElementType runType = run.getElementType();
                if (runType == NUMBER_SIMPLE || runType == NUMBER && StringUtil.endsWith(run.getText(), ".")) {
                    return Spacing.createSpacing(1, 1, 0, true, 1);
                }
            }
        }
        // LF after opening brace and before closing need to check if here-doc opener is in the line
        if (LF_ELEMENTS.contains(child1Type) && LF_ELEMENTS.contains(child2Type)) {
            if (!isNewLineForbiddenAt(child1Node)) {
                return Spacing.createSpacing(0, 0, 1, true, 1);
            } else {
                return Spacing.createSpacing(1, Integer.MAX_VALUE, 0, true, 1);
            }
        }
        // small inline blocks
        if (parentNodeType == BLOCK && !inGrepMapSort(parentNode) && !blockHasLessChildrenThan(parentNode, 2) && (BLOCK_OPENERS.contains(child1Type) && ((PerlFormattingBlock) child1).isFirst() || BLOCK_CLOSERS.contains(child2Type) && ((PerlFormattingBlock) child2).isLast()) && !isNewLineForbiddenAt(child1Node)) {
            return Spacing.createSpacing(0, 0, 1, true, 1);
        }
        if (parentNodeType == PARENTHESISED_CALL_ARGUMENTS && child2Type == RIGHT_PAREN && PsiUtilCore.getElementType(PsiTreeUtil.getDeepestLast(child1Node.getPsi())) == RIGHT_PAREN) {
            return Spacing.createSpacing(0, 0, 0, true, 0);
        }
        // hack for + ++/- --/~~ ~
        if ((child2Type == PREFIX_UNARY_EXPR || child2Type == PREF_PP_EXPR) && OPERATOR_COLLISIONS_MAP.containsKey(child1Type)) {
            IElementType rightSignType = PsiUtilCore.getElementType(child2Node.getFirstChildNode());
            if (OPERATOR_COLLISIONS_MAP.get(child1Type).contains(rightSignType)) {
                return Spacing.createSpacing(1, 1, 0, true, 1);
            }
        } else // hack for ++ +/-- -
        if (child1Type == SUFF_PP_EXPR && OPERATOR_COLLISIONS_MAP.containsKey(child2Type)) {
            IElementType leftSignType = PsiUtilCore.getElementType(child1Node.getLastChildNode());
            if (OPERATOR_COLLISIONS_MAP.get(child2Type).contains(leftSignType)) {
                return Spacing.createSpacing(1, 1, 0, true, 1);
            }
        }
    }
    return getSpacingBuilder().getSpacing(parent, child1, child2);
}
Also used : IElementType(com.intellij.psi.tree.IElementType) ASTNode(com.intellij.lang.ASTNode) PerlSyntheticBlock(com.perl5.lang.perl.idea.formatter.blocks.PerlSyntheticBlock) PsiPerlStatementModifier(com.perl5.lang.perl.psi.PsiPerlStatementModifier) CompositeElement(com.intellij.psi.impl.source.tree.CompositeElement) Nullable(org.jetbrains.annotations.Nullable)

Aggregations

PsiElement (com.intellij.psi.PsiElement)2 IElementType (com.intellij.psi.tree.IElementType)2 PsiPerlStatementModifier (com.perl5.lang.perl.psi.PsiPerlStatementModifier)2 PsiPerlStatementImpl (com.perl5.lang.perl.psi.impl.PsiPerlStatementImpl)2 ASTNode (com.intellij.lang.ASTNode)1 TextRange (com.intellij.openapi.util.TextRange)1 PsiReference (com.intellij.psi.PsiReference)1 CompositeElement (com.intellij.psi.impl.source.tree.CompositeElement)1 PerlSyntheticBlock (com.perl5.lang.perl.idea.formatter.blocks.PerlSyntheticBlock)1 PsiPerlExpr (com.perl5.lang.perl.psi.PsiPerlExpr)1 PsiPerlParenthesisedExpr (com.perl5.lang.perl.psi.PsiPerlParenthesisedExpr)1 PerlFileImpl (com.perl5.lang.perl.psi.impl.PerlFileImpl)1 NotNull (org.jetbrains.annotations.NotNull)1 Nullable (org.jetbrains.annotations.Nullable)1