KT-1426 Implement "Optimize Imports" in plugin - untested draft version
This commit is contained in:
+4
-3
@@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.psi.JetPsiFactory;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.ImportPath;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -36,7 +37,7 @@ import java.util.Collections;
|
||||
*/
|
||||
public class JavaBridgeConfiguration implements ModuleConfiguration {
|
||||
|
||||
public static final String[] DEFAULT_JAVA_IMPORTS = new String[] { "java.lang.*" };
|
||||
public static final ImportPath[] DEFAULT_JAVA_IMPORTS = new ImportPath[] { new ImportPath("java.lang.*") };
|
||||
|
||||
public static ModuleConfiguration createJavaBridgeConfiguration(@NotNull Project project, @NotNull BindingTrace trace, ModuleConfiguration delegateConfiguration) {
|
||||
return new JavaBridgeConfiguration(project, trace, delegateConfiguration);
|
||||
@@ -54,8 +55,8 @@ public class JavaBridgeConfiguration implements ModuleConfiguration {
|
||||
|
||||
@Override
|
||||
public void addDefaultImports(@NotNull WritableScope rootScope, @NotNull Collection<JetImportDirective> directives) {
|
||||
for (String importFQN : DEFAULT_JAVA_IMPORTS) {
|
||||
directives.add(JetPsiFactory.createImportDirective(project, importFQN));
|
||||
for (ImportPath importPath : DEFAULT_JAVA_IMPORTS) {
|
||||
directives.add(JetPsiFactory.createImportDirective(project, importPath));
|
||||
}
|
||||
delegateConfiguration.addDefaultImports(rootScope, directives);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetImportDirective;
|
||||
import org.jetbrains.jet.lang.psi.JetPsiFactory;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.ImportPath;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -30,7 +31,8 @@ import java.util.Collection;
|
||||
* @author svtk
|
||||
*/
|
||||
public class DefaultModuleConfiguration implements ModuleConfiguration {
|
||||
public static final String[] DEFAULT_JET_IMPORTS = new String[] { "kotlin.*", "kotlin.io.*" };
|
||||
public static final ImportPath[] DEFAULT_JET_IMPORTS = new ImportPath[] {
|
||||
new ImportPath("kotlin.*"), new ImportPath("kotlin.io.*") };
|
||||
|
||||
private Project project;
|
||||
|
||||
@@ -44,7 +46,7 @@ public class DefaultModuleConfiguration implements ModuleConfiguration {
|
||||
|
||||
@Override
|
||||
public void addDefaultImports(@NotNull WritableScope rootScope, @NotNull Collection<JetImportDirective> directives) {
|
||||
for (String defaultJetImport : DEFAULT_JET_IMPORTS) {
|
||||
for (ImportPath defaultJetImport : DEFAULT_JET_IMPORTS) {
|
||||
directives.add(JetPsiFactory.createImportDirective(project, defaultJetImport));
|
||||
}
|
||||
}
|
||||
@@ -52,5 +54,4 @@ public class DefaultModuleConfiguration implements ModuleConfiguration {
|
||||
@Override
|
||||
public void extendNamespaceScope(@NotNull BindingTrace trace, @NotNull NamespaceDescriptor namespaceDescriptor, @NotNull WritableScope namespaceMemberScope) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.util.LocalTimeCounter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.ImportPath;
|
||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
import org.jetbrains.jet.plugin.JetFileType;
|
||||
@@ -140,21 +140,35 @@ public class JetPsiFactory {
|
||||
return function.getValueParameters().get(0);
|
||||
}
|
||||
|
||||
// public static JetNamespace createNamespace(Project project, String text) {
|
||||
// JetFile file = createFile(project, text);
|
||||
// return file.getRootNamespace();
|
||||
// }
|
||||
|
||||
public static JetImportDirective createImportDirective(Project project, @NotNull String classPath) {
|
||||
if (classPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("import path must not be empty");
|
||||
}
|
||||
JetFile namespace = createFile(project, "import " + classPath);
|
||||
return namespace.getImportDirectives().iterator().next();
|
||||
@NotNull
|
||||
public static JetImportDirective createImportDirective(Project project, @NotNull String path) {
|
||||
return createImportDirective(project, new ImportPath(path));
|
||||
}
|
||||
|
||||
public static JetImportDirective createImportDirective(Project project, @NotNull FqName fqName) {
|
||||
return createImportDirective(project, fqName.getFqName());
|
||||
@NotNull
|
||||
public static JetImportDirective createImportDirective(Project project, @NotNull ImportPath importPath) {
|
||||
return createImportDirective(project, importPath, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetImportDirective createImportDirective(Project project, @NotNull ImportPath importPath, @Nullable String aliasName) {
|
||||
if (importPath.fqnPart().isRoot()) {
|
||||
throw new IllegalArgumentException("import path must not be empty");
|
||||
}
|
||||
|
||||
StringBuilder importDirectiveBuilder = new StringBuilder("import ");
|
||||
importDirectiveBuilder.append(importPath.getPathStr());
|
||||
|
||||
if (aliasName != null) {
|
||||
if (aliasName.isEmpty()) {
|
||||
throw new IllegalArgumentException("Alias must not be empty");
|
||||
}
|
||||
|
||||
importDirectiveBuilder.append(" as ").append(aliasName);
|
||||
}
|
||||
|
||||
JetFile namespace = createFile(project, importDirectiveBuilder.toString());
|
||||
return namespace.getImportDirectives().iterator().next();
|
||||
}
|
||||
|
||||
public static PsiElement createPrimaryConstructor(Project project) {
|
||||
|
||||
@@ -23,8 +23,8 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.ImportPath;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
import org.jetbrains.jet.util.QualifiedNamesUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
@@ -194,14 +194,14 @@ public class JetPsiUtil {
|
||||
}
|
||||
|
||||
@Nullable @JetElement.IfNotParsed
|
||||
public static String getImportPath(JetImportDirective importDirective) {
|
||||
public static ImportPath getImportPath(JetImportDirective importDirective) {
|
||||
final JetExpression importedReference = importDirective.getImportedReference();
|
||||
if (importedReference == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String text = importedReference.getText();
|
||||
return text.replaceAll(" ", "") + (importDirective.isAllUnder() ? ".*" : "");
|
||||
return new ImportPath(text.replaceAll(" ", "") + (importDirective.isAllUnder() ? ".*" : ""));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Nikolay Krasko
|
||||
*/
|
||||
public final class ImportPath {
|
||||
final @NotNull FqName fqName;
|
||||
final boolean isAllUnder;
|
||||
|
||||
public ImportPath(@NotNull FqName fqName, boolean isAllUnder) {
|
||||
this.fqName = fqName;
|
||||
this.isAllUnder = isAllUnder;
|
||||
}
|
||||
|
||||
public ImportPath(@NotNull String pathStr) {
|
||||
if (pathStr.endsWith(".*")) {
|
||||
this.isAllUnder = true;
|
||||
this.fqName = new FqName(pathStr.substring(0, pathStr.length() - 2));
|
||||
}
|
||||
else {
|
||||
this.isAllUnder = false;
|
||||
this.fqName = new FqName(pathStr);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPathStr() {
|
||||
return fqName.getFqName() + (isAllUnder ? ".*" : "");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FqName fqnPart() {
|
||||
return fqName;
|
||||
}
|
||||
|
||||
public boolean isAllUnder() {
|
||||
return isAllUnder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * fqName.hashCode() + (isAllUnder ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof ImportPath)) return false;
|
||||
|
||||
ImportPath other = (ImportPath) obj;
|
||||
return fqName.equals(other.fqName) && (isAllUnder == other.isAllUnder);
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,18 @@ package org.jetbrains.jet.util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.ImportPath;
|
||||
|
||||
/**
|
||||
* Common methods for working with qualified names strings.
|
||||
* Common methods for working with qualified names.
|
||||
*
|
||||
* @author Nikolay Krasko
|
||||
*/
|
||||
public final class QualifiedNamesUtil {
|
||||
|
||||
private QualifiedNamesUtil() {
|
||||
}
|
||||
|
||||
public static boolean isSubpackageOf(@NotNull final FqName subpackageName, @NotNull FqName packageName) {
|
||||
return subpackageName.equals(packageName) ||
|
||||
(subpackageName.getFqName().startsWith(packageName.getFqName()) && subpackageName.getFqName().charAt(packageName.getFqName().length()) == '.');
|
||||
@@ -45,6 +49,10 @@ public final class QualifiedNamesUtil {
|
||||
return fqn.indexOf('.') < 0;
|
||||
}
|
||||
|
||||
public static boolean isOneSegmentFQN(@NotNull FqName fqn) {
|
||||
return isOneSegmentFQN(fqn.getFqName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String fqnToShortName(@NotNull FqName fqn) {
|
||||
return getLastSegment(fqn);
|
||||
@@ -62,18 +70,18 @@ public final class QualifiedNamesUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String withoutLastSegment(@NotNull String fqn) {
|
||||
final int lastDotIndex = fqn.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
return fqn.substring(0, lastDotIndex);
|
||||
}
|
||||
|
||||
return "";
|
||||
public static FqName withoutLastSegment(@NotNull FqName fqName) {
|
||||
return fqName.parent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FqName withoutLastSegment(@NotNull FqName fqName) {
|
||||
return fqName.parent();
|
||||
public static FqName withoutFirstSegment(@NotNull FqName fqName) {
|
||||
if (fqName.isRoot() || fqName.parent().isRoot()) {
|
||||
return FqName.ROOT;
|
||||
}
|
||||
|
||||
String fqNameStr = fqName.getFqName();
|
||||
return new FqName(fqNameStr.substring(fqNameStr.indexOf('.'), fqNameStr.length()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -121,18 +129,19 @@ public final class QualifiedNamesUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that given fqn could be imported with import.
|
||||
*
|
||||
* @param importPath path from the import. Could contain .* part
|
||||
* @param fqn or another import directive
|
||||
*/
|
||||
public static boolean isImported(@NotNull String importPath, @NotNull String fqn) {
|
||||
if (importPath.endsWith("*")) {
|
||||
// TODO: import path is not valid FQN
|
||||
return withoutLastSegment(importPath).equals(withoutLastSegment(fqn));
|
||||
public static boolean isImported(@NotNull ImportPath alreadyImported, @NotNull FqName fqName) {
|
||||
if (alreadyImported.isAllUnder()) {
|
||||
return alreadyImported.fqnPart().equals(fqName.parent());
|
||||
}
|
||||
|
||||
return importPath.equals(fqn);
|
||||
return alreadyImported.fqnPart().equals(fqName);
|
||||
}
|
||||
|
||||
public static boolean isImported(@NotNull ImportPath alreadyImported, @NotNull ImportPath newImport) {
|
||||
if (newImport.isAllUnder()) {
|
||||
return alreadyImported.equals(newImport);
|
||||
}
|
||||
|
||||
return isImported(alreadyImported, newImport.fqnPart());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ 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 org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
@@ -49,7 +50,7 @@ public class JetAddImportAction implements QuestionAction {
|
||||
private final Project myProject;
|
||||
private final Editor myEditor;
|
||||
private final PsiElement myElement;
|
||||
private final List<String> possibleImports;
|
||||
private final List<FqName> possibleImports;
|
||||
|
||||
/**
|
||||
* @param project Project where action takes place.
|
||||
@@ -61,7 +62,7 @@ public class JetAddImportAction implements QuestionAction {
|
||||
@NotNull Project project,
|
||||
@NotNull Editor editor,
|
||||
@NotNull PsiElement element,
|
||||
@NotNull Iterable<String> imports
|
||||
@NotNull Iterable<FqName> imports
|
||||
) {
|
||||
myProject = project;
|
||||
myEditor = editor;
|
||||
@@ -90,14 +91,14 @@ public class JetAddImportAction implements QuestionAction {
|
||||
}
|
||||
|
||||
protected BaseListPopupStep getImportSelectionPopup() {
|
||||
return new BaseListPopupStep<String>(QuickFixBundle.message("class.to.import.chooser.title"), possibleImports) {
|
||||
return new BaseListPopupStep<FqName>(QuickFixBundle.message("class.to.import.chooser.title"), possibleImports) {
|
||||
@Override
|
||||
public boolean isAutoSelectionEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PopupStep onChosen(String selectedValue, boolean finalChoice) {
|
||||
public PopupStep onChosen(FqName selectedValue, boolean finalChoice) {
|
||||
if (selectedValue == null) {
|
||||
return FINAL_CHOICE;
|
||||
}
|
||||
@@ -107,7 +108,7 @@ public class JetAddImportAction implements QuestionAction {
|
||||
return FINAL_CHOICE;
|
||||
}
|
||||
|
||||
List<String> toExclude = AddImportAction.getAllExcludableStrings(selectedValue);
|
||||
List<String> toExclude = AddImportAction.getAllExcludableStrings(selectedValue.getFqName());
|
||||
|
||||
return new BaseListPopupStep<String>(null, toExclude) {
|
||||
@NotNull
|
||||
@@ -128,24 +129,25 @@ public class JetAddImportAction implements QuestionAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubstep(String selectedValue) {
|
||||
public boolean hasSubstep(FqName selectedValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getTextFor(String value) {
|
||||
return value;
|
||||
public String getTextFor(FqName value) {
|
||||
return value.getFqName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIconFor(String aValue) {
|
||||
public Icon getIconFor(FqName aValue) {
|
||||
// TODO: change icon
|
||||
return PlatformIcons.CLASS_ICON;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected static void addImport(final PsiElement element, final Project project, final String selectedImport) {
|
||||
protected static void addImport(final PsiElement element, final Project project, final FqName selectedImport) {
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments();
|
||||
|
||||
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
|
||||
@@ -154,13 +156,10 @@ public class JetAddImportAction implements QuestionAction {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
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,
|
||||
(JetFile)file
|
||||
ImportInsertHelper.addImportDirective(selectedImport,
|
||||
(JetFile) file
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,10 +36,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.asJava.JavaElementFinder;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTraceContext;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.ImportsResolver;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
|
||||
@@ -177,7 +174,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
|
||||
for (PsiMethod method : topLevelFunctionPrototypes) {
|
||||
FqName functionFQN = JetFromJavaDescriptorHelper.getJetTopLevelDeclarationFQN(method);
|
||||
if (functionFQN != null) {
|
||||
JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, functionFQN);
|
||||
JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, new ImportPath(functionFQN, false));
|
||||
Collection<? extends DeclarationDescriptor> declarationDescriptors = ImportsResolver.analyseImportReference(importDirective, jetScope, new BindingTraceContext());
|
||||
for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
|
||||
if (declarationDescriptor instanceof FunctionDescriptor) {
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
|
||||
import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportClassHelper;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -93,7 +93,7 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns
|
||||
bodyBuilder.append("val ");
|
||||
}
|
||||
bodyBuilder.append(descriptor.getName()).append(":").append(descriptor.getType());
|
||||
ImportClassHelper.addImportDirectiveIfNeeded(descriptor.getType(), file);
|
||||
ImportInsertHelper.addImportDirectiveIfNeeded(descriptor.getType(), file);
|
||||
String initializer = defaultInitializer(descriptor.getType(), JetStandardLibrary.getInstance());
|
||||
if (initializer != null) {
|
||||
bodyBuilder.append("=").append(initializer);
|
||||
@@ -118,14 +118,14 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns
|
||||
bodyBuilder.append(":");
|
||||
bodyBuilder.append(parameterDescriptor.getType().toString());
|
||||
|
||||
ImportClassHelper.addImportDirectiveIfNeeded(parameterDescriptor.getType(), file);
|
||||
ImportInsertHelper.addImportDirectiveIfNeeded(parameterDescriptor.getType(), file);
|
||||
}
|
||||
bodyBuilder.append(")");
|
||||
final JetType returnType = descriptor.getReturnType();
|
||||
final JetStandardLibrary stdlib = JetStandardLibrary.getInstance();
|
||||
if (!returnType.equals(stdlib.getTuple0Type())) {
|
||||
bodyBuilder.append(":").append(returnType.toString());
|
||||
ImportClassHelper.addImportDirectiveIfNeeded(returnType, file);
|
||||
ImportInsertHelper.addImportDirectiveIfNeeded(returnType, file);
|
||||
}
|
||||
|
||||
bodyBuilder.append("{").append("throw UnsupportedOperationException()").append("}");
|
||||
|
||||
@@ -31,12 +31,11 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.lang.psi.JetImportDirective;
|
||||
import org.jetbrains.jet.lang.psi.JetPsiUtil;
|
||||
import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.plugin.completion.JetLookupObject;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportClassHelper;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper;
|
||||
|
||||
/**
|
||||
* Performs number of code modification after insertion jet function:
|
||||
@@ -135,7 +134,7 @@ public class JetFunctionInsertHandler implements InsertHandler<LookupElement> {
|
||||
@Override
|
||||
public void run() {
|
||||
final FqName fqn = DescriptorUtils.getFQName(functionDescriptor).toSafe();
|
||||
ImportClassHelper.addImportDirective(fqn.getFqName(), file);
|
||||
ImportInsertHelper.addImportDirective(fqn, file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+3
-2
@@ -20,7 +20,8 @@ import com.intellij.codeInsight.completion.InsertHandler;
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.JavaPsiClassReferenceElement;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportClassHelper;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper;
|
||||
|
||||
/**
|
||||
* Handler for inserting java class completion.
|
||||
@@ -34,7 +35,7 @@ public class JetJavaClassInsertHandler implements InsertHandler<JavaPsiClassRefe
|
||||
public void handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
|
||||
if (context.getFile() instanceof JetFile) {
|
||||
final JetFile jetFile = (JetFile) context.getFile();
|
||||
ImportClassHelper.addImportDirective(item.getQualifiedName(), jetFile);
|
||||
ImportInsertHelper.addImportDirective(new FqName(item.getQualifiedName()), jetFile);
|
||||
}
|
||||
|
||||
// check annotation
|
||||
|
||||
@@ -17,19 +17,20 @@
|
||||
package org.jetbrains.jet.plugin.editor.importOptimizer;
|
||||
|
||||
import com.intellij.lang.ImportOptimizer;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.ImportPath;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper;
|
||||
import org.jetbrains.jet.util.QualifiedNamesUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Nikolay Krasko
|
||||
@@ -47,17 +48,79 @@ public class JetImportOptimizer implements ImportOptimizer {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
JetFile jetFile = (JetFile) file;
|
||||
Set<String> usedQualifiedNames = extractUsedQualifiedNames(jetFile);
|
||||
final JetFile jetFile = (JetFile) file;
|
||||
final Set<FqName> usedQualifiedNames = extractUsedQualifiedNames(jetFile);
|
||||
|
||||
final List<JetImportDirective> sortedDirectives = jetFile.getImportDirectives();
|
||||
Collections.sort(sortedDirectives, new Comparator<JetImportDirective>() {
|
||||
@Override
|
||||
public int compare(JetImportDirective directive1, JetImportDirective directive2) {
|
||||
ImportPath firstPath = JetPsiUtil.getImportPath(directive1);
|
||||
ImportPath secondPath = JetPsiUtil.getImportPath(directive2);
|
||||
|
||||
if (firstPath == null || secondPath == null) {
|
||||
return firstPath == null && secondPath == null ? 0 :
|
||||
firstPath == null ? -1 :
|
||||
1;
|
||||
}
|
||||
|
||||
// import bla.bla.bla.* should be before import bla.bla.bla.something
|
||||
if (firstPath.isAllUnder() && !secondPath.isAllUnder() && firstPath.fqnPart().equals(secondPath.fqnPart().parent())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!firstPath.isAllUnder() && secondPath.isAllUnder() && secondPath.fqnPart().equals(firstPath.fqnPart().parent())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return firstPath.getPathStr().compareTo(secondPath.getPathStr());
|
||||
}
|
||||
});
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Remove imports
|
||||
List<JetImportDirective> imports = jetFile.getImportDirectives();
|
||||
if (!imports.isEmpty()) {
|
||||
jetFile.deleteChildRange(imports.get(0), imports.get(imports.size() - 1));
|
||||
}
|
||||
|
||||
// Insert back only necessary imports in correct order
|
||||
for (JetImportDirective anImport : sortedDirectives) {
|
||||
ImportPath importPath = JetPsiUtil.getImportPath(anImport);
|
||||
if (importPath == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isUseful(importPath, anImport.getAliasName(), usedQualifiedNames)) {
|
||||
ImportInsertHelper.addImportDirective(importPath, anImport.getAliasName(), jetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public static Set<String> extractUsedQualifiedNames(JetFile jetFile) {
|
||||
public static boolean isUseful(ImportPath importPath, @Nullable String aliasName, Collection<FqName> usedNames) {
|
||||
if (aliasName != null) {
|
||||
// TODO: Add better analysis for aliases
|
||||
return true;
|
||||
}
|
||||
|
||||
final Set<String> usedQualifiedNames = new HashSet<String>();
|
||||
for (FqName usedName : usedNames) {
|
||||
if (QualifiedNamesUtil.isImported(importPath, usedName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Set<FqName> extractUsedQualifiedNames(JetFile jetFile) {
|
||||
|
||||
final Set<FqName> usedQualifiedNames = new HashSet<FqName>();
|
||||
jetFile.accept(new JetVisitorVoid() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
@@ -83,7 +146,7 @@ public class JetImportOptimizer implements ImportOptimizer {
|
||||
}
|
||||
|
||||
for (PsiElement psiReference : references) {
|
||||
String fqName = getElementFQName(psiReference);
|
||||
FqName fqName = getElementFQName(psiReference);
|
||||
if (fqName != null) {
|
||||
usedQualifiedNames.add(fqName);
|
||||
}
|
||||
@@ -101,7 +164,7 @@ public class JetImportOptimizer implements ImportOptimizer {
|
||||
|
||||
|
||||
@Nullable
|
||||
public static String getElementFQName(PsiElement element) {
|
||||
public static FqName getElementFQName(PsiElement element) {
|
||||
if (element instanceof JetClassOrObject) {
|
||||
return JetPsiUtil.getFQName((JetClassOrObject) element);
|
||||
}
|
||||
@@ -109,7 +172,10 @@ public class JetImportOptimizer implements ImportOptimizer {
|
||||
return JetPsiUtil.getFQName((JetNamedFunction) element);
|
||||
}
|
||||
if (element instanceof PsiClass) {
|
||||
return ((PsiClass) element).getQualifiedName();
|
||||
String qualifiedName = ((PsiClass) element).getQualifiedName();
|
||||
if (qualifiedName != null) {
|
||||
return new FqName(qualifiedName);
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod) element;
|
||||
@@ -117,15 +183,14 @@ public class JetImportOptimizer implements ImportOptimizer {
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
|
||||
if (containingClass != null) {
|
||||
String classFQN = containingClass.getQualifiedName();
|
||||
|
||||
if (classFQN != null) {
|
||||
if (QualifiedNamesUtil.fqnToShortName(classFQN).equals(JvmAbi.PACKAGE_CLASS)) {
|
||||
String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN);
|
||||
return QualifiedNamesUtil.combine(classParentFQN, method.getName());
|
||||
String classFQNStr = containingClass.getQualifiedName();
|
||||
if (classFQNStr != null) {
|
||||
FqName classFQN = new FqName(classFQNStr);
|
||||
if (classFQN.shortName().equals(JvmAbi.PACKAGE_CLASS)) {
|
||||
return QualifiedNamesUtil.combine(classFQN.parent(), method.getName());
|
||||
}
|
||||
else {
|
||||
return QualifiedNamesUtil.combine(containingClass.getQualifiedName(), method.getName());
|
||||
return QualifiedNamesUtil.combine(classFQN, method.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class AddReturnTypeFix extends JetIntentionAction<JetNamedDeclaration> {
|
||||
assert element instanceof JetFunction;
|
||||
newElement = addFunctionType(project, (JetFunction) element, type);
|
||||
}
|
||||
ImportClassHelper.addImportDirectiveIfNeeded(type, (JetFile) file);
|
||||
ImportInsertHelper.addImportDirectiveIfNeeded(type, (JetFile) file);
|
||||
element.replace(newElement);
|
||||
}
|
||||
|
||||
|
||||
@@ -225,13 +225,7 @@ public class ImportClassAndFunFix extends JetHintAction<JetSimpleNameExpression>
|
||||
|
||||
@NotNull
|
||||
private JetAddImportAction createAction(@NotNull Project project, @NotNull Editor editor) {
|
||||
Collection<String> suggesionStrings = Collections2.transform(suggestions, new Function<FqName, String>() {
|
||||
@Override
|
||||
public String apply(FqName fqName) {
|
||||
return fqName.getFqName();
|
||||
}
|
||||
});
|
||||
return new JetAddImportAction(project, editor, element, suggesionStrings);
|
||||
return new JetAddImportAction(project, editor, element, suggestions);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+47
-21
@@ -18,10 +18,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.DefaultModuleConfiguration;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.ImportPath;
|
||||
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
|
||||
import org.jetbrains.jet.lang.resolve.java.JavaBridgeConfiguration;
|
||||
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
|
||||
@@ -35,7 +37,10 @@ import java.util.List;
|
||||
/**
|
||||
* @author svtk
|
||||
*/
|
||||
public class ImportClassHelper {
|
||||
public class ImportInsertHelper {
|
||||
private ImportInsertHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add import directive corresponding to a type to file when it is needed.
|
||||
*
|
||||
@@ -51,36 +56,42 @@ public class ImportClassHelper {
|
||||
if (element != null && element.getContainingFile() == file) { //declaration is in the same file, so no import is needed
|
||||
return;
|
||||
}
|
||||
addImportDirective(JetPluginUtil.computeTypeFullName(type).getFqName(), file);
|
||||
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 importFqn full name of the import
|
||||
* @param file File where directive should be added.
|
||||
*/
|
||||
public static void addImportDirective(@NotNull String importString, @NotNull JetFile file) {
|
||||
public static void addImportDirective(@NotNull FqName importFqn, @NotNull JetFile file) {
|
||||
addImportDirective(new ImportPath(importFqn, false), null, file);
|
||||
}
|
||||
|
||||
// TODO: hack
|
||||
if (importString.startsWith(JavaDescriptorResolver.JAVA_ROOT + ".")) {
|
||||
importString = importString.substring((JavaDescriptorResolver.JAVA_ROOT + ".").length());
|
||||
public static void addImportDirective(@NotNull ImportPath importPath, @Nullable String aliasName, @NotNull JetFile file) {
|
||||
|
||||
if (QualifiedNamesUtil.getFirstSegment(importPath.fqnPart().getFqName()).equals(JavaDescriptorResolver.JAVA_ROOT)) {
|
||||
FqName withoutJavaRoot = QualifiedNamesUtil.withoutFirstSegment(importPath.fqnPart());
|
||||
importPath = new ImportPath(withoutJavaRoot, importPath.isAllUnder());
|
||||
}
|
||||
|
||||
if (isImportedByDefault(importString, JetPsiUtil.getFQName(file))) {
|
||||
if (isImportedByDefault(importPath, aliasName, JetPsiUtil.getFQName(file))) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<JetImportDirective> importDirectives = file.getImportDirectives();
|
||||
JetImportDirective newDirective = JetPsiFactory.createImportDirective(file.getProject(), importPath, aliasName);
|
||||
|
||||
JetImportDirective newDirective = JetPsiFactory.createImportDirective(file.getProject(), importString);
|
||||
List<JetImportDirective> importDirectives = file.getImportDirectives();
|
||||
|
||||
if (!importDirectives.isEmpty()) {
|
||||
|
||||
// Check if import is already present
|
||||
for (JetImportDirective directive : importDirectives) {
|
||||
String importPath = JetPsiUtil.getImportPath(directive);
|
||||
if (importPath != null && QualifiedNamesUtil.isImported(importPath, importString)) {
|
||||
ImportPath existentImportPath = JetPsiUtil.getImportPath(directive);
|
||||
if (existentImportPath != null &&
|
||||
directive.getAliasName() == null &&
|
||||
QualifiedNamesUtil.isImported(existentImportPath, importPath)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -101,25 +112,40 @@ public class ImportClassHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// Check that import is useless
|
||||
private static boolean isImportedByDefault(@NotNull String importString, @NotNull FqName filePackageFqn) {
|
||||
if (QualifiedNamesUtil.isOneSegmentFQN(importString) ||
|
||||
filePackageFqn.getFqName().equals(QualifiedNamesUtil.withoutLastSegment(importString))) {
|
||||
|
||||
/**
|
||||
* Check that import is useless.
|
||||
*/
|
||||
private static boolean isImportedByDefault(@NotNull ImportPath importPath, String aliasName, @NotNull FqName filePackageFqn) {
|
||||
if (importPath.fqnPart().isRoot()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String defaultJetImport : DefaultModuleConfiguration.DEFAULT_JET_IMPORTS) {
|
||||
if (QualifiedNamesUtil.isImported(defaultJetImport, importString)) {
|
||||
if (aliasName != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Single element import without .* and alias is useless
|
||||
if (!importPath.isAllUnder() && QualifiedNamesUtil.isOneSegmentFQN(importPath.fqnPart())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// There's no need to import a declaration from the package of current file
|
||||
if (!importPath.isAllUnder() && filePackageFqn.equals(importPath.fqnPart().parent())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (ImportPath defaultJetImport : DefaultModuleConfiguration.DEFAULT_JET_IMPORTS) {
|
||||
if (QualifiedNamesUtil.isImported(defaultJetImport, importPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (String defaultJavaImport : JavaBridgeConfiguration.DEFAULT_JAVA_IMPORTS) {
|
||||
if (QualifiedNamesUtil.isImported(defaultJavaImport + ".*", importString)) {
|
||||
for (ImportPath defaultJavaImport : JavaBridgeConfiguration.DEFAULT_JAVA_IMPORTS) {
|
||||
if (QualifiedNamesUtil.isImported(defaultJavaImport, importPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
|
||||
}
|
||||
}
|
||||
if (needImport) {
|
||||
ImportClassHelper.addImportDirectiveIfNeeded(type, (JetFile)file);
|
||||
ImportInsertHelper.addImportDirectiveIfNeeded(type, (JetFile) file);
|
||||
}
|
||||
element.replace(newElement);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.jet.plugin.quickfix;
|
||||
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.plugin.PluginTestCaseBase;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -28,19 +29,19 @@ import java.io.IOException;
|
||||
*/
|
||||
public class ImportClassHelperTest extends LightDaemonAnalyzerTestCase {
|
||||
public void testDoNotImportIfGeneralExist() {
|
||||
testImportInFile("jettesting.data.testFunction");
|
||||
doFileTest("jettesting.data.testFunction");
|
||||
}
|
||||
|
||||
public void testDoNotImportIfGeneralSpaceExist() {
|
||||
testImportInFile("jettesting.data.testFunction");
|
||||
doFileTest("jettesting.data.testFunction");
|
||||
}
|
||||
|
||||
public void testNoDefaultImport() {
|
||||
testImportInFile("kotlin.io.println");
|
||||
doFileTest("kotlin.io.println");
|
||||
}
|
||||
|
||||
public void testImportBeforeObject() {
|
||||
testImportInFile("java.util.HashSet");
|
||||
doFileTest("java.util.HashSet");
|
||||
}
|
||||
|
||||
public void testInsertInEmptyFile() throws IOException {
|
||||
@@ -48,7 +49,7 @@ public class ImportClassHelperTest extends LightDaemonAnalyzerTestCase {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ImportClassHelper.addImportDirective("java.util.ArrayList", (JetFile) getFile());
|
||||
ImportInsertHelper.addImportDirective(new FqName("java.util.ArrayList"), (JetFile) getFile());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -60,19 +61,19 @@ public class ImportClassHelperTest extends LightDaemonAnalyzerTestCase {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ImportClassHelper.addImportDirective("java.util.ArrayList", (JetFile) getFile());
|
||||
ImportInsertHelper.addImportDirective(new FqName("java.util.ArrayList"), (JetFile) getFile());
|
||||
}
|
||||
});
|
||||
|
||||
checkResultByText("package some\n\nimport java.util.ArrayList");
|
||||
}
|
||||
|
||||
public void testImportInFile(final String importString) {
|
||||
public void doFileTest(final String importString) {
|
||||
configureByFile(getTestName(false) + ".kt");
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ImportClassHelper.addImportDirective(importString, (JetFile) getFile());
|
||||
ImportInsertHelper.addImportDirective(new FqName(importString), (JetFile) getFile());
|
||||
}
|
||||
});
|
||||
checkResultByFile(getTestName(false) + ".kt.after");
|
||||
|
||||
Reference in New Issue
Block a user