diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java
index f751d55dbccf..a82d53d8187d 100644
--- a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java
@@ -30,6 +30,7 @@ import com.intellij.structuralsearch.plugin.ui.SearchContext;
import com.intellij.structuralsearch.plugin.ui.UIUtil;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -259,6 +260,7 @@ public abstract class StructuralSearchProfile {
return offset;
}
+ @Contract("null -> false")
public boolean isIdentifier(PsiElement element) {
return false;
}
@@ -272,7 +274,7 @@ public abstract class StructuralSearchProfile {
return false;
}
- @NotNull
+ @Contract("!null -> !null")
public PsiElement getPresentableElement(PsiElement element) {
return isIdentifier(element) ? element.getParent() : element;
}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java
index 22eedd9d3fb6..ae420eb36545 100644
--- a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java
@@ -1,4 +1,4 @@
-// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.structuralsearch;
import com.intellij.lang.Language;
@@ -46,13 +46,11 @@ public class StructuralSearchUtil {
return !isIdentifier(element) ? element : element.getParent();
}
- @NotNull
- public static PsiElement getPresentableElement(@NotNull PsiElement element) {
+ @Contract("!null -> !null")
+ public static PsiElement getPresentableElement(PsiElement element) {
+ if (element == null) return null;
final StructuralSearchProfile profile = getProfileByPsiElement(element);
- if (profile == null) {
- return element;
- }
- return profile.getPresentableElement(element);
+ return profile == null ? element : profile.getPresentableElement(element);
}
private static StructuralSearchProfile[] getNewStyleProfiles() {
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java b/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java
index 86e136898a3f..38d4b6666dfe 100644
--- a/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java
@@ -153,83 +153,69 @@ public class XmlStructuralSearchProfile extends StructuralSearchProfile {
private static class MyReplaceHandler extends StructuralReplaceHandler {
private final ReplacementContext myContext;
- private MyReplaceHandler(ReplacementContext context) {
+ MyReplaceHandler(ReplacementContext context) {
myContext = context;
}
public void replace(ReplacementInfo info, ReplaceOptions options) {
- PsiElement elementToReplace = info.getMatch(0);
+ final PsiElement elementToReplace = StructuralSearchUtil.getPresentableElement(info.getMatch(0));
assert elementToReplace != null;
- PsiElement elementParent = elementToReplace.getParent();
- String replacementToMake = info.getReplacement();
- boolean listContext = elementToReplace.getParent() instanceof XmlTag;
+ final String replacementToMake = info.getReplacement();
+ final PsiElement elementParent = elementToReplace.getParent();
+ final boolean listContext = elementParent instanceof XmlTag;
if (listContext) {
doReplaceInContext(info, elementToReplace, replacementToMake, elementParent, myContext);
}
-
- PsiElement[] statements = ReplacerUtil.createTreeForReplacement(replacementToMake, PatternTreeContext.Block, myContext);
-
- if (statements.length > 0) {
- PsiElement replacement = ReplacerUtil.copySpacesAndCommentsBefore(elementToReplace, statements, replacementToMake, elementParent);
-
- // preserve comments
- Replacer.handleComments(elementToReplace, replacement, info);
- elementToReplace.replace(replacement);
- }
else {
- elementToReplace.delete();
- }
- }
- }
+ final PsiElement[] statements = ReplacerUtil.createTreeForReplacement(replacementToMake, PatternTreeContext.Block, myContext);
+ if (statements.length > 0) {
+ PsiElement replacement = ReplacerUtil.copySpacesAndCommentsBefore(elementToReplace, statements, replacementToMake, elementParent);
- private static void doReplaceInContext(ReplacementInfo info,
- PsiElement elementToReplace,
- String replacementToMake,
- PsiElement elementParent,
- ReplacementContext context) {
- PsiElement[] statements = ReplacerUtil.createTreeForReplacement(replacementToMake, PatternTreeContext.Block, context);
-
- if (statements.length > 1) {
- elementParent.addRangeBefore(statements[0], statements[statements.length - 1], elementToReplace);
- }
- else if (statements.length == 1) {
- PsiElement replacement = statements[0];
-
- Replacer.handleComments(elementToReplace, replacement, info);
-
- try {
- elementParent.addBefore(replacement, elementToReplace);
- }
- catch (IncorrectOperationException e) {
- elementToReplace.replace(replacement);
- }
- }
-
- final int matchSize = info.getMatchesCount();
-
- for (int i = 0; i < matchSize; ++i) {
- PsiElement element = info.getMatch(i);
-
- if (element == null) continue;
- PsiElement firstToDelete = element;
- PsiElement lastToDelete = element;
- PsiElement prevSibling = element.getPrevSibling();
- PsiElement nextSibling = element.getNextSibling();
-
- if (prevSibling instanceof PsiWhiteSpace) {
- firstToDelete = prevSibling;
- }
- else if (prevSibling == null && nextSibling instanceof PsiWhiteSpace) {
- lastToDelete = nextSibling;
- }
- if (nextSibling instanceof XmlText && i + 1 < matchSize) {
- final PsiElement next = info.getMatch(i + 1);
- if (next != null && next == nextSibling.getNextSibling()) {
- lastToDelete = nextSibling;
+ // preserve comments
+ Replacer.handleComments(elementToReplace, replacement, info);
+ elementToReplace.replace(replacement);
+ }
+ else {
+ elementToReplace.delete();
}
}
- element.getParent().deleteChildRange(firstToDelete, lastToDelete);
+ }
+
+ private static void doReplaceInContext(ReplacementInfo info,
+ PsiElement elementToReplace,
+ String replacementToMake,
+ PsiElement elementParent,
+ ReplacementContext context) {
+ final PsiElement[] statements = ReplacerUtil.createTreeForReplacement(replacementToMake, PatternTreeContext.Block, context);
+
+ if (statements.length > 1) {
+ elementParent.addRangeBefore(statements[0], statements[statements.length - 1], elementToReplace);
+ }
+ else if (statements.length == 1) {
+ Replacer.handleComments(elementToReplace, statements[0], info);
+ try {
+ elementParent.addBefore(statements[0], elementToReplace);
+ }
+ catch (IncorrectOperationException e) {
+ elementToReplace.replace(statements[0]);
+ }
+ }
+
+ final int matchSize = info.getMatchesCount();
+ for (int i = 0; i < matchSize; ++i) {
+ final PsiElement element = StructuralSearchUtil.getPresentableElement(info.getMatch(i));
+ final PsiElement prevSibling = element.getPrevSibling();
+ element.getParent().deleteChildRange(isWhitespace(prevSibling) ? prevSibling : element, element);
+ }
+ }
+
+ private static boolean isWhitespace(PsiElement element) {
+ if (element instanceof PsiWhiteSpace) return true;
+ if (!(element instanceof XmlText)) return false;
+ final PsiElement firstChild = element.getFirstChild();
+ final PsiElement lastChild = element.getLastChild();
+ return firstChild == lastChild && firstChild instanceof PsiWhiteSpace;
}
}
@@ -241,7 +227,7 @@ public class XmlStructuralSearchProfile extends StructuralSearchProfile {
private static class XmlPredefinedConfigurations {
private static final String HTML_XML = SSRBundle.message("xml_html.category");
- private static Configuration[] createPredefinedTemplates() {
+ static Configuration[] createPredefinedTemplates() {
return new Configuration[]{
createSearchTemplateInfo("xml tag", "<'a/>", HTML_XML, StdFileTypes.XML),
createSearchTemplateInfo("xml attribute", "<'_tag 'attribute=\"'_value\"/>", HTML_XML, StdFileTypes.XML),
diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/XmlStructuralReplaceTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/XmlStructuralReplaceTest.java
index 4a052cfed6c0..7fea74aaad08 100644
--- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/XmlStructuralReplaceTest.java
+++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/XmlStructuralReplaceTest.java
@@ -94,6 +94,38 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase {
assertEquals(expected, actual);
}
+ public void testRemoveAttribute() {
+ String in = "";
+ String what = "";
+ String by = "";
+ String expected = "";
+
+ assertEquals(expected, Replacer.testReplace(in, what, by, options, getProject()));
+ }
+
+ public void testRemoveTag() {
+ String in = "\n" +
+ " liberation\n" +
+ " remuneration\n" +
+ "";
+ String what = "<'tag:[regex( c )]>'_text'tag>";
+ String by = "";
+ String expected = "\n" +
+ " liberation\n" +
+ "";
+
+ assertEquals(expected, Replacer.testReplace(in, what, by, options, getProject()));
+ }
+
+ public void testReplaceAttributeValue() {
+ String in = "";
+ String what = "<'_tag '_attr:[regex( id )]=\\''value\\'>";
+ String by = "\"two\"";
+ String expected = "";
+
+ assertEquals(expected, Replacer.testReplace(in, what, by, options, getProject()));
+ }
+
@NotNull
@Override
protected String getTestDataPath() {