Different modes of search
Down-shift to plain search in non-test mode if unknown reference encountered Internal action to check search algorithm
This commit is contained in:
+25
-12
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.search.usagesSearch
|
package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||||
|
|
||||||
import com.intellij.lang.java.JavaLanguage
|
import com.intellij.lang.java.JavaLanguage
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.search.*
|
import com.intellij.psi.search.*
|
||||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||||
@@ -39,6 +40,7 @@ import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
|||||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
|
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
|
||||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
||||||
import org.jetbrains.kotlin.idea.search.unionSafe
|
import org.jetbrains.kotlin.idea.search.unionSafe
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.DestructuringDeclarationUsageSearch.*
|
||||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||||
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
||||||
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
||||||
@@ -52,6 +54,12 @@ import org.jetbrains.kotlin.util.isValidOperator
|
|||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
enum class DestructuringDeclarationUsageSearch {
|
||||||
|
ALWAYS_SMART, ALWAYS_PLAIN, PLAIN_WHEN_NEEDED
|
||||||
|
}
|
||||||
|
|
||||||
|
var destructuringDeclarationUsageSearchMode = if (ApplicationManager.getApplication().isUnitTestMode) ALWAYS_SMART else PLAIN_WHEN_NEEDED
|
||||||
|
|
||||||
//TODO: compiled code
|
//TODO: compiled code
|
||||||
//TODO: check if it's too expensive
|
//TODO: check if it's too expensive
|
||||||
|
|
||||||
@@ -72,8 +80,12 @@ fun findDestructuringDeclarationUsages(
|
|||||||
consumer: Processor<PsiReference>,
|
consumer: Processor<PsiReference>,
|
||||||
optimizer: SearchRequestCollector
|
optimizer: SearchRequestCollector
|
||||||
) {
|
) {
|
||||||
// for local scope it's faster to use plain search
|
val usePlainSearch = when (destructuringDeclarationUsageSearchMode) {
|
||||||
if (scope is LocalSearchScope) {
|
ALWAYS_SMART -> false
|
||||||
|
ALWAYS_PLAIN -> true
|
||||||
|
PLAIN_WHEN_NEEDED -> scope is LocalSearchScope // for local scope it's faster to use plain search
|
||||||
|
}
|
||||||
|
if (usePlainSearch) {
|
||||||
doPlainSearch(ktDeclaration, scope, optimizer)
|
doPlainSearch(ktDeclaration, scope, optimizer)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -130,17 +142,18 @@ private class Processor(
|
|||||||
val classesToSearch = listOf(psiClass) + ClassInheritorsSearch.search(parameters).findAll()
|
val classesToSearch = listOf(psiClass) + ClassInheritorsSearch.search(parameters).findAll()
|
||||||
|
|
||||||
for (classToSearch in classesToSearch) {
|
for (classToSearch in classesToSearch) {
|
||||||
ReferencesSearch.search(classToSearch).forEach(Processor { reference ->
|
ReferencesSearch.search(classToSearch).forEach(Processor processor@ { reference -> //TODO: see KT-13607
|
||||||
if (!processDataClassUsage(reference)) {
|
if (processDataClassUsage(reference)) return@processor true
|
||||||
//TODO
|
|
||||||
val element = reference.element
|
if (destructuringDeclarationUsageSearchMode != ALWAYS_SMART) {
|
||||||
val document = PsiDocumentManager.getInstance(project).getDocument(element.containingFile)
|
plainSearchHandler(searchScope)
|
||||||
val lineAndCol = DiagnosticUtils.offsetToLineAndColumn(document, element.startOffset)
|
return@processor false
|
||||||
TODO("Unsupported reference: '${element.text}' in ${element.containingFile.name} line ${lineAndCol.line} column ${lineAndCol.column}")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val element = reference.element
|
||||||
|
val document = PsiDocumentManager.getInstance(project).getDocument(element.containingFile)
|
||||||
|
val lineAndCol = DiagnosticUtils.offsetToLineAndColumn(document, element.startOffset)
|
||||||
|
error("Unsupported reference: '${element.text}' in ${element.containingFile.name} line ${lineAndCol.line} column ${lineAndCol.column}")
|
||||||
})
|
})
|
||||||
|
|
||||||
// we must use plain search inside our data class (and inheritors) because implicit 'this' can happen anywhere
|
// we must use plain search inside our data class (and inheritors) because implicit 'this' can happen anywhere
|
||||||
|
|||||||
@@ -139,6 +139,11 @@
|
|||||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action id="CheckComponentsUsageSearchAction" class="org.jetbrains.kotlin.idea.actions.internal.CheckComponentsUsageSearchAction"
|
||||||
|
text="Check Component Functions Usage Search">
|
||||||
|
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||||
|
</action>
|
||||||
|
|
||||||
<action id="KotlinInternalMode" class="org.jetbrains.kotlin.idea.actions.internal.KotlinInternalModeToggleAction">
|
<action id="KotlinInternalMode" class="org.jetbrains.kotlin.idea.actions.internal.KotlinInternalModeToggleAction">
|
||||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||||
</action>
|
</action>
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.actions.internal
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||||
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.ui.Messages
|
||||||
|
import com.intellij.openapi.vfs.VfsUtilCore
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileVisitor
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.DestructuringDeclarationUsageSearch
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.destructuringDeclarationUsageSearchMode
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.util.*
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
|
class CheckComponentsUsageSearchAction : AnAction() {
|
||||||
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
|
val selectedFiles = selectedKotlinFiles(e).toList()
|
||||||
|
val project = CommonDataKeys.PROJECT.getData(e.dataContext)!!
|
||||||
|
|
||||||
|
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||||
|
{
|
||||||
|
runReadAction { process(selectedFiles, project) }
|
||||||
|
},
|
||||||
|
"Checking Data Classes",
|
||||||
|
true,
|
||||||
|
project)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun process(files: Collection<KtFile>, project: Project) {
|
||||||
|
val dataClasses = files.asSequence()
|
||||||
|
.flatMap { it.declarations.asSequence() }
|
||||||
|
.filterIsInstance<KtClass>()
|
||||||
|
.filter { it.isData() }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val progressIndicator = ProgressManager.getInstance().progressIndicator
|
||||||
|
for ((i, dataClass) in dataClasses.withIndex()) {
|
||||||
|
progressIndicator?.text = "Checking data class ${i + 1} of ${dataClasses.size}..."
|
||||||
|
progressIndicator?.text2 = dataClass.fqName?.asString() ?: ""
|
||||||
|
|
||||||
|
val parameter = dataClass.getPrimaryConstructor()?.valueParameters?.firstOrNull()
|
||||||
|
if (parameter != null) {
|
||||||
|
try {
|
||||||
|
var smartRefsCount = 0
|
||||||
|
var goldRefsCount = 0
|
||||||
|
ProgressManager.getInstance().runProcess(Runnable {
|
||||||
|
destructuringDeclarationUsageSearchMode = DestructuringDeclarationUsageSearch.ALWAYS_SMART
|
||||||
|
|
||||||
|
smartRefsCount = ReferencesSearch.search(parameter).findAll().size
|
||||||
|
|
||||||
|
destructuringDeclarationUsageSearchMode = DestructuringDeclarationUsageSearch.ALWAYS_PLAIN
|
||||||
|
|
||||||
|
goldRefsCount = ReferencesSearch.search(parameter).findAll().size
|
||||||
|
}, EmptyProgressIndicator())
|
||||||
|
|
||||||
|
if (smartRefsCount != goldRefsCount) {
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
Messages.showInfoMessage(project, "Difference found for data class ${dataClass.fqName?.asString()}. Found $smartRefsCount usage(s) but $goldRefsCount expected", "Error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
destructuringDeclarationUsageSearchMode = DestructuringDeclarationUsageSearch.PLAIN_WHEN_NEEDED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progressIndicator?.fraction = (i + 1) / dataClasses.size.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
Messages.showInfoMessage(project, "Analyzed ${dataClasses.size} classes. No difference found.", "Success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(e: AnActionEvent) {
|
||||||
|
if (!KotlinInternalMode.enabled) {
|
||||||
|
e.presentation.isVisible = false
|
||||||
|
e.presentation.isEnabled = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
e.presentation.isVisible = true
|
||||||
|
e.presentation.isEnabled = selectedKotlinFiles(e).any()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectedKotlinFiles(e: AnActionEvent): Sequence<KtFile> {
|
||||||
|
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf()
|
||||||
|
val project = CommonDataKeys.PROJECT.getData(e.dataContext) ?: return sequenceOf()
|
||||||
|
return allKotlinFiles(virtualFiles, project)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun allKotlinFiles(filesOrDirs: Array<VirtualFile>, project: Project): Sequence<KtFile> {
|
||||||
|
val manager = PsiManager.getInstance(project)
|
||||||
|
return allFiles(filesOrDirs)
|
||||||
|
.asSequence()
|
||||||
|
.mapNotNull { manager.findFile(it) as? KtFile }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
|
||||||
|
val result = ArrayList<VirtualFile>()
|
||||||
|
for (file in filesOrDirs) {
|
||||||
|
VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
|
||||||
|
override fun visitFile(file: VirtualFile): Boolean {
|
||||||
|
result.add(file)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@ import com.intellij.psi.search.LocalSearchScope
|
|||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.DestructuringDeclarationUsageSearch
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.destructuringDeclarationUsageSearchMode
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
@@ -65,9 +67,15 @@ class KotlinReferencesSearchTest(): AbstractSearcherTest() {
|
|||||||
val func = myFixtureProxy.elementAtCaret.getParentOfType<T>(false)!!
|
val func = myFixtureProxy.elementAtCaret.getParentOfType<T>(false)!!
|
||||||
val refs = ReferencesSearch.search(func).findAll().sortedBy { it.element.textRange.startOffset }
|
val refs = ReferencesSearch.search(func).findAll().sortedBy { it.element.textRange.startOffset }
|
||||||
|
|
||||||
// check that local references search gives the same result
|
// check that local reference search gives the same result
|
||||||
val localRefs = ReferencesSearch.search(func, LocalSearchScope(psiFile)).findAll()
|
try {
|
||||||
Assert.assertEquals(refs.size, localRefs.size)
|
destructuringDeclarationUsageSearchMode = DestructuringDeclarationUsageSearch.PLAIN_WHEN_NEEDED
|
||||||
|
val localRefs = ReferencesSearch.search(func, LocalSearchScope(psiFile)).findAll()
|
||||||
|
Assert.assertEquals(refs.size, localRefs.size)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
destructuringDeclarationUsageSearchMode = DestructuringDeclarationUsageSearch.ALWAYS_SMART
|
||||||
|
}
|
||||||
|
|
||||||
return refs
|
return refs
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user