Better sorting of variants in add import popup

This commit is contained in:
Valentin Kipyatkov
2015-08-21 18:11:41 +03:00
parent 5a4cd96c75
commit 628ce4ea32
11 changed files with 102 additions and 107 deletions
@@ -1,50 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionLocation
import com.intellij.codeInsight.completion.CompletionStatistician
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.psi.statistics.StatisticsInfo
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.renderer.DescriptorRenderer
public class KotlinCompletionStatistician : CompletionStatistician() {
override fun serialize(element: LookupElement, location: CompletionLocation): StatisticsInfo? {
val o = (element.`object` as? DeclarationLookupObject) ?: return null
if (o.descriptor != null) {
val container = o.descriptor!!.containingDeclaration
val containerFqName = when (container) {
is ClassDescriptor -> container.importableFqName?.asString()
is PackageFragmentDescriptor -> container.fqName.asString()
is ModuleDescriptor -> ""
else -> null
} ?: return StatisticsInfo.EMPTY
val signature = DescriptorRenderer.COMPACT.render(o.descriptor!!) //TODO: more compact presentation
return StatisticsInfo(containerFqName, signature)
}
else {
val fqName = o.importableFqName ?: return StatisticsInfo.EMPTY
return StatisticsInfo(fqName.parent().asString(), fqName.shortName().identifier)
}
}
}
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionLocation
import com.intellij.codeInsight.completion.CompletionStatistician
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.psi.PsiElement
import com.intellij.psi.statistics.StatisticsInfo
import com.intellij.psi.util.ProximityLocation
import com.intellij.psi.util.proximity.ProximityStatistician
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.renderer.DescriptorRenderer
class KotlinCompletionStatistician : CompletionStatistician() {
override fun serialize(element: LookupElement, location: CompletionLocation): StatisticsInfo? {
val o = (element.`object` as? DeclarationLookupObject) ?: return null
if (o.descriptor != null) {
return KotlinStatisticsInfo.forDescriptor(o.descriptor!!)
}
else {
val fqName = o.importableFqName ?: return StatisticsInfo.EMPTY
return StatisticsInfo("", fqName.asString())
}
}
}
class KotlinProximityStatistician : ProximityStatistician() {
override fun serialize(element: PsiElement, location: ProximityLocation): StatisticsInfo? {
if (element !is JetDeclaration) return null
val descriptor = element.resolveToDescriptor()
return KotlinStatisticsInfo.forDescriptor(descriptor)
}
}
object KotlinStatisticsInfo {
fun forDescriptor(descriptor: DeclarationDescriptor): StatisticsInfo {
val container = descriptor.containingDeclaration
val containerFqName = when (container) {
is ClassDescriptor -> container.importableFqName?.asString()
is PackageFragmentDescriptor -> container.fqName.asString()
is ModuleDescriptor -> ""
else -> null
} ?: return StatisticsInfo.EMPTY
val signature = DescriptorRenderer.COMPACT.render(descriptor) //TODO: more compact presentation
return StatisticsInfo("", "$containerFqName###$signature")
}
}
+3 -2
View File
@@ -320,8 +320,6 @@
<completion.contributor language="jet"
implementationClass="org.jetbrains.kotlin.idea.completion.KDocCompletionContributor"/>
<statistician key="completion" implementationClass="org.jetbrains.kotlin.idea.completion.KotlinCompletionStatistician"/>
<completion.confidence language="jet" implementationClass="com.intellij.codeInsight.completion.UnfocusedNameIdentifier"/>
<completion.confidence language="jet"
implementationClass="org.jetbrains.kotlin.idea.completion.confidence.UnfocusedPossibleFunctionParameter"/>
@@ -330,6 +328,9 @@
<lookup.charFilter implementation="org.jetbrains.kotlin.idea.completion.KotlinCompletionCharFilter"/>
<lookup.actionProvider implementation="org.jetbrains.kotlin.idea.completion.KotlinExcludeFromCompletionLookupActionProvider"/>
<statistician key="completion" implementationClass="org.jetbrains.kotlin.idea.completion.KotlinCompletionStatistician"/>
<statistician key="proximity" implementationClass="org.jetbrains.kotlin.idea.completion.KotlinProximityStatistician"/>
<psi.referenceContributor language="jet" implementation="org.jetbrains.kotlin.idea.references.JetReferenceContributor"/>
<renamePsiElementProcessor id="KotlinClass"
@@ -27,6 +27,10 @@ import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.statistics.StatisticsManager
import com.intellij.psi.util.ProximityLocation
import com.intellij.psi.util.proximity.PsiProximityComparator
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
@@ -38,7 +42,6 @@ import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile
@@ -56,7 +59,8 @@ public class KotlinAddImportAction(
candidates: Collection<DeclarationDescriptor>
) : QuestionAction {
private val prioritizer = Prioritizer(element.getContainingJetFile())
private val file = element.getContainingJetFile()
private val prioritizer = Prioritizer(file)
private inner class Variant(
val fqName: FqName,
@@ -145,7 +149,9 @@ public class KotlinAddImportAction(
project.executeWriteCommand(QuickFixBundle.message("add.import")) {
if (!element.isValid()) return@executeWriteCommand
val file = element.getContainingFile() as JetFile
val location = ProximityLocation(file, ModuleUtilCore.findModuleForPsiElement(file))
StatisticsManager.getInstance().incUseCount(PsiProximityComparator.STATISTICS_KEY, selectedVariant.declarationToImport, location)
val descriptor = selectedVariant.descriptorToImport
// for class or package we use ShortenReferences because we not necessary insert an import but may want to insert partly qualified name
if (descriptor is ClassDescriptor || descriptor is PackageViewDescriptor) {
@@ -159,36 +165,28 @@ public class KotlinAddImportAction(
private class Prioritizer(private val file: JetFile) {
private val classifier = ImportableFqNameClassifier(file)
private val currentModule = ModuleUtilCore.findModuleForPsiElement(file)
private val proximityComparator = PsiProximityComparator(file)
private enum class Location {
currentModule,
project,
libraries,
unknown
}
private inner class Priority(private val fqName: FqName, private val descriptor: DeclarationDescriptor) : Comparable<Priority> {
private val isDeprecated = KotlinBuiltIns.isDeprecated(descriptor)
private val classification = classifier.classify(fqName, false)
private val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
private class Priority(private val priority1: ImportableFqNameClassifier.Classification, private val priority2: Location) : Comparable<Priority> {
override fun compareTo(other: Priority): Int {
val c1 = priority1.compareTo(other.priority1)
if (isDeprecated != other.isDeprecated) {
return if (isDeprecated) +1 else -1
}
val c1 = classification.compareTo(other.classification)
if (c1 != 0) return c1
return priority2.compareTo(other.priority2)
val c2 = proximityComparator.compare(declaration, other.declaration)
if (c2 != 0) return c2
return fqName.asString().compareTo(other.fqName.asString())
}
}
fun priority(fqName: FqName, descriptor: DeclarationDescriptor): Priority {
val classification = classifier.classify(fqName, false)
val location = location(descriptor)
return Priority(classification, location)
}
private fun location(descriptor: DeclarationDescriptor): Location {
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor) ?: return Location.unknown
return when {
ModuleUtilCore.findModuleForPsiElement(declaration) == currentModule -> Location.currentModule
ProjectRootsUtil.isInProjectSource(declaration) -> Location.project
else -> Location.libraries
}
}
fun priority(fqName: FqName, descriptor: DeclarationDescriptor) = Priority(fqName, descriptor)
}
}
@@ -3,7 +3,7 @@
import java.util.*
import dependency.*
import dependency.Date
import java.util.Date
fun foo(d: Date<caret>) {
}
@@ -1,8 +0,0 @@
// "Import" "true"
// ERROR: Please specify constructor invocation; classifier 'HTMLStyleElement' does not have a companion object
package test
fun foo() {
<caret>HTMLStyleElement
}
@@ -1,10 +0,0 @@
// "Import" "true"
// ERROR: Please specify constructor invocation; classifier 'HTMLStyleElement' does not have a companion object
package test
import kotlin.js.dom.html.HTMLStyleElement
fun foo() {
HTMLStyleElement
}
@@ -53,9 +53,9 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/autoImports"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
@TestMetadata("ambiguousNamePreferFromProject.before.Main.kt")
public void testAmbiguousNamePreferFromProject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/ambiguousNamePreferFromProject.before.Main.kt");
@TestMetadata("ambiguousNamePreferFromJdk.before.Main.kt")
public void testAmbiguousNamePreferFromJdk() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/ambiguousNamePreferFromJdk.before.Main.kt");
doTestWithExtraFile(fileName);
}
@@ -361,12 +361,6 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName);
}
@TestMetadata("libraryClassJsRuntime.kt")
public void testLibraryClassJsRuntime() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/libraryClassJsRuntime.kt");
doTest(fileName);
}
@TestMetadata("libraryPropertyJsRuntime.kt")
public void testLibraryPropertyJsRuntime() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/libraryPropertyJsRuntime.kt");