From bf501b550a44dd7ffeda8d8c12e81bdf26273023 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Tue, 20 Nov 2018 17:48:39 +0300 Subject: [PATCH] run live template to adjust template from selection --- .../StructuralSearchTemplateBuilder.java | 127 ++++++++++++++++++ .../plugin/ui/StructuralSearchDialog.java | 8 ++ .../SSTemplateBuilderTest.java | 38 ++++++ 3 files changed, 173 insertions(+) create mode 100644 platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchTemplateBuilder.java create mode 100644 platform/structuralsearch/testSource/com/intellij/structuralsearch/SSTemplateBuilderTest.java diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchTemplateBuilder.java b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchTemplateBuilder.java new file mode 100644 index 000000000000..8c416eb41135 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchTemplateBuilder.java @@ -0,0 +1,127 @@ +// 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.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.codeInsight.template.*; +import com.intellij.psi.*; +import com.intellij.structuralsearch.impl.matcher.CompiledPattern; +import com.intellij.structuralsearch.impl.matcher.compiler.PatternCompiler; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StructuralSearchTemplateBuilder { + + private final TemplateBuilder myBuilder; + private final PsiFile myPsiFile; + + private final PlaceholderCount myClassCount = new PlaceholderCount("Class"); + private final PlaceholderCount myVarCount = new PlaceholderCount("Var"); + private final PlaceholderCount myFunCount = new PlaceholderCount("Fun"); + + private int myShift; + + public StructuralSearchTemplateBuilder(@NotNull PsiFile psiFile) { + myBuilder = TemplateBuilderFactory.getInstance().createTemplateBuilder(psiFile); + myPsiFile = psiFile; + } + + public TemplateBuilder buildTemplate() { + + JavaRecursiveElementVisitor visitor = new JavaRecursiveElementVisitor() { + + @Override + public void visitIdentifier(PsiIdentifier identifier) { + PsiElement parent = identifier.getParent(); + if (parent instanceof PsiClass) { + replaceElement(identifier, myClassCount, true); + } + else if (parent instanceof PsiReferenceExpression) { + if (parent.getParent() instanceof PsiMethodCallExpression) + replaceElement(identifier, myFunCount, true); + else + replaceElement(identifier, myVarCount, false); + } + else if (parent instanceof PsiJavaCodeReferenceElement) { + replaceElement(identifier, myClassCount, false); + } + } + + @Override + public void visitReferenceList(PsiReferenceList list) { + PsiJavaCodeReferenceElement[] elements = list.getReferenceElements(); + for (PsiJavaCodeReferenceElement element : elements) { + replaceElement(element.getReferenceNameElement(), myClassCount, false); + } + } + }; + + MatchOptions matchOptions = new MatchOptions(); + String text = myPsiFile.getText(); + matchOptions.setSearchPattern(text); + CompiledPattern pattern = PatternCompiler.compilePattern(myPsiFile.getProject(), matchOptions, false); + if (pattern == null) + return myBuilder; + + PsiElement current = pattern.getNodes().current(); + if (current != null) { + myShift = current.getTextRange().getStartOffset(); + current.accept(visitor); + } + + return myBuilder; + } + + private void replaceElement(@Nullable PsiElement element, PlaceholderCount count, boolean preferOriginal) { + if (element == null) { + return; + } + myBuilder.replaceRange(element.getTextRange().shiftLeft(myShift), new MyExpression(count.getPlaceholder(), element, preferOriginal)); + } + + private static class MyExpression extends Expression { + + private final String myPlaceholder; + private final String myOriginalText; + private final boolean myPreferOriginal; + + private MyExpression(String placeholder, PsiElement original, boolean preferOriginal) { + myPlaceholder = placeholder; + myOriginalText = original.getText(); + myPreferOriginal = preferOriginal; + } + + @Nullable + @Override + public Result calculateResult(ExpressionContext context) { + return new TextResult(myPreferOriginal ? myOriginalText : myPlaceholder); + } + + @Nullable + @Override + public Result calculateQuickResult(ExpressionContext context) { + return calculateResult(context); + } + + @Nullable + @Override + public LookupElement[] calculateLookupItems(ExpressionContext context) { + LookupElement[] elements = {LookupElementBuilder.create(myPlaceholder), LookupElementBuilder.create(myOriginalText)}; + return myPreferOriginal ? ArrayUtil.reverseArray(elements) : elements; + } + } + + private static class PlaceholderCount { + private final String myName; + private int myCount; + + private PlaceholderCount(String name) { + myName = name; + } + + public String getPlaceholder() { + return "$" + myName + ++myCount + "$"; + } + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java index 68d2b712256b..9202ceb7fbb9 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java @@ -2,6 +2,7 @@ package com.intellij.structuralsearch.plugin.ui; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.codeInsight.template.TemplateBuilder; import com.intellij.codeInsight.template.impl.TemplateEditorUtil; import com.intellij.codeInsight.template.impl.TemplateImplUtil; import com.intellij.find.FindBundle; @@ -74,6 +75,7 @@ import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * @author Bas Leijdekkers @@ -644,6 +646,12 @@ public class StructuralSearchDialog extends DialogWrapper { setSize(otherSize.width, getSize().height); } } + + Document document = mySearchCriteriaEdit.getDocument(); + PsiFile psiFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(document); + assert psiFile != null; + TemplateBuilder builder = new StructuralSearchTemplateBuilder(psiFile).buildTemplate(); + WriteCommandAction.runWriteCommandAction(getProject(), () -> builder.run(Objects.requireNonNull(mySearchCriteriaEdit.getEditor()), true)); } @Override diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/SSTemplateBuilderTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/SSTemplateBuilderTest.java new file mode 100644 index 000000000000..8c5352ac6543 --- /dev/null +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/SSTemplateBuilderTest.java @@ -0,0 +1,38 @@ +// 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.codeInsight.template.Template; +import com.intellij.codeInsight.template.TemplateBuilderImpl; +import com.intellij.codeInsight.template.TemplateManager; +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; + +public class SSTemplateBuilderTest extends LightCodeInsightFixtureTestCase { + + public void testClassTemplate() { + doTest("class foo extends bar, next implements xxx", "class foo extends $Class2$, $Class3$ implements $Class4$"); + } + + public void testStatement() { + doTest("i=1", "$Var1$=1"); + } + + public void testMethodCall() { + Template template = doTest("foo()", "foo()"); + assertEquals(1, template.getSegmentsCount()); + } + + private Template doTest(String text, String expected) { + PsiFile psiFile = myFixture.configureByText(JavaFileType.INSTANCE, text); + TemplateBuilderImpl builder = (TemplateBuilderImpl)new StructuralSearchTemplateBuilder(getFile()).buildTemplate(); + Template template = WriteCommandAction.writeCommandAction(getProject()).compute(() -> { + Template inlineTemplate = builder.buildInlineTemplate(); + TemplateManager.getInstance(getProject()).startTemplate(getEditor(), inlineTemplate); + return inlineTemplate; + }); + assertEquals(expected, psiFile.getText()); + return template; + } +}