From 3155a6631992dabcf2f722bf5af861036126b58c Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Wed, 5 Mar 2014 15:06:08 +0400 Subject: [PATCH] Move, rewrite and rename JetImportOptimizer Rewrite it to Kotlin and rename to KotlinImportOptimizer, move to imports package Create ImportsUtils.kt --- idea/src/META-INF/plugin.xml | 2 +- .../KotlinCopyPasteReferenceProcessor.kt | 13 +- .../importOptimizer/JetImportOptimizer.java | 153 ------------------ .../jet/plugin/imports/ImportsUtils.kt | 34 ++++ .../plugin/imports/KotlinImportOptimizer.kt | 109 +++++++++++++ .../FoldingAfterOptimizeImportsTest.kt | 4 +- .../OptimizeImportsMultiFileTest.java | 4 +- .../importOptimizer/OptimizeImportsTest.java | 4 +- 8 files changed, 151 insertions(+), 172 deletions(-) delete mode 100644 idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java create mode 100644 idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt create mode 100644 idea/src/org/jetbrains/jet/plugin/imports/KotlinImportOptimizer.kt diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index bc6c335d1b7..feefd52f27e 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -147,7 +147,7 @@ - + diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinCopyPasteReferenceProcessor.kt b/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinCopyPasteReferenceProcessor.kt index e88ac2f3638..de1015c68af 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinCopyPasteReferenceProcessor.kt +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinCopyPasteReferenceProcessor.kt @@ -64,6 +64,7 @@ import org.jetbrains.jet.lang.psi.JetTypeReference import com.intellij.util.containers.ContainerUtil import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor +import org.jetbrains.jet.plugin.imports.* //NOTE: this class is based on CopyPasteReferenceProcessor and JavaCopyPasteReferenceProcessor public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor { @@ -375,18 +376,6 @@ private fun PsiElement.isInCopiedArea(fileCopiedFrom: JetFile, startOffsets: Int } } -public val DeclarationDescriptor.importableFqName: FqName? - get() { - if (this is ConstructorDescriptor) return getContainingDeclaration().importableFqName - val mayBeUnsafe = DescriptorUtils.getFqName(this) - return if (mayBeUnsafe.isSafe()) { - mayBeUnsafe.toSafe() - } - else { - null - } - } - private val DeclarationDescriptor.isExtension: Boolean get() = this is CallableDescriptor && getReceiverParameter() != null diff --git a/idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java b/idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java deleted file mode 100644 index 6ee81995a10..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2010-2014 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.plugin.editor.importOptimizer; - -import com.google.common.collect.Lists; -import com.intellij.lang.ImportOptimizer; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.progress.ProgressIndicatorProvider; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiReference; -import com.intellij.psi.util.PsiTreeUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; -import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.ImportPath; -import org.jetbrains.jet.lang.resolve.name.FqName; -import org.jetbrains.jet.lang.resolve.name.Name; -import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; -import org.jetbrains.jet.lang.resolve.name.NamePackage; -import org.jetbrains.jet.plugin.codeInsight.CodeInsightPackage; -import org.jetbrains.jet.plugin.references.JetReference; - -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static org.jetbrains.jet.plugin.quickfix.ImportInsertHelper.needImport; - -public class JetImportOptimizer implements ImportOptimizer { - @Override - public boolean supports(PsiFile file) { - return file instanceof JetFile; - } - - @NotNull - @Override - public Runnable processFile(final PsiFile file) { - return new Runnable() { - - @Override - public void run() { - final JetFile jetFile = (JetFile) file; - final Set usedQualifiedNames = extractUsedQualifiedNames(jetFile); - - final List directives = jetFile.getImportDirectives(); - - final List directivesBeforeCurrent = Lists.newArrayList(); - final List directivesAfterCurrent = jetFile.getImportDirectives(); - - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - // Remove only unnecessary imports - for (JetImportDirective anImport : directives) { - directivesAfterCurrent.remove(anImport); - - ImportPath importPath = JetPsiUtil.getImportPath(anImport); - if (importPath == null) { - continue; - } - - if (isUseful(importPath, usedQualifiedNames) && - needImport(importPath, jetFile, directivesBeforeCurrent) && - needImport(importPath, jetFile, directivesAfterCurrent)) { - directivesBeforeCurrent.add(anImport); - } - else { - anImport.delete(); - } - } - } - }); - } - }; - } - - public static boolean isUseful(ImportPath importPath, Collection usedNames) { - if (importPath.hasAlias()) { - // TODO: Add better analysis for aliases - return true; - } - - for (FqName usedName : usedNames) { - if (NamePackage.isImported(usedName, importPath)) { - return true; - } - } - - return false; - } - - public static Set extractUsedQualifiedNames(JetFile jetFile) { - final Set usedQualifiedNames = new HashSet(); - jetFile.accept(new JetVisitorVoid() { - @Override - public void visitElement(PsiElement element) { - ProgressIndicatorProvider.checkCanceled(); - element.acceptChildren(this); - } - - @Override - public void visitUserType(@NotNull JetUserType type) { - if (type.getQualifier() == null) { - super.visitUserType(type); - } - else { - JetTypeArgumentList argumentList = type.getTypeArgumentList(); - if (argumentList != null) { - super.visitTypeArgumentList(argumentList); - } - visitUserType(type.getQualifier()); - } - } - - @Override - public void visitJetElement(@NotNull JetElement element) { - if (PsiTreeUtil.getParentOfType(element, JetImportDirective.class) != null || - PsiTreeUtil.getParentOfType(element, JetPackageDirective.class) != null) { - return; - } - PsiReference reference = element.getReference(); - if (reference instanceof JetReference) { - Collection referencedDescriptors = ((JetReference) reference).resolveToDescriptors(); - for (DeclarationDescriptor descriptor : referencedDescriptors) { - FqName importableFqName = CodeInsightPackage.getImportableFqName(descriptor); - if (importableFqName != null) { - usedQualifiedNames.add(importableFqName); - } - } - } - super.visitJetElement(element); - } - }); - - return usedQualifiedNames; - } -} diff --git a/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt b/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt new file mode 100644 index 00000000000..7c3bc1a6097 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2014 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.plugin.imports + +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor +import org.jetbrains.jet.lang.resolve.name.FqName +import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor +import org.jetbrains.jet.lang.resolve.DescriptorUtils + +public val DeclarationDescriptor.importableFqName: FqName? + get() { + if (this is ConstructorDescriptor) return getContainingDeclaration().importableFqName + val mayBeUnsafe = DescriptorUtils.getFqName(this) + return if (mayBeUnsafe.isSafe()) { + mayBeUnsafe.toSafe() + } + else { + null + } + } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/imports/KotlinImportOptimizer.kt b/idea/src/org/jetbrains/jet/plugin/imports/KotlinImportOptimizer.kt new file mode 100644 index 00000000000..b82c279f6de --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/imports/KotlinImportOptimizer.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2014 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.plugin.imports + +import com.intellij.lang.ImportOptimizer +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.progress.ProgressIndicatorProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.jet.lang.psi.* +import org.jetbrains.jet.lang.resolve.ImportPath +import org.jetbrains.jet.lang.resolve.name.* +import org.jetbrains.jet.plugin.references.JetReference +import java.util.HashSet +import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper.needImport +import java.util.ArrayList + +public class KotlinImportOptimizer() : ImportOptimizer { + + override fun supports(file: PsiFile?) = file is JetFile + + override fun processFile(file: PsiFile?) = Runnable() { + val jetFile = file as JetFile + val usedQualifiedNames = extractUsedQualifiedNames(jetFile) + + val directives = jetFile.getImportDirectives() + + val directivesBeforeCurrent = ArrayList() + val directivesAfterCurrent = jetFile.getImportDirectives() + + ApplicationManager.getApplication()!!.runWriteAction(Runnable { + // Remove only unnecessary imports + for (anImport in directives) { + directivesAfterCurrent.remove(anImport) + + val importPath = JetPsiUtil.getImportPath(anImport) + if (importPath == null) { + continue + } + + if (isUseful(importPath, usedQualifiedNames) && needImport(importPath, jetFile, directivesBeforeCurrent) && needImport(importPath, jetFile, directivesAfterCurrent)) { + directivesBeforeCurrent.add(anImport) + } + else { + anImport.delete() + } + } + }) + } + + private fun isUseful(importPath: ImportPath, usedNames: Collection): Boolean { + // TODO: Add better analysis for aliases + return importPath.hasAlias() || usedNames.any { it.isImported(importPath) } + } + + private fun extractUsedQualifiedNames(jetFile: JetFile): Set { + val usedQualifiedNames = HashSet() + jetFile.accept(object : JetVisitorVoid() { + override fun visitElement(element: PsiElement?) { + ProgressIndicatorProvider.checkCanceled() + element?.acceptChildren(this) + } + + override fun visitUserType(`type`: JetUserType) { + val qualifier = `type`.getQualifier() + if (qualifier == null) { + super.visitUserType(`type`) + } + else { + val argumentList = `type`.getTypeArgumentList() + if (argumentList != null) { + super.visitTypeArgumentList(argumentList) + } + visitUserType(qualifier) + } + } + + override fun visitJetElement(element: JetElement) { + if (PsiTreeUtil.getParentOfType(element, javaClass()) != null || + PsiTreeUtil.getParentOfType(element, javaClass()) != null) { + return + } + val reference = element.getReference() + if (reference is JetReference) { + val referencedDescriptors = reference.resolveToDescriptors() + usedQualifiedNames.addAll(referencedDescriptors.map { it.importableFqName }.filterNotNull()) + } + super.visitJetElement(element) + } + }) + + return usedQualifiedNames + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/folding/FoldingAfterOptimizeImportsTest.kt b/idea/tests/org/jetbrains/jet/plugin/folding/FoldingAfterOptimizeImportsTest.kt index c18baab0525..bea76b946e3 100644 --- a/idea/tests/org/jetbrains/jet/plugin/folding/FoldingAfterOptimizeImportsTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/folding/FoldingAfterOptimizeImportsTest.kt @@ -21,12 +21,12 @@ import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.UndoConfirmationPolicy import com.intellij.openapi.editor.FoldRegion import org.jetbrains.jet.plugin.PluginTestCaseBase -import org.jetbrains.jet.plugin.editor.importOptimizer.JetImportOptimizer import java.io.File import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture import org.jetbrains.jet.InTextDirectivesUtils import com.intellij.openapi.editor.Editor import org.jetbrains.jet.plugin.folding.AbstractKotlinFoldingTest.doTestWithSettings +import org.jetbrains.jet.plugin.imports.KotlinImportOptimizer class FoldingAfterOptimizeImportsTest : AbstractKotlinFoldingTest() { private val fixture: JavaCodeInsightTestFixture @@ -55,7 +55,7 @@ class FoldingAfterOptimizeImportsTest : AbstractKotlinFoldingTest() { getFoldingRegion(0).checkRegion(false, findStringWithPrefixes("// REGION BEFORE: ")) CommandProcessor.getInstance()?.executeCommand(fixture.getProject(), - JetImportOptimizer().processFile(fixture.getFile()), + KotlinImportOptimizer().processFile(fixture.getFile()), "Optimize Imports", null, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION) getFoldingRegion(0).checkRegion(false, findStringWithPrefixes("// REGION AFTER: ")) diff --git a/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsMultiFileTest.java b/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsMultiFileTest.java index e5ef4d9959a..a0cac69390b 100644 --- a/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsMultiFileTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsMultiFileTest.java @@ -20,7 +20,7 @@ import com.intellij.codeInsight.CodeInsightTestCase; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import org.jetbrains.jet.plugin.PluginTestCaseBase; -import org.jetbrains.jet.plugin.editor.importOptimizer.JetImportOptimizer; +import org.jetbrains.jet.plugin.imports.KotlinImportOptimizer; import java.io.File; @@ -68,7 +68,7 @@ public class OptimizeImportsMultiFileTest extends CodeInsightTestCase { private void invokeFormatFile() { CommandProcessor.getInstance().executeCommand( - getProject(), new JetImportOptimizer().processFile(getFile()), + getProject(), new KotlinImportOptimizer().processFile(getFile()), "Optimize Imports", null, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION); } diff --git a/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsTest.java b/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsTest.java index 5068b341505..81e0e974d46 100644 --- a/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/importOptimizer/OptimizeImportsTest.java @@ -20,7 +20,7 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import org.jetbrains.jet.plugin.JetLightCodeInsightFixtureTestCase; import org.jetbrains.jet.plugin.PluginTestCaseBase; -import org.jetbrains.jet.plugin.editor.importOptimizer.JetImportOptimizer; +import org.jetbrains.jet.plugin.imports.KotlinImportOptimizer; import java.io.File; @@ -94,7 +94,7 @@ public class OptimizeImportsTest extends JetLightCodeInsightFixtureTestCase { } private void invokeOptimizeImports() { - CommandProcessor.getInstance().executeCommand(myFixture.getProject(), new JetImportOptimizer().processFile(myFixture.getFile()), + CommandProcessor.getInstance().executeCommand(myFixture.getProject(), new KotlinImportOptimizer().processFile(myFixture.getFile()), "Optimize Imports", null, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION); } }