From 861efe85adeb8a0a32cdbb4047ec938e929469d8 Mon Sep 17 00:00:00 2001 From: "Maxim.Medvedev" Date: Wed, 16 May 2012 10:37:48 +0400 Subject: [PATCH] create groovy method from usage by groovy ref --- .../impl/quickfix/CreateFromUsageUtils.java | 1 + .../quickfix/CreateMethodFromUsageFix.java | 2 +- .../template/impl/JavaTemplateUtil.java | 8 +- .../groovy/annotator/GroovyAnnotator.java | 8 +- .../annotator/intentions/CreateClassFix.java | 6 +- .../intentions/CreateMethodFromUsageFix.java | 65 +++++++------- .../intentions/base/IntentionUtils.java | 90 ++++++++++++++----- .../expressions/ChooseTypeExpression.java | 10 ++- 8 files changed, 125 insertions(+), 65 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java index bd80039efab6..d966cb14d7a7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java @@ -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 diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java index fb13632e2224..8117897e5965 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java @@ -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 visitor = new PsiTypeVisitor() { diff --git a/java/java-impl/src/com/intellij/codeInsight/template/impl/JavaTemplateUtil.java b/java/java-impl/src/com/intellij/codeInsight/template/impl/JavaTemplateUtil.java index 8768820d289b..f21854b70a23 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/impl/JavaTemplateUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/impl/JavaTemplateUtil.java @@ -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); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index d0f4e3966024..1d0368b1bfff 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -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)); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassFix.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassFix.java index 277c4cd30a1b..dcc285a8e05b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassFix.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassFix.java @@ -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); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateMethodFromUsageFix.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateMethodFromUsageFix.java index e051436b712f..8b02cc79bd6b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateMethodFromUsageFix.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateMethodFromUsageFix.java @@ -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() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/base/IntentionUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/base/IntentionUtils.java index eefcae38a423..5b25841fc6dd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/base/IntentionUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/base/IntentionUtils.java @@ -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); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/template/expressions/ChooseTypeExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/template/expressions/ChooseTypeExpression.java index b2cc84051433..d5e4a2dd2cbc 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/template/expressions/ChooseTypeExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/template/expressions/ChooseTypeExpression.java @@ -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;