From f40bb57d79970c209d8ecb576317b3acb7376f78 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Sat, 23 Jul 2016 15:03:47 +0300 Subject: [PATCH] Refactored to use exiting impl of AutoImportBase --- .../kotlin/idea/quickfix/AutoImportFix.kt | 125 ++++++++--- .../idea/quickfix/AutoImportStaticFix.kt | 208 ------------------ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 2 +- 3 files changed, 100 insertions(+), 235 deletions(-) delete mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportStaticFix.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt index 0a058e944c7..6f9f30dd3fa 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt @@ -47,6 +47,7 @@ import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.util.OperatorNameConventions @@ -59,7 +60,7 @@ import java.util.* /** * Check possibility and perform fix for unresolved references. */ -internal abstract class AutoImportFixBase(expression: T) : +internal abstract class AutoImportFixBase(expression: T) : KotlinQuickFixAction(expression), HighPriorityAction, HintAction { private val modificationCountOnCreate = PsiModificationTracker.SERVICE.getInstance(element.project).modificationCount @@ -126,8 +127,6 @@ internal abstract class AutoImportFixBase(expression: T) : val file = element.containingFile as KtFile - fun filterByCallType(descriptor: DeclarationDescriptor) = callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) - val searchScope = getResolveScope(file) val bindingContext = element.analyze(BodyResolveMode.PARTIAL) @@ -146,25 +145,10 @@ internal abstract class AutoImportFixBase(expression: T) : return true } - val result = ArrayList() - val indicesHelper = KotlinIndicesHelper(resolutionFacade, searchScope, ::isVisible) - val expression = element - if (expression is KtSimpleNameExpression) { - if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { - if (ProjectStructureUtil.isJsKotlinModule(file)) { - indicesHelper.getKotlinClasses({ it == nameStr }, { true }).filterTo(result, ::filterByCallType) - } - else { - indicesHelper.getJvmClassesByName(nameStr).filterTo(result, ::filterByCallType) - } - - indicesHelper.getTopLevelCallablesByName(nameStr).filterTo(result, ::filterByCallType) - } - } - - result.addAll(indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, element, bindingContext) { it == nameStr }) + val result = fillCandidates(callTypeAndReceiver, file, bindingContext, + indicesHelper, nameStr, filterByCallType = { callTypeAndReceiver.callType.descriptorKindFilter.accepts(it) }) return if (result.size > 1) reduceCandidatesBasedOnDependencyRuleViolation(result, file) @@ -172,6 +156,10 @@ internal abstract class AutoImportFixBase(expression: T) : result } + abstract fun fillCandidates(callTypeAndReceiver: CallTypeAndReceiver<*, *>, file: KtFile, + bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper, + nameStr: String, filterByCallType: (descriptor: DeclarationDescriptor) -> (Boolean)): List + private fun reduceCandidatesBasedOnDependencyRuleViolation( candidates: Collection, file: PsiFile): Collection { val project = file.project @@ -183,7 +171,32 @@ internal abstract class AutoImportFixBase(expression: T) : } } -internal class AutoImportFix(expression: KtSimpleNameExpression) : AutoImportFixBase(expression) { +internal abstract class AutoImportFixBaseDynamicContext(expression: T) : AutoImportFixBase(expression) { + override fun fillCandidates(callTypeAndReceiver: CallTypeAndReceiver<*, *>, file: KtFile, bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper, nameStr: String, filterByCallType: (DeclarationDescriptor) -> Boolean): List { + val result = ArrayList() + + val expression = element + + if (expression is KtSimpleNameExpression) { + if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { + if (ProjectStructureUtil.isJsKotlinModule(file)) { + indicesHelper.getKotlinClasses({ it == nameStr }, { true }).filterTo(result, filterByCallType) + } + else { + indicesHelper.getJvmClassesByName(nameStr).filterTo(result, filterByCallType) + } + + indicesHelper.getTopLevelCallablesByName(nameStr).filterTo(result, filterByCallType) + } + } + + result.addAll(indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, expression, bindingContext) { it == nameStr }) + return result + } + +} + +internal class AutoImportFix(expression: KtSimpleNameExpression) : AutoImportFixBaseDynamicContext(expression) { override fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element) override val importNames: Collection = run { @@ -211,6 +224,7 @@ internal class AutoImportFix(expression: KtSimpleNameExpression) : AutoImportFix emptyList() } + override fun getSupportedErrors() = ERRORS companion object : KotlinSingleIntentionActionFactory() { @@ -223,7 +237,8 @@ internal class AutoImportFix(expression: KtSimpleNameExpression) : AutoImportFix } } -internal class MissingInvokeAutoImportFix(expression: KtExpression) : AutoImportFixBase(expression) { + +internal class MissingInvokeAutoImportFix(expression: KtExpression) : AutoImportFixBaseDynamicContext(expression) { override val importNames = OperatorNameConventions.INVOKE.singletonList() override fun getCallTypeAndReceiver() = CallTypeAndReceiver.OPERATOR(element) @@ -239,7 +254,7 @@ internal class MissingInvokeAutoImportFix(expression: KtExpression) : AutoImport } internal open class MissingArrayAccessorAutoImportFix(element: KtArrayAccessExpression, override val importNames: Collection, private val showHint: Boolean) : - AutoImportFixBase(element) { + AutoImportFixBaseDynamicContext(element) { override fun getCallTypeAndReceiver() = CallTypeAndReceiver.OPERATOR(element.arrayExpression!!) @@ -274,7 +289,7 @@ internal open class MissingArrayAccessorAutoImportFix(element: KtArrayAccessExpr internal class MissingDelegateAccessorsAutoImportFix( element: KtExpression, override val importNames: Collection, private val solveSeveralProblems: Boolean) : - AutoImportFixBase(element) { + AutoImportFixBaseDynamicContext(element) { override fun getCallTypeAndReceiver() = CallTypeAndReceiver.DELEGATE(element) override fun createAction(project: Project, editor: Editor): KotlinAddImportAction { @@ -315,7 +330,7 @@ internal class MissingDelegateAccessorsAutoImportFix( } internal class MissingComponentsAutoImportFix(element: KtExpression, override val importNames: Collection, private val solveSeveralProblems: Boolean) : - AutoImportFixBase(element) { + AutoImportFixBaseDynamicContext(element) { override fun getCallTypeAndReceiver() = CallTypeAndReceiver.OPERATOR(element) override fun createAction(project: Project, editor: Editor): KotlinAddImportAction { @@ -349,6 +364,64 @@ internal class MissingComponentsAutoImportFix(element: KtExpression, override va } } +internal class AutoImportStaticFix(expression: KtSimpleNameExpression) : AutoImportFixBase(expression) { + + override fun getText() = KotlinBundle.message("import.static.fix") + + override fun fillCandidates(callTypeAndReceiver: CallTypeAndReceiver<*, *>, file: KtFile, bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper, nameStr: String, filterByCallType: (DeclarationDescriptor) -> Boolean): List { + val result = ArrayList() + val expression = element + if (expression is KtSimpleNameExpression) { + if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { + indicesHelper.getKotlinStatics(nameStr).filterTo(result, filterByCallType) + if (!(ProjectStructureUtil.isJsKotlinModule(file))) { + indicesHelper.getJvmStatics(nameStr).filterTo(result, filterByCallType) + } + } + } + return result + } + + override fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element) + + override val importNames: Collection = run { + if (element.getIdentifier() == null) { + val conventionName = KtPsiUtil.getConventionName(element) + if (conventionName != null) { + if (element is KtOperationReferenceExpression) { + val elementType = element.firstChild.node.elementType + if (elementType in OperatorConventions.ASSIGNMENT_OPERATIONS) { + val counterpart = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS[elementType] + val counterpartName = OperatorConventions.BINARY_OPERATION_NAMES[counterpart] + if (counterpartName != null) { + return@run listOf(conventionName, counterpartName) + } + } + } + + return@run conventionName.singletonOrEmptyList() + } + } + else if (Name.isValidIdentifier(element.getReferencedName())) { + return@run Name.identifier(element.getReferencedName()).singletonOrEmptyList() + } + + emptyList() + } + + override fun getSupportedErrors(): Collection> = ERRORS + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic) = + (diagnostic.psiElement as? KtSimpleNameExpression)?.let(::AutoImportStaticFix) + + override fun isApplicableForCodeFragment() = true + + private val ERRORS: Collection> by lazy(LazyThreadSafetyMode.PUBLICATION) { QuickFixes.getInstance().getDiagnostics(this) } + } + +} + object AutoImportForMissingOperatorFactory : KotlinSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { val element = diagnostic.psiElement as? KtExpression ?: return null @@ -357,7 +430,7 @@ object AutoImportForMissingOperatorFactory : KotlinSingleIntentionActionFactory( when (name) { OperatorNameConventions.GET, OperatorNameConventions.SET -> { if (element is KtArrayAccessExpression) { - return object: MissingArrayAccessorAutoImportFix(element, name.singletonList(), false) { + return object : MissingArrayAccessorAutoImportFix(element, name.singletonList(), false) { override fun getSupportedErrors() = Errors.OPERATOR_MODIFIER_REQUIRED.singletonList() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportStaticFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportStaticFix.kt deleted file mode 100644 index 610e31a5469..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportStaticFix.kt +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2010-2016 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.kotlin.idea.quickfix - -import com.intellij.codeInsight.hint.HintManager -import com.intellij.codeInsight.intention.HighPriorityAction -import com.intellij.codeInspection.HintAction -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.command.CommandProcessor -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.project.Project -import com.intellij.packageDependencies.DependencyValidationManager -import com.intellij.psi.PsiFile -import com.intellij.psi.util.PsiModificationTracker -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility -import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.diagnostics.DiagnosticFactory -import org.jetbrains.kotlin.idea.KotlinBundle -import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction -import org.jetbrains.kotlin.idea.actions.createSingleImportAction -import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper -import org.jetbrains.kotlin.idea.core.isVisible -import org.jetbrains.kotlin.idea.project.ProjectStructureUtil -import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.types.expressions.OperatorConventions -import org.jetbrains.kotlin.utils.CachedValueProperty -import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList -import java.util.* - -/** - * Created by Semoro on 08.07.16. - * ©XCodersTeam, 2016 - */ - -class AutoStaticImportFix(expression: KtSimpleNameExpression) : - KotlinQuickFixAction(expression), HighPriorityAction, HintAction { - - private val modificationCountOnCreate = PsiModificationTracker.SERVICE.getInstance(element.project).modificationCount - - private val suggestionCount: Int by CachedValueProperty( - calculator = { computeSuggestions().size }, - timestampCalculator = { PsiModificationTracker.SERVICE.getInstance(element.project).modificationCount } - ) - - - override fun showHint(editor: Editor): Boolean { - if (!element.isValid || isOutdated()) return false - - if (ApplicationManager.getApplication().isUnitTestMode && HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) return false - - if (suggestionCount == 0) return false - - return createAction(element.project, editor).showHint() - } - - override fun getText() = KotlinBundle.message("import.static.fix") - - override fun getFamilyName() = KotlinBundle.message("import.fix") - - override fun isAvailable(project: Project, editor: Editor?, file: PsiFile) - = super.isAvailable(project, editor, file) && suggestionCount > 0 - - override fun invoke(project: Project, editor: Editor?, file: KtFile) { - CommandProcessor.getInstance().runUndoTransparentAction { - createAction(project, editor!!).execute() - } - } - - override fun startInWriteAction() = true - - private fun isOutdated() = modificationCountOnCreate != PsiModificationTracker.SERVICE.getInstance(element.project).modificationCount - - private fun createAction(project: Project, editor: Editor): KotlinAddImportAction { - return createSingleImportAction(project, editor, element, computeSuggestions()) - } - - fun computeSuggestions(): Collection { - if (!element.isValid) return listOf() - if (element.containingFile !is KtFile) return emptyList() - - val callTypeAndReceiver = getCallTypeAndReceiver() - - if (callTypeAndReceiver is CallTypeAndReceiver.UNKNOWN) return emptyList() - - if (importNames.isEmpty()) return emptyList() - - return importNames.flatMapTo(LinkedHashSet()) { - computeSuggestionsForName(it, callTypeAndReceiver) - } - } - - fun computeSuggestionsForName(name: Name, callTypeAndReceiver: CallTypeAndReceiver<*, *>): - Collection { - val nameStr = name.asString() - if (nameStr.isEmpty()) return emptyList() - - val file = element.containingFile as KtFile - - fun filterByCallType(descriptor: DeclarationDescriptor) = callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) - - val searchScope = getResolveScope(file) - - val bindingContext = element.analyze(BodyResolveMode.PARTIAL) - - val diagnostics = bindingContext.diagnostics.forElement(element) - - if (!diagnostics.any { it.factory in getSupportedErrors() }) return emptyList() - - val resolutionFacade = element.getResolutionFacade() - - fun isVisible(descriptor: DeclarationDescriptor): Boolean { - if (descriptor is DeclarationDescriptorWithVisibility) { - return descriptor.isVisible(element, callTypeAndReceiver.receiver as? KtExpression, bindingContext, resolutionFacade) - } - - return true - } - - val result = ArrayList() - - val indicesHelper = KotlinIndicesHelper(resolutionFacade, searchScope, ::isVisible) - - val expression = element - if (expression is KtSimpleNameExpression) { - if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { - indicesHelper.getKotlinStatics(nameStr).filterTo(result, ::filterByCallType) - if (!(ProjectStructureUtil.isJsKotlinModule(file))) { - indicesHelper.getJvmStatics(nameStr).filterTo(result, ::filterByCallType) - } - } - } - return if (result.size > 1) - reduceCandidatesBasedOnDependencyRuleViolation(result, file) - else - result - } - - private fun reduceCandidatesBasedOnDependencyRuleViolation( - candidates: Collection, file: PsiFile): Collection { - val project = file.project - val validationManager = DependencyValidationManager.getInstance(project) - return candidates.filter { - val targetFile = DescriptorToSourceUtilsIde.getAnyDeclaration(project, it)?.containingFile ?: return@filter true - validationManager.getViolatorDependencyRules(file, targetFile).isEmpty() - } - } - - fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element) - - val importNames: Collection = run { - if (element.getIdentifier() == null) { - val conventionName = KtPsiUtil.getConventionName(element) - if (conventionName != null) { - if (element is KtOperationReferenceExpression) { - val elementType = element.firstChild.node.elementType - if (elementType in OperatorConventions.ASSIGNMENT_OPERATIONS) { - val counterpart = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS[elementType] - val counterpartName = OperatorConventions.BINARY_OPERATION_NAMES[counterpart] - if (counterpartName != null) { - return@run listOf(conventionName, counterpartName) - } - } - } - - return@run conventionName.singletonOrEmptyList() - } - } - else if (Name.isValidIdentifier(element.getReferencedName())) { - return@run Name.identifier(element.getReferencedName()).singletonOrEmptyList() - } - - emptyList() - } - - fun getSupportedErrors(): Collection> = ERRORS - - companion object : KotlinSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic) = - (diagnostic.psiElement as? KtSimpleNameExpression)?.let { AutoStaticImportFix(it) } - - override fun isApplicableForCodeFragment() = true - - private val ERRORS: Collection> by lazy(LazyThreadSafetyMode.PUBLICATION) { QuickFixes.getInstance().getDiagnostics(this) } - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 6fb60262a31..27f0a420ada 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -128,7 +128,7 @@ class QuickFixRegistrar : QuickFixContributor { NON_PRIVATE_CONSTRUCTOR_IN_ENUM.registerFactory(removeModifierFactory) NON_PRIVATE_CONSTRUCTOR_IN_SEALED.registerFactory(removeModifierFactory) - UNRESOLVED_REFERENCE.registerFactory(AutoStaticImportFix) + UNRESOLVED_REFERENCE.registerFactory(AutoImportStaticFix) UNRESOLVED_REFERENCE.registerFactory(AutoImportFix) UNRESOLVED_REFERENCE.registerFactory(AddTestLibQuickFix)