create groovy method from usage by groovy ref

This commit is contained in:
Maxim.Medvedev
2012-05-16 10:45:37 +04:00
parent c140ff201f
commit 861efe85ad
8 changed files with 125 additions and 65 deletions
@@ -202,6 +202,7 @@ public class CreateFromUsageUtils {
PsiDocumentManager manager = PsiDocumentManager.getInstance(method.getProject());
manager.doPostponedOperationsAndUnblockDocument(manager.getDocument(containingFile));
EditorModificationUtil.insertStringAtCaret(newEditor, lineIndent);
EditorModificationUtil.insertStringAtCaret(newEditor, "\n", false, false);
}
else {
//correct position caret for groovy and java methods
@@ -285,7 +285,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
}
}
private static boolean checkTypeParam(final PsiMethod method, final PsiTypeParameter typeParameter) {
public static boolean checkTypeParam(final PsiMethod method, final PsiTypeParameter typeParameter) {
final String typeParameterName = typeParameter.getName();
final PsiTypeVisitor<Boolean> visitor = new PsiTypeVisitor<Boolean>() {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -54,11 +54,15 @@ public class JavaTemplateUtil {
PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method != null) {
PsiTypeParameterList paramList = method.getTypeParameterList();
PsiTypeParameter[] params = paramList.getTypeParameters();
PsiTypeParameter[] params = paramList != null ? paramList.getTypeParameters() : PsiTypeParameter.EMPTY_ARRAY;
for (PsiTypeParameter param : params) {
if (param.getName().equals(aClass.getName())) return;
}
try {
if (paramList == null) {
final PsiTypeParameterList newList = JVMElementFactories.getFactory(method.getLanguage(), project).createTypeParameterList();
paramList = (PsiTypeParameterList)method.addAfter(newList, method.getModifierList());
}
paramList.add(aClass.copy());
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
}
@@ -1785,13 +1785,15 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
if (targetClass == null) return;
addDynamicAnnotation(annotation, refExpr);
if (targetClass instanceof GrMemberOwner && targetClass.isWritable()) {
if (targetClass.isWritable()) {
if (!(targetClass instanceof GroovyScriptClass)) {
annotation.registerFix(new CreateFieldFromUsageFix(refExpr, (GrMemberOwner)targetClass));
if (targetClass instanceof GrMemberOwner) {
annotation.registerFix(new CreateFieldFromUsageFix(refExpr, (GrMemberOwner)targetClass));
}
}
if (refExpr.getParent() instanceof GrCall && refExpr.getParent() instanceof GrExpression) {
annotation.registerFix(new CreateMethodFromUsageFix(refExpr, (GrMemberOwner)targetClass));
annotation.registerFix(new CreateMethodFromUsageFix(refExpr, targetClass));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -23,6 +23,7 @@ import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -84,7 +85,8 @@ public abstract class CreateClassFix {
GrMethod method = GroovyPsiElementFactory.getInstance(project).createConstructorFromText(name, paramTypes, paramNames, "{\n}");
method = targetClass.addMemberDeclaration(method, null);
IntentionUtils.createTemplateForMethod(argTypes, paramTypesExpressions, method, targetClass, new TypeConstraint[0], true);
final PsiNameIdentifierOwner context = PsiTreeUtil.getParentOfType(myRefElement, PsiMethod.class, PsiClass.class);
IntentionUtils.createTemplateForMethod(argTypes, paramTypesExpressions, method, targetClass, new TypeConstraint[0], true, context);
} else {
putCursor(project, targetClass.getContainingFile(), targetClass);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -18,9 +18,7 @@ package org.jetbrains.plugins.groovy.annotator.intentions;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiType;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
@@ -28,12 +26,8 @@ import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.intentions.base.IntentionUtils;
import org.jetbrains.plugins.groovy.lang.editor.template.expressions.ChooseTypeExpression;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.SupertypeConstraint;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint;
@@ -45,10 +39,10 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
* @author ven
*/
public class CreateMethodFromUsageFix implements IntentionAction {
private final GrMemberOwner myTargetClass;
private final PsiClass myTargetClass;
private final GrReferenceExpression myRefExpression;
public CreateMethodFromUsageFix(GrReferenceExpression refExpression, GrMemberOwner targetClass) {
public CreateMethodFromUsageFix(GrReferenceExpression refExpression, PsiClass targetClass) {
myRefExpression = refExpression;
myTargetClass = targetClass;
}
@@ -68,27 +62,21 @@ public class CreateMethodFromUsageFix implements IntentionAction {
}
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
StringBuilder methodBuffer = new StringBuilder();
if (PsiUtil.isInStaticContext(myRefExpression, myTargetClass)) methodBuffer.append("static ");
methodBuffer.append("Object ").append(myRefExpression.getReferenceName()).append(" (");
final JVMElementFactory factory = JVMElementFactories.getFactory(myTargetClass.getLanguage(), project);
PsiMethod method = factory.createMethod(myRefExpression.getReferenceName(), PsiType.VOID);
if (PsiUtil.isInStaticContext(myRefExpression, myTargetClass)) {
method.getModifierList().setModifierProperty(PsiModifier.STATIC, true);
}
PsiType[] argTypes = PsiUtil.getArgumentTypes(myRefExpression, false);
assert argTypes != null;
ChooseTypeExpression[] paramTypesExpressions = new ChooseTypeExpression[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
PsiType argType = argTypes[i];
if (argType == null) argType = TypesUtil.getJavaLangObject(myRefExpression);
if (i > 0) methodBuffer.append(", ");
methodBuffer.append("Object o").append(i);
paramTypesExpressions[i] =
new ChooseTypeExpression(new TypeConstraint[]{SupertypeConstraint.create(argType)}, myRefExpression.getManager());
}
methodBuffer.append(") {\n}");
GrMethod method = GroovyPsiElementFactory.getInstance(project).createMethodFromText(methodBuffer.toString());
GrMemberOwner owner = myTargetClass;
ChooseTypeExpression[] paramTypesExpressions = setupParams(method, argTypes, factory);
TypeConstraint[] constraints = GroovyExpectedTypesProvider.calculateTypeConstraints((GrExpression)myRefExpression.getParent());
PsiElement parent = myTargetClass instanceof GrTypeDefinition
? ((GrTypeDefinition)myTargetClass).getBody()
: ((GroovyScriptClass)myTargetClass).getContainingFile();
PsiElement parent = myTargetClass instanceof GroovyScriptClass
? ((GroovyScriptClass)myTargetClass).getContainingFile()
: myTargetClass;
if (PsiTreeUtil.isAncestor(parent, myRefExpression, false)) {
PsiElement prevParent = PsiTreeUtil.findPrevParent(parent, myRefExpression);
PsiElement sibling = PsiUtil.skipWhitespaces(prevParent.getNextSibling(), true);
@@ -98,13 +86,28 @@ public class CreateMethodFromUsageFix implements IntentionAction {
else {
sibling = prevParent.getNextSibling();
}
method = owner.addMemberDeclaration(method, sibling);
method = (PsiMethod)myTargetClass.addAfter(method, sibling);
}
else {
method = owner.addMemberDeclaration(method, null);
method = (PsiMethod)myTargetClass.add(method);
}
IntentionUtils.createTemplateForMethod(argTypes, paramTypesExpressions, method, owner, constraints, false);
final PsiElement context = PsiTreeUtil.getParentOfType(myRefExpression, PsiClass.class, PsiMethod.class);
IntentionUtils.createTemplateForMethod(argTypes, paramTypesExpressions, method, myTargetClass, constraints, false, context);
}
private ChooseTypeExpression[] setupParams(PsiMethod method, PsiType[] argTypes, JVMElementFactory factory) {
final PsiParameterList parameterList = method.getParameterList();
ChooseTypeExpression[] paramTypesExpressions = new ChooseTypeExpression[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
PsiType argType = TypesUtil.unboxPrimitiveTypeWrapper(argTypes[i]);
if (argType == null) argType = TypesUtil.getJavaLangObject(myRefExpression);
final PsiParameter p = factory.createParameter("o", argType);
parameterList.add(p);
paramTypesExpressions[i] = new ChooseTypeExpression(new TypeConstraint[]{SupertypeConstraint.create(argType)}, myRefExpression.getManager());
}
return paramTypesExpressions;
}
public boolean startInWriteAction() {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -16,15 +16,16 @@
package org.jetbrains.plugins.groovy.intentions.base;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.template.Template;
import com.intellij.codeInsight.template.TemplateBuilderImpl;
import com.intellij.codeInsight.template.TemplateManager;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateMethodFromUsageFix;
import com.intellij.codeInsight.template.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiType;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -33,12 +34,7 @@ import org.jetbrains.plugins.groovy.lang.editor.template.expressions.ChooseTypeE
import org.jetbrains.plugins.groovy.lang.editor.template.expressions.ParameterNameExpression;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint;
/**
@@ -47,6 +43,8 @@ import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint;
*/
public class IntentionUtils {
private static final Logger LOG = Logger.getInstance(IntentionUtils.class);
public static void replaceExpression(@NotNull String newExpression, @NotNull GrExpression expression) throws IncorrectOperationException {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(expression.getProject());
final GrExpression newCall = factory.createExpressionFromText(newExpression);
@@ -62,27 +60,29 @@ public class IntentionUtils {
public static void createTemplateForMethod(PsiType[] argTypes,
ChooseTypeExpression[] paramTypesExpressions,
GrMethod method,
GrMemberOwner owner,
TypeConstraint[] constraints, boolean isConstructor) {
PsiMethod method,
PsiClass owner,
TypeConstraint[] constraints,
boolean isConstructor,
final PsiElement context) {
Project project = owner.getProject();
GrTypeElement typeElement = method.getReturnTypeElementGroovy();
final Project project = owner.getProject();
PsiTypeElement typeElement = method.getReturnTypeElement();
ChooseTypeExpression expr = new ChooseTypeExpression(constraints, PsiManager.getInstance(project));
TemplateBuilderImpl builder = new TemplateBuilderImpl(method);
if (!isConstructor) {
assert typeElement != null;
builder.replaceElement(typeElement, expr);
}
GrParameter[] parameters = method.getParameterList().getParameters();
PsiParameter[] parameters = method.getParameterList().getParameters();
assert parameters.length == argTypes.length;
for (int i = 0; i < parameters.length; i++) {
GrParameter parameter = parameters[i];
GrTypeElement parameterTypeElement = parameter.getTypeElementGroovy();
PsiParameter parameter = parameters[i];
PsiTypeElement parameterTypeElement = parameter.getTypeElement();
builder.replaceElement(parameterTypeElement, paramTypesExpressions[i]);
builder.replaceElement(parameter.getNameIdentifierGroovy(), new ParameterNameExpression());
builder.replaceElement(parameter.getNameIdentifier(), new ParameterNameExpression());
}
GrOpenBlock body = method.getBlock();
PsiCodeBlock body = method.getBody();
assert body != null;
PsiElement lbrace = body.getLBrace();
assert lbrace != null;
@@ -91,11 +91,53 @@ public class IntentionUtils {
method = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(method);
Template template = builder.buildTemplate();
Editor newEditor = QuickfixUtil.positionCursor(project, owner.getContainingFile(), method);
final PsiFile targetFile = owner.getContainingFile();
final Editor newEditor = QuickfixUtil.positionCursor(project, targetFile, method);
TextRange range = method.getTextRange();
newEditor.getDocument().deleteString(range.getStartOffset(), range.getEndOffset());
TemplateManager manager = TemplateManager.getInstance(project);
manager.startTemplate(newEditor, template);
TemplateEditingListener templateListener = new TemplateEditingAdapter() {
@Override
public void templateFinished(Template template, boolean brokenOff) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
PsiDocumentManager.getInstance(project).commitDocument(newEditor.getDocument());
final int offset = newEditor.getCaretModel().getOffset();
PsiMethod method = PsiTreeUtil.findElementOfClassAtOffset(targetFile, offset - 1, PsiMethod.class, false);
if (context instanceof PsiMethod) {
final PsiTypeParameter[] typeParameters = ((PsiMethod)context).getTypeParameters();
if (typeParameters.length > 0) {
for (PsiTypeParameter typeParameter : typeParameters) {
if (CreateMethodFromUsageFix.checkTypeParam(method, typeParameter)) {
final JVMElementFactory factory = JVMElementFactories.getFactory(method.getLanguage(), method.getProject());
PsiTypeParameterList list = method.getTypeParameterList();
if (list == null) {
PsiTypeParameterList newList = factory.createTypeParameterList();
list = (PsiTypeParameterList)method.addAfter(newList, method.getModifierList());
}
list.add(factory.createTypeParameter(typeParameter.getName(), typeParameter.getExtendsList().getReferencedTypes()));
}
}
}
}
if (method != null) {
try {
CreateFromUsageUtils.setupMethodBody(method);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
CreateFromUsageUtils.setupEditor(method, newEditor);
}
}
});
}
};
manager.startTemplate(newEditor, template, templateListener);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -22,6 +22,7 @@ import com.intellij.codeInsight.template.Expression;
import com.intellij.codeInsight.template.ExpressionContext;
import com.intellij.codeInsight.template.PsiTypeResult;
import com.intellij.codeInsight.template.Result;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTypesUtil;
@@ -86,7 +87,12 @@ public class ChooseTypeExpression extends Expression {
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments();
PsiType type = myTypePointer.getType();
if (type != null) {
return new PsiTypeResult(type, context.getProject());
return new PsiTypeResult(type, context.getProject()) {
@Override
public void handleRecalc(PsiFile psiFile, Document document, int segmentStart, int segmentEnd) {
if (myItems.length <= 1) super.handleRecalc(psiFile, document, segmentStart, segmentEnd);
}
};
}
return null;