replace UsagesSearch with ReferencesSearch in Find Usages

This commit is contained in:
Dmitry Jemerov
2015-08-31 14:03:30 +02:00
parent f76520c771
commit 0bc9ef5c0b
4 changed files with 264 additions and 88 deletions
@@ -22,7 +22,6 @@ import com.intellij.psi.*
import com.intellij.psi.impl.cache.CacheManager import com.intellij.psi.impl.cache.CacheManager
import com.intellij.psi.search.* import com.intellij.psi.search.*
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.Processor import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.KotlinLightMethod import org.jetbrains.kotlin.asJava.KotlinLightMethod
import org.jetbrains.kotlin.asJava.KotlinLightParameter import org.jetbrains.kotlin.asJava.KotlinLightParameter
@@ -31,24 +30,37 @@ import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.idea.JetFileType
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchLocation import org.jetbrains.kotlin.idea.search.usagesSearch.*
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
import org.jetbrains.kotlin.idea.search.usagesSearch.getClassNameForCompanionObject
import org.jetbrains.kotlin.idea.search.usagesSearch.getSpecialNamesToSearch
import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.parents
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean,
val acceptOverloads: Boolean,
val acceptExtensionsOfDeclarationClass: Boolean) {
fun anyEnabled(): Boolean = acceptCallableOverrides || acceptOverloads || acceptExtensionsOfDeclarationClass
companion object {
val Empty = KotlinReferencesSearchOptions(false, false, false)
}
}
public class KotlinReferencesSearchParameters(elementToSearch: PsiElement,
scope: SearchScope,
ignoreAccessScope: Boolean,
optimizer: SearchRequestCollector?,
val kotlinOptions: KotlinReferencesSearchOptions)
: ReferencesSearch.SearchParameters(elementToSearch, scope, ignoreAccessScope, optimizer) {
}
public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters>() { public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters>() {
override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) { override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
val element = queryParameters.getElementToSearch() val element = queryParameters.getElementToSearch()
val unwrappedElement = element.namedUnwrappedElement val unwrappedElement = element.namedUnwrappedElement ?: return
if (unwrappedElement == null || !ProjectRootsUtil.isInProjectOrLibSource(unwrappedElement)) return
val classNameForCompanionObject = unwrappedElement.getClassNameForCompanionObject() val classNameForCompanionObject = unwrappedElement.getClassNameForCompanionObject()
val words = unwrappedElement.getSpecialNamesToSearch() + val words = unwrappedElement.getSpecialNamesToSearch() +
@@ -61,10 +73,22 @@ public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, Referenc
else else
({true}) ({true})
val kotlinOptions = (queryParameters as? KotlinReferencesSearchParameters)?.kotlinOptions
?: KotlinReferencesSearchOptions.Empty
val resultProcessor = MyRequestResultProcessor(unwrappedElement, refFilter, kotlinOptions)
if (kotlinOptions.anyEnabled()) {
val name = unwrappedElement.name
if (name != null) {
queryParameters.optimizer.searchWord(name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement,
resultProcessor)
}
}
words.forEach { word -> words.forEach { word ->
queryParameters.optimizer.searchWord(word, effectiveSearchScope, queryParameters.optimizer.searchWord(word, effectiveSearchScope,
UsagesSearchLocation.EVERYWHERE.searchContext, true, unwrappedElement, UsagesSearchLocation.EVERYWHERE.searchContext, true, unwrappedElement,
MyRequestResultProcessor(unwrappedElement, refFilter)) resultProcessor)
} }
if (unwrappedElement is JetParameter) { if (unwrappedElement is JetParameter) {
@@ -91,12 +115,13 @@ public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, Referenc
namedArgsScope = GlobalSearchScope.filesScope(project, filesWithFunctionName.asList()) namedArgsScope = GlobalSearchScope.filesScope(project, filesWithFunctionName.asList())
} }
val processor = MyRequestResultProcessor(parameter, { it.isNamedArgumentReference() }, KotlinReferencesSearchOptions.Empty)
queryParameters.optimizer.searchWord(parameterName, queryParameters.optimizer.searchWord(parameterName,
namedArgsScope, namedArgsScope,
KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT, KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT,
true, true,
parameter, parameter,
MyRequestResultProcessor(parameter) { it.isNamedArgumentReference() }) processor)
} }
private fun PsiReference.isNamedArgumentReference(): Boolean { private fun PsiReference.isNamedArgumentReference(): Boolean {
@@ -105,7 +130,8 @@ public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, Referenc
private class MyRequestResultProcessor( private class MyRequestResultProcessor(
private val unwrappedElement: PsiElement, private val unwrappedElement: PsiElement,
private val filter: (PsiReference) -> Boolean private val filter: (PsiReference) -> Boolean,
private val options: KotlinReferencesSearchOptions
) : RequestResultProcessor() { ) : RequestResultProcessor() {
private val referenceService = PsiReferenceService.getService() private val referenceService = PsiReferenceService.getService()
@@ -116,11 +142,29 @@ public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, Referenc
when { when {
!filter(ref) -> true !filter(ref) -> true
!ReferenceRange.containsOffsetInElement(ref, offsetInElement) -> true !ReferenceRange.containsOffsetInElement(ref, offsetInElement) -> true
!ref.isReferenceTo(unwrappedElement) -> true !ref.isReferenceToTarget(unwrappedElement) -> true
else -> consumer.process(ref) else -> consumer.process(ref)
} }
} }
} }
private fun PsiReference.isReferenceToTarget(element: PsiElement): Boolean {
if (isReferenceTo(element)) {
return true
}
if (element is JetNamedDeclaration) {
if (options.acceptCallableOverrides && isCallableOverrideUsage(element)) {
return true
}
if (options.acceptOverloads && isUsageInContainingDeclaration(element)) {
return true
}
if (options.acceptExtensionsOfDeclarationClass && isExtensionOfDeclarationClassUsage(element)) {
return true
}
}
return false
}
} }
companion object { companion object {
@@ -194,7 +238,8 @@ public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, Referenc
searchNamedElement(queryParameters, declaration as PsiNamedElement) searchNamedElement(queryParameters, declaration as PsiNamedElement)
} }
else if (declaration is JetPropertyAccessor) { else if (declaration is JetPropertyAccessor) {
searchNamedElement(queryParameters, PsiTreeUtil.getParentOfType<JetProperty>(declaration, javaClass<JetProperty>())) val property = declaration.getStrictParentOfType<JetProperty>()
searchNamedElement(queryParameters, property)
} }
else if (declaration is JetFunction) { else if (declaration is JetFunction) {
val staticFromCompanionObject = findStaticMethodFromCompanionObject(declaration) val staticFromCompanionObject = findStaticMethodFromCompanionObject(declaration)
@@ -225,11 +270,16 @@ public class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, Referenc
private fun isOnlyKotlinSearch(searchScope: SearchScope) = private fun isOnlyKotlinSearch(searchScope: SearchScope) =
searchScope is LocalSearchScope && searchScope.getScope().all { it.getContainingFile().getFileType() == JetFileType.INSTANCE } searchScope is LocalSearchScope && searchScope.getScope().all { it.getContainingFile().getFileType() == JetFileType.INSTANCE }
private fun searchNamedElement(queryParameters: ReferencesSearch.SearchParameters, element: PsiNamedElement?, private fun searchNamedElement(queryParameters: ReferencesSearch.SearchParameters,
element: PsiNamedElement?,
name: String? = element?.getName()) { name: String? = element?.getName()) {
if (name != null && element != null) { if (name != null && element != null) {
val scope = runReadAction { queryParameters.getEffectiveSearchScope() } val scope = runReadAction { queryParameters.getEffectiveSearchScope() }
queryParameters.getOptimizer().searchWord(name, scope, true, element) val context = UsageSearchContext.IN_CODE + UsageSearchContext.IN_FOREIGN_LANGUAGES + UsageSearchContext.IN_COMMENTS
val kotlinOptions = (queryParameters as? KotlinReferencesSearchParameters)?.kotlinOptions
?: KotlinReferencesSearchOptions.Empty
queryParameters.getOptimizer().searchWord(name, scope, context.toShort(), true, element,
MyRequestResultProcessor(element, { true }, kotlinOptions))
} }
} }
} }
@@ -18,35 +18,39 @@ package org.jetbrains.kotlin.idea.findUsages.handlers
import com.intellij.find.findUsages.AbstractFindUsagesDialog import com.intellij.find.findUsages.AbstractFindUsagesDialog
import com.intellij.find.findUsages.FindUsagesOptions import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.find.findUsages.JavaFindUsagesHandler
import com.intellij.find.findUsages.JavaFindUsagesHandlerFactory
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.PsiElementProcessor import com.intellij.psi.search.PsiElementProcessor
import com.intellij.psi.search.PsiElementProcessorAdapter import com.intellij.psi.search.PsiElementProcessorAdapter
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.util.FilteredQuery
import com.intellij.util.Processor import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.KotlinLightMethod
import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindClassUsagesDialog import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindClassUsagesDialog
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
import org.jetbrains.kotlin.idea.findUsages.toSearchTarget import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.findUsages.toClassHelper import org.jetbrains.kotlin.idea.search.usagesSearch.isConstructorUsage
import org.jetbrains.kotlin.idea.findUsages.toClassDeclarationsHelper import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage
import org.jetbrains.kotlin.idea.search.usagesSearch.search
import org.jetbrains.kotlin.idea.util.application.runReadAction
import java.util.Collections
import com.intellij.find.findUsages.JavaFindUsagesHandler
import com.intellij.find.findUsages.JavaFindUsagesHandlerFactory
import com.intellij.psi.*
import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.util.CommonProcessors
import org.jetbrains.kotlin.asJava.KotlinLightMethod
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isInheritable import org.jetbrains.kotlin.psi.psiUtil.effectiveDeclarations
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver
import java.util.*
public class KotlinFindClassUsagesHandler( public class KotlinFindClassUsagesHandler(
jetClass: JetClassOrObject, jetClass: JetClassOrObject,
@@ -73,9 +77,9 @@ public class KotlinFindClassUsagesHandler(
PsiElementProcessorAdapter( PsiElementProcessorAdapter(
object : PsiElementProcessor<PsiClass> { object : PsiElementProcessor<PsiClass> {
public override fun execute(element: PsiClass): Boolean { public override fun execute(element: PsiClass): Boolean {
val traitOrInterface = element.isInterface() val isInterface = element.isInterface()
return when { return when {
traitOrInterface && options.isDerivedInterfaces, !traitOrInterface && options.isDerivedClasses -> isInterface && options.isDerivedInterfaces, !isInterface && options.isDerivedClasses ->
KotlinFindUsagesHandler.processUsage(processor, element.getNavigationElement()) KotlinFindUsagesHandler.processUsage(processor, element.getNavigationElement())
else -> true else -> true
} }
@@ -86,26 +90,94 @@ public class KotlinFindClassUsagesHandler(
} }
val classOrObject = element as JetClassOrObject val classOrObject = element as JetClassOrObject
val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor)
val uniqueProcessor = CommonProcessors.UniqueProcessor(processor) if (kotlinOptions.isUsages || kotlinOptions.searchConstructorUsages) {
if (!processClassReferences(classOrObject, kotlinOptions, referenceProcessor)) return false
}
return runReadAction { if (kotlinOptions.isFieldsUsages || kotlinOptions.isMethodsUsages) {
val target = kotlinOptions.toSearchTarget(classOrObject, true) if (!processMemberReferences(classOrObject, kotlinOptions, referenceProcessor)) return false
val classUsages = kotlinOptions.toClassHelper().newRequest(target).search() }
val declarationUsages = kotlinOptions.toClassDeclarationsHelper().newRequest(target).search()
if (kotlinOptions.searchConstructorUsages) { if (kotlinOptions.isUsages && classOrObject is JetObjectDeclaration && classOrObject.isCompanion()) {
val constructors = classOrObject.toLightClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY if (!processClassObjectInternalReferences(classOrObject, referenceProcessor)) {
for (constructor in constructors) { return false
if (constructor !is KotlinLightMethod) continue }
constructor.processDelegationCallConstructorUsages(constructor.getUseScope()) { }
it.getCalleeExpression()?.mainReference?.let { KotlinFindUsagesHandler.processUsage(uniqueProcessor, it) }
if (kotlinOptions.searchConstructorUsages) {
val constructors = classOrObject.toLightClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY
for (constructor in constructors) {
if (constructor !is KotlinLightMethod) continue
constructor.processDelegationCallConstructorUsages(constructor.getUseScope()) {
it.getCalleeExpression()?.mainReference?.let { referenceProcessor.process(it) }
}
}
}
if (options.isDerivedClasses || options.isDerivedInterfaces) {
if (!processInheritors()) return false
}
return true
}
private fun processClassReferences(classOrObject: JetClassOrObject,
options: KotlinClassFindUsagesOptions,
processor: Processor<PsiReference>): Boolean {
var usagesQuery = ReferencesSearch.search(classOrObject)
if (options.isSkipImportStatements) {
usagesQuery = FilteredQuery(usagesQuery) { !it.isImportUsage() }
}
if (!options.searchConstructorUsages) {
usagesQuery = FilteredQuery(usagesQuery) { !it.isConstructorUsage(classOrObject) }
}
else if (!options.isUsages) {
usagesQuery = FilteredQuery(usagesQuery) { it.isConstructorUsage(classOrObject) }
}
return usagesQuery.forEach(processor)
}
private fun processClassObjectInternalReferences(classObject: JetObjectDeclaration,
processor: Processor<PsiReference>): Boolean {
val klass = classObject.getStrictParentOfType<JetClass>() ?: return true
val companionObjectDescriptor = classObject.descriptor
var stop: Boolean = false
klass.acceptChildren(object : JetVisitorVoid() {
override fun visitJetElement(element: JetElement) {
if (element == classObject) return // skip companion object itself
if (stop) return
element.acceptChildren(this)
val bindingContext = element.analyze()
val call = bindingContext[BindingContext.CALL, element] ?: return
val resolvedCall = bindingContext[BindingContext.RESOLVED_CALL, call] ?: return
if ((resolvedCall.getDispatchReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor
|| (resolvedCall.getExtensionReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor) {
element.getReferences().forEach {
if (!stop && !processor.process(it)) {
stop = true
}
} }
} }
} }
})
return !stop
}
(classUsages + declarationUsages).all { KotlinFindUsagesHandler.processUsage(uniqueProcessor, it)} && processInheritors() private fun processMemberReferences(classOrObject: JetClassOrObject,
options: KotlinClassFindUsagesOptions,
processor: Processor<PsiReference>): Boolean {
for (decl in classOrObject.effectiveDeclarations()) {
if ((decl is JetNamedFunction && options.isMethodsUsages) ||
((decl is JetProperty || decl is JetParameter) && options.isFieldsUsages)) {
if (!ReferencesSearch.search(decl).forEach(processor)) return false
}
} }
return true
} }
protected override fun getStringsToSearch(element: PsiElement): Collection<String> { protected override fun getStringsToSearch(element: PsiElement): Collection<String> {
@@ -16,29 +16,35 @@
package org.jetbrains.kotlin.idea.findUsages.handlers package org.jetbrains.kotlin.idea.findUsages.handlers
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector
import com.intellij.find.findUsages.AbstractFindUsagesDialog import com.intellij.find.findUsages.AbstractFindUsagesDialog
import com.intellij.find.findUsages.FindUsagesOptions import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.util.CommonProcessors import com.intellij.util.*
import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.findUsages.* import org.jetbrains.kotlin.idea.findUsages.KotlinCallableFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindFunctionUsagesDialog import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindFunctionUsagesDialog
import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindPropertyUsagesDialog import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindPropertyUsagesDialog
import org.jetbrains.kotlin.idea.search.allScope
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearch import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchHelper import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.JetConstructor
import org.jetbrains.kotlin.psi.JetFunction import org.jetbrains.kotlin.psi.JetFunction
import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.JetParameter
public abstract class KotlinFindMemberUsagesHandler<T : JetNamedDeclaration> public abstract class KotlinFindMemberUsagesHandler<T : JetNamedDeclaration>
protected constructor(declaration: T, elementsToSearch: Collection<PsiElement>, factory: KotlinFindUsagesHandlerFactory) protected constructor(declaration: T, elementsToSearch: Collection<PsiElement>, factory: KotlinFindUsagesHandlerFactory)
@@ -48,10 +54,6 @@ public abstract class KotlinFindMemberUsagesHandler<T : JetNamedDeclaration>
elementsToSearch: Collection<PsiElement>, elementsToSearch: Collection<PsiElement>,
factory: KotlinFindUsagesHandlerFactory) : KotlinFindMemberUsagesHandler<JetFunction>(declaration, elementsToSearch, factory) { factory: KotlinFindUsagesHandlerFactory) : KotlinFindMemberUsagesHandler<JetFunction>(declaration, elementsToSearch, factory) {
override fun getSearchHelper(options: KotlinCallableFindUsagesOptions): UsagesSearchHelper<JetFunction> {
return (options as KotlinFunctionFindUsagesOptions).toHelper()
}
override fun getFindUsagesOptions(dataContext: DataContext?): FindUsagesOptions = factory.findFunctionOptions override fun getFindUsagesOptions(dataContext: DataContext?): FindUsagesOptions = factory.findFunctionOptions
override fun getFindUsagesDialog(isSingleFile: Boolean, toShowInNewTab: Boolean, mustOpenInNewTab: Boolean): AbstractFindUsagesDialog { override fun getFindUsagesDialog(isSingleFile: Boolean, toShowInNewTab: Boolean, mustOpenInNewTab: Boolean): AbstractFindUsagesDialog {
@@ -63,50 +65,83 @@ public abstract class KotlinFindMemberUsagesHandler<T : JetNamedDeclaration>
return super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab) return super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab)
} }
override fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions {
val kotlinOptions = options as KotlinFunctionFindUsagesOptions
return KotlinReferencesSearchOptions(true,
kotlinOptions.isIncludeOverloadUsages,
kotlinOptions.isIncludeOverloadUsages)
}
override fun applyQueryFilters(element: PsiElement, options: FindUsagesOptions, query: Query<PsiReference>): Query<PsiReference> {
val kotlinOptions = options as KotlinFunctionFindUsagesOptions
return query
.applyFilter(kotlinOptions.isSkipImportStatements) { !it.isImportUsage() }
}
} }
private class Property(declaration: JetNamedDeclaration, elementsToSearch: Collection<PsiElement>, factory: KotlinFindUsagesHandlerFactory) : KotlinFindMemberUsagesHandler<JetNamedDeclaration>(declaration, elementsToSearch, factory) { private class Property(declaration: JetNamedDeclaration, elementsToSearch: Collection<PsiElement>, factory: KotlinFindUsagesHandlerFactory) : KotlinFindMemberUsagesHandler<JetNamedDeclaration>(declaration, elementsToSearch, factory) {
override fun getSearchHelper(options: KotlinCallableFindUsagesOptions): UsagesSearchHelper<JetNamedDeclaration> {
return (options as KotlinPropertyFindUsagesOptions).toHelper()
}
override fun getFindUsagesOptions(dataContext: DataContext?): FindUsagesOptions = factory.findPropertyOptions override fun getFindUsagesOptions(dataContext: DataContext?): FindUsagesOptions = factory.findPropertyOptions
override fun getFindUsagesDialog(isSingleFile: Boolean, toShowInNewTab: Boolean, mustOpenInNewTab: Boolean): AbstractFindUsagesDialog { override fun getFindUsagesDialog(isSingleFile: Boolean, toShowInNewTab: Boolean, mustOpenInNewTab: Boolean): AbstractFindUsagesDialog {
return KotlinFindPropertyUsagesDialog(getElement(), getProject(), factory.findPropertyOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this) return KotlinFindPropertyUsagesDialog(getElement(), getProject(), factory.findPropertyOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this)
} }
}
protected abstract fun getSearchHelper(options: KotlinCallableFindUsagesOptions): UsagesSearchHelper<T> override fun applyQueryFilters(element: PsiElement, options: FindUsagesOptions, query: Query<PsiReference>): Query<PsiReference> {
var kotlinOptions = options as KotlinPropertyFindUsagesOptions
if (!kotlinOptions.isReadAccess && !kotlinOptions.isWriteAccess) {
return EmptyQuery()
}
val result = query
.applyFilter(kotlinOptions.isSkipImportStatements) { !it.isImportUsage() }
if (!kotlinOptions.isReadAccess || !kotlinOptions.isWriteAccess) {
val detector = KotlinReadWriteAccessDetector()
return FilteredQuery(result) {
val access = detector.getReferenceAccess(element, it)
when (access) {
ReadWriteAccessDetector.Access.Read -> kotlinOptions.isReadAccess
ReadWriteAccessDetector.Access.Write -> kotlinOptions.isWriteAccess
ReadWriteAccessDetector.Access.ReadWrite -> true
}
}
}
return result
}
override fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions {
return KotlinReferencesSearchOptions(true, false, false)
}
}
override fun searchReferences(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Boolean { override fun searchReferences(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Boolean {
val kotlinOptions = options as KotlinCallableFindUsagesOptions val kotlinOptions = options as KotlinCallableFindUsagesOptions
val request = runReadAction { val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor)
@suppress("UNCHECKED_CAST")
val searchTarget = if (element is JetParameter)
options.toSearchTarget<T>(element as T, restrictByTarget = false) // named argument usages are outside getUseScope for JetParameter
else
options.toSearchTarget<T>(element as T, restrictByTarget = true)
getSearchHelper(kotlinOptions).newRequest(searchTarget)
}
val uniqueProcessor = CommonProcessors.UniqueProcessor(processor) val uniqueProcessor = CommonProcessors.UniqueProcessor(processor)
for (ref in UsagesSearch.search(request)) { if (options.isUsages) {
KotlinFindUsagesHandler.processUsage(uniqueProcessor, ref) val searchParameters = KotlinReferencesSearchParameters(element,
} element.project.allScope(),
ignoreAccessScope = false,
optimizer = null,
kotlinOptions = createKotlinReferencesSearchOptions(options))
val psiMethod: PsiMethod? = when (element) { val query = applyQueryFilters(element, options, ReferencesSearch.search(searchParameters))
is PsiMethod -> element if (!query.forEach(referenceProcessor)) return false
is JetConstructor<*> -> runReadAction { LightClassUtil.getLightClassMethod(element as JetFunction) }
else -> null val psiMethod: PsiMethod? = when (element) {
} is PsiMethod -> element
if (psiMethod != null) { is JetFunction -> runReadAction { LightClassUtil.getLightClassMethod(element) }
for (ref in MethodReferencesSearch.search(psiMethod, options.searchScope, true)) { else -> null
KotlinFindUsagesHandler.processUsage(uniqueProcessor, ref) }
if (psiMethod != null) {
val query = applyQueryFilters(element, options, MethodReferencesSearch.search(psiMethod, options.searchScope, true))
if (!query.forEach(referenceProcessor)) return false
} }
} }
@@ -119,6 +154,12 @@ public abstract class KotlinFindMemberUsagesHandler<T : JetNamedDeclaration>
return true return true
} }
protected abstract fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions
protected abstract fun applyQueryFilters(element: PsiElement,
options: FindUsagesOptions,
query: Query<PsiReference>): Query<PsiReference>
override fun isSearchForTextOccurencesAvailable(psiElement: PsiElement, isSingleFile: Boolean): Boolean = !isSingleFile override fun isSearchForTextOccurencesAvailable(psiElement: PsiElement, isSingleFile: Boolean): Boolean = !isSingleFile
companion object { companion object {
@@ -133,3 +174,8 @@ public abstract class KotlinFindMemberUsagesHandler<T : JetNamedDeclaration>
} }
} }
} }
fun Query<PsiReference>.applyFilter(flag: Boolean, condition: (PsiReference) -> Boolean): Query<PsiReference> {
return if (flag) FilteredQuery(this, condition) else this
}
@@ -18,19 +18,17 @@ package org.jetbrains.kotlin.idea.findUsages.handlers
import com.intellij.find.findUsages.FindUsagesHandler import com.intellij.find.findUsages.FindUsagesHandler
import com.intellij.find.findUsages.FindUsagesOptions import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.util.CommonProcessors
import com.intellij.util.Processor import com.intellij.util.Processor
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.KotlinReferenceUsageInfo import org.jetbrains.kotlin.idea.findUsages.KotlinReferenceUsageInfo
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import java.util.*
import java.util.ArrayList
import java.util.Collections
public abstract class KotlinFindUsagesHandler<T : PsiElement>(psiElement: T, public abstract class KotlinFindUsagesHandler<T : PsiElement>(psiElement: T,
private val elementsToSearch: Collection<PsiElement>, private val elementsToSearch: Collection<PsiElement>,
@@ -100,5 +98,15 @@ public abstract class KotlinFindUsagesHandler<T : PsiElement>(psiElement: T,
val usageInfo = runReadAction(callback) val usageInfo = runReadAction(callback)
return if (usageInfo != null) process(usageInfo) else true return if (usageInfo != null) process(usageInfo) else true
} }
protected fun createReferenceProcessor(usageInfoProcessor: Processor<UsageInfo>): Processor<PsiReference> {
val uniqueProcessor = CommonProcessors.UniqueProcessor(usageInfoProcessor)
return object: Processor<PsiReference> {
override fun process(t: PsiReference): Boolean {
return KotlinFindUsagesHandler.processUsage(uniqueProcessor, t)
}
}
}
} }
} }