From 685f27b2cec9406541c01f41eb884f17facc71a3 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 15 Jan 2015 17:02:43 +0100 Subject: [PATCH] generate equals/hashCode: provide implicit variables for completion; show available variables as hint for CE users --- .../generation/GenerateEqualsHelper.java | 32 +++++++++++++--- .../codeInsight/generation/equalsHelper.vm | 2 +- .../ui/EqualsHashCodeTemplatesPanel.java | 5 ++- .../generate/template/TemplatesManager.java | 38 +++++++++++++++++++ .../view/GenerateTemplateConfigurable.java | 33 +++++++++++++++- 5 files changed, 100 insertions(+), 10 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHelper.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHelper.java index cbdcd0917d37..3e0f2d4a29bc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHelper.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHelper.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.generation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.codeStyle.*; import com.intellij.psi.search.GlobalSearchScope; @@ -38,6 +39,13 @@ import java.util.*; */ public class GenerateEqualsHelper implements Runnable { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateEqualsHelper"); + + @NonNls private static final String INSTANCE_NAME = "instanceBaseName"; + @NonNls private static final String BASE_PARAM_NAME = "baseParamName"; + @NonNls private static final String SUPER_HAS_EQUALS = "superHasEquals"; + @NonNls private static final String CHECK_PARAMETER_WITH_INSTANCEOF = "checkParameterWithInstanceof"; + @NonNls private static final String SUPER_HAS_HASH_CODE = "superHasHashCode"; + private final PsiClass myClass; private final PsiField[] myEqualsFields; private final PsiField[] myHashCodeFields; @@ -141,7 +149,21 @@ public class GenerateEqualsHelper implements Runnable { } } + public static Map getEqualsImplicitVars(Project project) { + final Map map = new LinkedHashMap(); + final PsiType stringType = project != null ? PsiType.getJavaLangString(PsiManager.getInstance(project), GlobalSearchScope.allScope(project)) + : PsiType.NULL; + map.put(INSTANCE_NAME, stringType); + map.put(BASE_PARAM_NAME, stringType); + map.put(CHECK_PARAMETER_WITH_INSTANCEOF, PsiType.BOOLEAN); + map.put(SUPER_HAS_EQUALS, PsiType.BOOLEAN); + return map; + } + public static Map getHashCodeImplicitVars() { + return ContainerUtil.newLinkedHashMap(Pair.create(SUPER_HAS_HASH_CODE, PsiType.BOOLEAN)); + } + private PsiMethod createEquals() throws IncorrectOperationException { @NonNls StringBuilder buffer = new StringBuilder(); CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(myProject); @@ -156,15 +178,15 @@ public class GenerateEqualsHelper implements Runnable { String[] nameSuggestions = codeStyleManager .suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, classType).names; String instanceBaseName = nameSuggestions.length > 0 && nameSuggestions[0].length() < 10 ? nameSuggestions[0] : "that"; - contextMap.put("instanceName", instanceBaseName); + contextMap.put(INSTANCE_NAME, instanceBaseName); final PsiType objectType = PsiType.getJavaLangObject(myClass.getManager(), myClass.getResolveScope()); nameSuggestions = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, objectType).names; final String objectBaseName = nameSuggestions.length > 0 ? nameSuggestions[0] : "object"; - contextMap.put("baseParamName", objectBaseName); + contextMap.put(BASE_PARAM_NAME, objectBaseName); final MethodSignature equalsSignature = getEqualsSignature(myProject, myClass.getResolveScope()); - contextMap.put("superHasEquals", superMethodExists(equalsSignature)); - contextMap.put("checkParameterWithInstanceof", myCheckParameterWithInstanceof); + contextMap.put(SUPER_HAS_EQUALS, superMethodExists(equalsSignature)); + contextMap.put(CHECK_PARAMETER_WITH_INSTANCEOF, myCheckParameterWithInstanceof); final String methodText = GenerationUtil .velocityGenerateCode(myClass, equalsFields, myNonNullSet, new HashMap(), contextMap, @@ -203,7 +225,7 @@ public class GenerateEqualsHelper implements Runnable { @NonNls StringBuilder buffer = new StringBuilder(); final HashMap contextMap = new HashMap(); - contextMap.put("superHasHashCode", mySuperHasHashCode); + contextMap.put(SUPER_HAS_HASH_CODE, mySuperHasHashCode); final String methodText = GenerationUtil .velocityGenerateCode(myClass, Arrays.asList(myHashCodeFields), myNonNullSet, new HashMap(), contextMap, diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/equalsHelper.vm b/java/java-impl/src/com/intellij/codeInsight/generation/equalsHelper.vm index 2345a8be84eb..52c05f12d9af 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/equalsHelper.vm +++ b/java/java-impl/src/com/intellij/codeInsight/generation/equalsHelper.vm @@ -1,4 +1,4 @@ -#set($classInstanceName = $helper.getUniqueLocalVarName($instanceName, $fields, $settings)) +#set($classInstanceName = $helper.getUniqueLocalVarName($instanceBaseName, $fields, $settings)) #set($paramName = $helper.getUniqueLocalVarName($baseParamName, $fields, $settings)) #macro(addClassInstance) #if ($settings.generateFinalLocals) diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/ui/EqualsHashCodeTemplatesPanel.java b/java/java-impl/src/com/intellij/codeInsight/generation/ui/EqualsHashCodeTemplatesPanel.java index 144c8da3586e..936708aaefea 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/ui/EqualsHashCodeTemplatesPanel.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/ui/EqualsHashCodeTemplatesPanel.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.generation.ui; import com.intellij.codeInsight.generation.EqualsHashCodeTemplatesManager; +import com.intellij.codeInsight.generation.GenerateEqualsHelper; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; @@ -135,8 +136,8 @@ public class EqualsHashCodeTemplatesPanel extends NamedItemsListEditor item) { - final GenerateTemplateConfigurable equalsConfigurable = new GenerateTemplateConfigurable(item.first, myProject); - final GenerateTemplateConfigurable hashCodeConfigurable = new GenerateTemplateConfigurable(item.second, myProject); + final GenerateTemplateConfigurable equalsConfigurable = new GenerateTemplateConfigurable(item.first, GenerateEqualsHelper.getEqualsImplicitVars(myProject), myProject); + final GenerateTemplateConfigurable hashCodeConfigurable = new GenerateTemplateConfigurable(item.second, GenerateEqualsHelper.getHashCodeImplicitVars(), myProject); return new UnnamedConfigurable() { @Nullable @Override diff --git a/plugins/generate-tostring/src/org/jetbrains/java/generate/template/TemplatesManager.java b/plugins/generate-tostring/src/org/jetbrains/java/generate/template/TemplatesManager.java index d69e87d11f69..d6e310626741 100644 --- a/plugins/generate-tostring/src/org/jetbrains/java/generate/template/TemplatesManager.java +++ b/plugins/generate-tostring/src/org/jetbrains/java/generate/template/TemplatesManager.java @@ -20,18 +20,29 @@ package org.jetbrains.java.generate.template; import com.intellij.openapi.components.*; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.java.generate.element.FieldElement; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.reflect.Method; import java.util.*; public abstract class TemplatesManager implements PersistentStateComponent { + public static final Key> TEMPLATE_IMPLICITS = Key.create("TEMPLATE_IMPLICITS"); + private TemplatesState myState = new TemplatesState(); public abstract TemplateResource[] getDefaultTemplates(); @@ -104,4 +115,31 @@ public abstract class TemplatesManager implements PersistentStateComponent elementClass) { + final List methodNames = + ContainerUtil.mapNotNull(elementClass.getMethods(), + new Function() { + @Override + public String fun(Method method) { + final String methodName = method.getName(); + if (methodName.startsWith("set")) { + //hide setters from completion list + return null; + } + return method.getGenericReturnType().getTypeName() + " " + methodName + "();"; + } + }); + final String text = "interface " + elementClass.getSimpleName() + " {\n" + StringUtil.join(methodNames, "\n") + "}"; + final PsiClass aClass = JavaPsiFacade.getElementFactory(project).createClassFromText(text, null).getInnerClasses()[0]; + return JavaPsiFacade.getElementFactory(project).createType(aClass); + } } \ No newline at end of file diff --git a/plugins/generate-tostring/src/org/jetbrains/java/generate/view/GenerateTemplateConfigurable.java b/plugins/generate-tostring/src/org/jetbrains/java/generate/view/GenerateTemplateConfigurable.java index ac326c3cff45..52dabb5a35f0 100644 --- a/plugins/generate-tostring/src/org/jetbrains/java/generate/view/GenerateTemplateConfigurable.java +++ b/plugins/generate-tostring/src/org/jetbrains/java/generate/view/GenerateTemplateConfigurable.java @@ -30,20 +30,33 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ex.MultiLineLabel; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.PsiType; import com.intellij.util.LocalTimeCounter; +import org.jetbrains.java.generate.element.ClassElement; import org.jetbrains.java.generate.template.TemplateResource; +import org.jetbrains.java.generate.template.TemplatesManager; import javax.swing.*; +import java.awt.*; +import java.util.*; +import java.util.List; public class GenerateTemplateConfigurable implements UnnamedConfigurable{ private final TemplateResource template; private final Editor myEditor; + private final List availableImplicits = new ArrayList(); - public GenerateTemplateConfigurable(TemplateResource template, Project project) { + public GenerateTemplateConfigurable(TemplateResource template, Project project) { + this(template, Collections.emptyMap(), project); + } + + public GenerateTemplateConfigurable(TemplateResource template, Map contextMap, Project project) { this.template = template; final EditorFactory factory = EditorFactory.getInstance(); Document doc = factory.createDocument(template.getTemplate()); @@ -51,6 +64,15 @@ public class GenerateTemplateConfigurable implements UnnamedConfigurable{ if (project != null && ftl != null) { final PsiFile file = PsiFileFactory.getInstance(project) .createFileFromText(template.getFileName(), ftl, template.getTemplate(), LocalTimeCounter.currentTime(), true); + if (!template.isDefault()) { + final HashMap map = new LinkedHashMap(); + map.put("java_version", PsiType.INT); + map.put("class", TemplatesManager.createElementType(project, ClassElement.class)); + map.put("fields", TemplatesManager.createFieldListElementType(project)); + map.putAll(contextMap); + availableImplicits.addAll(map.keySet()); + file.getViewProvider().putUserData(TemplatesManager.TEMPLATE_IMPLICITS, map); + } final Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document != null) { doc = document; @@ -60,7 +82,14 @@ public class GenerateTemplateConfigurable implements UnnamedConfigurable{ } public JComponent createComponent() { - return myEditor.getComponent(); + final JComponent component = myEditor.getComponent(); + if (availableImplicits.isEmpty()) { + return component; + } + final JPanel panel = new JPanel(new BorderLayout()); + panel.add(component, BorderLayout.CENTER); + panel.add(new MultiLineLabel("Available implicit variables:\n" + StringUtil.join(availableImplicits, ", ")), BorderLayout.SOUTH); + return panel; } public boolean isModified() {