KT-1426 Implement "Optimize Imports" in plugin - untested draft version

This commit is contained in:
Nikolay Krasko
2012-03-15 21:22:03 +04:00
parent f93ef619f6
commit 76cc231cb2
17 changed files with 308 additions and 131 deletions
@@ -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());
}
}