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