From e173a311dd591ea8cc571284c1017fcf02d3b4bb Mon Sep 17 00:00:00 2001 From: "Maxim.Medvedev" Date: Tue, 26 Jun 2012 10:41:29 +0400 Subject: [PATCH] IDEA-86368 Groovy: Create From Usage: Code Completion for destination package is missing in Create Class/Interface/Enum dialog --- .../plugins/groovy/GroovyBundle.properties | 1 + .../groovy/annotator/GroovyAnnotator.java | 21 ++- .../intentions/CreateClassActionBase.java | 28 +++- .../annotator/intentions/CreateClassFix.java | 158 +++++++++++------- 4 files changed, 134 insertions(+), 74 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties index 21fc4b593aaa..440dd72eb7aa 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties @@ -124,6 +124,7 @@ import.class=Import Class create.class.family.name=Create Class create.class.text=Create Class {0} create.interface.text=Create Interface {0} +create.annotation.text=Create Annotation {0} static.declaration.in.inner.class=Inner classes cannot have static declarations constructors.are.not.allowed.in.anonymous.class=Constructors are not allowed in anonymous class no.such.property=Property ''{0}'' does not exist 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 8de676cd4da2..ea9b38ae3188 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; import com.intellij.codeInsight.daemon.impl.quickfix.AddMethodBodyFix; +import com.intellij.codeInsight.daemon.impl.quickfix.CreateClassKind; import com.intellij.codeInsight.daemon.impl.quickfix.CreateConstructorMatchingSuperFix; import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix; import com.intellij.codeInsight.generation.OverrideImplementUtil; @@ -1981,21 +1982,29 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { } else { if (shouldBeInterface(refElement)) { - annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.INTERFACE)); + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.INTERFACE)); } else if (shouldBeClass(refElement)) { - annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.CLASS)); - annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.ENUM)); + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.CLASS)); + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.ENUM)); + } + else if (shouldBeAnnotation(refElement)) { + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.ANNOTATION)); } else { - annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.CLASS)); - annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.INTERFACE)); - annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.ENUM)); + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.CLASS)); + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.INTERFACE)); + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.ENUM)); + annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassKind.ANNOTATION)); } } } } + private static boolean shouldBeAnnotation(GrReferenceElement element) { + return element.getParent() instanceof GrAnnotation; + } + private static boolean shouldBeInterface(GrReferenceElement myRefElement) { PsiElement parent = myRefElement.getParent(); return parent instanceof GrImplementsClause || parent instanceof GrExtendsClause && parent.getParent() instanceof GrInterfaceDefinition; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassActionBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassActionBase.java index 07822de94efb..0c341df41ece 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassActionBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/CreateClassActionBase.java @@ -16,7 +16,9 @@ package org.jetbrains.plugins.groovy.annotator.intentions; + import com.intellij.codeInsight.daemon.impl.quickfix.CreateClassKind; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.impl.CreateClassDialog; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.WriteAction; @@ -24,7 +26,9 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -40,12 +44,12 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini * @author ilyas */ public abstract class CreateClassActionBase implements IntentionAction { - private Type myType; + private CreateClassKind myType; protected final GrReferenceElement myRefElement; private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.annotator.intentions.CreateClassActionBase"); - public CreateClassActionBase(Type type, GrReferenceElement refElement) { + public CreateClassActionBase(CreateClassKind type, GrReferenceElement refElement) { myType = type; myRefElement = refElement; } @@ -60,6 +64,8 @@ public abstract class CreateClassActionBase implements IntentionAction { return GroovyBundle.message("create.class.text", referenceName); case INTERFACE: return GroovyBundle.message("create.interface.text", referenceName); + case ANNOTATION: + return GroovyBundle.message("create.annotation.text", referenceName); default: return ""; } @@ -75,11 +81,11 @@ public abstract class CreateClassActionBase implements IntentionAction { } public boolean startInWriteAction() { - return true; + return false; } - protected Type getType() { + protected CreateClassKind getType() { return myType; } @@ -145,7 +151,17 @@ public abstract class CreateClassActionBase implements IntentionAction { } } - public static enum Type { - ENUM, CLASS, INTERFACE + @Nullable + protected PsiDirectory getTargetDirectory(Project project, String qualifier, String name, Module module, String title) { + CreateClassDialog dialog = new CreateClassDialog(project, title, name, qualifier, getType(), false, module) { + @Override + protected boolean reportBaseInSourceSelectionInTest() { + return true; + } + }; + dialog.show(); + if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null; + + return dialog.getTargetDirectory(); } } 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 c7a44098fe4b..e8ee8f9e0a2e 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 @@ -16,12 +16,15 @@ package org.jetbrains.plugins.groovy.annotator.intentions; +import com.intellij.codeInsight.daemon.impl.quickfix.CreateClassKind; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.module.Module; 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; @@ -34,7 +37,6 @@ import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; @@ -49,58 +51,91 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; public abstract class CreateClassFix { public static IntentionAction createClassFromNewAction(final GrNewExpression expression) { - return new CreateClassActionBase(CreateClassActionBase.Type.CLASS, expression.getReferenceElement()) { + return new CreateClassActionBase(CreateClassKind.CLASS, expression.getReferenceElement()) { public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { if (!(file instanceof GroovyFileBase)) return; - GroovyFileBase groovyFile = (GroovyFileBase) file; - final String qualifier = groovyFile instanceof GroovyFile ? groovyFile.getPackageName() : ""; + GroovyFileBase groovyFile = (GroovyFileBase)file; final PsiManager manager = myRefElement.getManager(); - final String name = myRefElement.getReferenceName(); - assert name != null; - final Module module = ModuleUtil.findModuleForPsiElement(file); + + final String qualifier; + final String name; + final Module module; + final AccessToken accessToken = ReadAction.start(); + try { + qualifier = groovyFile instanceof GroovyFile ? groovyFile.getPackageName() : ""; + name = myRefElement.getReferenceName(); + assert name != null; + module = ModuleUtil.findModuleForPsiElement(file); + } + finally { + accessToken.finish(); + } + PsiDirectory targetDirectory = getTargetDirectory(project, qualifier, name, module, getText()); if (targetDirectory == null) return; GrTypeDefinition targetClass = createClassByType(targetDirectory, name, manager, myRefElement, NewGroovyClassAction.GROOVY_CLASS); + if (targetClass == null) return; - GrArgumentList argList = expression.getArgumentList(); - if (argList != null && argList.getNamedArguments().length + argList.getExpressionArguments().length > 0 && targetClass != null) { - - PsiType[] argTypes = PsiUtil.getArgumentTypes(myRefElement, false); - assert argTypes != null; - - ChooseTypeExpression[] paramTypesExpressions = new ChooseTypeExpression[argTypes.length]; - String[] paramTypes = new String[argTypes.length]; - String[] paramNames = new String[argTypes.length]; - - for (int i = 0; i < argTypes.length; i++) { - PsiType argType = argTypes[i]; - if (argType == null) argType = TypesUtil.getJavaLangObject(myRefElement); - paramTypes[i] = "Object"; - paramNames[i] = "o" + i; - paramTypesExpressions[i] = new ChooseTypeExpression(new TypeConstraint[]{SupertypeConstraint.create(argType)}, myRefElement.getManager()); - } - - GrMethod method = GroovyPsiElementFactory.getInstance(project).createConstructorFromText(name, paramTypes, paramNames, "{\n}"); - method = targetClass.addMemberDeclaration(method, null); - final PsiNameIdentifierOwner context = PsiTreeUtil.getParentOfType(myRefElement, PsiMethod.class, PsiClass.class); - IntentionUtils.createTemplateForMethod(argTypes, paramTypesExpressions, method, targetClass, new TypeConstraint[0], true, context); - } else { + PsiType[] argTypes = getArgTypes(myRefElement); + if (argTypes != null) { + generateConstructor(myRefElement, name, argTypes, targetClass, project); + } + else { putCursor(project, targetClass.getContainingFile(), targetClass); } addImportForClass(groovyFile, qualifier, targetClass); } - }; } - public static IntentionAction createClassFixAction(final GrReferenceElement refElement, CreateClassActionBase.Type type) { - return new CreateClassActionBase(type, refElement) { + @Nullable + private static PsiType[] getArgTypes(GrReferenceElement refElement) { + final AccessToken accessToken = ReadAction.start(); + try { + return PsiUtil.getArgumentTypes(refElement, false); + } + finally { + accessToken.finish(); + } + } + private static void generateConstructor(@NotNull PsiElement refElement, + @NotNull String name, + @NotNull PsiType[] argTypes, + @NotNull GrTypeDefinition targetClass, + @NotNull Project project) { + final AccessToken writeLock = WriteAction.start(); + try { + ChooseTypeExpression[] paramTypesExpressions = new ChooseTypeExpression[argTypes.length]; + String[] paramTypes = new String[argTypes.length]; + String[] paramNames = new String[argTypes.length]; + + for (int i = 0; i < argTypes.length; i++) { + PsiType argType = argTypes[i]; + if (argType == null) argType = TypesUtil.getJavaLangObject(refElement); + paramTypes[i] = "Object"; + paramNames[i] = "o" + i; + paramTypesExpressions[i] = new ChooseTypeExpression(new TypeConstraint[]{SupertypeConstraint.create(argType)}, refElement.getManager()); + } + + GrMethod method = GroovyPsiElementFactory.getInstance(project).createConstructorFromText(name, paramTypes, paramNames, "{\n}"); + + method = targetClass.addMemberDeclaration(method, null); + final PsiNameIdentifierOwner context = PsiTreeUtil.getParentOfType(refElement, PsiMethod.class, PsiClass.class); + IntentionUtils.createTemplateForMethod(argTypes, paramTypesExpressions, method, targetClass, new TypeConstraint[0], true, context); + } + finally { + writeLock.finish(); + } + } + + public static IntentionAction createClassFixAction(final GrReferenceElement refElement, CreateClassKind type) { + return new CreateClassActionBase(type, refElement) { public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { if (!(file instanceof GroovyFileBase)) return; - GroovyFileBase groovyFile = (GroovyFileBase) file; + GroovyFileBase groovyFile = (GroovyFileBase)file; final String qualifier = groovyFile instanceof GroovyFile ? groovyFile.getPackageName() : ""; final PsiManager manager = PsiManager.getInstance(project); final String name = myRefElement.getReferenceName(); @@ -109,18 +144,7 @@ public abstract class CreateClassFix { if (targetDirectory == null) return; - String templateName = null; - switch (getType()) { - case ENUM: - templateName = NewGroovyClassAction.GROOVY_ENUM; - break; - case CLASS: - templateName = NewGroovyClassAction.GROOVY_CLASS; - break; - case INTERFACE: - templateName = NewGroovyClassAction.GROOVY_INTERFACE; - break; - } + String templateName = getTemplateName(getType()); assert name != null; PsiClass targetClass = createClassByType(targetDirectory, name, manager, myRefElement, templateName); if (targetClass != null) { @@ -128,29 +152,39 @@ public abstract class CreateClassFix { putCursor(project, targetClass.getContainingFile(), targetClass); } } - }; } - @Nullable - private static PsiDirectory getTargetDirectory(Project project, String qualifier, String name, Module module, String title) { - GroovyCreateClassDialog dialog = new GroovyCreateClassDialog(project, title, name, qualifier, module); - dialog.show(); - if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null; - return dialog.getTargetDirectory(); + private static String getTemplateName(CreateClassKind createClassKind) { + switch (createClassKind) { + case ENUM: + return NewGroovyClassAction.GROOVY_ENUM; + case CLASS: + return NewGroovyClassAction.GROOVY_CLASS; + case INTERFACE: + return NewGroovyClassAction.GROOVY_INTERFACE; + case ANNOTATION: + return NewGroovyClassAction.GROOVY_ANNOTATION; + default: + return null; + } } - protected static void addImportForClass(GroovyFileBase groovyFile, String qualifier, PsiClass targetClass) throws IncorrectOperationException { - if (targetClass != null) { - // add import for created class - String qualifiedName = targetClass.getQualifiedName(); - if (qualifiedName != null && qualifiedName.contains(".")) { - String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf(".")); - if (!packageName.equals(qualifier)) { + protected static void addImportForClass(@NotNull GroovyFileBase groovyFile, @NotNull String qualifier, @NotNull PsiClass targetClass) + throws IncorrectOperationException { + // add import for created class + String qualifiedName = targetClass.getQualifiedName(); + if (qualifiedName != null && qualifiedName.contains(".")) { + String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf(".")); + if (!packageName.equals(qualifier)) { + final AccessToken accessToken = WriteAction.start(); + try { groovyFile.addImportForClass(targetClass); } + finally { + accessToken.finish(); + } } } } - }