Search in sources :

Example 66 with PsiComment

use of com.intellij.psi.PsiComment in project Perl5-IDEA by Camelcade.

the class PerlUsePackageQuickFix method applyFix.

@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
    PsiElement newStatementContainer = descriptor.getPsiElement();
    if (!FileModificationService.getInstance().prepareFileForWrite(newStatementContainer.getContainingFile())) {
        return;
    }
    PsiElement newStatement = PerlElementFactory.createUseStatement(project, myPackageName);
    PsiElement afterAnchor = null;
    PsiElement beforeAnchor = null;
    PsiElement baseUseStatement = PsiTreeUtil.findChildOfType(newStatementContainer, PerlUseStatement.class);
    if (baseUseStatement != null) {
        if (// pragma or version
        ((PerlUseStatement) baseUseStatement).isPragmaOrVersion()) {
            while (true) {
                // trying to find next use statement
                PsiElement nextStatement = baseUseStatement;
                while ((nextStatement = nextStatement.getNextSibling()) != null && PerlParserDefinition.WHITE_SPACE_AND_COMMENTS.contains(nextStatement.getNode().getElementType())) {
                }
                if (nextStatement instanceof PerlUseStatement && // found more use pragma/version
                ((PerlUseStatement) nextStatement).isPragmaOrVersion()) {
                    baseUseStatement = nextStatement;
                } else {
                    afterAnchor = baseUseStatement;
                    // we've got last pragma statement
                    break;
                }
            }
        } else // not a pragma
        {
            beforeAnchor = baseUseStatement;
        }
    } else // no uses found
    {
        PsiPerlNamespaceDefinition baseNamespace = PsiTreeUtil.findChildOfType(newStatementContainer, PsiPerlNamespaceDefinition.class);
        if (// got a namespace definition
        baseNamespace != null && baseNamespace.getBlock() != null) {
            newStatementContainer = baseNamespace.getBlock();
            if (newStatementContainer != null && !(newStatementContainer instanceof PsiPerlNamespaceContent)) {
                afterAnchor = newStatementContainer.getFirstChild();
            } else if (newStatementContainer != null && newStatementContainer.getFirstChild() != null) {
                beforeAnchor = newStatementContainer.getFirstChild();
            }
        } else {
            PsiElement anchor = newStatementContainer.getFirstChild();
            if (anchor instanceof PsiComment) {
                while (anchor.getNextSibling() != null && PerlPsiUtil.isCommentOrSpace(anchor.getNextSibling())) {
                    anchor = anchor.getNextSibling();
                }
                afterAnchor = anchor;
            } else if (anchor != null) {
                beforeAnchor = anchor;
            }
        }
    }
    if (afterAnchor != null) {
        newStatementContainer = afterAnchor.getParent();
        newStatement = newStatementContainer.addAfter(newStatement, afterAnchor);
    } else if (beforeAnchor != null) {
        newStatementContainer = beforeAnchor.getParent();
        newStatement = newStatementContainer.addBefore(newStatement, beforeAnchor);
    } else if (newStatementContainer != null) {
        newStatement = newStatementContainer.add(newStatement);
    }
    if (newStatement != null) {
        PsiElement newLineElement = PerlElementFactory.createNewLine(project);
        PsiElement nextSibling = newStatement.getNextSibling();
        PsiElement preveSibling = newStatement.getPrevSibling();
        newStatementContainer = newStatement.getParent();
        if (nextSibling == null || !(nextSibling instanceof PsiWhiteSpace) || !StringUtil.equals(nextSibling.getText(), "\n")) {
            newStatementContainer.addAfter(newLineElement, newStatement);
        }
        if ((preveSibling == null && !(newStatementContainer instanceof PsiFile)) || !(preveSibling instanceof PsiWhiteSpace) || !StringUtil.equals(preveSibling.getText(), "\n")) {
            newStatementContainer.addBefore(newLineElement, newStatement);
        }
    }
}
Also used : PsiComment(com.intellij.psi.PsiComment) PsiPerlNamespaceDefinition(com.perl5.lang.perl.psi.PsiPerlNamespaceDefinition) PsiFile(com.intellij.psi.PsiFile) PerlUseStatement(com.perl5.lang.perl.psi.PerlUseStatement) PsiElement(com.intellij.psi.PsiElement) PsiPerlNamespaceContent(com.perl5.lang.perl.psi.PsiPerlNamespaceContent) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace)

Example 67 with PsiComment

use of com.intellij.psi.PsiComment in project Perl5-IDEA by Camelcade.

the class PerlFoldingBuilder method getCommentsDescriptors.

/**
 * Searching for sequential comments (starting from newline or subblock beginning) and making folding descriptors for such blocks of size > 1
 *
 * @param comments list of collected comments
 * @param document document to search in
 * @return list of FoldingDescriptros
 */
private List<FoldingDescriptor> getCommentsDescriptors(@NotNull List<PsiComment> comments, @NotNull Document document) {
    List<FoldingDescriptor> descriptors = new ArrayList<>();
    TokenSet commentExcludedTokens = getCommentExcludedTokens();
    int currentOffset = 0;
    for (PsiComment comment : comments) {
        ASTNode commentNode = comment.getNode();
        IElementType commentElementType = commentNode.getElementType();
        if (currentOffset <= comment.getTextOffset() && // skips already collapsed blocks
        !commentExcludedTokens.contains(commentElementType)) {
            if (commentElementType == POD) {
                TextRange commentRange = comment.getTextRange();
                int startOffset = commentRange.getStartOffset();
                int endOffset = commentRange.getEndOffset();
                if (comment.getText().endsWith("\n")) {
                    endOffset--;
                }
                currentOffset = endOffset;
                descriptors.add(new FoldingDescriptor(commentNode, new TextRange(startOffset, endOffset)));
                continue;
            }
            boolean isCollapsable = false;
            PsiElement lastComment = comment;
            if (commentElementType == COMMENT_BLOCK || // template blocks are always collapsable
            commentElementType == getTemplateBlockElementType()) {
                isCollapsable = true;
            } else {
                // checking if this is a first element of block or starts from newline
                while (true) {
                    lastComment = lastComment.getPrevSibling();
                    if (lastComment == null || lastComment instanceof PsiComment) {
                        isCollapsable = true;
                        break;
                    } else if (lastComment instanceof PsiWhiteSpace) {
                        // whitespace with newline
                        if (StringUtil.containsLineBreak(lastComment.getNode().getChars())) {
                            isCollapsable = true;
                            break;
                        }
                    } else // non-whitespace block
                    {
                        break;
                    }
                }
            }
            if (isCollapsable) {
                // looking for an end
                int startOffset = comment.getTextOffset();
                if (comment.getText().startsWith("\n") && startOffset > 0 && document.getCharsSequence().charAt(startOffset - 1) != '\n') {
                    startOffset++;
                }
                int endOffset = comment.getTextRange().getEndOffset();
                PsiElement currentComment = comment;
                while (currentComment != null) {
                    if (currentComment instanceof PsiComment && !commentExcludedTokens.contains(currentComment.getNode().getElementType()) && !currentComment.getText().contains("todo") && !currentComment.getText().contains("fixme")) {
                        endOffset = currentComment.getTextOffset() + currentComment.getTextLength();
                        if (currentComment.getText().endsWith("\n")) {
                            endOffset--;
                        }
                    } else if (!(currentComment instanceof PsiWhiteSpace)) {
                        break;
                    }
                    currentComment = currentComment.getNextSibling();
                }
                if (endOffset > startOffset) {
                    int startLine = document.getLineNumber(startOffset);
                    int endLine = document.getLineNumber(endOffset);
                    if (endLine > startLine) {
                        currentOffset = endOffset;
                        descriptors.add(new FoldingDescriptor(commentNode, new TextRange(startOffset, endOffset)));
                    }
                }
            }
        }
    }
    return descriptors;
}
Also used : IElementType(com.intellij.psi.tree.IElementType) FoldingDescriptor(com.intellij.lang.folding.FoldingDescriptor) PsiComment(com.intellij.psi.PsiComment) TokenSet(com.intellij.psi.tree.TokenSet) ArrayList(java.util.ArrayList) ASTNode(com.intellij.lang.ASTNode) TextRange(com.intellij.openapi.util.TextRange) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace)

Example 68 with PsiComment

use of com.intellij.psi.PsiComment in project phpinspectionsea by kalessil.

the class DisconnectedForeachInstructionInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpForeach(@NotNull ForeachStatement foreach) {
            final GroupStatement foreachBody = ExpressionSemanticUtil.getGroupStatement(foreach);
            /* ensure foreach structure is ready for inspection */
            if (foreachBody != null) {
                final PsiElement[] statements = foreachBody.getChildren();
                if (statements.length > 0 && Stream.of(statements).anyMatch(s -> OpenapiTypesUtil.is(s, PhpElementTypes.HTML))) {
                    return;
                }
                /* pre-collect introduced and internally used variables */
                final Set<String> allModifiedVariables = this.collectCurrentAndOuterLoopVariables(foreach);
                final Map<PsiElement, Set<String>> instructionDependencies = new HashMap<>();
                /* iteration 1 - investigate what are dependencies and influence */
                for (final PsiElement oneInstruction : statements) {
                    if (oneInstruction instanceof PhpPsiElement && !(oneInstruction instanceof PsiComment)) {
                        final Set<String> individualDependencies = new HashSet<>();
                        instructionDependencies.put(oneInstruction, individualDependencies);
                        investigateInfluence((PhpPsiElement) oneInstruction, individualDependencies, allModifiedVariables);
                    }
                }
                /* iteration 2 - analyse dependencies */
                for (final PsiElement oneInstruction : statements) {
                    if (oneInstruction instanceof PhpPsiElement && !(oneInstruction instanceof PsiComment)) {
                        boolean isDependOnModified = false;
                        /* check if any dependency is overridden */
                        final Set<String> individualDependencies = instructionDependencies.get(oneInstruction);
                        if (individualDependencies != null && !individualDependencies.isEmpty()) {
                            isDependOnModified = individualDependencies.stream().anyMatch(allModifiedVariables::contains);
                            individualDependencies.clear();
                        }
                        /* verify and report if violation detected */
                        if (!isDependOnModified) {
                            final ExpressionType target = getExpressionType(oneInstruction);
                            if (ExpressionType.NEW != target && ExpressionType.ASSIGNMENT != target && ExpressionType.CLONE != target && ExpressionType.INCREMENT != target && ExpressionType.DECREMENT != target && ExpressionType.DOM_ELEMENT_CREATE != target && ExpressionType.ACCUMULATE_IN_ARRAY != target && ExpressionType.CONTROL_STATEMENTS != target) {
                                /* loops, ifs, switches, try's needs to be reported on keyword, others - complete */
                                final PsiElement reportingTarget = oneInstruction instanceof ControlStatement || oneInstruction instanceof Try || oneInstruction instanceof PhpSwitch ? oneInstruction.getFirstChild() : oneInstruction;
                                /* secure exceptions with '<?= ?>' constructions, false-positives with html */
                                if (!OpenapiTypesUtil.isPhpExpressionImpl(oneInstruction) && oneInstruction.getTextLength() > 0) {
                                    /* inner looping termination/continuation should be taken into account */
                                    final PsiElement loopInterrupter = PsiTreeUtil.findChildOfAnyType(oneInstruction, true, PhpBreak.class, PhpContinue.class, PhpReturn.class, OpenapiPlatformUtil.classes.get("PhpThrow"));
                                    /* operating with variables should be taken into account */
                                    final boolean isVariablesUsed = PsiTreeUtil.findChildOfAnyType(oneInstruction, true, (Class) Variable.class) != null;
                                    if (null == loopInterrupter && isVariablesUsed) {
                                        holder.registerProblem(reportingTarget, MessagesPresentationUtil.prefixWithEa(messageDisconnected));
                                    }
                                }
                            }
                            if (SUGGEST_USING_CLONE && (ExpressionType.DOM_ELEMENT_CREATE == target || ExpressionType.NEW == target)) {
                                holder.registerProblem(oneInstruction, MessagesPresentationUtil.prefixWithEa(messageUseClone));
                            }
                        }
                    }
                }
                /* release containers content */
                allModifiedVariables.clear();
                instructionDependencies.values().forEach(Set::clear);
                instructionDependencies.clear();
            }
        }

        private Set<String> collectCurrentAndOuterLoopVariables(@NotNull ForeachStatement foreach) {
            final Set<String> variables = new HashSet<>();
            PsiElement current = foreach;
            while (current != null && !(current instanceof Function) && !(current instanceof PsiFile)) {
                if (current instanceof ForeachStatement) {
                    ((ForeachStatement) current).getVariables().forEach(v -> variables.add(v.getName()));
                }
                current = current.getParent();
            }
            return variables;
        }

        private void investigateInfluence(@Nullable PhpPsiElement oneInstruction, @NotNull Set<String> individualDependencies, @NotNull Set<String> allModifiedVariables) {
            for (final Variable variable : PsiTreeUtil.findChildrenOfType(oneInstruction, Variable.class)) {
                final String variableName = variable.getName();
                PsiElement valueContainer = variable;
                PsiElement parent = variable.getParent();
                while (parent instanceof FieldReference) {
                    valueContainer = parent;
                    parent = parent.getParent();
                }
                /* a special case: `[] = ` and `array() = ` unboxing */
                if (OpenapiTypesUtil.is(parent, PhpElementTypes.ARRAY_VALUE)) {
                    parent = parent.getParent();
                    if (parent instanceof ArrayCreationExpression) {
                        parent = parent.getParent();
                    }
                }
                final PsiElement grandParent = parent.getParent();
                /* writing into variable */
                if (parent instanceof AssignmentExpression) {
                    /* php-specific `list(...) =` , `[...] =` construction */
                    if (parent instanceof MultiassignmentExpression) {
                        final MultiassignmentExpression assignment = (MultiassignmentExpression) parent;
                        if (assignment.getValue() != variable) {
                            allModifiedVariables.add(variableName);
                            individualDependencies.add(variableName);
                            continue;
                        }
                    } else {
                        final AssignmentExpression assignment = (AssignmentExpression) parent;
                        if (assignment.getVariable() == valueContainer) {
                            /* we are modifying the variable */
                            allModifiedVariables.add(variableName);
                            /* self-assignment and field assignment counted as the variable dependent on itself  */
                            if (assignment instanceof SelfAssignmentExpression || valueContainer instanceof FieldReference) {
                                individualDependencies.add(variableName);
                            }
                            /* assignments as call arguments counted as the variable dependent on itself */
                            if (grandParent instanceof ParameterList) {
                                individualDependencies.add(variableName);
                            }
                            continue;
                        }
                    }
                }
                /* adding into an arrays; we both depend and modify the container */
                if (parent instanceof ArrayAccessExpression && valueContainer == ((ArrayAccessExpression) parent).getValue()) {
                    allModifiedVariables.add(variableName);
                    individualDependencies.add(variableName);
                }
                if (parent instanceof ParameterList) {
                    if (grandParent instanceof MethodReference) {
                        /* an object consumes the variable, perhaps modification takes place */
                        final MethodReference reference = (MethodReference) grandParent;
                        final PsiElement referenceOperator = OpenapiPsiSearchUtil.findResolutionOperator(reference);
                        if (OpenapiTypesUtil.is(referenceOperator, PhpTokenTypes.ARROW)) {
                            final PsiElement variableCandidate = reference.getFirstPsiChild();
                            if (variableCandidate instanceof Variable) {
                                allModifiedVariables.add(((Variable) variableCandidate).getName());
                                continue;
                            }
                        }
                    } else if (OpenapiTypesUtil.isFunctionReference(grandParent)) {
                        /* php will create variable, if it is by reference */
                        final FunctionReference reference = (FunctionReference) grandParent;
                        final int position = ArrayUtils.indexOf(reference.getParameters(), variable);
                        if (position != -1) {
                            final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                            if (resolved instanceof Function) {
                                final Parameter[] parameters = ((Function) resolved).getParameters();
                                if (parameters.length > position && parameters[position].isPassByRef()) {
                                    allModifiedVariables.add(variableName);
                                    individualDependencies.add(variableName);
                                    continue;
                                }
                            }
                        }
                    }
                }
                /* increment/decrement are also write operations */
                final ExpressionType type = this.getExpressionType(parent);
                if (ExpressionType.INCREMENT == type || ExpressionType.DECREMENT == type) {
                    allModifiedVariables.add(variableName);
                    individualDependencies.add(variableName);
                    continue;
                }
                /* TODO: lookup for array access and property access */
                individualDependencies.add(variableName);
            }
            /* handle compact function usage */
            for (final FunctionReference reference : PsiTreeUtil.findChildrenOfType(oneInstruction, FunctionReference.class)) {
                if (OpenapiTypesUtil.isFunctionReference(reference)) {
                    final String functionName = reference.getName();
                    if (functionName != null && functionName.equals("compact")) {
                        for (final PsiElement argument : reference.getParameters()) {
                            if (argument instanceof StringLiteralExpression) {
                                final String compactedVariableName = ((StringLiteralExpression) argument).getContents();
                                if (!compactedVariableName.isEmpty()) {
                                    individualDependencies.add(compactedVariableName);
                                }
                            }
                        }
                    }
                }
            }
        }

        @NotNull
        private ExpressionType getExpressionType(@Nullable PsiElement expression) {
            if (expression instanceof PhpBreak || expression instanceof PhpContinue || expression instanceof PhpReturn) {
                return ExpressionType.CONTROL_STATEMENTS;
            }
            /* regular '...;' statements */
            if (OpenapiTypesUtil.isStatementImpl(expression)) {
                return getExpressionType(((Statement) expression).getFirstPsiChild());
            }
            /* unary operations */
            if (expression instanceof UnaryExpression) {
                final PsiElement operation = ((UnaryExpression) expression).getOperation();
                if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opINCREMENT)) {
                    return ExpressionType.INCREMENT;
                }
                if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opDECREMENT)) {
                    return ExpressionType.DECREMENT;
                }
            }
            /* different types of assignments */
            if (expression instanceof AssignmentExpression) {
                final AssignmentExpression assignment = (AssignmentExpression) expression;
                final PsiElement variable = assignment.getVariable();
                if (variable instanceof Variable) {
                    final PsiElement value = assignment.getValue();
                    if (value instanceof NewExpression) {
                        return ExpressionType.NEW;
                    } else if (value instanceof UnaryExpression) {
                        if (OpenapiTypesUtil.is(((UnaryExpression) value).getOperation(), PhpTokenTypes.kwCLONE)) {
                            return ExpressionType.CLONE;
                        }
                    } else if (value instanceof MethodReference) {
                        final MethodReference call = (MethodReference) value;
                        final String methodName = call.getName();
                        if (methodName != null && methodName.equals("createElement")) {
                            final PsiElement resolved = OpenapiResolveUtil.resolveReference(call);
                            if (resolved instanceof Method && ((Method) resolved).getFQN().equals("\\DOMDocument.createElement")) {
                                return ExpressionType.DOM_ELEMENT_CREATE;
                            }
                        }
                    }
                    /* allow all assignations afterwards */
                    return ExpressionType.ASSIGNMENT;
                }
                /* accumulating something in external container */
                if (variable instanceof ArrayAccessExpression) {
                    final ArrayAccessExpression storage = (ArrayAccessExpression) variable;
                    if (null == storage.getIndex() || null == storage.getIndex().getValue()) {
                        return ExpressionType.ACCUMULATE_IN_ARRAY;
                    }
                }
            }
            return ExpressionType.OTHER;
        }
    };
}
Also used : BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) Set(java.util.Set) HashMap(java.util.HashMap) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) HashSet(java.util.HashSet) Nullable(org.jetbrains.annotations.Nullable) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) Stream(java.util.stream.Stream) Map(java.util.Map) PsiComment(com.intellij.psi.PsiComment) PsiElement(com.intellij.psi.PsiElement) PsiFile(com.intellij.psi.PsiFile) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) PhpElementTypes(com.jetbrains.php.lang.parser.PhpElementTypes) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) ArrayUtils(org.apache.commons.lang.ArrayUtils) javax.swing(javax.swing) Set(java.util.Set) HashSet(java.util.HashSet) HashMap(java.util.HashMap) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiFile(com.intellij.psi.PsiFile) PsiElement(com.intellij.psi.PsiElement) HashSet(java.util.HashSet) PsiComment(com.intellij.psi.PsiComment) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

PsiComment (com.intellij.psi.PsiComment)68 PsiElement (com.intellij.psi.PsiElement)47 PsiWhiteSpace (com.intellij.psi.PsiWhiteSpace)21 Nullable (org.jetbrains.annotations.Nullable)19 IElementType (com.intellij.psi.tree.IElementType)11 ArrayList (java.util.ArrayList)11 TextRange (com.intellij.openapi.util.TextRange)9 PsiFile (com.intellij.psi.PsiFile)8 NotNull (org.jetbrains.annotations.NotNull)8 FoldingDescriptor (com.intellij.lang.folding.FoldingDescriptor)7 ASTNode (com.intellij.lang.ASTNode)6 Matcher (java.util.regex.Matcher)6 Document (com.intellij.openapi.editor.Document)4 LineRange (com.intellij.codeInsight.editorActions.moveUpDown.LineRange)3 PsiDocComment (com.intellij.psi.javadoc.PsiDocComment)3 XmlTag (com.intellij.psi.xml.XmlTag)3 Commenter (com.intellij.lang.Commenter)2 Language (com.intellij.lang.Language)2 Editor (com.intellij.openapi.editor.Editor)2 UnfairTextRange (com.intellij.openapi.util.UnfairTextRange)2