From 1bb8ca6ed564a602f2339fe0306bdf49d2e51f30 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 30 Jul 2015 23:01:04 +0300 Subject: [PATCH] On searching parameter usages asking if user wants to search usages of parameters of overriders too (as in Java) --- .../KotlinReferencesSearcher.kt | 6 +- .../KotlinFindUsagesHandlerFactory.kt | 121 +++++++++++++----- .../idea/findUsages/findUsagesOptions.kt | 12 ++ .../DelegatingFindMemberUsagesHandler.kt | 50 ++++---- .../DeprecatedSymbolUsageInWholeProjectFix.kt | 2 +- .../idea/refactoring/JetRefactoringUtil.java | 4 +- .../JetChangeSignatureUsageProcessor.java | 2 +- .../changeSignature/findUsagesUtil.kt | 35 +++++ 8 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/findUsagesUtil.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt index c5c6743cbcd..756129081e1 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt @@ -61,13 +61,15 @@ public class KotlinReferencesSearcher : QueryExecutorBase - queryParameters.getOptimizer().searchWord(word, queryParameters.getEffectiveSearchScope(), + queryParameters.getOptimizer().searchWord(word, effectiveSearchScope, UsagesSearchLocation.EVERYWHERE.searchContext, true, unwrappedElement, resultProcessor) } - if (!(unwrappedElement is JetElement && isOnlyKotlinSearch(queryParameters.getEffectiveSearchScope()))) { + if (!(unwrappedElement is JetElement && isOnlyKotlinSearch(effectiveSearchScope))) { searchLightElements(queryParameters, element) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt index 227be3946fa..55f3a3ed185 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt @@ -16,21 +16,28 @@ package org.jetbrains.kotlin.idea.findUsages +import com.intellij.CommonBundle +import com.intellij.find.FindBundle import com.intellij.find.findUsages.FindUsagesHandler import com.intellij.find.findUsages.FindUsagesHandlerFactory +import com.intellij.find.findUsages.FindUsagesOptions +import com.intellij.find.findUsages.JavaFindUsagesHandlerFactory +import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages import com.intellij.psi.PsiElement +import com.intellij.psi.search.searches.OverridingMethodsSearch +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.idea.findUsages.handlers.DelegatingFindMemberUsagesHandler import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindMemberUsagesHandler -import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil -import com.intellij.find.findUsages.FindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinTypeParameterFindUsagesHandler -import com.intellij.find.findUsages.JavaFindUsagesHandlerFactory -import org.jetbrains.kotlin.idea.findUsages.handlers.DelegatingFindMemberUsagesHandler +import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil import org.jetbrains.kotlin.plugin.findUsages.handlers.KotlinFindUsagesHandlerDecorator -import com.intellij.openapi.extensions.Extensions -import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.isOverridable +import org.jetbrains.kotlin.psi.psiUtil.parameterIndex public class KotlinFindUsagesHandlerFactory(project: Project) : FindUsagesHandlerFactory() { val javaHandlerFactory = JavaFindUsagesHandlerFactory(project) @@ -48,44 +55,94 @@ public class KotlinFindUsagesHandlerFactory(project: Project) : FindUsagesHandle element is JetTypeParameter || element is JetConstructor<*> - public override fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean): FindUsagesHandler? { - val handler = when (element) { + public fun createFindUsagesHandlerNoQuestions(element: PsiElement): FindUsagesHandler { + return createFindUsagesHandler(element, forHighlightUsages = false, canAsk = false) + } + + public override fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean): FindUsagesHandler { + return createFindUsagesHandler(element, forHighlightUsages, canAsk = !forHighlightUsages) + } + + private fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean, canAsk: Boolean): FindUsagesHandler { + val handler = createFindUsagesHandlerNoDecoration(element, canAsk) + + return Extensions.getArea(element.project).getExtensionPoint(KotlinFindUsagesHandlerDecorator.EP_NAME).extensions.fold(handler) { + handler, decorator -> decorator.decorateHandler(element, forHighlightUsages, handler) + } + } + + private fun createFindUsagesHandlerNoDecoration(element: PsiElement, canAsk: Boolean): FindUsagesHandler { + when (element) { is JetClassOrObject -> - KotlinFindClassUsagesHandler(element, this) + return KotlinFindClassUsagesHandler(element, this) - is JetNamedFunction, is JetProperty, is JetParameter, is JetConstructor<*> -> { - val declaration = element as JetNamedDeclaration - - if (forHighlightUsages) return KotlinFindMemberUsagesHandler.getInstance(declaration, factory = this) - JetRefactoringUtil.checkSuperMethods(declaration, null, "super.methods.action.key.find.usages")?.let { callables -> - when (callables.size()) { - 0 -> FindUsagesHandler.NULL_HANDLER - 1 -> { - val target = callables.first().unwrapped ?: return FindUsagesHandler.NULL_HANDLER - if (target is JetNamedDeclaration) { - KotlinFindMemberUsagesHandler.getInstance(target, factory = this) - } - else { - javaHandlerFactory.createFindUsagesHandler(target, false) + is JetParameter -> { + if (canAsk) { + val function = element.ownerFunction + if (function != null && function.isOverridable()) { + val psiMethod = function.toLightMethods().singleOrNull() + if (psiMethod != null) { + val hasOverridden = OverridingMethodsSearch.search(psiMethod).any() + if (hasOverridden && askWhetherShouldSearchForParameterInOverridingMethods(element)) { + val parametersCount = psiMethod.parameterList.parametersCount + val parameterIndex = element.parameterIndex() + assert(parameterIndex < parametersCount) + val overridingParameters = OverridingMethodsSearch.search(psiMethod, true) + .filter { it.parameterList.parametersCount == parametersCount } + .map { it.parameterList.parameters[parameterIndex].unwrapped } + .filterNotNull() + return handlerForMultiple(element, listOf(element) + overridingParameters) } } - else -> DelegatingFindMemberUsagesHandler(declaration, callables, this) + } } + + return KotlinFindMemberUsagesHandler.getInstance(element, factory = this) + } + + is JetNamedFunction, is JetProperty, is JetConstructor<*> -> { + val declaration = element as JetNamedDeclaration + + if (!canAsk) { + return KotlinFindMemberUsagesHandler.getInstance(declaration, factory = this) + } + + val declarationsToSearch = JetRefactoringUtil.checkSuperMethods(declaration, null, "super.methods.action.key.find.usages") + return handlerForMultiple(declaration, declarationsToSearch) } is JetTypeParameter -> - KotlinTypeParameterFindUsagesHandler(element, this) + return KotlinTypeParameterFindUsagesHandler(element, this) else -> - throw IllegalArgumentException("unexpected element type: " + element) + throw IllegalArgumentException("unexpected element type: $element") } - - for (decorator in Extensions.getArea(element.getProject()).getExtensionPoint(KotlinFindUsagesHandlerDecorator.EP_NAME).getExtensions()) { - val decorated = decorator.decorateHandler(element, forHighlightUsages, handler!!) - if (decorated != handler) return decorated - } - return handler } + private fun handlerForMultiple(originalDeclaration: JetNamedDeclaration, declarations: Collection): FindUsagesHandler { + return when (declarations.size()) { + 0 -> FindUsagesHandler.NULL_HANDLER + + 1 -> { + val target = declarations.single().unwrapped ?: return FindUsagesHandler.NULL_HANDLER + if (target is JetNamedDeclaration) { + KotlinFindMemberUsagesHandler.getInstance(target, factory = this) + } + else { + javaHandlerFactory.createFindUsagesHandler(target, false)!! + } + } + + else -> DelegatingFindMemberUsagesHandler(originalDeclaration, declarations, factory = this) + } + } + + private fun askWhetherShouldSearchForParameterInOverridingMethods(parameter: JetParameter): Boolean { + return Messages.showOkCancelDialog(parameter.project, + FindBundle.message("find.parameter.usages.in.overriding.methods.prompt", parameter.name), + FindBundle.message("find.parameter.usages.in.overriding.methods.title"), + CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText(), + Messages.getQuestionIcon()) == Messages.OK + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt index db51ce5a52e..c34969464aa 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt @@ -65,6 +65,18 @@ fun KotlinFunctionFindUsagesOptions.toJavaMethodOptions(project: Project): JavaM return javaOptions } +fun KotlinPropertyFindUsagesOptions.toJavaVariableOptions(project: Project): JavaVariableFindUsagesOptions { + val javaOptions = JavaVariableFindUsagesOptions(project) + javaOptions.fastTrack = fastTrack + javaOptions.isSearchForTextOccurrences = isSearchForTextOccurrences + javaOptions.isSkipImportStatements = isSkipImportStatements + javaOptions.isReadAccess = isReadAccess + javaOptions.isWriteAccess = isWriteAccess + javaOptions.isUsages = isUsages + javaOptions.searchScope = searchScope + return javaOptions +} + public class KotlinPropertyFindUsagesOptions(project: Project): KotlinCallableFindUsagesOptions, JavaVariableFindUsagesOptions(project) { override var searchOverrides: Boolean = false } diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/DelegatingFindMemberUsagesHandler.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/DelegatingFindMemberUsagesHandler.kt index 0bc7c3a4e51..1c59d564e85 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/DelegatingFindMemberUsagesHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/DelegatingFindMemberUsagesHandler.kt @@ -23,13 +23,12 @@ import com.intellij.find.findUsages.JavaFindUsagesHandler import com.intellij.openapi.actionSystem.DataContext import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiParameter import com.intellij.usageView.UsageInfo import com.intellij.util.Processor -import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory -import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions -import org.jetbrains.kotlin.idea.findUsages.toJavaMethodOptions +import org.jetbrains.kotlin.idea.findUsages.* +import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.psi.JetNamedDeclaration -import kotlin.properties.Delegates class DelegatingFindMemberUsagesHandler( val declaration: JetNamedDeclaration, @@ -38,19 +37,32 @@ class DelegatingFindMemberUsagesHandler( ) : FindUsagesHandler(declaration) { private val kotlinHandler = KotlinFindMemberUsagesHandler.getInstance(declaration, elementsToSearch, factory) - private fun getHandler(element: PsiElement): FindUsagesHandler? = - when (element) { - is JetNamedDeclaration -> - KotlinFindMemberUsagesHandler.getInstance(element, elementsToSearch, factory) + private data class HandlerAndOptions( + val handler: FindUsagesHandler, + val options: FindUsagesOptions? + ) - is PsiMethod -> - JavaFindUsagesHandler(element, elementsToSearch.toTypedArray(), factory.javaHandlerFactory) + private fun getHandlerAndOptions(element: PsiElement, options: FindUsagesOptions?): HandlerAndOptions? { + return when (element) { + is JetNamedDeclaration -> + HandlerAndOptions(KotlinFindMemberUsagesHandler.getInstance(element, elementsToSearch, factory), options) - else -> null - } + is PsiMethod -> + /* Can't have KotlinPropertyFindUsagesOptions here since Kotlin properties do not override java methods, so + * elementsToSearch contains property declarations only */ + HandlerAndOptions(JavaFindUsagesHandler(element, elementsToSearch.toTypedArray(), factory.javaHandlerFactory), + (options as KotlinFunctionFindUsagesOptions?)?.toJavaMethodOptions(project)) + + is PsiParameter -> + HandlerAndOptions(JavaFindUsagesHandler(element, elementsToSearch.toTypedArray(), factory.javaHandlerFactory), + (options as KotlinPropertyFindUsagesOptions?)?.toJavaVariableOptions(project)) + + else -> null + } + } override fun getFindUsagesDialog(isSingleFile: Boolean, toShowInNewTab: Boolean, mustOpenInNewTab: Boolean): AbstractFindUsagesDialog { - return getHandler(getPsiElement())?.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab) + return getHandlerAndOptions(getPsiElement(), null)?.handler?.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab) ?: super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab) } @@ -67,16 +79,8 @@ class DelegatingFindMemberUsagesHandler( } override fun processElementUsages(element: PsiElement, processor: Processor, options: FindUsagesOptions): Boolean { - val handler = getHandler(element) - if (handler == null) return true - - val handlerOptions = when (handler) { - /* Can't have KotlinPropertyFindUsagesOptions here since Kotlin properties do not override java methods, so - * elementsToSearch contains property declarations only */ - is JavaFindUsagesHandler -> (options as KotlinFunctionFindUsagesOptions).toJavaMethodOptions(element.getProject()) - else -> options - } - return handler.processElementUsages(element, processor, handlerOptions) + val (handler, handlerOptions) = runReadAction { getHandlerAndOptions(element, options) } ?: return true + return handler.processElementUsages(element, processor, handlerOptions!!) } override fun isSearchForTextOccurencesAvailable(psiElement: PsiElement, isSingleFile: Boolean): Boolean = !isSingleFile diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageInWholeProjectFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageInWholeProjectFix.kt index 4d06f4a234e..9d69b1f6a4d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageInWholeProjectFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageInWholeProjectFix.kt @@ -87,7 +87,7 @@ public class DeprecatedSymbolUsageInWholeProjectFix( override fun run(indicator: ProgressIndicator) { val usages = runReadAction { val searchScope = JetSourceFilterScope.kotlinSources(GlobalSearchScope.projectScope(project), project) - val findUsagesHandler = KotlinFindUsagesHandlerFactory(project).createFindUsagesHandler(psiElement, false)!! + val findUsagesHandler = KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(psiElement) val processor = CommonProcessors.CollectProcessor() val options = createFindUsagesOptions(psiElement, searchScope, project) findUsagesHandler.processElementUsages(psiElement, processor, options) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/JetRefactoringUtil.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/JetRefactoringUtil.java index 777ecc571db..f18f22820d6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/JetRefactoringUtil.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/JetRefactoringUtil.java @@ -121,9 +121,7 @@ public class JetRefactoringUtil { return markAsJava ? "[Java] " + description : description; } - @KotlinSignature( - "fun checkSuperMethods(declaration: JetDeclaration, ignore: Collection?, actionStringKey: String): MutableList?") - @Nullable + @NotNull public static List checkSuperMethods( @NotNull JetDeclaration declaration, @Nullable Collection ignore, diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java index 23548efd245..ef9b55cfc9e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java @@ -303,7 +303,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro String oldParamName = oldParam.getName(); if (parameterInfo == newReceiverInfo || (oldParamName != null && !oldParamName.equals(parameterInfo.getName()))) { - for (PsiReference reference : ReferencesSearch.search(oldParam, oldParam.getUseScope())) { + for (PsiReference reference : ChangeSignaturePackage.findParameterUsages(oldParam)) { PsiElement element = reference.getElement(); if ((element instanceof JetSimpleNameExpression || element instanceof KDocName) && diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/findUsagesUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/findUsagesUtil.kt new file mode 100644 index 00000000000..5311d862ea3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/findUsagesUtil.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2015 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.refactoring.changeSignature + +import com.intellij.psi.PsiReference +import com.intellij.usageView.UsageInfo +import com.intellij.util.CommonProcessors +import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory +import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions +import org.jetbrains.kotlin.psi.JetParameter + +// searching parameter usages via ReferenceSearch is currently too slow (uses non-optimized search for named arguments), +// that's why we use KotlinFindUsagesHandler +fun findParameterUsages(parameter: JetParameter): Collection { + val project = parameter.project + val findUsagesHandler = KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(parameter) + val processor = CommonProcessors.CollectProcessor() + val options = KotlinPropertyFindUsagesOptions(project) + findUsagesHandler.processElementUsages(parameter, processor, options) + return processor.results.map { it.reference }.filterNotNull() +} \ No newline at end of file