Optimization of parameter usages search
This commit is contained in:
@@ -179,13 +179,24 @@ public class JetParameter extends JetNamedDeclarationStub<KotlinParameterStub> i
|
||||
@NotNull
|
||||
@Override
|
||||
public SearchScope getUseScope() {
|
||||
PsiElement parent = getParent();
|
||||
if (parent != null) {
|
||||
PsiElement grandparent = parent.getParent();
|
||||
if (grandparent instanceof JetFunction && !(grandparent instanceof JetFunctionLiteral)) {
|
||||
return grandparent.getUseScope();
|
||||
}
|
||||
JetDeclaration function = getOwnerFunction();
|
||||
if (function != null && !(function instanceof JetFunctionLiteral)) {
|
||||
return function.getUseScope();
|
||||
}
|
||||
return super.getUseScope();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SearchScope getUseScopeExceptNamedArguments() {
|
||||
return super.getUseScope();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JetFunction getOwnerFunction() {
|
||||
PsiElement parent = getParent();
|
||||
if (parent == null) return null;
|
||||
PsiElement grandparent = parent.getParent();
|
||||
if (!(grandparent instanceof JetFunction)) return null;
|
||||
return (JetFunction) grandparent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ import org.jetbrains.kotlin.lexer.JetLexer
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import java.util.ArrayDeque
|
||||
|
||||
class KotlinFilterLexer(table: OccurrenceConsumer): BaseFilterLexer(JetLexer(), table) {
|
||||
val KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT: Short = 0x20
|
||||
|
||||
class KotlinFilterLexer(private val occurrenceConsumer: OccurrenceConsumer): BaseFilterLexer(JetLexer(), occurrenceConsumer) {
|
||||
private val codeTokens = TokenSet.orSet(
|
||||
TokenSet.create(*ALL_SEARCHABLE_OPERATIONS.toTypedArray()),
|
||||
TokenSet.create(JetTokens.IDENTIFIER, JetTokens.FIELD_IDENTIFIER)
|
||||
@@ -46,15 +48,30 @@ class KotlinFilterLexer(table: OccurrenceConsumer): BaseFilterLexer(JetLexer(),
|
||||
)
|
||||
|
||||
private val MAX_PREV_TOKENS = 2
|
||||
private val previousTokens = ArrayDeque<IElementType>(MAX_PREV_TOKENS)
|
||||
private val prevTokens = ArrayDeque<IElementType>(MAX_PREV_TOKENS)
|
||||
private var prevTokenStart = -1
|
||||
private var prevTokenEnd = -1
|
||||
|
||||
override fun advance() {
|
||||
val tokenType = myDelegate.tokenType
|
||||
|
||||
when (tokenType) {
|
||||
in codeTokens -> addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt())
|
||||
JetTokens.EQ -> {
|
||||
if (prevTokens.peekFirst() == JetTokens.IDENTIFIER) {
|
||||
val prevPrev = prevTokens.elementAtOrNull(1)
|
||||
if (prevPrev == JetTokens.COMMA || prevPrev == JetTokens.LPAR) {
|
||||
occurrenceConsumer.addOccurrence(bufferSequence, null, prevTokenStart, prevTokenEnd, KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JetTokens.LPAR -> if (isMultiDeclarationPosition()) addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt())
|
||||
JetTokens.LPAR -> {
|
||||
if (isMultiDeclarationPosition()) {
|
||||
addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
in codeTokens -> addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt())
|
||||
|
||||
in JetTokens.STRINGS -> scanWordsInToken(UsageSearchContext.IN_STRINGS + UsageSearchContext.IN_FOREIGN_LANGUAGES, false, true)
|
||||
|
||||
@@ -66,26 +83,29 @@ class KotlinFilterLexer(table: OccurrenceConsumer): BaseFilterLexer(JetLexer(),
|
||||
!in skipTokens -> scanWordsInToken(UsageSearchContext.IN_PLAIN_TEXT.toInt(), false, false)
|
||||
}
|
||||
|
||||
if (tokenType != TokenType.WHITE_SPACE) {
|
||||
if (previousTokens.size() == MAX_PREV_TOKENS) {
|
||||
previousTokens.removeLast()
|
||||
if (tokenType != TokenType.WHITE_SPACE && tokenType !in commentTokens) {
|
||||
if (prevTokens.size() == MAX_PREV_TOKENS) {
|
||||
prevTokens.removeLast()
|
||||
}
|
||||
previousTokens.addFirst(tokenType)
|
||||
|
||||
prevTokens.addFirst(tokenType)
|
||||
prevTokenStart = tokenStart
|
||||
prevTokenEnd = tokenEnd
|
||||
}
|
||||
|
||||
myDelegate.advance()
|
||||
}
|
||||
|
||||
private fun isMultiDeclarationPosition(): Boolean {
|
||||
val first = previousTokens.peekFirst()
|
||||
val first = prevTokens.peekFirst()
|
||||
if (first == JetTokens.VAL_KEYWORD || first == JetTokens.VAR_KEYWORD) return true
|
||||
return first == JetTokens.LPAR && previousTokens.elementAtOrNull(1) == JetTokens.FOR_KEYWORD
|
||||
return first == JetTokens.LPAR && prevTokens.elementAtOrNull(1) == JetTokens.FOR_KEYWORD
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinIdIndexer: LexerBasedIdIndexer() {
|
||||
override fun createLexer(consumer: OccurrenceConsumer): Lexer = KotlinFilterLexer(consumer)
|
||||
|
||||
override fun getVersion() = 2
|
||||
}
|
||||
|
||||
class KotlinTodoIndexer: LexerBasedTodoIndexer(), IdAndToDoScannerBasedOnFilterLexer {
|
||||
|
||||
+59
-3
@@ -18,15 +18,21 @@ package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.UsageSearchContext
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.matchesTarget
|
||||
import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT
|
||||
import org.jetbrains.kotlin.idea.search.and
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.False
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.True
|
||||
import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope
|
||||
import org.jetbrains.kotlin.lexer.JetSingleValueToken
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -196,7 +202,7 @@ class ClassDeclarationsUsagesSearchHelper(
|
||||
|
||||
for (decl in target.element.effectiveDeclarations()) {
|
||||
if ((decl is JetNamedFunction && functionUsages) || ((decl is JetProperty || decl is JetParameter) && propertyUsages)) {
|
||||
items.add(declHelper.newItem(target.retarget(decl as JetNamedDeclaration)))
|
||||
items.add(declHelper.newItem(target.withTarget(decl as JetNamedDeclaration)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,10 +243,46 @@ val isPropertyReadOnlyUsage = (PsiReference::isPropertyReadOnlyUsage).searchFilt
|
||||
class PropertyUsagesSearchHelper(
|
||||
public val readUsages: Boolean = true,
|
||||
public val writeUsages: Boolean = true,
|
||||
public val namedArgumentUsages: Boolean = true,
|
||||
public override val selfUsages: Boolean = true,
|
||||
public override val overrideUsages: Boolean = true,
|
||||
skipImports: Boolean = false
|
||||
) : DefaultSearchHelper<JetNamedDeclaration>(skipImports), OverrideSearchHelper {
|
||||
override fun makeItemList(target: UsagesSearchTarget<JetNamedDeclaration>): List<UsagesSearchRequestItem> {
|
||||
val element = target.element
|
||||
if (element is JetParameter) {
|
||||
val realUsagesScope = element.useScopeExceptNamedArguments
|
||||
val realUsagesTarget = target.withScope(realUsagesScope and target.effectiveScope)
|
||||
val realUsagesFilter = makeFilter(target) and !(PsiReference::isNamedArgumentUsage).searchFilter
|
||||
val realUsagesRequest = UsagesSearchRequestItem(realUsagesTarget, makeWordList(target), realUsagesFilter, makeAdditionalSearchDelegate(target.element))
|
||||
|
||||
if (!namedArgumentUsages) {
|
||||
return listOf(realUsagesRequest)
|
||||
}
|
||||
|
||||
val function = element.ownerFunction
|
||||
?: return listOf(realUsagesRequest) // parameter in for or something like that
|
||||
if (function.nameAsName?.isSpecial ?: true) { // function literal or anonymous function
|
||||
return listOf(realUsagesRequest)
|
||||
}
|
||||
|
||||
var scope = target.effectiveScope
|
||||
if (scope is GlobalSearchScope) {
|
||||
scope = JetSourceFilterScope.kotlinSources(scope, element.project)
|
||||
}
|
||||
|
||||
val additionalFilter1 = AdditionalFileFilter(element.name!!, KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT, true)
|
||||
val additionalFilter2 = AdditionalFileFilter(function.name!!, UsageSearchContext.IN_CODE, true)
|
||||
val namedArgsRequest = UsagesSearchRequestItem(target.withScope(scope),
|
||||
listOf(element.name!!),
|
||||
(PsiReference::isNamedArgumentUsage).searchFilter,
|
||||
additionalFileFilters = listOf(additionalFilter1, additionalFilter2))
|
||||
return listOf(realUsagesRequest, namedArgsRequest)
|
||||
}
|
||||
|
||||
return super<DefaultSearchHelper>.makeItemList(target)
|
||||
}
|
||||
|
||||
override fun makeWordList(target: UsagesSearchTarget<JetNamedDeclaration>): List<String> {
|
||||
return with(target.element) {
|
||||
ContainerUtil.createMaybeSingletonList(getName()) +
|
||||
@@ -250,12 +292,26 @@ class PropertyUsagesSearchHelper(
|
||||
}
|
||||
|
||||
override fun makeFilter(target: UsagesSearchTarget<JetNamedDeclaration>): UsagesSearchFilter {
|
||||
val readWriteUsage = when {
|
||||
val readWriteFilter = when {
|
||||
readUsages && writeUsages -> True
|
||||
readUsages -> isPropertyReadOnlyUsage
|
||||
writeUsages -> !isPropertyReadOnlyUsage
|
||||
else -> False
|
||||
}
|
||||
return isTargetOrOverrideUsage and readWriteUsage and isFilteredImport
|
||||
var result = isTargetOrOverrideUsage and readWriteFilter and isFilteredImport
|
||||
if (!namedArgumentUsages) {
|
||||
result = result and !(PsiReference::isNamedArgumentUsage).searchFilter
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiReference.isNamedArgumentUsage(): Boolean {
|
||||
if (this !is JetSimpleNameReference) return false
|
||||
return expression.parent is JetValueArgumentName
|
||||
}
|
||||
|
||||
|
||||
|
||||
+37
-33
@@ -16,40 +16,25 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.util.QueryFactory
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchSession
|
||||
import com.intellij.psi.search.PsiSearchHelper
|
||||
import com.intellij.util.Query
|
||||
import com.intellij.util.MergeQuery
|
||||
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.psi.search.UsageSearchContext
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.openapi.application.QueryExecutorBase
|
||||
import com.intellij.psi.PsiReferenceService
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.ReferenceRange
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.*
|
||||
import org.jetbrains.kotlin.idea.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.kotlin.idea.util.application.runReadAction
|
||||
import com.intellij.psi.search.TextOccurenceProcessor
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.PsiManagerEx
|
||||
import com.intellij.psi.impl.cache.impl.id.IdIndex
|
||||
import com.intellij.psi.impl.cache.impl.id.IdIndexEntry
|
||||
import com.intellij.psi.impl.search.PsiSearchHelperImpl
|
||||
import com.intellij.psi.search.*
|
||||
import com.intellij.psi.search.searches.ReferenceDescriptor
|
||||
import com.intellij.util.*
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.indexing.FileBasedIndex
|
||||
import org.jetbrains.kotlin.idea.search.and
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.False
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import java.util.Collections
|
||||
|
||||
public data class UsagesSearchLocation(
|
||||
val inCode: Boolean = true,
|
||||
@@ -81,7 +66,10 @@ public data class UsagesSearchTarget<out T : PsiElement>(
|
||||
val location: UsagesSearchLocation = UsagesSearchLocation.DEFAULT,
|
||||
val restrictByTargetScope: Boolean = true
|
||||
) {
|
||||
fun <U: PsiElement> retarget(element: U) =
|
||||
fun <U: PsiElement> withTarget(element: U) =
|
||||
UsagesSearchTarget(element, scope, location, restrictByTargetScope)
|
||||
|
||||
fun withScope(scope: SearchScope) =
|
||||
UsagesSearchTarget(element, scope, location, restrictByTargetScope)
|
||||
|
||||
val effectiveScope: SearchScope
|
||||
@@ -100,11 +88,18 @@ public interface UsagesSearchFilter {
|
||||
fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean
|
||||
}
|
||||
|
||||
public data class UsagesSearchRequestItem(
|
||||
public class UsagesSearchRequestItem(
|
||||
val target: UsagesSearchTarget<PsiElement>,
|
||||
val words: List<String>,
|
||||
val filter: UsagesSearchFilter,
|
||||
val additionalSearchDelegate: ((Processor<PsiReference>) -> Boolean)?
|
||||
val additionalSearchDelegate: ((Processor<PsiReference>) -> Boolean)? = null,
|
||||
val additionalFileFilters: Collection<AdditionalFileFilter> = emptyList()
|
||||
)
|
||||
|
||||
public data class AdditionalFileFilter(
|
||||
val word: String,
|
||||
val searchContext: Short,
|
||||
val caseSensitively: Boolean
|
||||
)
|
||||
|
||||
public data class UsagesSearchRequest(val project: Project, val items: List<UsagesSearchRequestItem>) {
|
||||
@@ -153,7 +148,16 @@ public class KotlinPsiSearchHelper(private val project: Project): PsiSearchHelpe
|
||||
}
|
||||
|
||||
public fun processFilesWithText(item: UsagesSearchRequestItem, consumer: Processor<PsiReference>): Boolean {
|
||||
val scope = runReadAction { item.target.effectiveScope }
|
||||
var scope = runReadAction { item.target.effectiveScope }
|
||||
|
||||
for (filter in item.additionalFileFilters) {
|
||||
if (scope !is GlobalSearchScope) break
|
||||
val processor = CommonProcessors.CollectProcessor<VirtualFile>()
|
||||
processFilesWithText(scope, filter.searchContext, filter.caseSensitively, filter.word, processor)
|
||||
if (processor.results.isEmpty()) return true
|
||||
scope = GlobalSearchScope.filesScope(project, processor.results)
|
||||
}
|
||||
|
||||
return item.words.all { word ->
|
||||
val textProcessor = ResultTextProcessorImpl(item, consumer)
|
||||
processElementsWithWord(textProcessor, scope, word, item.target.location.searchContext, true)
|
||||
|
||||
@@ -16,19 +16,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.findUsages
|
||||
|
||||
import com.intellij.find.findUsages.JavaMethodFindUsagesOptions
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.find.findUsages.JavaClassFindUsagesOptions
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.ClassUsagesSearchHelper
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchTarget
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchLocation
|
||||
import com.intellij.find.findUsages.FindUsagesOptions
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.PropertyUsagesSearchHelper
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.ClassDeclarationsUsagesSearchHelper
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.FunctionUsagesSearchHelper
|
||||
import com.intellij.find.findUsages.JavaClassFindUsagesOptions
|
||||
import com.intellij.find.findUsages.JavaMethodFindUsagesOptions
|
||||
import com.intellij.find.findUsages.JavaVariableFindUsagesOptions
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.*
|
||||
|
||||
public class KotlinClassFindUsagesOptions(project: Project) : JavaClassFindUsagesOptions(project) {
|
||||
public var searchConstructorUsages: Boolean = true
|
||||
@@ -101,6 +96,7 @@ fun KotlinPropertyFindUsagesOptions.toHelper(): PropertyUsagesSearchHelper =
|
||||
PropertyUsagesSearchHelper(
|
||||
selfUsages = isUsages,
|
||||
overrideUsages = isUsages,
|
||||
namedArgumentUsages = isUsages,
|
||||
readUsages = isReadAccess,
|
||||
writeUsages = isWriteAccess,
|
||||
skipImports = isSkipImportStatements
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
|
||||
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
|
||||
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.*
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
@@ -220,7 +219,7 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
val searchHelper: UsagesSearchHelper<out JetNamedDeclaration> = when (declaration) {
|
||||
is JetClassOrObject -> ClassUsagesSearchHelper(constructorUsages = true, nonConstructorUsages = true, skipImports = true)
|
||||
is JetNamedFunction -> FunctionUsagesSearchHelper(skipImports = true)
|
||||
is JetProperty, is JetParameter -> PropertyUsagesSearchHelper(skipImports = true)
|
||||
is JetProperty, is JetParameter -> PropertyUsagesSearchHelper(skipImports = true, namedArgumentUsages = false)
|
||||
else -> DefaultSearchHelper<JetNamedDeclaration>()
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-3
@@ -1,5 +1,3 @@
|
||||
[kotlinPrivatePropertyUsages2.0.kt] Named argument (9: 33) public class ServerEx(): Server(foo = "!") {
|
||||
[kotlinPrivatePropertyUsages2.0.kt] Value read (10: 45) override fun processRequest() = "foo" + foo // this reference is found as a side effect of big use scope of constructor parameter:
|
||||
[kotlinPrivatePropertyUsages2.0.kt] Value read (6: 33) open fun processRequest() = foo
|
||||
[kotlinPrivatePropertyUsages2.1.kt] Named argument (5: 24) println(Server(foo = "!").foo)
|
||||
[kotlinPrivatePropertyUsages2.1.kt] Value read (5: 35) println(Server(foo = "!").foo)
|
||||
[kotlinPrivatePropertyUsages2.1.kt] Named argument (5: 24) println(Server(foo = "!").foo)
|
||||
Reference in New Issue
Block a user