Find Usages: Add support of naming conventions

#KT-1356 Fixed
This commit is contained in:
Alexey Sedunov
2014-07-16 18:06:38 +04:00
parent 8bd0dda459
commit d226a11c8e
48 changed files with 775 additions and 133 deletions
@@ -21,20 +21,13 @@ import com.intellij.lang.findUsages.FindUsagesProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import org.jetbrains.jet.lang.psi.*
import org.jetbrains.jet.lexer.JetLexer
import com.intellij.lang.cacheBuilder.DefaultWordsScanner
import org.jetbrains.jet.lexer.JetTokens
import com.intellij.psi.tree.TokenSet
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetParameterInfo
public class JetFindUsagesProvider : FindUsagesProvider {
class JetWordsScanner : DefaultWordsScanner(JetLexer(), TokenSet.create(JetTokens.IDENTIFIER), JetTokens.COMMENTS, JetTokens.STRINGS)
public override fun canFindUsagesFor(psiElement: PsiElement): Boolean =
psiElement is JetNamedDeclaration
public override fun getWordsScanner(): WordsScanner =
JetWordsScanner()
KotlinWordsScanner()
public override fun getHelpId(psiElement: PsiElement): String? = null
@@ -0,0 +1,58 @@
/*
* 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.findUsages
import org.jetbrains.jet.plugin.search.usagesSearch.*
import com.intellij.lang.cacheBuilder.WordsScanner
import com.intellij.util.Processor
import org.jetbrains.jet.lexer.JetLexer
import com.intellij.lang.cacheBuilder.WordOccurrence
import org.jetbrains.jet.lexer.JetTokens
import com.intellij.lang.cacheBuilder.SimpleWordsScanner
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
class KotlinWordsScanner() : WordsScanner {
private val lexer = JetLexer()
private val simpleWordsScanner = SimpleWordsScanner()
fun scanWords(kind: WordOccurrence.Kind, processor: Processor<WordOccurrence>) {
val tokenStart = lexer.getTokenStart()
simpleWordsScanner.processWords(lexer.getTokenSequence()) {
it!!.init(lexer.getBufferSequence(), tokenStart + it.getStart(), tokenStart + it.getEnd(), kind)
processor.process(it)
}
}
override fun processWords(fileText: CharSequence, processor: Processor<WordOccurrence>) {
lexer.start(fileText)
val occurrence = WordOccurrence(null, 0, 0, null)
stream { lexer.getTokenType() }.forEach { elementType ->
// todo: replace with when
if (ALL_SEARCHABLE_OPERATIONS.contains(elementType)) {
occurrence.init(lexer.getBufferSequence(), lexer.getTokenStart(), lexer.getTokenEnd(), WordOccurrence.Kind.CODE)
processor.process(occurrence)
}
else if (JetTokens.COMMENTS.contains(elementType)) scanWords(WordOccurrence.Kind.COMMENTS, processor)
else if (JetTokens.STRINGS.contains(elementType)) scanWords(WordOccurrence.Kind.LITERALS, processor)
else scanWords(WordOccurrence.Kind.CODE, processor)
lexer.advance()
}
}
}
@@ -20,6 +20,10 @@ import org.jetbrains.jet.lang.psi.JetMultiDeclaration
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import com.intellij.openapi.util.TextRange
import java.util.Collections
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.resolve.source.PsiSourceElement
import org.jetbrains.jet.lang.resolve.source.getPsi
class JetMultiDeclarationReference(element: JetMultiDeclaration) : JetMultiReference<JetMultiDeclaration>(element) {
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
@@ -28,14 +32,10 @@ class JetMultiDeclarationReference(element: JetMultiDeclaration) : JetMultiRefer
}.filterNotNull()
}
override fun getRangeInElement(): TextRange? {
val entries = expression.getEntries()
if (entries.isEmpty()) {
return TextRange.EMPTY_RANGE
}
val start = entries.first!!.getStartOffsetInParent()
val end = entries.last!!.getStartOffsetInParent() + entries.last!!.getTextLength()
return TextRange(start, end)
val start = expression.getLPar()
val end = expression.getRPar()
if (start == null || end == null) return TextRange.EMPTY_RANGE
return TextRange(start.getStartOffsetInParent(), end.getStartOffsetInParent())
}
}
@@ -28,17 +28,13 @@ import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import java.util.Collections
import java.util.HashSet
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor
import com.intellij.psi.JavaPsiFacade
import org.jetbrains.jet.lang.psi.JetReferenceExpression
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jet.asJava.*
import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.utils.keysToMap
import com.intellij.psi.PsiMethod
import org.jetbrains.jet.plugin.search.allScope
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
@@ -65,20 +61,16 @@ abstract class AbstractJetReference<T : JetElement>(element: T)
return null
}
override fun isReferenceTo(element: PsiElement?): Boolean {
return element != null && matchesTarget(element)
}
override fun getCanonicalText(): String = "<TBD>"
override fun handleElementRename(newElementName: String?): PsiElement? = throw IncorrectOperationException()
override fun bindToElement(element: PsiElement): PsiElement = throw IncorrectOperationException()
protected fun checkElementMatch(referenceTarget: PsiElement, elementToMatch: PsiElement): Boolean {
val unwrappedTarget = referenceTarget.namedUnwrappedElement
val unwrappedElementToMatch = elementToMatch.namedUnwrappedElement
return (unwrappedTarget == unwrappedElementToMatch) ||
(referenceTarget is PsiMethod && referenceTarget.isConstructor() && referenceTarget.getContainingClass() == elementToMatch)
}
[suppress("CAST_NEVER_SUCCEEDS")]
override fun getVariants(): Array<Any> = PsiReference.EMPTY_ARRAY as Array<Any>
@@ -152,21 +144,6 @@ public abstract class JetSimpleReference<T : JetReferenceExpression>(expression:
}
return context[BindingContext.AMBIGUOUS_REFERENCE_TARGET, expression].orEmpty()
}
override fun isReferenceTo(element: PsiElement?): Boolean {
val resolvedElement = resolve()
if (resolvedElement == null || element == null) {
return false
}
return checkElementMatch(resolvedElement, element)
}
}
public abstract class JetMultiReference<T : JetElement>(expression: T) : AbstractJetReference<T>(expression) {
override fun isReferenceTo(element: PsiElement?): Boolean {
if (element == null) {
return false
}
return multiResolve(false).map { it.getElement() }.filterNotNull().any { checkElementMatch(it, element) }
}
}
public abstract class JetMultiReference<T : JetElement>(expression: T) : AbstractJetReference<T>(expression)
@@ -0,0 +1,61 @@
/*
* 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.references
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiElement
import org.jetbrains.jet.asJava.unwrapped
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
import com.intellij.psi.PsiMethod
import org.jetbrains.jet.lang.psi.JetPropertyAccessor
import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
import org.jetbrains.jet.lang.psi.JetProperty
import java.util.HashSet
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
// Navigation element of the resolved reference
// For property accessor return enclosing property
val PsiReference.unwrappedTargets: Set<PsiElement>
get() {
fun PsiElement.adjust(): PsiElement? {
val target = unwrapped
return if (target is JetPropertyAccessor) target.getParentByType(javaClass<JetProperty>()) else target
}
return when (this) {
is JetMultiReference<*> -> multiResolve(false).map { it.getElement()?.adjust() }.filterNotNullTo(HashSet<PsiElement>())
else -> ContainerUtil.createMaybeSingletonSet(resolve()?.adjust())
}
}
fun PsiReference.matchesTarget(target: PsiElement): Boolean {
val unwrapped = target.unwrapped
return when {
unwrapped in unwrappedTargets ->
true
this is JetReference
&& unwrappedTargets.any { it is PsiMethod && it.isConstructor() && it.getContainingClass() == unwrapped } ->
true
else ->
false
}
}
@@ -24,11 +24,13 @@ import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.AsJavaPackage;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod;
import org.jetbrains.jet.plugin.JetPluginUtil;
import org.jetbrains.jet.plugin.search.usagesSearch.*;
public class KotlinReferencesSearcher extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
public static void processJetClassOrObject(
@@ -43,20 +45,48 @@ public class KotlinReferencesSearcher extends QueryExecutorBase<PsiReference, Re
}
});
if (lightClass != null) {
queryParameters.getOptimizer().searchWord(className, queryParameters.getEffectiveSearchScope(), true, lightClass);
searchNamedElement(queryParameters, lightClass, className);
}
}
}
@Override
public void processQuery(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull Processor<PsiReference> consumer) {
public void processQuery(
@NotNull final ReferencesSearch.SearchParameters queryParameters,
@NotNull final Processor<PsiReference> consumer
) {
PsiElement element = queryParameters.getElementToSearch();
PsiElement unwrappedElement = AsJavaPackage.getUnwrapped(element);
if (unwrappedElement == null
|| !JetPluginUtil.isInSource(unwrappedElement)
|| JetPluginUtil.isKtFileInGradleProjectInWrongFolder(unwrappedElement)) return;
final PsiNamedElement unwrappedElement = AsJavaPackage.getNamedUnwrappedElement(element);
if (unwrappedElement == null || JetPluginUtil.isKtFileInGradleProjectInWrongFolder(unwrappedElement)) return;
ApplicationManager.getApplication().runReadAction(
new Runnable() {
@Override
public void run() {
KotlinPsiSearchHelper searchHelper = new KotlinPsiSearchHelper(queryParameters.getElementToSearch().getProject());
UsagesSearchTarget<PsiNamedElement> searchTarget = new UsagesSearchTarget<PsiNamedElement>(
unwrappedElement,
queryParameters.getEffectiveSearchScope(),
UsagesSearchLocation.EVERYWHERE,
false
);
UsagesSearchRequestItem requestItem = new UsagesSearchRequestItem(
searchTarget,
UsagesSearchPackage.getSpecialNamesToSearch(unwrappedElement),
UsagesSearchPackage.getIsTargetUsage()
);
searchHelper.processFilesWithText(requestItem, consumer);
}
}
);
if (!JetPluginUtil.isInSource(unwrappedElement)) return;
searchLightElements(queryParameters, element);
}
private static void searchLightElements(ReferencesSearch.SearchParameters queryParameters, PsiElement element) {
if (element instanceof JetClassOrObject) {
processJetClassOrObject((JetClassOrObject) element, queryParameters);
}
@@ -99,7 +129,10 @@ public class KotlinReferencesSearcher extends QueryExecutorBase<PsiReference, Re
}
private static void searchNamedElement(ReferencesSearch.SearchParameters queryParameters, PsiNamedElement element) {
String name = element != null ? element.getName() : null;
searchNamedElement(queryParameters, element, element != null ? element.getName() : null);
}
private static void searchNamedElement(ReferencesSearch.SearchParameters queryParameters, PsiNamedElement element, @Nullable String name) {
if (name != null) {
queryParameters.getOptimizer().searchWord(name, queryParameters.getEffectiveSearchScope(), true, element);
}
@@ -0,0 +1,83 @@
/*
* 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.search.usagesSearch
import org.jetbrains.jet.lang.resolve.name.Name
import org.jetbrains.jet.lexer.JetToken
import org.jetbrains.jet.lang.types.expressions.OperatorConventions.*
import com.google.common.collect.ImmutableSet
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.lexer.JetSingleValueToken
import java.util.regex.Pattern
public val ALL_SEARCHABLE_OPERATIONS: ImmutableSet<JetToken> = ImmutableSet
.builder<JetToken>()
.addAll(UNARY_OPERATION_NAMES.keySet())
.addAll(BINARY_OPERATION_NAMES.keySet())
.addAll(ASSIGNMENT_OPERATIONS.keySet())
.addAll(COMPARISON_OPERATIONS)
.addAll(EQUALS_OPERATIONS)
.addAll(IDENTITY_EQUALS_OPERATIONS)
.addAll(IN_OPERATIONS)
.add(JetTokens.LBRACKET)
.add(JetTokens.LPAR)
.add(JetTokens.BY_KEYWORD)
.build()
public val ALL_SEARCHABLE_OPERATION_PATTERNS: Set<String> =
ALL_SEARCHABLE_OPERATIONS.map { (it as JetSingleValueToken).getValue() }.toSet()
public val INDEXING_OPERATION_NAMES: ImmutableSet<Name> =
ImmutableSet.of(Name.identifier("get"), Name.identifier("set"))
public val INVOKE_OPERATION_NAME: Name = Name.identifier("invoke")
public val ITERATOR_OPERATION_NAME: Name = Name.identifier("iterator")
public val COMPONENT_OPERATION_PATTERN: Pattern = Pattern.compile("component\\d+")
public val IN_OPERATIONS_TO_SEARCH: ImmutableSet<JetToken> = ImmutableSet.of(JetTokens.IN_KEYWORD)
public val COMPARISON_OPERATIONS_TO_SEARCH: ImmutableSet<JetToken> = ImmutableSet.of<JetToken>(JetTokens.LT, JetTokens.GT)
public fun Name.getOperationSymbolsToSearch(): Set<JetToken> {
when (this) {
COMPARE_TO -> return COMPARISON_OPERATIONS_TO_SEARCH
EQUALS -> return EQUALS_OPERATIONS
IDENTITY_EQUALS -> return IDENTITY_EQUALS_OPERATIONS
CONTAINS -> return IN_OPERATIONS_TO_SEARCH
INVOKE_OPERATION_NAME -> return ImmutableSet.of<JetToken>(JetTokens.LPAR)
ITERATOR_OPERATION_NAME -> return ImmutableSet.of<JetToken>(JetTokens.IN_KEYWORD)
in INDEXING_OPERATION_NAMES -> return ImmutableSet.of<JetToken>(JetTokens.LBRACKET)
}
if (COMPONENT_OPERATION_PATTERN.matcher(asString()).matches()) return ImmutableSet.of<JetToken>(JetTokens.LPAR)
val unaryOp = UNARY_OPERATION_NAMES.inverse()[this]
if (unaryOp != null) return ImmutableSet.of(unaryOp)
val binaryOp = BINARY_OPERATION_NAMES.inverse()[this]
if (binaryOp != null) {
val assignmentOp = ASSIGNMENT_OPERATION_COUNTERPARTS.inverse()[binaryOp]
return if (assignmentOp != null) ImmutableSet.of<JetToken>(binaryOp, assignmentOp) else ImmutableSet.of<JetToken>(binaryOp)
}
val assignmentOp = ASSIGNMENT_OPERATIONS.inverse()[this]
if (assignmentOp != null) return ImmutableSet.of<JetToken>(assignmentOp)
return ImmutableSet.of<JetToken>()
}
@@ -18,8 +18,6 @@ package org.jetbrains.jet.plugin.search.usagesSearch
import com.intellij.psi.PsiReference
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
import org.jetbrains.jet.lang.psi.JetImportDirective
import org.jetbrains.jet.plugin.search.usagesSearch.*
import org.jetbrains.jet.plugin.search.usagesSearch.UsagesSearchFilter.*
import org.jetbrains.jet.lang.psi.JetProperty
@@ -28,34 +26,35 @@ import java.util.Collections
import java.util.ArrayList
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
import com.intellij.psi.PsiNamedElement
import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.jet.asJava.LightClassUtil
import com.intellij.psi.PsiMethod
import com.intellij.util.Query
import com.intellij.psi.search.SearchRequestCollector
import org.jetbrains.jet.lang.psi.JetCallableDeclaration
import org.jetbrains.jet.lang.psi.JetParameter
import org.jetbrains.jet.lang.psi.JetPsiUtil
import org.jetbrains.jet.lang.psi.psiUtil.*
import org.jetbrains.jet.asJava.LightClassUtil.PropertyAccessorsPsiMethods
import org.jetbrains.jet.lang.psi.psiUtil.*
import org.jetbrains.jet.lang.resolve.name.Name
import org.jetbrains.jet.lexer.JetSingleValueToken
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.plugin.references.*
import org.jetbrains.jet.lang.psi.JetDeclaration
import com.intellij.util.containers.ContainerUtil
val isTargetUsage = (PsiReference::isTargetUsage).searchFilter
fun JetNamedDeclaration.namesWithAccessors(readable: Boolean = true, writable: Boolean = true): List<String> {
val name = getName()!!
val isTargetUsage = (PsiReference::matchesTarget).searchFilter
fun PsiNamedElement.getAccessorNames(readable: Boolean = true, writable: Boolean = true): List<String> {
fun PropertyAccessorsPsiMethods.toNameList(): List<String> {
val getter = getGetter()
val setter = getSetter()
val result = arrayListOf(name)
val result = ArrayList<String>()
if (readable && getter != null) result.add(getter.getName())
if (writable && setter != null) result.add(setter.getName())
return result
}
if (JetPsiUtil.isLocal(this)) return Collections.singletonList(name)
if (this !is JetDeclaration || JetPsiUtil.isLocal(this)) return Collections.emptyList()
when (this) {
is JetProperty ->
@@ -66,21 +65,32 @@ fun JetNamedDeclaration.namesWithAccessors(readable: Boolean = true, writable: B
}
}
return Collections.singletonList(name)
return Collections.emptyList()
}
public fun PsiNamedElement.getSpecialNamesToSearch(): List<String> {
val name = getName()
return when {
name == null || !Name.isValidIdentifier(name) -> Collections.emptyList<String>()
this is JetParameter -> {
if (!hasValOrVarNode()) return Collections.emptyList<String>()
val context = AnalyzerFacadeWithCache.getContextForElement(this)
val paramDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as? ValueParameterDescriptor
context[BindingContext.DATA_CLASS_COMPONENT_FUNCTION, paramDescriptor]?.let {
listOf(it.getName().asString(), JetTokens.LPAR.getValue())
} ?: Collections.emptyList<String>()
}
else -> Name.identifier(name).getOperationSymbolsToSearch().map { (it as JetSingleValueToken).getValue() }
}
}
public abstract class UsagesSearchHelper<T : PsiNamedElement> {
protected open fun makeFilter(target: UsagesSearchTarget<T>): UsagesSearchFilter = isTargetUsage
protected open fun makeWordList(target: UsagesSearchTarget<T>): List<String> {
return with(target) {
val name = element.getName()
when {
name == null -> Collections.emptyList<String>()
element is JetProperty, element is JetParameter -> (element as JetNamedDeclaration).namesWithAccessors()
else -> Collections.singletonList(name)
}
return with(target.element) {
ContainerUtil.createMaybeSingletonList(getName()) + getAccessorNames() + getSpecialNamesToSearch()
}
}
@@ -185,7 +195,11 @@ class PropertyUsagesSearchHelper(
skipImports: Boolean = false
) : DefaultSearchHelper<JetNamedDeclaration>(skipImports), OverrideSearchHelper {
override fun makeWordList(target: UsagesSearchTarget<JetNamedDeclaration>): List<String> {
return target.element.namesWithAccessors(readable = readUsages, writable = writeUsages)
return with(target.element) {
ContainerUtil.createMaybeSingletonList(getName()) +
getAccessorNames(readable = readUsages, writable = writeUsages) +
getSpecialNamesToSearch()
}
}
override fun makeFilter(target: UsagesSearchTarget<JetNamedDeclaration>): UsagesSearchFilter {
@@ -19,7 +19,6 @@ package org.jetbrains.jet.plugin.search.usagesSearch
import com.intellij.psi.PsiReference
import com.intellij.util.QueryFactory
import com.intellij.psi.search.SearchScope
import com.intellij.psi.PsiElement
import com.intellij.psi.search.SearchRequestCollector
import com.intellij.psi.search.SearchSession
import com.intellij.psi.search.PsiSearchHelper
@@ -29,7 +28,6 @@ import com.intellij.psi.search.SearchRequestQuery
import com.intellij.util.UniqueResultsQuery
import com.intellij.psi.search.searches.ReferenceDescriptor
import com.intellij.util.containers.ContainerUtil
import com.intellij.openapi.application.ApplicationManager
import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.PsiElement
import com.intellij.psi.search.RequestResultProcessor
@@ -38,15 +36,22 @@ import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.psi.PsiReferenceService
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.ReferenceRange
import java.util.Collections
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import java.util.ArrayList
import com.intellij.util.EmptyQuery
import com.intellij.openapi.project.Project
import java.util.ArrayDeque
import org.jetbrains.jet.plugin.search.usagesSearch.UsagesSearchFilter.*
import org.jetbrains.jet.plugin.search.and
import com.intellij.psi.impl.search.PsiSearchHelperImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.cache.impl.id.IdIndexEntry
import java.util.Collections
import com.intellij.openapi.roots.FileIndexFacade
import com.intellij.util.indexing.FileBasedIndex
import com.intellij.psi.impl.cache.impl.id.IdIndex
import org.jetbrains.jet.plugin.refactoring.runReadAction
import com.intellij.psi.search.TextOccurenceProcessor
import org.jetbrains.jet.plugin.references.JetMultiDeclarationReference
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiManagerEx
public data class UsagesSearchLocation(
val inCode: Boolean = true,
@@ -103,39 +108,61 @@ public data class UsagesSearchRequest(val project: Project, val items: List<Usag
val collector: SearchRequestCollector = SearchRequestCollector(SearchSession())
}
public object UsagesSearch: QueryFactory<PsiReference, UsagesSearchRequest>() {
{
class ResultProcessorImpl(private val node: UsagesSearchRequestItem) : RequestResultProcessor() {
private val referenceService = PsiReferenceService.getService()!!
public class KotlinPsiSearchHelper(private val project: Project): PsiSearchHelperImpl(PsiManager.getInstance(project) as PsiManagerEx) {
class ResultTextProcessorImpl(
private val node: UsagesSearchRequestItem,
private val consumer: Processor<PsiReference>
): TextOccurenceProcessor {
private val referenceService = PsiReferenceService.getService()!!
override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor<PsiReference>): Boolean {
return referenceService.getReferences(element, PsiReferenceService.Hints.NO_HINTS).all { ref ->
ProgressManager.checkCanceled()
override fun execute(element: PsiElement, offsetInElement: Int): Boolean {
return referenceService.getReferences(element, PsiReferenceService.Hints.NO_HINTS).all { ref ->
ProgressManager.checkCanceled()
when {
!ReferenceRange.containsOffsetInElement(ref, offsetInElement) -> true
!node.filter.accepts(ref, node) -> true
else -> consumer.process(ref)
}
when {
!ReferenceRange.containsOffsetInElement(ref, offsetInElement) -> true
!node.filter.accepts(ref, node) -> true
else -> consumer.process(ref)
}
}
}
}
override fun processFilesWithText(
scope: GlobalSearchScope,
searchContext: Short,
caseSensitively: Boolean,
text: String,
processor: Processor<VirtualFile>
): Boolean {
if (text !in ALL_SEARCHABLE_OPERATION_PATTERNS) {
return super.processFilesWithText(scope, searchContext, caseSensitively, text, processor)
}
val entries = Collections.singletonList(IdIndexEntry(text, caseSensitively))
val index = FileIndexFacade.getInstance(project)
val checker: (Int?) -> Boolean = { (it!! and searchContext.toInt()) != 0 }
return runReadAction{
FileBasedIndex.getInstance().processFilesContainingAllKeys(IdIndex.NAME, entries, scope, checker) { file ->
!index.shouldBeFound(scope, file) || processor.process(file)
}
}!!
}
public fun processFilesWithText(item: UsagesSearchRequestItem, consumer: Processor<PsiReference>): Boolean {
return item.words.all { word ->
val textProcessor = ResultTextProcessorImpl(item, consumer)
processElementsWithWord(textProcessor, item.target.scope, word, UsageSearchContext.IN_CODE, true)
}
}
}
public object UsagesSearch: QueryFactory<PsiReference, UsagesSearchRequest>() {
{
object ExecutorImpl: QueryExecutorBase<PsiReference, UsagesSearchRequest>() {
override fun processQuery(request: UsagesSearchRequest, consumer: Processor<PsiReference>) {
for (item in request.items) {
with (item) {
if (filter != False) {
ApplicationManager.getApplication()?.runReadAction {
for (word in words) {
request.collector.searchWord(
word, target.scope, target.location.searchContext, true, ResultProcessorImpl(item)
)
}
}
}
}
}
val searchHelper = KotlinPsiSearchHelper(request.project)
request.items.filter { it.filter != False }.all { searchHelper.processFilesWithText(it, consumer) }
}
}
@@ -23,7 +23,6 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.psi.*
import org.jetbrains.jet.lang.psi.psiUtil.*
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import com.intellij.psi.PsiReference
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
@@ -38,14 +37,13 @@ import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod
import org.jetbrains.jet.asJava.unwrapped
import org.jetbrains.jet.lang.resolve.OverrideResolver
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
// Navigation element of the resolved reference
// For property accessor return enclosing property
val PsiReference.unwrappedTarget: PsiElement?
get() {
val target = resolve()?.unwrapped
return if (target is JetPropertyAccessor) target.getParentByType(javaClass<JetProperty>()) else target
}
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jet.plugin.references.JetMultiReference
import java.util.HashSet
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
import org.jetbrains.jet.plugin.references.JetReference
import com.intellij.psi.PsiMethod
import org.jetbrains.jet.plugin.references.*
val JetDeclaration.descriptor: DeclarationDescriptor?
get() = AnalyzerFacadeWithCache.getContextForElement(this).get(BindingContext.DECLARATION_TO_DESCRIPTOR, this)
@@ -53,21 +51,19 @@ val JetDeclaration.descriptor: DeclarationDescriptor?
val JetParameter.propertyDescriptor: PropertyDescriptor?
get() = AnalyzerFacadeWithCache.getContextForElement(this).get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, this)
fun PsiReference.isTargetUsage(target: PsiElement): Boolean {
return target.unwrapped == unwrappedTarget
}
fun PsiReference.checkUsageVsOriginalDescriptor(
target: JetDeclaration,
declarationToDescriptor: (JetDeclaration) -> DeclarationDescriptor? = {it.descriptor},
checker: (usageDescriptor: DeclarationDescriptor, targetDescriptor: DeclarationDescriptor) -> Boolean
): Boolean {
val refTarget = unwrappedTarget
if (refTarget !is JetDeclaration) return false
val usageDescriptor = declarationToDescriptor(refTarget)
val targetDescriptor = declarationToDescriptor(target)
return usageDescriptor != null && targetDescriptor != null && checker(usageDescriptor, targetDescriptor)
return unwrappedTargets.any {
if (it is JetDeclaration) {
val usageDescriptor = declarationToDescriptor(it)
val targetDescriptor = declarationToDescriptor(target)
usageDescriptor != null && targetDescriptor != null && checker(usageDescriptor, targetDescriptor)
}
else false
}
}
fun PsiReference.isImportUsage(): Boolean =