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);
}
}
}
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;
}
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;
}
};
}
Aggregations