ImportClassHelper (quick fixes) refactoring

This commit is contained in:
svtk
2011-12-29 15:40:20 +04:00
parent be40b01d0e
commit a61f848861
21 changed files with 92 additions and 60 deletions
@@ -4,14 +4,12 @@ import com.google.common.collect.Lists;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.resolve.java.JavaClassDescriptor; import org.jetbrains.jet.lang.resolve.java.JavaClassDescriptor;
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.DeferredType; import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.JetType;
import java.util.LinkedList; import java.util.LinkedList;
@@ -41,7 +39,7 @@ public class JetPluginUtil {
DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
LinkedList<String> fullName = Lists.newLinkedList(); LinkedList<String> fullName = Lists.newLinkedList();
while (declarationDescriptor != null) { while (declarationDescriptor != null && !(declarationDescriptor instanceof ModuleDescriptor)) {
fullName.addFirst(declarationDescriptor.getName()); fullName.addFirst(declarationDescriptor.getName());
declarationDescriptor = declarationDescriptor.getContainingDeclaration(); declarationDescriptor = declarationDescriptor.getContainingDeclaration();
} }
@@ -53,6 +51,11 @@ public class JetPluginUtil {
} }
public static boolean checkTypeIsStandard(JetType type, Project project) { public static boolean checkTypeIsStandard(JetType type, Project project) {
if (JetStandardClasses.isAny(type) || JetStandardClasses.isNothingOrNullableNothing(type) || JetStandardClasses.isUnit(type) ||
JetStandardClasses.isTupleType(type) || JetStandardClasses.isFunctionType(type)) {
return true;
}
LinkedList<String> fullName = computeTypeFullNameList(type); LinkedList<String> fullName = computeTypeFullNameList(type);
if (fullName.size() == 3 && fullName.getFirst().equals("java") && fullName.get(1).equals("lang")) { if (fullName.size() == 3 && fullName.getFirst().equals("java") && fullName.get(1).equals("lang")) {
return true; return true;
@@ -13,8 +13,10 @@ import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.PlatformIcons; import com.intellij.util.PlatformIcons;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.plugin.quickfix.ImportClassHelper; import org.jetbrains.jet.plugin.quickfix.ImportClassHelper;
import javax.swing.*; import javax.swing.*;
@@ -138,9 +140,11 @@ public class JetAddImportAction implements QuestionAction {
public void run() { public void run() {
// TODO: See {@link com.intellij.codeInsight.daemon.impl.actions.AddImportAction#_addImport} for more ideas. // TODO: See {@link com.intellij.codeInsight.daemon.impl.actions.AddImportAction#_addImport} for more ideas.
// TODO: Optimize imports // TODO: Optimize imports
PsiFile file = element.getContainingFile();
if (!(file instanceof JetFile)) return;
ImportClassHelper.addImportDirective( ImportClassHelper.addImportDirective(
selectedImport, selectedImport,
ImportClassHelper.findOuterNamespace(element) (JetFile)file
); );
} }
}); });
@@ -11,6 +11,8 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetStandardClasses;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.JetBundle; import org.jetbrains.jet.plugin.JetBundle;
@@ -37,8 +39,14 @@ public class AddReturnTypeFix extends JetIntentionAction<JetNamedDeclaration> {
return JetBundle.message("add.return.type"); return JetBundle.message("add.return.type");
} }
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return super.isAvailable(project, editor, file) && !ErrorUtils.isErrorType(type);
}
@Override @Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (!(file instanceof JetFile)) return;
PsiElement newElement; PsiElement newElement;
if (element instanceof JetProperty) { if (element instanceof JetProperty) {
newElement = addPropertyType(project, (JetProperty) element, type); newElement = addPropertyType(project, (JetProperty) element, type);
@@ -47,7 +55,8 @@ public class AddReturnTypeFix extends JetIntentionAction<JetNamedDeclaration> {
assert element instanceof JetFunction; assert element instanceof JetFunction;
newElement = addFunctionType(project, (JetFunction) element, type); newElement = addFunctionType(project, (JetFunction) element, type);
} }
ImportClassHelper.perform(type, element, newElement); ImportClassHelper.addImportDirectiveIfNeeded(type, (JetFile)file);
element.replace(newElement);
} }
@Override @Override
@@ -10,10 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.JetParameter; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.JetPropertyAccessor;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lang.psi.JetTypeReference;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.JetBundle; import org.jetbrains.jet.plugin.JetBundle;
@@ -57,7 +54,7 @@ public class ChangeAccessorTypeFix extends JetIntentionAction<JetPropertyAccesso
assert typeReference != null; assert typeReference != null;
CodeEditUtil.replaceChild(parameter.getNode(), typeReference.getNode(), newTypeReference.getNode()); CodeEditUtil.replaceChild(parameter.getNode(), typeReference.getNode(), newTypeReference.getNode());
} }
ImportClassHelper.perform(type, element, newElement); element.replace(newElement);
} }
public static JetIntentionActionFactory<JetPropertyAccessor> createFactory() { public static JetIntentionActionFactory<JetPropertyAccessor> createFactory() {
@@ -2,11 +2,12 @@ package org.jetbrains.jet.plugin.quickfix;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetDeclaration; import org.jetbrains.jet.lang.psi.JetDeclaration;
import org.jetbrains.jet.lang.psi.JetImportDirective;
import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetImportDirective;
import org.jetbrains.jet.lang.psi.JetPsiFactory; import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacade;
import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.JetPluginUtil; import org.jetbrains.jet.plugin.JetPluginUtil;
@@ -17,48 +18,34 @@ import java.util.List;
* @author svtk * @author svtk
*/ */
public class ImportClassHelper { public class ImportClassHelper {
public static void perform(@NotNull JetType type, @NotNull PsiElement elementToReplace, @NotNull PsiElement newElement) { /**
if (JetPluginUtil.checkTypeIsStandard(type, elementToReplace.getProject()) || ErrorUtils.isError(type.getMemberScope().getContainingDeclaration())) { * Add import directive corresponding to a type to file when it is needed.
elementToReplace.replace(newElement); *
* @param type type to import
* @param file file where import directive should be added
*/
public static void addImportDirectiveIfNeeded(@NotNull JetType type, @NotNull JetFile file) {
if (JetPluginUtil.checkTypeIsStandard(type, file.getProject()) || ErrorUtils.isErrorType(type)) {
return; return;
} }
perform(JetPluginUtil.computeTypeFullName(type), elementToReplace, newElement); BindingContext bindingContext = AnalyzerFacade.analyzeFileWithCache(file, AnalyzerFacade.SINGLE_DECLARATION_PROVIDER);
} PsiElement element = bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, type.getMemberScope().getContainingDeclaration());
if (element != null && element.getContainingFile() == file) { //declaration is in the same file, so no import is needed
public static void perform(@NotNull String typeFullName, @NotNull JetFile namespace) { return;
perform(typeFullName, namespace, null, null);
}
/**
* Get the outer namespace PSI element for given element in the tree.
*
* @param element Some element in the tree.
* @return A namespace element in the tree.
*/
public static JetFile findOuterNamespace(@NotNull PsiElement element) {
PsiElement parent = element;
while (!(parent instanceof JetFile)) {
parent = parent.getParent();
assert parent != null;
} }
addImportDirective(JetPluginUtil.computeTypeFullName(type), file);
return (JetFile) parent;
}
public static void perform(@NotNull String typeFullName, @NotNull PsiElement elementToReplace, @NotNull PsiElement newElement) {
perform(typeFullName, findOuterNamespace(elementToReplace), elementToReplace, newElement);
} }
/** /**
* Add import directive into the PSI tree for the given namespace. * Add import directive into the PSI tree for the given namespace.
* *
* @param importString full name of the import. Can contain .* if necessary. * @param importString full name of the import. Can contain .* if necessary.
* @param namespace Namespace where directive should be added. * @param file File where directive should be added.
*/ */
public static void addImportDirective(@NotNull String importString, @NotNull JetFile namespace) { public static void addImportDirective(@NotNull String importString, @NotNull JetFile file) {
List<JetImportDirective> importDirectives = namespace.getImportDirectives(); List<JetImportDirective> importDirectives = file.getImportDirectives();
JetImportDirective newDirective = JetPsiFactory.createImportDirective(namespace.getProject(), importString); JetImportDirective newDirective = JetPsiFactory.createImportDirective(file.getProject(), importString);
String lineSeparator = System.getProperty("line.separator"); String lineSeparator = System.getProperty("line.separator");
if (!importDirectives.isEmpty()) { if (!importDirectives.isEmpty()) {
@@ -72,22 +59,14 @@ public class ImportClassHelper {
JetImportDirective lastDirective = importDirectives.get(importDirectives.size() - 1); JetImportDirective lastDirective = importDirectives.get(importDirectives.size() - 1);
lastDirective.getParent().addAfter(newDirective, lastDirective); lastDirective.getParent().addAfter(newDirective, lastDirective);
lastDirective.getParent().addAfter(JetPsiFactory.createWhiteSpace(namespace.getProject(), lineSeparator), lastDirective); lastDirective.getParent().addAfter(JetPsiFactory.createWhiteSpace(file.getProject(), lineSeparator), lastDirective);
} }
else { else {
List<JetDeclaration> declarations = namespace.getDeclarations(); List<JetDeclaration> declarations = file.getDeclarations();
assert !declarations.isEmpty(); assert !declarations.isEmpty();
JetDeclaration firstDeclaration = declarations.iterator().next(); JetDeclaration firstDeclaration = declarations.iterator().next();
firstDeclaration.getParent().addBefore(newDirective, firstDeclaration); firstDeclaration.getParent().addBefore(newDirective, firstDeclaration);
firstDeclaration.getParent().addBefore(JetPsiFactory.createWhiteSpace(namespace.getProject(), lineSeparator + lineSeparator), firstDeclaration); firstDeclaration.getParent().addBefore(JetPsiFactory.createWhiteSpace(file.getProject(), lineSeparator + lineSeparator), firstDeclaration);
}
}
public static void perform(@NotNull String typeFullName, @NotNull JetFile namespace, @Nullable PsiElement elementToReplace, @Nullable PsiElement newElement) {
addImportDirective(typeFullName, namespace);
if (elementToReplace != null && newElement != null && elementToReplace != newElement) {
elementToReplace.replace(newElement);
} }
} }
} }
@@ -78,6 +78,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
@Override @Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (!(file instanceof JetFile)) return;
JetProperty newElement = (JetProperty) element.copy(); JetProperty newElement = (JetProperty) element.copy();
JetPropertyAccessor getter = newElement.getGetter(); JetPropertyAccessor getter = newElement.getGetter();
if (removeGetter && getter != null) { if (removeGetter && getter != null) {
@@ -102,10 +103,9 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
} }
} }
if (needImport) { if (needImport) {
ImportClassHelper.perform(type, element, newElement); ImportClassHelper.addImportDirectiveIfNeeded(type, (JetFile)file);
} else {
element.replace(newElement);
} }
element.replace(newElement);
} }
public static JetIntentionActionFactory<JetProperty> createFactory() { public static JetIntentionActionFactory<JetProperty> createFactory() {
@@ -0,0 +1,5 @@
// "Add return type declaration" "false"
class A() {
public val <caret>t = foo()
}
@@ -0,0 +1,9 @@
// "Add return type declaration" "true"
package a
import b.B
class A() {
public val <caret>a : B = b.foo()
}
@@ -0,0 +1,7 @@
// "Add return type declaration" "true"
class A() {}
class B() {
public val <caret>a : A = A()
}
@@ -0,0 +1,5 @@
package b
class B() {}
fun foo() = B()
@@ -0,0 +1,7 @@
// "Add return type declaration" "true"
package a
class A() {
public val <caret>a = b.foo()
}
@@ -0,0 +1,7 @@
// "Add return type declaration" "true"
class A() {}
class B() {
public val <caret>a = A()
}
@@ -147,7 +147,7 @@ public class JetPsiCheckerMultifileTest extends DaemonAnalyzerTestCase {
} }
}); });
final ArrayList<File> fileResult = new ArrayList<File>(allTestFiles); final ArrayList<File> fileResult = new ArrayList<File>(mainFiles);
fileResult.addAll(dataFiles); fileResult.addAll(dataFiles);
return fileResult; return fileResult;