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 org.jetbrains.annotations.NotNull;
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.resolve.java.JavaClassDescriptor;
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.DeferredType;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.*;
import java.util.LinkedList;
@@ -41,7 +39,7 @@ public class JetPluginUtil {
DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
LinkedList<String> fullName = Lists.newLinkedList();
while (declarationDescriptor != null) {
while (declarationDescriptor != null && !(declarationDescriptor instanceof ModuleDescriptor)) {
fullName.addFirst(declarationDescriptor.getName());
declarationDescriptor = declarationDescriptor.getContainingDeclaration();
}
@@ -53,6 +51,11 @@ public class JetPluginUtil {
}
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);
if (fullName.size() == 3 && fullName.getFirst().equals("java") && fullName.get(1).equals("lang")) {
return true;
@@ -13,8 +13,10 @@ import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.PlatformIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.plugin.quickfix.ImportClassHelper;
import javax.swing.*;
@@ -138,9 +140,11 @@ public class JetAddImportAction implements QuestionAction {
public void run() {
// TODO: See {@link com.intellij.codeInsight.daemon.impl.actions.AddImportAction#_addImport} for more ideas.
// TODO: Optimize imports
PsiFile file = element.getContainingFile();
if (!(file instanceof JetFile)) return;
ImportClassHelper.addImportDirective(
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.DiagnosticWithPsiElement;
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.plugin.JetBundle;
@@ -37,8 +39,14 @@ public class AddReturnTypeFix extends JetIntentionAction<JetNamedDeclaration> {
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
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (!(file instanceof JetFile)) return;
PsiElement newElement;
if (element instanceof JetProperty) {
newElement = addPropertyType(project, (JetProperty) element, type);
@@ -47,7 +55,8 @@ public class AddReturnTypeFix extends JetIntentionAction<JetNamedDeclaration> {
assert element instanceof JetFunction;
newElement = addFunctionType(project, (JetFunction) element, type);
}
ImportClassHelper.perform(type, element, newElement);
ImportClassHelper.addImportDirectiveIfNeeded(type, (JetFile)file);
element.replace(newElement);
}
@Override
@@ -10,10 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.JetParameter;
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.psi.*;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.JetBundle;
@@ -57,7 +54,7 @@ public class ChangeAccessorTypeFix extends JetIntentionAction<JetPropertyAccesso
assert typeReference != null;
CodeEditUtil.replaceChild(parameter.getNode(), typeReference.getNode(), newTypeReference.getNode());
}
ImportClassHelper.perform(type, element, newElement);
element.replace(newElement);
}
public static JetIntentionActionFactory<JetPropertyAccessor> createFactory() {
@@ -2,11 +2,12 @@ package org.jetbrains.jet.plugin.quickfix;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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.JetImportDirective;
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.JetType;
import org.jetbrains.jet.plugin.JetPluginUtil;
@@ -17,48 +18,34 @@ import java.util.List;
* @author svtk
*/
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())) {
elementToReplace.replace(newElement);
/**
* Add import directive corresponding to a type to file when it is needed.
*
* @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;
}
perform(JetPluginUtil.computeTypeFullName(type), elementToReplace, newElement);
}
public static void perform(@NotNull String typeFullName, @NotNull JetFile namespace) {
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;
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
return;
}
return (JetFile) parent;
}
public static void perform(@NotNull String typeFullName, @NotNull PsiElement elementToReplace, @NotNull PsiElement newElement) {
perform(typeFullName, findOuterNamespace(elementToReplace), elementToReplace, newElement);
addImportDirective(JetPluginUtil.computeTypeFullName(type), file);
}
/**
* Add import directive into the PSI tree for the given namespace.
*
* @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) {
List<JetImportDirective> importDirectives = namespace.getImportDirectives();
public static void addImportDirective(@NotNull String importString, @NotNull JetFile file) {
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");
if (!importDirectives.isEmpty()) {
@@ -72,22 +59,14 @@ public class ImportClassHelper {
JetImportDirective lastDirective = importDirectives.get(importDirectives.size() - 1);
lastDirective.getParent().addAfter(newDirective, lastDirective);
lastDirective.getParent().addAfter(JetPsiFactory.createWhiteSpace(namespace.getProject(), lineSeparator), lastDirective);
lastDirective.getParent().addAfter(JetPsiFactory.createWhiteSpace(file.getProject(), lineSeparator), lastDirective);
}
else {
List<JetDeclaration> declarations = namespace.getDeclarations();
List<JetDeclaration> declarations = file.getDeclarations();
assert !declarations.isEmpty();
JetDeclaration firstDeclaration = declarations.iterator().next();
firstDeclaration.getParent().addBefore(newDirective, firstDeclaration);
firstDeclaration.getParent().addBefore(JetPsiFactory.createWhiteSpace(namespace.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);
firstDeclaration.getParent().addBefore(JetPsiFactory.createWhiteSpace(file.getProject(), lineSeparator + lineSeparator), firstDeclaration);
}
}
}
@@ -78,6 +78,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (!(file instanceof JetFile)) return;
JetProperty newElement = (JetProperty) element.copy();
JetPropertyAccessor getter = newElement.getGetter();
if (removeGetter && getter != null) {
@@ -102,10 +103,9 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
}
}
if (needImport) {
ImportClassHelper.perform(type, element, newElement);
} else {
element.replace(newElement);
ImportClassHelper.addImportDirectiveIfNeeded(type, (JetFile)file);
}
element.replace(newElement);
}
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);
return fileResult;