blob: 9192f5ff578638a1aa55f09a041691314c5721cd [file] [log] [blame]
package com.intellij.tasks.jira.jql.codeinsight;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.tasks.jira.jql.psi.*;
import org.jetbrains.annotations.NotNull;
import static com.intellij.openapi.editor.DefaultLanguageHighlighterColors.CONSTANT;
/**
* At first, it checks the following errors:
* <ol>
* <li>not-list operand was given inside 'IN' clauses and vice-versa
* <li>constants 'empty' or 'null' weren't used inside 'IS' clauses and vice-versa
* </ol>
*
*
* It also highlights JQL identifiers (fields and function names) differently
* from unquoted strings.
*
* @author Mikhail Golubev
*/
public class JqlAnnotator implements Annotator {
@Override
public void annotate(@NotNull PsiElement element, final @NotNull AnnotationHolder holder) {
// error checks
element.accept(new JqlElementVisitor() {
@Override
public void visitEmptyValue(JqlEmptyValue emptyValue) {
JqlSimpleClause clause = PsiTreeUtil.getParentOfType(emptyValue, JqlSimpleClause.class);
if (clause != null && !isEmptyClause(clause)) {
holder.createErrorAnnotation(emptyValue, String.format("Not expecting '%s' here", emptyValue.getText()));
}
}
@Override
public void visitJqlList(JqlList list) {
JqlSimpleClause clause = PsiTreeUtil.getParentOfType(list, JqlSimpleClause.class);
if (clause != null && !isListClause(clause)) {
holder.createErrorAnnotation(list, String.format("Not expecting list of values here"));
}
}
@Override
public void visitJqlSimpleClause(JqlSimpleClause clause) {
JqlOperand operand = clause.getOperand();
if (operand == null) {
return;
}
boolean operandIsListLiteral = operand instanceof JqlList;
boolean operandIsListFunction = false;
if (operand instanceof JqlFunctionCall) {
JqlFunctionCall functionCall = (JqlFunctionCall)operand;
JqlStandardFunction standardFunction = JqlStandardFunction.byName(functionCall.getFunctionName().getText());
operandIsListFunction = standardFunction != null && standardFunction.hasMultipleResults();
}
boolean hasListOperand = operandIsListLiteral || operandIsListFunction;
if (isListClause(clause) && !hasListOperand) {
holder.createErrorAnnotation(operand, "Expecting list of values here");
}
boolean hasEmptyOperand = operand instanceof JqlEmptyValue;
if (isEmptyClause(clause) && !hasEmptyOperand) {
holder.createErrorAnnotation(operand, "Expecting 'empty' or 'null' here");
}
}
@Override
public void visitJqlIdentifier(JqlIdentifier identifier) {
Annotation annotation = holder.createInfoAnnotation(identifier, null);
annotation.setEnforcedTextAttributes(CONSTANT.getDefaultAttributes());
}
});
}
private static boolean isEmptyClause(JqlTerminalClause clause) {
JqlTerminalClause.Type clauseType = clause.getType();
return clauseType == JqlTerminalClause.Type.IS || clauseType == JqlTerminalClause.Type.IS_NOT;
}
private static boolean isListClause(JqlTerminalClause clause) {
JqlTerminalClause.Type clauseType = clause.getType();
return clauseType != null && clauseType.isListOperator();
}
}