generate equals/hashCode: provide implicit variables for completion; show available variables as hint for CE users

This commit is contained in:
Anna Kozlova
2015-01-15 17:08:42 +01:00
parent 16d44523f9
commit 685f27b2ce
5 changed files with 100 additions and 10 deletions
@@ -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<String, PsiType> getEqualsImplicitVars(Project project) {
final Map<String, PsiType> map = new LinkedHashMap<String, PsiType>();
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<String, PsiType> 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<String, String>(), contextMap,
@@ -203,7 +225,7 @@ public class GenerateEqualsHelper implements Runnable {
@NonNls StringBuilder buffer = new StringBuilder();
final HashMap<String, Object> contextMap = new HashMap<String, Object>();
contextMap.put("superHasHashCode", mySuperHasHashCode);
contextMap.put(SUPER_HAS_HASH_CODE, mySuperHasHashCode);
final String methodText = GenerationUtil
.velocityGenerateCode(myClass, Arrays.asList(myHashCodeFields), myNonNullSet, new HashMap<String, String>(), contextMap,
@@ -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)
@@ -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<Couple<Te
@Override
protected UnnamedConfigurable createConfigurable(Couple<TemplateResource> 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
@@ -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<TemplatesState> {
public static final Key<Map<String, PsiType>> 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<Templ
}
}
}
@NotNull
public static PsiType createFieldListElementType(Project project) {
final PsiType classType = createElementType(project, FieldElement.class);
final PsiClass listClass = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_UTIL_LIST, GlobalSearchScope.allScope(project));
return listClass != null ? JavaPsiFacade.getElementFactory(project).createType(listClass, classType) : PsiType.NULL;
}
@NotNull
public static PsiType createElementType(Project project, Class<?> elementClass) {
final List<String> methodNames =
ContainerUtil.mapNotNull(elementClass.getMethods(),
new Function<Method, String>() {
@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);
}
}
@@ -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<String> availableImplicits = new ArrayList<String>();
public GenerateTemplateConfigurable(TemplateResource template, Project project) {
public GenerateTemplateConfigurable(TemplateResource template, Project project) {
this(template, Collections.<String, PsiType>emptyMap(), project);
}
public GenerateTemplateConfigurable(TemplateResource template, Map<String, PsiType> 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<String, PsiType> map = new LinkedHashMap<String, PsiType>();
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() {