package org.elixir_lang.inspection;
import com.intellij.codeInspection.*;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import org.elixir_lang.psi.*;
import org.elixir_lang.psi.call.arguments.star.NoParentheses;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class KeywordsNotAtEnd extends LocalInspectionTool {
@Nls
@NotNull
@Override
public String getDisplayName() {
return "Keywords not at end";
}
@Nls
@NotNull
@Override
public String getGroupDisplayName() {
return "Elixir";
}
@NotNull
@Override
public String getShortName() {
return "KeywordsNotAtEnd";
}
public boolean isEnabledByDefault() {
return true;
}
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
checkFile(file, problemsHolder);
return problemsHolder.getResultsArray();
}
private static void checkFile(final PsiFile file, final ProblemsHolder problemsHolder) {
file.accept(
new PsiRecursiveElementWalkingVisitor() {
@Override
public void visitElement(PsiElement element) {
PsiElement keywordsElementNotAtEnd = null;
PsiElement listElement = null;
if (element instanceof ElixirNoParenthesesKeywords) {
ElixirNoParenthesesKeywords keywordsElement = (ElixirNoParenthesesKeywords) element;
PsiElement parent = element.getParent();
if (parent instanceof ElixirMatchedWhenOperation) {
PsiElement grandParent = parent.getParent();
if (grandParent instanceof ElixirNoParenthesesOneArgument) {
listElement = grandParent;
keywordsElementNotAtEnd = findKeywordsElementNotAtEnd(
listElement,
parent,
keywordsElement
);
}
} else if (parent instanceof ElixirNoParenthesesOneArgument) {
PsiElement grandParent = parent.getParent();
if (grandParent instanceof NoParentheses) {
PsiElement greatGrandParent = grandParent.getParent();
if (greatGrandParent instanceof ElixirNoParenthesesOneArgument) {
listElement = greatGrandParent;
keywordsElementNotAtEnd = findKeywordsElementNotAtEnd(
listElement,
grandParent,
keywordsElement
);
} else if (greatGrandParent instanceof ElixirParenthesesArguments) {
listElement = greatGrandParent;
keywordsElementNotAtEnd = findKeywordsElementNotAtEnd(
listElement,
grandParent,
keywordsElement
);
}
}
} else if (parent instanceof ElixirUnmatchedWhenOperation) {
PsiElement grandParent = parent.getParent();
if (grandParent instanceof ElixirParenthesesArguments) {
listElement = grandParent;
keywordsElementNotAtEnd = findKeywordsElementNotAtEnd(
listElement,
parent,
keywordsElement
);
}
} else if (parent instanceof NoParentheses) {
PsiElement grandParent = parent.getParent();
if (grandParent instanceof ElixirNoParenthesesManyStrictNoParenthesesExpression) {
PsiElement greatGrandParent = grandParent.getParent();
if (greatGrandParent instanceof ElixirNoParenthesesOneArgument) {
listElement = greatGrandParent;
keywordsElementNotAtEnd = findKeywordsElementNotAtEnd(
listElement,
grandParent,
keywordsElement
);
}
}
}
}
if (keywordsElementNotAtEnd != null) {
TextRange listElementTextRange = listElement.getTextRange();
TextRange keywordsTextRange = keywordsElementNotAtEnd.getTextRange();
int listElementStartOffset = listElementTextRange.getStartOffset();
TextRange relativeTextRange = new TextRange(
keywordsTextRange.getStartOffset() - listElementStartOffset,
keywordsTextRange.getEndOffset() - listElementStartOffset
);
problemsHolder.registerProblem(
listElement,
"Keywords appear before the end of list. Move keywords after positional arguments.",
ProblemHighlightType.ERROR,
relativeTextRange
);
}
super.visitElement(element);
}
}
);
}
@Nullable
private static PsiElement findKeywordsElementNotAtEnd(PsiElement listElement, PsiElement listChildWithKeywords, ElixirNoParenthesesKeywords keywords) {
PsiElement keywordsElementNotAtEnd = null;
if (listElement.getLastChild() != listChildWithKeywords) {
keywordsElementNotAtEnd = keywords;
}
return keywordsElementNotAtEnd;
}
}