Initial implementation of KT-8209 Support static member completion without writing the class name
This commit is contained in:
@@ -29,10 +29,10 @@ import java.util.*
|
||||
class KtClassBody : KtElementImplStub<KotlinPlaceHolderStub<KtClassBody>>, KtDeclarationContainer {
|
||||
constructor(node: ASTNode) : super(node)
|
||||
|
||||
override fun getParent() = parentByStub
|
||||
|
||||
constructor(stub: KotlinPlaceHolderStub<KtClassBody>) : super(stub, CLASS_BODY)
|
||||
|
||||
override fun getParent() = parentByStub
|
||||
|
||||
override fun getDeclarations() = Arrays.asList(*getStubOrPsiChildren(DECLARATION_TYPES, KtDeclaration.ARRAY_FACTORY))
|
||||
|
||||
override fun <R, D> accept(visitor: KtVisitor<R, D>, data: D) = visitor.visitClassBody(this, data)
|
||||
|
||||
+8
-4
@@ -196,7 +196,7 @@ class BasicCompletionSession(
|
||||
|
||||
packageNames.forEach { collector.addElement(basicLookupElementFactory.createLookupElementForPackage(it)) }
|
||||
}
|
||||
|
||||
|
||||
flushToResultSet()
|
||||
|
||||
NamedArgumentCompletion.complete(collector, expectedInfos)
|
||||
@@ -227,6 +227,10 @@ class BasicCompletionSession(
|
||||
flushToResultSet()
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.completeStaticMembers && callTypeAndReceiver is CallTypeAndReceiver.DEFAULT && prefix.isNotEmpty()) {
|
||||
StaticMembersCompletion(collector, prefixMatcher, indicesHelper(false), lookupElementFactory).complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,13 +414,13 @@ class BasicCompletionSession(
|
||||
lookupElement
|
||||
}
|
||||
|
||||
parameterNameAndTypeCompletion.addFromParametersInFile(position, resolutionFacade, isVisibleFilter)
|
||||
parameterNameAndTypeCompletion.addFromParametersInFile(position, resolutionFacade, isVisibleFilterCheckAlways)
|
||||
flushToResultSet()
|
||||
|
||||
parameterNameAndTypeCompletion.addFromImportedClasses(position, bindingContext, isVisibleFilter)
|
||||
parameterNameAndTypeCompletion.addFromImportedClasses(position, bindingContext, isVisibleFilterCheckAlways)
|
||||
flushToResultSet()
|
||||
|
||||
parameterNameAndTypeCompletion.addFromAllClasses(parameters, indicesHelper)
|
||||
parameterNameAndTypeCompletion.addFromAllClasses(parameters, indicesHelper(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-12
@@ -57,19 +57,23 @@ class CompletionSessionConfiguration(
|
||||
val completeNonImportedDeclarations: Boolean,
|
||||
val completeNonAccessibleDeclarations: Boolean,
|
||||
val filterOutJavaGettersAndSetters: Boolean,
|
||||
val completeJavaClassesNotToBeUsed: Boolean
|
||||
val completeJavaClassesNotToBeUsed: Boolean,
|
||||
val completeStaticMembers: Boolean
|
||||
)
|
||||
|
||||
fun CompletionSessionConfiguration(parameters: CompletionParameters) = CompletionSessionConfiguration(
|
||||
completeNonImportedDeclarations = parameters.invocationCount >= 2,
|
||||
completeNonAccessibleDeclarations = parameters.invocationCount >= 2,
|
||||
filterOutJavaGettersAndSetters = parameters.invocationCount < 2,
|
||||
completeJavaClassesNotToBeUsed = parameters.invocationCount >= 2
|
||||
completeJavaClassesNotToBeUsed = parameters.invocationCount >= 2,
|
||||
completeStaticMembers = parameters.invocationCount >= 2
|
||||
)
|
||||
|
||||
abstract class CompletionSession(protected val configuration: CompletionSessionConfiguration,
|
||||
protected val parameters: CompletionParameters,
|
||||
resultSet: CompletionResultSet) {
|
||||
abstract class CompletionSession(
|
||||
protected val configuration: CompletionSessionConfiguration,
|
||||
protected val parameters: CompletionParameters,
|
||||
resultSet: CompletionResultSet
|
||||
) {
|
||||
protected val position = parameters.getPosition()
|
||||
private val file = position.getContainingFile() as KtFile
|
||||
protected val resolutionFacade = file.getResolutionFacade()
|
||||
@@ -124,7 +128,8 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
||||
private fun ((Name) -> Boolean).or(otherFilter: (Name) -> Boolean): (Name) -> Boolean
|
||||
= { this(it) || otherFilter(it) }
|
||||
|
||||
protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it) }
|
||||
protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = configuration.completeNonAccessibleDeclarations) }
|
||||
protected val isVisibleFilterCheckAlways: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = false) }
|
||||
|
||||
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, moduleDescriptor, isVisibleFilter)
|
||||
|
||||
@@ -151,8 +156,10 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
||||
override fun contains(file: VirtualFile) = super.contains(file) && file != parameters.getOriginalFile().getVirtualFile()
|
||||
}
|
||||
|
||||
protected val indicesHelper: KotlinIndicesHelper
|
||||
get() = KotlinIndicesHelper(resolutionFacade, searchScope, isVisibleFilter, true)
|
||||
protected fun indicesHelper(mayIncludeInaccessible: Boolean): KotlinIndicesHelper {
|
||||
val filter = if (mayIncludeInaccessible) isVisibleFilter else isVisibleFilterCheckAlways
|
||||
return KotlinIndicesHelper(resolutionFacade, searchScope, filter, visibilityFilterMayIncludeAccessible = mayIncludeInaccessible)
|
||||
}
|
||||
|
||||
protected val toFromOriginalFileMapper: ToFromOriginalFileMapper
|
||||
= ToFromOriginalFileMapper(parameters.originalFile as KtFile, position.containingFile as KtFile, parameters.offset)
|
||||
@@ -178,7 +185,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
||||
get() = 0
|
||||
}
|
||||
|
||||
private fun isVisibleDescriptor(descriptor: DeclarationDescriptor): Boolean {
|
||||
private fun isVisibleDescriptor(descriptor: DeclarationDescriptor, completeNonAccessible: Boolean): Boolean {
|
||||
if (!configuration.completeJavaClassesNotToBeUsed && descriptor is ClassDescriptor) {
|
||||
val classification = descriptor.importableFqName?.let { importableFqNameClassifier.classify(it, isPackage = false) }
|
||||
if (classification == ImportableFqNameClassifier.Classification.notToBeUsedInKotlin) return false
|
||||
@@ -189,7 +196,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
||||
if (descriptor is DeclarationDescriptorWithVisibility) {
|
||||
val visible = descriptor.isVisible(inDescriptor, bindingContext, nameExpression)
|
||||
if (visible) return true
|
||||
return configuration.completeNonAccessibleDeclarations && !descriptor.isFromLibrary()
|
||||
return completeNonAccessible && !descriptor.isFromLibrary()
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -309,6 +316,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
||||
var notImportedExtensions: Collection<CallableDescriptor> = emptyList()
|
||||
if (callTypeAndReceiver.shouldCompleteCallableExtensions()) {
|
||||
val nameFilter: (String) -> Boolean = { prefixMatcher.prefixMatches(it) }
|
||||
val indicesHelper = indicesHelper(true)
|
||||
val extensions = if (runtimeReceiver != null)
|
||||
indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, listOf(runtimeReceiver.type), nameFilter)
|
||||
else
|
||||
@@ -384,7 +392,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
||||
}
|
||||
|
||||
protected fun getTopLevelCallables(): Collection<DeclarationDescriptor> {
|
||||
return indicesHelper.getTopLevelCallables({ prefixMatcher.prefixMatches(it) })
|
||||
return indicesHelper(true).getTopLevelCallables({ prefixMatcher.prefixMatches(it) })
|
||||
.filterShadowedNonImported()
|
||||
}
|
||||
|
||||
@@ -399,7 +407,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
||||
}
|
||||
|
||||
protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) {
|
||||
AllClassesCompletion(parameters, indicesHelper, prefixMatcher, resolutionFacade, kindFilter)
|
||||
AllClassesCompletion(parameters, indicesHelper(true), prefixMatcher, resolutionFacade, kindFilter)
|
||||
.collect(
|
||||
{ descriptor -> collector.addElement(basicLookupElementFactory.createLookupElement(descriptor), notImported = true) },
|
||||
{ javaClass -> collector.addElement(basicLookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) }
|
||||
|
||||
@@ -27,8 +27,7 @@ import com.intellij.patterns.ElementPattern
|
||||
import com.intellij.patterns.StandardPatterns
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
||||
@@ -40,6 +39,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
@@ -69,7 +69,8 @@ enum class ItemPriority {
|
||||
DEFAULT,
|
||||
IMPLEMENT,
|
||||
OVERRIDE,
|
||||
NAMED_PARAMETER
|
||||
NAMED_PARAMETER,
|
||||
STATIC_MEMBER
|
||||
}
|
||||
|
||||
val ITEM_PRIORITY_KEY = Key<ItemPriority>("ITEM_PRIORITY_KEY")
|
||||
@@ -152,7 +153,7 @@ fun InsertionContext.isAfterDot(): Boolean {
|
||||
val chars = getDocument().getCharsSequence()
|
||||
while (offset > 0) {
|
||||
offset--
|
||||
val c = chars.charAt(offset)
|
||||
val c = chars[offset]
|
||||
if (!Character.isWhitespace(c)) {
|
||||
return c == '.'
|
||||
}
|
||||
@@ -335,7 +336,7 @@ private open class BaseTypeLookupElement(type: KotlinType, baseLookupElement: Lo
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), fullText)
|
||||
context.setTailOffset(context.getStartOffset() + fullText.length())
|
||||
context.setTailOffset(context.getStartOffset() + fullText.length)
|
||||
shortenReferences(context, context.getStartOffset(), context.getTailOffset())
|
||||
}
|
||||
}
|
||||
@@ -350,3 +351,49 @@ fun <T> ElementPattern<T>.andNot(rhs: ElementPattern<T>) = StandardPatterns.and(
|
||||
fun <T> ElementPattern<T>.or(rhs: ElementPattern<T>) = StandardPatterns.or(this, rhs)
|
||||
|
||||
fun singleCharPattern(char: Char) = StandardPatterns.character().equalTo(char)
|
||||
|
||||
fun LookupElement.decorateAsStaticMember(
|
||||
memberDescriptor: DeclarationDescriptor,
|
||||
classDescriptor: ClassDescriptor,
|
||||
classNameAsLookupString: Boolean
|
||||
): LookupElement {
|
||||
val qualifierPresentation = classDescriptor.getName().asString()
|
||||
val qualifierText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classDescriptor)
|
||||
|
||||
return object: LookupElementDecorator<LookupElement>(this) {
|
||||
override fun getAllLookupStrings(): Set<String> {
|
||||
return if (classNameAsLookupString) setOf(delegate.lookupString, qualifierPresentation) else super.getAllLookupStrings()
|
||||
}
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
delegate.renderElement(presentation)
|
||||
|
||||
presentation.itemText = qualifierPresentation + "." + presentation.itemText
|
||||
|
||||
val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.containingDeclaration) + ")"
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
presentation.appendTailText(tailText, true)
|
||||
}
|
||||
else {
|
||||
presentation.setTailText(tailText, true)
|
||||
}
|
||||
|
||||
if (presentation.typeText.isNullOrEmpty()) {
|
||||
presentation.typeText = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.defaultType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val prefix = qualifierText + "."
|
||||
|
||||
val offset = context.startOffset
|
||||
context.document.insertString(offset, prefix)
|
||||
context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset + prefix.length)
|
||||
|
||||
shortenReferences(context, offset, offset + prefix.length)
|
||||
PsiDocumentManager.getInstance(context.project).doPostponedOperationsAndUnblockDocument(context.document)
|
||||
|
||||
super.handleInsert(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -257,13 +257,15 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
}
|
||||
|
||||
val somethingAdded = session.complete()
|
||||
if (!somethingAdded && parameters.getInvocationCount() < 2) {
|
||||
if (!somethingAdded && parameters.invocationCount < 2) {
|
||||
// Rerun completion if nothing was found
|
||||
val newConfiguration = CompletionSessionConfiguration(
|
||||
completeNonImportedDeclarations = true,
|
||||
completeNonAccessibleDeclarations = false,
|
||||
filterOutJavaGettersAndSetters = false,
|
||||
completeJavaClassesNotToBeUsed = false)
|
||||
completeJavaClassesNotToBeUsed = false,
|
||||
completeStaticMembers = parameters.invocationCount > 0
|
||||
)
|
||||
BasicCompletionSession(newConfiguration, parameters, result).complete()
|
||||
}
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.PrefixMatcher
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
|
||||
class StaticMembersCompletion(
|
||||
private val collector: LookupElementsCollector,
|
||||
private val prefixMatcher: PrefixMatcher,
|
||||
private val indicesHelper: KotlinIndicesHelper,
|
||||
private val lookupElementFactory: LookupElementFactory
|
||||
) {
|
||||
//TODO: SAM-adapters
|
||||
//TODO: different priority&behaviour when static import from the same class exist
|
||||
//TODO: filter out those that are visible from bases/imports etc
|
||||
//TODO: filter out those that are accessible from SmartCompletion.additionalItems
|
||||
fun complete() {
|
||||
val descriptorKindFilter = DescriptorKindFilter.CALLABLES exclude DescriptorKindExclude.Extensions
|
||||
val nameFilter: (String) -> Boolean = { prefixMatcher.prefixMatches(it) }
|
||||
|
||||
for (member in indicesHelper.getJavaStaticMembers(descriptorKindFilter, nameFilter)) {
|
||||
collector.addElement(createLookupElement(member) ?: continue)
|
||||
}
|
||||
|
||||
for (member in indicesHelper.getObjectMembers(descriptorKindFilter, nameFilter)) {
|
||||
collector.addElement(createLookupElement(member) ?: continue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLookupElement(descriptor: CallableDescriptor): LookupElement? {
|
||||
var classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return null
|
||||
if (classDescriptor.isCompanionObject) {
|
||||
classDescriptor = classDescriptor.containingDeclaration as? ClassDescriptor ?: return null
|
||||
}
|
||||
return lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false)
|
||||
.decorateAsStaticMember(descriptor, classDescriptor, classNameAsLookupString = false)
|
||||
.assignPriority(ItemPriority.STATIC_MEMBER)
|
||||
.suppressAutoInsertion()
|
||||
}
|
||||
}
|
||||
+6
-48
@@ -16,24 +16,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
|
||||
import org.jetbrains.kotlin.idea.completion.LookupElementFactory
|
||||
import org.jetbrains.kotlin.idea.completion.decorateAsStaticMember
|
||||
import org.jetbrains.kotlin.idea.completion.fuzzyType
|
||||
import org.jetbrains.kotlin.idea.completion.shortenReferences
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.core.isVisible
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
@@ -107,45 +100,10 @@ class StaticMembers(
|
||||
}
|
||||
|
||||
private fun createLookupElements(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): Collection<LookupElement> {
|
||||
val lookupElements = lookupElementFactory.createLookupElementsInSmartCompletion(memberDescriptor, bindingContext, useReceiverTypes = false)
|
||||
val qualifierPresentation = classDescriptor.getName().asString()
|
||||
val qualifierText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classDescriptor)
|
||||
|
||||
return lookupElements.map {
|
||||
object: LookupElementDecorator<LookupElement>(it) {
|
||||
override fun getAllLookupStrings() = setOf(delegate.lookupString, qualifierPresentation)
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
getDelegate().renderElement(presentation)
|
||||
|
||||
presentation.setItemText(qualifierPresentation + "." + presentation.getItemText())
|
||||
|
||||
val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")"
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
presentation.appendTailText(tailText, true)
|
||||
}
|
||||
else {
|
||||
presentation.setTailText(tailText, true)
|
||||
}
|
||||
|
||||
if (presentation.getTypeText().isNullOrEmpty()) {
|
||||
presentation.setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.getDefaultType()))
|
||||
}
|
||||
return lookupElementFactory.createLookupElementsInSmartCompletion(memberDescriptor, bindingContext, useReceiverTypes = false)
|
||||
.map {
|
||||
it.decorateAsStaticMember(memberDescriptor, classDescriptor, classNameAsLookupString = true)
|
||||
.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER)
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val prefix = qualifierText + "."
|
||||
|
||||
val offset = context.startOffset
|
||||
context.document.insertString(offset, prefix)
|
||||
context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset + prefix.length)
|
||||
|
||||
shortenReferences(context, offset, offset + prefix.length)
|
||||
PsiDocumentManager.getInstance(context.project).doPostponedOperationsAndUnblockDocument(context.document)
|
||||
|
||||
super.handleInsert(context)
|
||||
}
|
||||
}.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fun foo() {
|
||||
SOU<caret>
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// EXIST_JAVA_ONLY: { allLookupStrings: "SOUTH, getSOUTH", itemText: "GridBagConstraints.SOUTH", tailText: " (java.awt)", typeText: "Int", attributes: "" }
|
||||
// EXIST_JAVA_ONLY: { allLookupStrings: "SOUTHWEST, getSOUTHWEST", itemText: "GridBagConstraints.SOUTHWEST", tailText: " (java.awt)", typeText: "Int", attributes: "" }
|
||||
// ABSENT: serialVersionUID
|
||||
@@ -0,0 +1,8 @@
|
||||
fun foo() {
|
||||
invoke<caret>
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// EXIST_JAVA_ONLY: { allLookupStrings: "invokeLater", itemText: "SwingUtilities.invokeLater", tailText: "(Runnable!) (javax.swing)", typeText: "Unit", attributes: "" }
|
||||
// EXIST_JAVA_ONLY: { allLookupStrings: "invokeAndWait", itemText: "SwingUtilities.invokeAndWait", tailText: "(Runnable!) (javax.swing)", typeText: "Unit", attributes: "" }
|
||||
// ABSENT: { itemText: "SwingUtilities.convertScreenLocationToParent" }
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package test
|
||||
|
||||
object KotlinObject {
|
||||
fun funFromObject() { }
|
||||
private fun privateFun(){}
|
||||
}
|
||||
|
||||
class KotlinClass {
|
||||
companion object SomeName {
|
||||
fun funFromCompanionObject() { }
|
||||
private fun privateFun(){}
|
||||
}
|
||||
}
|
||||
|
||||
class AnotherKotlinClass {
|
||||
private companion object {
|
||||
fun funFromPrivateCompanionObject() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package first
|
||||
|
||||
fun testFun() {
|
||||
funFromO<caret>
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// EXIST: { allLookupStrings: "funFromObject", itemText: "KotlinObject.funFromObject", tailText: "() (test)", typeText: "Unit", attributes: "" }
|
||||
// EXIST: { allLookupStrings: "funFromCompanionObject", itemText: "KotlinClass.funFromCompanionObject", tailText: "() (test)", typeText: "Unit", attributes: "" }
|
||||
// ABSENT: privateFun
|
||||
// ABSENT: funFromPrivateCompanionObject
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo() {
|
||||
invoke<caret>
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// ELEMENT_TEXT: "SwingUtilities.invokeLater"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
fun foo() {
|
||||
SwingUtilities.invokeLater(<caret>)
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// ELEMENT_TEXT: "SwingUtilities.invokeLater"
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo() {
|
||||
fromLit<caret>
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// ELEMENT_TEXT: "Regex.fromLiteral"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import kotlin.text.Regex
|
||||
|
||||
fun foo() {
|
||||
Regex.fromLiteral(<caret>)
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// ELEMENT_TEXT: "Regex.fromLiteral"
|
||||
+21
@@ -2095,6 +2095,27 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/staticMembers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StaticMembers extends AbstractJSBasicCompletionTest {
|
||||
public void testAllFilesPresentInStaticMembers() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/staticMembers"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("JavaStaticFields.kt")
|
||||
public void testJavaStaticFields() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/staticMembers/JavaStaticFields.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("JavaStaticMethods.kt")
|
||||
public void testJavaStaticMethods() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/staticMembers/JavaStaticMethods.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/substitutedSignature")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+21
@@ -2095,6 +2095,27 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/staticMembers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StaticMembers extends AbstractJvmBasicCompletionTest {
|
||||
public void testAllFilesPresentInStaticMembers() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/staticMembers"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("JavaStaticFields.kt")
|
||||
public void testJavaStaticFields() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/staticMembers/JavaStaticFields.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("JavaStaticMethods.kt")
|
||||
public void testJavaStaticMethods() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/staticMembers/JavaStaticMethods.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/substitutedSignature")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+6
@@ -281,6 +281,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ObjectMembers")
|
||||
public void testObjectMembers() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/ObjectMembers/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ParameterNameAndTypeNestedClasses")
|
||||
public void testParameterNameAndTypeNestedClasses() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeNestedClasses/");
|
||||
|
||||
+21
@@ -530,6 +530,27 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/handlers/basic/staticMembers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StaticMembers extends AbstractBasicCompletionHandlerTest {
|
||||
public void testAllFilesPresentInStaticMembers() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/basic/staticMembers"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("classObjectMethod.kt")
|
||||
public void testClassObjectMethod() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/staticMembers/classObjectMethod.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("JavaStaticMethod.kt")
|
||||
public void testJavaStaticMethod() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/staticMembers/JavaStaticMethod.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -17,10 +17,16 @@
|
||||
package org.jetbrains.kotlin.idea.core
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.PsiShortNamesCache
|
||||
import com.intellij.psi.stubs.StringStubIndexExtension
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.indexing.IdFilter
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaFieldDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
@@ -29,14 +35,16 @@ import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||
import org.jetbrains.kotlin.idea.util.receiverTypes
|
||||
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.isAnnotatedAsHidden
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
||||
@@ -46,7 +54,8 @@ public class KotlinIndicesHelper(
|
||||
private val resolutionFacade: ResolutionFacade,
|
||||
private val scope: GlobalSearchScope,
|
||||
visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
||||
applyExcludeSettings: Boolean
|
||||
applyExcludeSettings: Boolean = true,
|
||||
private val visibilityFilterMayIncludeAccessible: Boolean = false
|
||||
) {
|
||||
|
||||
private val moduleDescriptor = resolutionFacade.moduleDescriptor
|
||||
@@ -60,20 +69,15 @@ public class KotlinIndicesHelper(
|
||||
}
|
||||
|
||||
public fun getTopLevelCallablesByName(name: String): Collection<CallableDescriptor> {
|
||||
val declarations = HashSet<KtNamedDeclaration>()
|
||||
val declarations = HashSet<KtCallableDeclaration>()
|
||||
declarations.addTopLevelNonExtensionCallablesByName(KotlinFunctionShortNameIndex.getInstance(), name)
|
||||
declarations.addTopLevelNonExtensionCallablesByName(KotlinPropertyShortNameIndex.getInstance(), name)
|
||||
return declarations.flatMap {
|
||||
if (it.getContainingJetFile().isCompiled()) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
||||
resolutionFacade.resolveImportReference(moduleDescriptor, it.getFqName()!!).filterIsInstance<CallableDescriptor>()
|
||||
}
|
||||
else {
|
||||
(resolutionFacade.resolveToDescriptor(it) as? CallableDescriptor).singletonOrEmptyList()
|
||||
}
|
||||
}.filter { it.getExtensionReceiverParameter() == null && descriptorFilter(it) }
|
||||
return declarations
|
||||
.flatMap { it.resolveToDescriptorsWithHack() }
|
||||
.filter { it.extensionReceiverParameter == null && descriptorFilter(it) }
|
||||
}
|
||||
|
||||
private fun MutableSet<KtNamedDeclaration>.addTopLevelNonExtensionCallablesByName(
|
||||
private fun MutableSet<KtCallableDeclaration>.addTopLevelNonExtensionCallablesByName(
|
||||
index: StringStubIndexExtension<out KtCallableDeclaration>,
|
||||
name: String
|
||||
) {
|
||||
@@ -84,7 +88,10 @@ public class KotlinIndicesHelper(
|
||||
return (KotlinTopLevelFunctionFqnNameIndex.getInstance().getAllKeys(project).asSequence() +
|
||||
KotlinTopLevelPropertyFqnNameIndex.getInstance().getAllKeys(project).asSequence())
|
||||
.map { FqName(it) }
|
||||
.filter { nameFilter(it.shortName().asString()) }
|
||||
.filter {
|
||||
ProgressManager.checkCanceled()
|
||||
nameFilter(it.shortName().asString())
|
||||
}
|
||||
.toSet()
|
||||
.flatMap { findTopLevelCallables(it).filter(descriptorFilter) }
|
||||
}
|
||||
@@ -115,8 +122,9 @@ public class KotlinIndicesHelper(
|
||||
val declarations = index.getAllKeys(project)
|
||||
.asSequence()
|
||||
.filter {
|
||||
ProgressManager.checkCanceled()
|
||||
KotlinTopLevelExtensionsByReceiverTypeIndex.receiverTypeNameFromKey(it) in receiverTypeNames
|
||||
&& nameFilter(KotlinTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
|
||||
&& nameFilter(KotlinTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
|
||||
}
|
||||
.flatMap { index.get(it, project, scope).asSequence() }
|
||||
|
||||
@@ -140,24 +148,12 @@ public class KotlinIndicesHelper(
|
||||
val result = LinkedHashSet<CallableDescriptor>()
|
||||
|
||||
fun processDescriptor(descriptor: CallableDescriptor) {
|
||||
if (descriptorFilter(descriptor)) {
|
||||
if (descriptor.extensionReceiverParameter != null && descriptorFilter(descriptor)) {
|
||||
result.addAll(descriptor.substituteExtensionIfCallable(receiverTypes, callType))
|
||||
}
|
||||
}
|
||||
|
||||
for (declaration in declarations) {
|
||||
if (declaration.getContainingJetFile().isCompiled()) {
|
||||
//TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
||||
for (descriptor in resolutionFacade.resolveImportReference(moduleDescriptor, declaration.getFqName()!!)) {
|
||||
if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) {
|
||||
processDescriptor(descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
processDescriptor(resolutionFacade.resolveToDescriptor(declaration) as CallableDescriptor)
|
||||
}
|
||||
}
|
||||
declarations.forEach { it.resolveToDescriptorsWithHack().forEach(::processDescriptor) }
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -172,7 +168,10 @@ public class KotlinIndicesHelper(
|
||||
public fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
|
||||
return KotlinFullClassNameIndex.getInstance().getAllKeys(project).asSequence()
|
||||
.map { FqName(it) }
|
||||
.filter { nameFilter(it.shortName().asString()) }
|
||||
.filter {
|
||||
ProgressManager.checkCanceled()
|
||||
nameFilter(it.shortName().asString())
|
||||
}
|
||||
.toList()
|
||||
.flatMap { getClassDescriptorsByFQName(it, kindFilter) }
|
||||
}
|
||||
@@ -190,6 +189,73 @@ public class KotlinIndicesHelper(
|
||||
.filter(descriptorFilter)
|
||||
}
|
||||
|
||||
public fun getObjectMembers(descriptorKindFilter: DescriptorKindFilter, nameFilter: (String) -> Boolean): Collection<CallableDescriptor> {
|
||||
val result = LinkedHashSet<CallableDescriptor>()
|
||||
|
||||
fun addFromIndex(index: StringStubIndexExtension<out KtCallableDeclaration>) {
|
||||
for (name in index.getAllKeys(project)) {
|
||||
ProgressManager.checkCanceled()
|
||||
if (!nameFilter(name)) continue
|
||||
|
||||
for (declaration in index.get(name, project, scope)) {
|
||||
if (declaration.parent.parent !is KtObjectDeclaration) continue
|
||||
if (!visibilityFilterMayIncludeAccessible && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue
|
||||
declaration.resolveToDescriptorsWithHack().filterTo(result) { descriptorKindFilter.accepts(it) && descriptorFilter(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptorKindFilter.kindMask.and(DescriptorKindFilter.FUNCTIONS_MASK) != 0) {
|
||||
addFromIndex(KotlinFunctionShortNameIndex.getInstance())
|
||||
}
|
||||
if (descriptorKindFilter.kindMask.and(DescriptorKindFilter.VARIABLES_MASK) != 0) {
|
||||
addFromIndex(KotlinPropertyShortNameIndex.getInstance())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public fun getJavaStaticMembers(descriptorKindFilter: DescriptorKindFilter, nameFilter: (String) -> Boolean): Collection<CallableDescriptor> {
|
||||
val result = LinkedHashSet<CallableDescriptor>()
|
||||
|
||||
val idFilter = IdFilter.getProjectIdFilter(resolutionFacade.project, false)
|
||||
val shortNamesCache = PsiShortNamesCache.getInstance(project)
|
||||
|
||||
val methodNamesProcessor = Processor<String> { name ->
|
||||
ProgressManager.checkCanceled()
|
||||
if (!nameFilter(name)) return@Processor true
|
||||
|
||||
for (method in shortNamesCache.getMethodsByName(name, scope)) {
|
||||
if (!method.hasModifierProperty(PsiModifier.STATIC)) continue
|
||||
if (!visibilityFilterMayIncludeAccessible && method.hasModifierProperty(PsiModifier.PRIVATE)) continue
|
||||
val descriptor = method.getJavaMethodDescriptor() ?: continue
|
||||
if (descriptorKindFilter.accepts(descriptor) && descriptorFilter(descriptor)) {
|
||||
result.add(descriptor)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
shortNamesCache.processAllMethodNames(methodNamesProcessor, scope, idFilter)
|
||||
|
||||
val fieldNamesProcessor = Processor<String> { name ->
|
||||
ProgressManager.checkCanceled()
|
||||
if (!nameFilter(name)) return@Processor true
|
||||
|
||||
for (field in shortNamesCache.getFieldsByName(name, scope)) {
|
||||
if (!field.hasModifierProperty(PsiModifier.STATIC)) continue
|
||||
if (!visibilityFilterMayIncludeAccessible && field.hasModifierProperty(PsiModifier.PRIVATE)) continue
|
||||
val descriptor = field.getJavaFieldDescriptor() ?: continue
|
||||
if (descriptorKindFilter.accepts(descriptor) && descriptorFilter(descriptor)) {
|
||||
result.add(descriptor)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
shortNamesCache.processAllFieldNames(fieldNamesProcessor, scope, idFilter)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun findTopLevelCallables(fqName: FqName): Collection<CallableDescriptor> {
|
||||
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName)
|
||||
.filterIsInstance<CallableDescriptor>()
|
||||
@@ -202,5 +268,14 @@ public class KotlinIndicesHelper(
|
||||
return CodeInsightSettings.getInstance().EXCLUDED_PACKAGES
|
||||
.any { excluded -> fqName == excluded || (fqName.startsWith(excluded) && fqName[excluded.length()] == '.') }
|
||||
}
|
||||
|
||||
private fun KtCallableDeclaration.resolveToDescriptorsWithHack(): Collection<CallableDescriptor> {
|
||||
if (getContainingJetFile().isCompiled()) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
||||
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance<CallableDescriptor>()
|
||||
}
|
||||
else {
|
||||
return (resolutionFacade.resolveToDescriptor(this) as? CallableDescriptor).singletonOrEmptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ internal abstract class AutoImportFixBase<T: KtExpression>(expression: T) :
|
||||
|
||||
val result = ArrayList<DeclarationDescriptor>()
|
||||
|
||||
val indicesHelper = KotlinIndicesHelper(element.getResolutionFacade(), searchScope, ::isVisible, true)
|
||||
val indicesHelper = KotlinIndicesHelper(element.getResolutionFacade(), searchScope, ::isVisible)
|
||||
|
||||
val expression = element
|
||||
if (expression is KtSimpleNameExpression) {
|
||||
|
||||
Reference in New Issue
Block a user