Commit a162f710 authored by Bas Leijdekkers's avatar Bas Leijdekkers
Browse files

SSR: simpler Replacer

parent 2b0b340c
Showing with 205 additions and 256 deletions
+205 -256
// 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.refactoring.typeMigration; package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.fileTypes.StdFileTypes;
...@@ -89,8 +90,7 @@ public class TypeConversionDescriptor extends TypeConversionDescriptorBase { ...@@ -89,8 +90,7 @@ public class TypeConversionDescriptor extends TypeConversionDescriptorBase {
final ReplaceOptions options = new ReplaceOptions(); final ReplaceOptions options = new ReplaceOptions();
final MatchOptions matchOptions = options.getMatchOptions(); final MatchOptions matchOptions = options.getMatchOptions();
matchOptions.setFileType(StdFileTypes.JAVA); matchOptions.setFileType(StdFileTypes.JAVA);
final Replacer replacer = new Replacer(project, null); final String replacement = Replacer.testReplace(expression.getText(), stringToReplace, replaceByString, options, project);
final String replacement = replacer.testReplace(expression.getText(), stringToReplace, replaceByString, options);
return (PsiExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(expression.replace( return (PsiExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(expression.replace(
JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(replacement, expression))); JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(replacement, expression)));
} }
......
/* // 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.
* 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.plugin.replace.impl; package com.intellij.structuralsearch.plugin.replace.impl;
import com.intellij.codeInsight.template.Template; import com.intellij.codeInsight.template.Template;
...@@ -47,59 +33,62 @@ import java.util.List; ...@@ -47,59 +33,62 @@ import java.util.List;
*/ */
public class Replacer { public class Replacer {
private final Project project; private final Project project;
private ReplacementBuilder replacementBuilder; private final ReplaceOptions options;
private ReplaceOptions options; private final StructuralReplaceHandler replaceHandler;
private ReplacementContext context; private final ReplacementBuilder replacementBuilder;
private StructuralReplaceHandler replaceHandler;
private PsiElement lastAffectedElement = null; private PsiElement lastAffectedElement = null;
public Replacer(Project project, ReplaceOptions options) { public Replacer(Project project, ReplaceOptions options) {
this.project = project; this.project = project;
this.options = options; this.options = options;
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(options.getMatchOptions().getFileType());
assert profile != null;
replaceHandler = profile.getReplaceHandler(new ReplacementContext(options, project));
assert replaceHandler != null;
replacementBuilder = new ReplacementBuilder(this.project, this.options);
} }
public static String stripTypedVariableDecoration(final String type) { public static String stripTypedVariableDecoration(final String type) {
return type.substring(1,type.length()-1); return type.substring(1, type.length() - 1);
} }
public static int insertSubstitution(StringBuilder result, int offset, final ParameterInfo info, String image) { public static int insertSubstitution(StringBuilder result, int offset, final ParameterInfo info, String image) {
if (image.length() > 0) result.insert(offset+ info.getStartIndex(),image); if (!image.isEmpty()) {
offset += image.length(); result.insert(offset + info.getStartIndex(), image);
offset += image.length();
}
return offset; return offset;
} }
public String testReplace(String in, String what, String by, ReplaceOptions options) throws IncorrectOperationException { public static String testReplace(String in, String what, String by, ReplaceOptions options, Project project) {
return testReplace(in, what, by, options, false); return testReplace(in, what, by, options, project, false);
} }
public String testReplace(String in, String what, String by, ReplaceOptions options, boolean filePattern) { public static String testReplace(String in, String what, String by, ReplaceOptions options, Project project, boolean sourceIsFile) {
FileType type = options.getMatchOptions().getFileType(); FileType type = options.getMatchOptions().getFileType();
return testReplace(in, what, by, options, filePattern, false, type, null); return testReplace(in, what, by, options, project, sourceIsFile, false, type, null);
} }
public String testReplace(String in, String what, String by, ReplaceOptions options, boolean filePattern, boolean createPhysicalFile, public static String testReplace(String in, String what, String by, ReplaceOptions replaceOptions, Project project, boolean sourceIsFile,
FileType sourceFileType, Language sourceDialect) { boolean createPhysicalFile, FileType sourceFileType, Language sourceDialect) {
this.options = options; replaceOptions.setReplacement(by);
final MatchOptions matchOptions = this.options.getMatchOptions();
this.options.setReplacement(by);
replacementBuilder=null;
context = null;
replaceHandler = null;
final MatchOptions matchOptions = replaceOptions.getMatchOptions();
matchOptions.clearVariableConstraints(); matchOptions.clearVariableConstraints();
matchOptions.fillSearchCriteria(what); matchOptions.fillSearchCriteria(what);
Matcher.validate(project, matchOptions); Matcher.validate(project, matchOptions);
checkSupportedReplacementPattern(project, options); checkSupportedReplacementPattern(project, replaceOptions);
Matcher matcher = new Matcher(project); final Replacer replacer = new Replacer(project, replaceOptions);
final Matcher matcher = new Matcher(project);
try { try {
PsiElement firstElement, lastElement, parent; final PsiElement firstElement, lastElement, parent;
if (options.getMatchOptions().getScope() == null) { if (replaceOptions.getMatchOptions().getScope() == null) {
PsiElement[] elements = MatcherImplUtil.createTreeFromText( final PsiElement[] elements = MatcherImplUtil.createTreeFromText(
in, in,
filePattern ? PatternTreeContext.File : PatternTreeContext.Block, sourceIsFile ? PatternTreeContext.File : PatternTreeContext.Block,
sourceFileType, sourceDialect, null, sourceFileType, sourceDialect, null,
project, project,
createPhysicalFile createPhysicalFile
...@@ -111,7 +100,7 @@ public class Replacer { ...@@ -111,7 +100,7 @@ public class Replacer {
matchOptions.setScope(new LocalSearchScope(elements)); matchOptions.setScope(new LocalSearchScope(elements));
} else { } else {
parent = ((LocalSearchScope)options.getMatchOptions().getScope()).getScope()[0]; parent = ((LocalSearchScope)matchOptions.getScope()).getScope()[0];
firstElement = parent.getFirstChild(); firstElement = parent.getFirstChild();
lastElement = parent.getLastChild(); lastElement = parent.getLastChild();
} }
...@@ -121,24 +110,24 @@ public class Replacer { ...@@ -121,24 +110,24 @@ public class Replacer {
final List<ReplacementInfo> resultPtrList = new SmartList<>(); final List<ReplacementInfo> resultPtrList = new SmartList<>();
for (final MatchResult result : sink.getMatches()) { for (final MatchResult result : sink.getMatches()) {
resultPtrList.add(buildReplacement(result)); resultPtrList.add(replacer.buildReplacement(result));
} }
int startOffset = firstElement.getTextRange().getStartOffset(); int startOffset = firstElement.getTextRange().getStartOffset();
int endOffset = filePattern ? 0 : parent.getTextLength() - lastElement.getTextRange().getEndOffset(); int endOffset = sourceIsFile ? 0 : parent.getTextLength() - lastElement.getTextRange().getEndOffset();
// get nodes from text may contain // get nodes from text may contain
PsiElement prevSibling = firstElement.getPrevSibling(); final PsiElement prevSibling = firstElement.getPrevSibling();
if (prevSibling instanceof PsiWhiteSpace) { if (prevSibling instanceof PsiWhiteSpace) {
startOffset -= prevSibling.getTextLength() - 1; startOffset -= prevSibling.getTextLength() - 1;
} }
PsiElement nextSibling = lastElement.getNextSibling(); final PsiElement nextSibling = lastElement.getNextSibling();
if (nextSibling instanceof PsiWhiteSpace) { if (nextSibling instanceof PsiWhiteSpace) {
endOffset -= nextSibling.getTextLength() - 1; endOffset -= nextSibling.getTextLength() - 1;
} }
replaceAll(resultPtrList); replacer.replaceAll(resultPtrList);
String result = parent.getText(); String result = parent.getText();
result = result.substring(startOffset); result = result.substring(startOffset);
...@@ -150,17 +139,13 @@ public class Replacer { ...@@ -150,17 +139,13 @@ public class Replacer {
throw new IncorrectOperationException(e); throw new IncorrectOperationException(e);
} }
finally { finally {
options.getMatchOptions().setScope(null); matchOptions.setScope(null);
} }
} }
public void replaceAll(final List<ReplacementInfo> infos) { public void replaceAll(final List<ReplacementInfo> infos) {
for (ReplacementInfo info : infos) { for (ReplacementInfo info : infos) {
PsiElement element = info.getMatch(0); replaceHandler.prepare(info);
initContextAndHandler(element);
if (replaceHandler != null) {
replaceHandler.prepare(info);
}
} }
((ApplicationImpl)ApplicationManager.getApplication()).runWriteActionWithCancellableProgressInDispatchThread( ((ApplicationImpl)ApplicationManager.getApplication()).runWriteActionWithCancellableProgressInDispatchThread(
...@@ -201,11 +186,7 @@ public class Replacer { ...@@ -201,11 +186,7 @@ public class Replacer {
} }
public void replace(ReplacementInfo info) { public void replace(ReplacementInfo info) {
initContextAndHandler(info.getMatch(0)); replaceHandler.prepare(info);
if (replaceHandler != null) {
replaceHandler.prepare(info);
}
reformatAndPostProcess(doReplace(info)); reformatAndPostProcess(doReplace(info));
} }
...@@ -218,11 +199,7 @@ public class Replacer { ...@@ -218,11 +199,7 @@ public class Replacer {
final PsiElement elementParent = StructuralSearchUtil.getPresentableElement(element).getParent(); final PsiElement elementParent = StructuralSearchUtil.getPresentableElement(element).getParent();
CodeStyleManager.getInstance(project).performActionWithFormatterDisabled( CodeStyleManager.getInstance(project).performActionWithFormatterDisabled(
(Runnable)() -> { (Runnable)() -> replaceHandler.replace(info, options)
if (replaceHandler != null) {
replaceHandler.replace(info, options);
}
}
); );
if (!elementParent.isValid() || !elementParent.isWritable()) { if (!elementParent.isValid() || !elementParent.isWritable()) {
...@@ -245,21 +222,7 @@ public class Replacer { ...@@ -245,21 +222,7 @@ public class Replacer {
final int parentOffset = elementParent.getTextRange().getStartOffset(); final int parentOffset = elementParent.getTextRange().getStartOffset();
CodeStyleManager.getInstance(project).reformatRange(containingFile, parentOffset, parentOffset + elementParent.getTextLength(), true); CodeStyleManager.getInstance(project).reformatRange(containingFile, parentOffset, parentOffset + elementParent.getTextLength(), true);
} }
if (replaceHandler != null) { replaceHandler.postProcess(elementParent, options);
replaceHandler.postProcess(elementParent, options);
}
}
private void initContextAndHandler(PsiElement psiContext) {
if (context == null) {
context = new ReplacementContext(options, project);
}
if (replaceHandler == null) {
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByPsiElement(psiContext);
if (profile != null) {
replaceHandler = profile.getReplaceHandler(context);
}
}
} }
public static void handleComments(final PsiElement el, final PsiElement replacement, ReplacementInfo replacementInfo) { public static void handleComments(final PsiElement el, final PsiElement replacement, ReplacementInfo replacementInfo) {
...@@ -350,10 +313,6 @@ public class Replacer { ...@@ -350,10 +313,6 @@ public class Replacer {
public ReplacementInfo buildReplacement(MatchResult result) { public ReplacementInfo buildReplacement(MatchResult result) {
final ReplacementInfoImpl replacementInfo = new ReplacementInfoImpl(result, project); final ReplacementInfoImpl replacementInfo = new ReplacementInfoImpl(result, project);
if (replacementBuilder == null) {
replacementBuilder = new ReplacementBuilder(project, options);
}
replacementInfo.setReplacement(replacementBuilder.process(result, replacementInfo, options.getMatchOptions().getFileType())); replacementInfo.setReplacement(replacementBuilder.process(result, replacementInfo, options.getMatchOptions().getFileType()));
return replacementInfo; return replacementInfo;
......
// 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; package com.intellij.structuralsearch;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
...@@ -7,13 +7,11 @@ import com.intellij.openapi.util.io.FileUtilRt; ...@@ -7,13 +7,11 @@ import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.pom.java.LanguageLevel; import com.intellij.pom.java.LanguageLevel;
import com.intellij.structuralsearch.plugin.replace.ReplaceOptions; import com.intellij.structuralsearch.plugin.replace.ReplaceOptions;
import com.intellij.structuralsearch.plugin.replace.impl.Replacer;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
abstract class StructuralReplaceTestCase extends LightQuickFixTestCase { abstract class StructuralReplaceTestCase extends LightQuickFixTestCase {
protected Replacer replacer;
protected ReplaceOptions options; protected ReplaceOptions options;
@Override @Override
...@@ -23,7 +21,6 @@ abstract class StructuralReplaceTestCase extends LightQuickFixTestCase { ...@@ -23,7 +21,6 @@ abstract class StructuralReplaceTestCase extends LightQuickFixTestCase {
LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_4); LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_4);
options = new ReplaceOptions(); options = new ReplaceOptions();
replacer = new Replacer(getProject(), null);
} }
protected String loadFile(String fileName) throws IOException { protected String loadFile(String fileName) throws IOException {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
package com.intellij.structuralsearch; package com.intellij.structuralsearch;
import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.structuralsearch.plugin.replace.impl.Replacer;
import com.intellij.testFramework.PlatformTestUtil; import com.intellij.testFramework.PlatformTestUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
...@@ -13,22 +14,14 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase { ...@@ -13,22 +14,14 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase {
super.setUp(); super.setUp();
options.getMatchOptions().setFileType(StdFileTypes.XML); options.getMatchOptions().setFileType(StdFileTypes.XML);
} }
public void testReplaceXmlAndHtml() { public void testReplaceXmlAndHtml() {
String s1 = "<a/>"; String s1 = "<a/>";
String s2 = "<a/>"; String s2 = "<a/>";
String s3 = "<a><b/></a>"; String s3 = "<a><b/></a>";
String expectedResult = "<a><b/></a>"; String expectedResult = "<a><b/></a>";
String actualResult = replacer.testReplace(s1,s2,s3,options); assertEquals("First tag replacement", expectedResult, Replacer.testReplace(s1, s2, s3, options, getProject()));
assertEquals(
"First tag replacement",
expectedResult,
actualResult
);
String s4 = "<group id=\"EditorTabPopupMenu\">\n" + String s4 = "<group id=\"EditorTabPopupMenu\">\n" +
" <reference id=\"Compile\"/>\n" + " <reference id=\"Compile\"/>\n" +
...@@ -42,7 +35,6 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase { ...@@ -42,7 +35,6 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase {
String s5 = "<reference id=\"'_Value\"/>"; String s5 = "<reference id=\"'_Value\"/>";
String s6 = "<reference ref=\"$Value$\"/>"; String s6 = "<reference ref=\"$Value$\"/>";
actualResult = replacer.testReplace(s4,s5,s6,options);
expectedResult = "<group id=\"EditorTabPopupMenu\">\n" + expectedResult = "<group id=\"EditorTabPopupMenu\">\n" +
" <reference ref=\"Compile\"/>\n" + " <reference ref=\"Compile\"/>\n" +
" <reference ref=\"RunContextPopupGroup\"/>\n" + " <reference ref=\"RunContextPopupGroup\"/>\n" +
...@@ -52,42 +44,32 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase { ...@@ -52,42 +44,32 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase {
" <separator/>\n" + " <separator/>\n" +
" <reference ref=\"ExternalToolsGroup\"/>\n" + " <reference ref=\"ExternalToolsGroup\"/>\n" +
"</group>"; "</group>";
assertEquals( assertEquals("Replace tag", expectedResult, Replacer.testReplace(s4, s5, s6, options, getProject()));
"Replace tag",
expectedResult,
actualResult
);
String s7 = "<h4 class=\"a\">My title<aaa>ZZZZ</aaa> My title 3</h4>\n" + String s7 = "<h4 class=\"a\">My title<aaa>ZZZZ</aaa> My title 3</h4>\n" +
"<h4>My title 2</h4>"; "<h4>My title 2</h4>";
String s8 = "<h4 class=\"a\">'_Content*</h4>"; String s8 = "<h4 class=\"a\">'_Content*</h4>";
String s9 = "<h5>$Content$</h5>"; String s9 = "<h5>$Content$</h5>";
actualResult = replacer.testReplace(s7,s8,s9,options);
expectedResult = "<h5>My title <aaa>ZZZZ</aaa> My title 3</h5>\n" + expectedResult = "<h5>My title <aaa>ZZZZ</aaa> My title 3</h5>\n" +
"<h4>My title 2</h4>"; "<h4>My title 2</h4>";
assertEquals("Replace tag saving content", expectedResult, Replacer.testReplace(s7, s8, s9, options, getProject()));
assertEquals(
"Replace tag saving content",
expectedResult,
actualResult
);
expectedResult = "\n" + expectedResult = "\n" +
"<h4>My title 2</h4>"; "<h4>My title 2</h4>";
assertEquals("Delete tag", expectedResult, replacer.testReplace(s7, s8, "", options)); assertEquals("Delete tag", expectedResult, Replacer.testReplace(s7, s8, "", options, getProject()));
String what = "<'_H:h4 class=\"a\">'_Content*</'_H>"; String what = "<'_H:h4 class=\"a\">'_Content*</'_H>";
String by = "<$H$>$Content$</$H$>"; String by = "<$H$>$Content$</$H$>";
expectedResult = "<h4>My title <aaa>ZZZZ</aaa> My title 3</h4>\n" + expectedResult = "<h4>My title <aaa>ZZZZ</aaa> My title 3</h4>\n" +
"<h4>My title 2</h4>"; "<h4>My title 2</h4>";
assertEquals("Replace with variable", expectedResult, replacer.testReplace(s7, what, by, options)); assertEquals("Replace with variable", expectedResult, Replacer.testReplace(s7, what, by, options, getProject()));
String in = "<b>Cry 'Havoc!', and <i>let slip the<br> dogs of war</i></b>"; String in = "<b>Cry 'Havoc!', and <i>let slip the<br> dogs of war</i></b>";
what = "<'_Tag:b >'_Content2*</'_Tag>"; what = "<'_Tag:b >'_Content2*</'_Tag>";
by = "<$Tag$ id=\"unique\">$Content2$</$Tag$>"; by = "<$Tag$ id=\"unique\">$Content2$</$Tag$>";
expectedResult = "<b id=\"unique\">Cry 'Havoc!', and <i>let slip the<br> dogs of war</i></b>"; expectedResult = "<b id=\"unique\">Cry 'Havoc!', and <i>let slip the<br> dogs of war</i></b>";
assertEquals("Replace complex content with variable", expectedResult, replacer.testReplace(in, what, by, options)); assertEquals("Replace complex content with variable", expectedResult, Replacer.testReplace(in, what, by, options, getProject()));
} }
public void testHtmlReplacement1() throws IOException { public void testHtmlReplacement1() throws IOException {
...@@ -108,7 +90,7 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase { ...@@ -108,7 +90,7 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase {
String by = "<input $a$ id=\"someId1\" />"; String by = "<input $a$ id=\"someId1\" />";
String expected = "<input class=\"other\" type=\"text\" ng-model=\"someModel\" placeholder=\"Some placeholder\" id=\"someId1\" />"; String expected = "<input class=\"other\" type=\"text\" ng-model=\"someModel\" placeholder=\"Some placeholder\" id=\"someId1\" />";
String actual = replacer.testReplace(in, what, by, options); String actual = Replacer.testReplace(in, what, by, options, getProject());
assertEquals(expected, actual); assertEquals(expected, actual);
} }
...@@ -130,7 +112,7 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase { ...@@ -130,7 +112,7 @@ public class XmlStructuralReplaceTest extends StructuralReplaceTestCase {
String replacement = loadFile(replacementFileName); String replacement = loadFile(replacementFileName);
String expectedResult = loadFile(outFileName); String expectedResult = loadFile(outFileName);
assertEquals(message, expectedResult, replacer.testReplace(content,pattern,replacement,options,filepattern)); assertEquals(message, expectedResult, Replacer.testReplace(content, pattern, replacement, options,getProject(), filepattern));
options.getMatchOptions().setFileType(StdFileTypes.XML); options.getMatchOptions().setFileType(StdFileTypes.XML);
} }
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment