SSR: more robust xml/html matching when only whitespace is different

This commit is contained in:
Bas Leijdekkers
2017-05-29 22:49:39 +02:00
parent 6b64068d2a
commit 5460c16cc0
2 changed files with 46 additions and 54 deletions
@@ -65,7 +65,13 @@ public class XmlStructuralSearchProfile extends StructuralSearchProfile {
@NotNull
@Override
public NodeFilter getLexicalNodesFilter() {
return element -> element instanceof PsiWhiteSpace || element instanceof PsiErrorElement;
return element -> {
if (element instanceof XmlText) {
final PsiElement child = element.getFirstChild();
return child == element.getLastChild() && child instanceof PsiWhiteSpace;
}
return element instanceof PsiWhiteSpace || element instanceof PsiErrorElement;
};
}
@Override
@@ -1,16 +1,26 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.structuralsearch.impl.matcher;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.XmlElementVisitor;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.xml.*;
import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
import java.util.ArrayList;
import java.util.List;
import com.intellij.psi.PsiElement;
import com.intellij.psi.XmlElementVisitor;
import com.intellij.psi.xml.*;
import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
/**
* @author Eugene.Kudelevsky
@@ -40,8 +50,9 @@ public class XmlMatchingVisitor extends XmlElementVisitor {
}
if (myMatchingVisitor.getResult() && isTypedVar) {
MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(attribute.getName());
myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(another, myMatchingVisitor.getMatchContext()));
final SubstitutionHandler handler =
(SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(attribute.getName());
myMatchingVisitor.setResult(handler.handle(another, myMatchingVisitor.getMatchContext()));
}
}
@@ -49,14 +60,11 @@ public class XmlMatchingVisitor extends XmlElementVisitor {
final XmlAttributeValue another = (XmlAttributeValue)myMatchingVisitor.getElement();
final String text = value.getValue();
final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(text);
MatchingHandler handler;
if (isTypedVar && (handler = myMatchingVisitor.getMatchContext().getPattern().getHandler( text )) instanceof SubstitutionHandler) {
String text2 = another.getText();
int offset = text2.length() > 0 && ( text2.charAt(0) == '"' || text2.charAt(0) == '\'') ? 1:0;
myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(another, offset, text2.length() - offset,
myMatchingVisitor.getMatchContext()));
if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(text)) {
final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(text);
final String text2 = another.getText();
final int offset = text2.length() > 0 && (text2.charAt(0) == '"' || text2.charAt(0) == '\'') ? 1 : 0;
myMatchingVisitor.setResult(handler.handle(another, offset, text2.length() - offset, myMatchingVisitor.getMatchContext()));
} else {
myMatchingVisitor.setResult(matches(text, another.getValue()));
}
@@ -70,55 +78,33 @@ public class XmlMatchingVisitor extends XmlElementVisitor {
myMatchingVisitor.matchInAnyOrder(tag.getAttributes(), another.getAttributes()));
if(myMatchingVisitor.getResult()) {
final XmlTagChild[] contentChildren = tag.getValue().getChildren();
final XmlTagChild[] children = tag.getValue().getChildren();
if (contentChildren.length > 0) {
PsiElement[] patternNodes = contentChildren;
PsiElement[] matchedNodes = another.getValue().getChildren();
if (contentChildren.length != 1) {
patternNodes = filterOutWhitespace(patternNodes);
matchedNodes = filterOutWhitespace(matchedNodes);
}
final boolean result = myMatchingVisitor.matchSequentially(
new ArrayBackedNodeIterator(patternNodes),
new ArrayBackedNodeIterator(matchedNodes)
);
myMatchingVisitor.setResult(result);
final SsrFilteringNodeIterator patternNodes = new SsrFilteringNodeIterator(new ArrayBackedNodeIterator(children));
if (patternNodes.current() != null) {
final SsrFilteringNodeIterator matchNodes =
new SsrFilteringNodeIterator(new ArrayBackedNodeIterator(another.getValue().getChildren()));
myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(patternNodes, matchNodes));
}
}
if (myMatchingVisitor.getResult() && isTypedVar) {
final PsiElement[] children = another.getChildren();
if (children.length > 1) {
MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler( tag.getName() );
myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(children[1], myMatchingVisitor.getMatchContext()));
final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(tag.getName());
myMatchingVisitor.setResult(handler.handle(children[1], myMatchingVisitor.getMatchContext()));
}
}
}
private static PsiElement[] filterOutWhitespace(PsiElement[] children) {
final List<PsiElement> result = new ArrayList<>(children.length);
for(PsiElement child : children) {
if (child instanceof XmlText) {
final PsiElement[] grandChildren = child.getChildren();
if (grandChildren.length == 1 && grandChildren[0] instanceof PsiWhiteSpace) {
continue;
}
}
result.add(child);
}
return PsiUtilCore.toPsiElementArray(result);
}
@Override public void visitXmlText(XmlText text) {
final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(text);
final PsiElement element = myMatchingVisitor.getElement();
if (isTypedVar) {
myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(text, element));
} else {
myMatchingVisitor.setResult(matches(text.getText(), element.getText()));
}
else {
myMatchingVisitor.setResult(element instanceof XmlText && matches(text.getText().trim(), element.getText().trim()));
}
}