IDEA-67727 - now for real

This commit is contained in:
sweinreuter
2011-04-11 19:31:56 +02:00
parent e592896a4e
commit 0acbfbfd17
6 changed files with 76 additions and 27 deletions
@@ -20,23 +20,29 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.intellij.lang.xpath.psi.XPathBinaryExpression;
import org.intellij.lang.xpath.psi.XPathExpression;
import org.intellij.lang.xpath.psi.impl.XPathChangeUtil;
import org.jetbrains.annotations.NotNull;
class ExpressionReplacementFix implements IntentionAction {
private final String myReplacement;
private final XPathBinaryExpression myExpr;
private final String myDisplay;
private final XPathExpression myExpr;
public ExpressionReplacementFix(String replacement, XPathBinaryExpression expr) {
public ExpressionReplacementFix(String replacement, XPathExpression expr) {
this(replacement, replacement, expr);
}
public ExpressionReplacementFix(String replacement, String display, XPathExpression expression) {
myReplacement = replacement;
myExpr = expr;
myDisplay = display;
myExpr = expression;
}
@NotNull
@Override
public String getText() {
return "Replace with '" + myReplacement + "'";
return "Replace with '" + myDisplay + "'";
}
@NotNull
@@ -15,6 +15,7 @@
*/
package org.intellij.lang.xpath.validation;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.lang.ASTNode;
@@ -24,20 +25,21 @@ import com.intellij.lang.annotation.Annotator;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import org.intellij.lang.xpath.XPath2TokenTypes;
import org.intellij.lang.xpath.XPathElementType;
import org.intellij.lang.xpath.XPathFileType;
import org.intellij.lang.xpath.context.functions.Function;
import org.jetbrains.annotations.NotNull;
import org.intellij.lang.xpath.XPathTokenTypes;
import org.intellij.lang.xpath.context.ContextProvider;
import org.intellij.lang.xpath.context.NamespaceContext;
import org.intellij.lang.xpath.context.VariableContext;
import org.intellij.lang.xpath.context.XPathVersion;
import org.intellij.lang.xpath.context.functions.Function;
import org.intellij.lang.xpath.context.functions.Parameter;
import org.intellij.lang.xpath.psi.*;
import org.intellij.lang.xpath.psi.impl.PrefixedNameImpl;
import org.intellij.lang.xpath.psi.impl.XPathChangeUtil;
import org.jetbrains.annotations.NotNull;
import javax.xml.namespace.QName;
import java.util.Arrays;
@@ -104,25 +106,57 @@ public final class XPathAnnotator extends XPath2ElementVisitor implements Annota
}
@Override
public void visitXPathBinaryExpression(final XPathBinaryExpression o) {
if (o.getContainingFile().getLanguage() == XPathFileType.XPATH2.getLanguage()) {
final XPathExpression operand = o.getLOperand();
final XPathElementType operator = o.getOperator();
if (operand instanceof XPathNumber) {
if (operator != XPathTokenTypes.STAR && XPath2TokenTypes.KEYWORDS.contains(operator)) {
final String op = o.getOperationSign();
if (o.getText().startsWith(operand.getText() + op)) {
final Annotation annotation =
myHolder.createErrorAnnotation(o, "Number literal must be followed by whitespace in XPath 2");
public void visitXPathNumber(XPathNumber number) {
if (number.getXPathVersion() == XPathVersion.V2) {
final XPathExpression rOperand = o.getROperand();
if (rOperand != null) {
final String replacement = operand.getText() + " " + op + " " + rOperand.getText();
annotation.registerFix(new ExpressionReplacementFix(replacement, o));
final PsiElement leaf = PsiTreeUtil.nextLeaf(number);
if (leaf != null) {
final IElementType elementType = leaf.getNode().getElementType();
if (elementType != XPathTokenTypes.STAR && XPath2TokenTypes.KEYWORDS.contains(elementType)) {
final TextRange range = TextRange.create(number.getTextRange().getStartOffset(), leaf.getTextRange().getEndOffset());
final Annotation annotation =
myHolder.createErrorAnnotation(range, "Number literal must be followed by whitespace in XPath 2");
final XPathBinaryExpression expression = PsiTreeUtil.getParentOfType(number, XPathBinaryExpression.class, true);
if (expression != null) {
final XPathExpression lOperand = expression.getLOperand();
if (number == lOperand) {
final XPathExpression rOperand = expression.getROperand();
if (rOperand != null) {
final String display = number.getText() + " " + expression.getOperationSign();
final String replacement = display + " " + rOperand.getText();
assert PsiEquivalenceUtil.areElementsEquivalent(expression, XPathChangeUtil.createExpression(expression, replacement));
annotation.registerFix(new ExpressionReplacementFix(replacement, display, expression));
}
} else if (number == expression.getROperand()) {
final PsiElement next = PsiTreeUtil.getParentOfType(PsiTreeUtil.nextLeaf(expression), XPathExpression.class, true);
if (next instanceof XPathBinaryExpression) {
final XPathBinaryExpression left = (XPathBinaryExpression)next;
final XPathExpression rOperand = left.getROperand();
if (rOperand != null && lOperand != null) {
final String display = number.getText() + " " + left.getOperationSign();
final String replacement = lOperand.getText() + " " + expression.getOperationSign() + " " + display + " " + rOperand.getText();
assert PsiEquivalenceUtil.areElementsEquivalent(next, XPathChangeUtil.createExpression(next, replacement));
annotation.registerFix(new ExpressionReplacementFix(replacement, display, (XPathExpression)next));
}
}
}
}
}
}
}
super.visitXPathNumber(number);
}
@Override
public void visitXPathBinaryExpression(final XPathBinaryExpression o) {
if (o.getXPathVersion() == XPathVersion.V2) {
final XPathElementType operator = o.getOperator();
final XPathExpression operand = o.getLOperand();
if (XPath2TokenTypes.COMP_OPS.contains(operator)) {
if (operand instanceof XPathBinaryExpression && XPath2TokenTypes.COMP_OPS.contains(((XPathBinaryExpression)operand).getOperator())) {
final Annotation annotation = myHolder.createErrorAnnotation(o, "Consecutive comparison is not allowed in XPath 2");
@@ -224,14 +258,18 @@ public final class XPathAnnotator extends XPath2ElementVisitor implements Annota
if (call.getQName().getPrefix() != null && contextProvider.getFunctionContext().allowsExtensions()) {
final PsiReference[] references = call.getReferences();
if (references.length > 1 && references[1].resolve() == null) {
final Annotation ann = holder.createErrorAnnotation(qName.getPrefixNode(), "Extension namespace prefix '" + qName.getPrefix() + "' has not been declared");
final Annotation ann = holder.createErrorAnnotation(qName.getPrefixNode(), "Extension namespace prefix '" +
qName.getPrefix() +
"' has not been declared");
ann.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
} else {
if (name != null) {
holder.createWarningAnnotation(node, "Unknown function '" + name + "'");
} else if (qName.getPrefixNode() != null) {
final Annotation ann = holder.createErrorAnnotation(qName.getPrefixNode(), "Extension namespace prefix '" + qName.getPrefix() + "' has not been declared");
final Annotation ann = holder.createErrorAnnotation(qName.getPrefixNode(), "Extension namespace prefix '" +
qName.getPrefix() +
"' has not been declared");
ann.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
}
@@ -33,6 +33,10 @@ public class XPath2HighlightingTest extends XPath2HighlightingTestBase {
doXPathHighlighting();
}
public void testNumberFollowedByToken2() throws Throwable {
doXPathHighlighting();
}
@Override
protected String getSubPath() {
return "xpath/highlighting";
@@ -1 +1 @@
10div 2 + 3 mod 2
10div 2 + 3 mod 2div 5
@@ -1 +1 @@
<error descr="Number literal must be followed by whitespace in XPath 2">10div 2</error> + 3 mod 2
<error descr="Number literal must be followed by whitespace in XPath 2">10div</error> 2 + 3 mod 2
@@ -0,0 +1 @@
5 div <error descr="Number literal must be followed by whitespace in XPath 2">2gt</error> 1