Non-imported extension functions and properties in smart completion + adding import on completing of non-imported property
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
<patterns>
|
||||
<pattern testClass="org.jetbrains.jet.completion.JvmSmartCompletionTestGenerated" />
|
||||
<pattern testClass="org.jetbrains.jet.completion.handlers.SmartCompletionHandlerTestGenerated" />
|
||||
<pattern testClass="org.jetbrains.jet.completion.handlers.SmartCompletionMultifileHandlerTest" />
|
||||
</patterns>
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
|
||||
@@ -31,7 +31,9 @@ import org.jetbrains.jet.plugin.completion.smart.SmartCompletion
|
||||
import org.jetbrains.jet.plugin.references.JetSimpleNameReference
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
import org.jetbrains.jet.plugin.caches.KotlinIndicesHelper
|
||||
import org.jetbrains.jet.plugin.search.searchScopeForSourceElementDependencies
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.module.Module
|
||||
|
||||
class CompletionSessionConfiguration(
|
||||
val completeNonImportedDeclarations: Boolean,
|
||||
@@ -44,7 +46,6 @@ fun CompletionSessionConfiguration(parameters: CompletionParameters) = Completio
|
||||
abstract class CompletionSessionBase(protected val configuration: CompletionSessionConfiguration,
|
||||
protected val parameters: CompletionParameters,
|
||||
resultSet: CompletionResultSet) {
|
||||
|
||||
protected val position: PsiElement = parameters.getPosition()
|
||||
protected val jetReference: JetSimpleNameReference? = position.getParent()?.getReferences()?.filterIsInstance(javaClass<JetSimpleNameReference>())?.firstOrNull()
|
||||
protected val resolveSession: ResolveSessionForBodies = (position.getContainingFile() as JetFile).getLazyResolveSession()
|
||||
@@ -59,6 +60,14 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
|
||||
protected val prefixMatcher: PrefixMatcher = this.resultSet.getPrefixMatcher()
|
||||
|
||||
protected val collector: LookupElementsCollector = LookupElementsCollector(prefixMatcher, resolveSession, { isVisibleDescriptor(it) })
|
||||
protected var anythingAdded: Boolean = false
|
||||
|
||||
protected val project: Project = position.getProject()
|
||||
protected val indicesHelper: KotlinIndicesHelper = KotlinIndicesHelper(project)
|
||||
protected val module: Module? = ModuleUtilCore.findModuleForPsiElement(parameters.getOriginalFile())
|
||||
protected val searchScope: GlobalSearchScope = if (module != null) GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) else GlobalSearchScope.EMPTY_SCOPE
|
||||
|
||||
protected fun isVisibleDescriptor(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (configuration.completeNonAccessibleDeclarations) return true
|
||||
|
||||
@@ -68,6 +77,46 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun flushToResultSet() {
|
||||
if (!collector.isEmpty) {
|
||||
anythingAdded = true
|
||||
}
|
||||
collector.flushToResultSet(resultSet)
|
||||
}
|
||||
|
||||
public fun complete(): Boolean {
|
||||
doComplete()
|
||||
flushToResultSet()
|
||||
return anythingAdded
|
||||
}
|
||||
|
||||
protected abstract fun doComplete()
|
||||
|
||||
protected fun shouldRunTopLevelCompletion(): Boolean {
|
||||
if (!configuration.completeNonImportedDeclarations) return false
|
||||
|
||||
if (position.getNode()!!.getElementType() == JetTokens.IDENTIFIER) {
|
||||
val parent = position.getParent()
|
||||
if (parent is JetSimpleNameExpression && !JetPsiUtil.isSelectorInQualified(parent)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun shouldRunExtensionsCompletion(): Boolean {
|
||||
return configuration.completeNonImportedDeclarations || prefixMatcher.getPrefix().length >= 3
|
||||
}
|
||||
|
||||
protected fun getKotlinTopLevelDeclarations(): Collection<DeclarationDescriptor> {
|
||||
val filter = { (name: String) -> prefixMatcher.prefixMatches(name) }
|
||||
return indicesHelper.getTopLevelCallables(filter, jetReference!!.expression, resolveSession, searchScope) +
|
||||
indicesHelper.getTopLevelObjects(filter, resolveSession, searchScope)
|
||||
}
|
||||
|
||||
protected fun getKotlinExtensions(): Collection<CallableDescriptor> {
|
||||
return indicesHelper.getCallableExtensions({ prefixMatcher.prefixMatches(it) }, jetReference!!.expression, resolveSession, searchScope)
|
||||
}
|
||||
}
|
||||
|
||||
class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
@@ -75,14 +124,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
resultSet: CompletionResultSet)
|
||||
: CompletionSessionBase(configuration, parameters, resultSet) {
|
||||
|
||||
private val collector = LookupElementsCollector(prefixMatcher, resolveSession, { isVisibleDescriptor(it) })
|
||||
private var anythingAdded = false
|
||||
|
||||
private val project = position.getProject()
|
||||
private val indicesHelper = KotlinIndicesHelper(project)
|
||||
private val searchScope = searchScopeForSourceElementDependencies(parameters.getOriginalFile()) ?: GlobalSearchScope.EMPTY_SCOPE
|
||||
|
||||
public fun complete(): Boolean {
|
||||
override fun doComplete() {
|
||||
assert(parameters.getCompletionType() == CompletionType.BASIC)
|
||||
|
||||
if (!NamedParametersCompletion.isOnlyNamedParameterExpected(position)) {
|
||||
@@ -111,16 +153,6 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
}
|
||||
|
||||
NamedParametersCompletion.complete(position, collector)
|
||||
|
||||
flushToResultSet()
|
||||
return anythingAdded
|
||||
}
|
||||
|
||||
private fun flushToResultSet() {
|
||||
if (!collector.isEmpty) {
|
||||
anythingAdded = true
|
||||
}
|
||||
collector.flushToResultSet(resultSet)
|
||||
}
|
||||
|
||||
private fun addNonImported() {
|
||||
@@ -137,11 +169,11 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
|
||||
if (shouldRunTopLevelCompletion()) {
|
||||
TypesCompletion(parameters, resolveSession, prefixMatcher).addAllTypes(collector)
|
||||
addKotlinTopLevelDeclarations()
|
||||
collector.addDescriptorElements(getKotlinTopLevelDeclarations())
|
||||
}
|
||||
|
||||
if (shouldRunExtensionsCompletion()) {
|
||||
addKotlinExtensions()
|
||||
collector.addDescriptorElements(getKotlinExtensions())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,16 +194,6 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
private fun addKotlinTopLevelDeclarations() {
|
||||
val filter = { (name: String) -> prefixMatcher.prefixMatches(name) }
|
||||
collector.addDescriptorElements(indicesHelper.getTopLevelCallables(filter, jetReference!!.expression, resolveSession, searchScope))
|
||||
collector.addDescriptorElements(indicesHelper.getTopLevelObjects(filter, resolveSession, searchScope))
|
||||
}
|
||||
|
||||
private fun addKotlinExtensions() {
|
||||
collector.addDescriptorElements(indicesHelper.getCallableExtensions({ prefixMatcher.prefixMatches(it) }, jetReference!!.expression, resolveSession, searchScope))
|
||||
}
|
||||
|
||||
private fun shouldRunOnlyTypeCompletion(): Boolean {
|
||||
// Check that completion in the type annotation context and if there's a qualified
|
||||
// expression we are at first of it
|
||||
@@ -184,21 +206,6 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
return false
|
||||
}
|
||||
|
||||
private fun shouldRunTopLevelCompletion(): Boolean {
|
||||
if (!configuration.completeNonImportedDeclarations) return false
|
||||
|
||||
if (position.getNode()!!.getElementType() == JetTokens.IDENTIFIER) {
|
||||
val parent = position.getParent()
|
||||
if (parent is JetSimpleNameExpression && !JetPsiUtil.isSelectorInQualified(parent)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun shouldRunExtensionsCompletion(): Boolean {
|
||||
return configuration.completeNonImportedDeclarations || prefixMatcher.getPrefix().length >= 3
|
||||
}
|
||||
|
||||
private fun addReferenceVariants(filterCondition: (DeclarationDescriptor) -> Boolean = { true }) {
|
||||
val descriptors = TipsManager.getReferenceVariants(jetReference!!.expression, bindingContext!!)
|
||||
collector.addDescriptorElements(descriptors.filter { filterCondition(it) })
|
||||
@@ -207,11 +214,43 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
|
||||
class SmartCompletionSession(configuration: CompletionSessionConfiguration, parameters: CompletionParameters, resultSet: CompletionResultSet)
|
||||
: CompletionSessionBase(configuration, parameters, resultSet) {
|
||||
public fun complete() {
|
||||
|
||||
override fun doComplete() {
|
||||
if (jetReference != null) {
|
||||
val descriptors = TipsManager.getReferenceVariants(jetReference.expression, bindingContext!!)
|
||||
val completion = SmartCompletion(jetReference.expression, resolveSession, { isVisibleDescriptor(it) }, parameters.getOriginalFile() as JetFile)
|
||||
completion.buildLookupElements(descriptors)?.forEach { resultSet.addElement(it) }
|
||||
val result = completion.execute()
|
||||
if (result != null) {
|
||||
collector.addElements(result.additionalItems)
|
||||
|
||||
val filter = result.declarationFilter
|
||||
if (filter != null) {
|
||||
TipsManager.getReferenceVariants(jetReference.expression, bindingContext!!)
|
||||
.forEach { collector.addElements(filter(it)) }
|
||||
|
||||
flushToResultSet()
|
||||
|
||||
processNonImported { collector.addElements(filter(it)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processNonImported(processor: (DeclarationDescriptor) -> Unit) {
|
||||
val prefix = prefixMatcher.getPrefix()
|
||||
|
||||
// Try to avoid computing not-imported descriptors for empty prefix
|
||||
if (prefix.isEmpty()) {
|
||||
if (!configuration.completeNonImportedDeclarations) return
|
||||
|
||||
if (PsiTreeUtil.getParentOfType(jetReference!!.expression, javaClass<JetDotQualifiedExpression>()) == null) return
|
||||
}
|
||||
|
||||
if (shouldRunTopLevelCompletion()) {
|
||||
getKotlinTopLevelDeclarations().forEach(processor)
|
||||
}
|
||||
|
||||
if (shouldRunExtensionsCompletion()) {
|
||||
getKotlinExtensions().forEach(processor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import org.jetbrains.jet.plugin.completion.handlers.JetClassInsertHandler
|
||||
import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.completion.handlers.BaseDeclarationInsertHandler
|
||||
import org.jetbrains.jet.plugin.completion.handlers.JetPropertyInsertHandler
|
||||
|
||||
public object DescriptorLookupConverter {
|
||||
public fun createLookupElement(analyzer: KotlinCodeAnalyzer, descriptor: DeclarationDescriptor, declaration: PsiElement?): LookupElement {
|
||||
@@ -100,6 +101,8 @@ public object DescriptorLookupConverter {
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyDescriptor -> JetPropertyInsertHandler
|
||||
|
||||
is ClassDescriptor -> JetClassInsertHandler
|
||||
|
||||
else -> BaseDeclarationInsertHandler()
|
||||
|
||||
@@ -83,4 +83,8 @@ class LookupElementsCollector(private val prefixMatcher: PrefixMatcher,
|
||||
elements.add(element)
|
||||
}
|
||||
}
|
||||
|
||||
public fun addElements(elements: Iterable<LookupElement>) {
|
||||
elements.forEach { addElement(it) }
|
||||
}
|
||||
}
|
||||
|
||||
+47
-37
@@ -28,7 +28,6 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.jet.plugin.formatter.JetCodeStyleSettings
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.jet.lang.psi.JetQualifiedExpression
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper
|
||||
@@ -36,6 +35,46 @@ import com.intellij.openapi.editor.Document
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.jet.plugin.completion.DeclarationLookupObject
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
|
||||
public abstract class JetCallableInsertHandler : BaseDeclarationInsertHandler() {
|
||||
public override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
super.handleInsert(context, item)
|
||||
|
||||
addImport(context, item)
|
||||
}
|
||||
|
||||
private fun addImport(context : InsertionContext, item : LookupElement) {
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments()
|
||||
|
||||
ApplicationManager.getApplication()?.runReadAction { () : Unit ->
|
||||
val startOffset = context.getStartOffset()
|
||||
val element = context.getFile().findElementAt(startOffset)
|
||||
|
||||
if (element == null) return@runReadAction
|
||||
|
||||
val file = context.getFile()
|
||||
val o = item.getObject()
|
||||
if (file is JetFile && o is DeclarationLookupObject) {
|
||||
val descriptor = o.descriptor as? CallableDescriptor
|
||||
if (descriptor != null) {
|
||||
if (PsiTreeUtil.getParentOfType(element, javaClass<JetQualifiedExpression>()) != null &&
|
||||
descriptor.getReceiverParameter() == null) {
|
||||
return@runReadAction
|
||||
}
|
||||
|
||||
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||
ApplicationManager.getApplication()?.runWriteAction {
|
||||
ImportInsertHelper.addImportDirectiveIfNeeded(DescriptorUtils.getFqNameSafe(descriptor), file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object JetPropertyInsertHandler : JetCallableInsertHandler()
|
||||
|
||||
public enum class CaretPosition {
|
||||
IN_BRACKETS
|
||||
@@ -44,7 +83,7 @@ public enum class CaretPosition {
|
||||
|
||||
public data class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean)
|
||||
|
||||
public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lambdaInfo: GenerateLambdaInfo?) : BaseDeclarationInsertHandler() {
|
||||
public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lambdaInfo: GenerateLambdaInfo?) : JetCallableInsertHandler() {
|
||||
{
|
||||
if (caretPosition == CaretPosition.AFTER_BRACKETS && lambdaInfo != null) {
|
||||
throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with lambdaInfo != null combination is not supported")
|
||||
@@ -54,7 +93,10 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lam
|
||||
public override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
super.handleInsert(context, item)
|
||||
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments()
|
||||
val psiDocumentManager = PsiDocumentManager.getInstance(context.getProject())
|
||||
psiDocumentManager.commitAllDocuments()
|
||||
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.getDocument())
|
||||
|
||||
if (context.getCompletionChar() == '(') {
|
||||
context.setAddCompletionChar(false)
|
||||
}
|
||||
@@ -62,13 +104,9 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lam
|
||||
val startOffset = context.getStartOffset()
|
||||
val element = context.getFile().findElementAt(startOffset)
|
||||
|
||||
if (element == null) return
|
||||
|
||||
if (shouldAddBrackets(element)) {
|
||||
if (element != null && shouldAddBrackets(element)) {
|
||||
addBrackets(context, element)
|
||||
}
|
||||
|
||||
addImports(context, item)
|
||||
}
|
||||
|
||||
private fun addBrackets(context : InsertionContext, offsetElement : PsiElement) {
|
||||
@@ -145,36 +183,8 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lam
|
||||
return -1
|
||||
}
|
||||
|
||||
private open fun isInsertSpacesInOneLineFunctionEnabled(project : Project)
|
||||
private fun isInsertSpacesInOneLineFunctionEnabled(project : Project)
|
||||
= CodeStyleSettingsManager.getSettings(project)
|
||||
.getCustomSettings(javaClass<JetCodeStyleSettings>())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
|
||||
|
||||
private open fun addImports(context : InsertionContext, item : LookupElement) {
|
||||
ApplicationManager.getApplication()?.runReadAction { () : Unit ->
|
||||
val startOffset = context.getStartOffset()
|
||||
val element = context.getFile().findElementAt(startOffset)
|
||||
|
||||
if (element == null) return@runReadAction
|
||||
|
||||
val file = context.getFile()
|
||||
val o = item.getObject()
|
||||
if (file is JetFile && o is DeclarationLookupObject) {
|
||||
val descriptor = o.descriptor as? SimpleFunctionDescriptor
|
||||
if (descriptor != null) {
|
||||
if (PsiTreeUtil.getParentOfType(element, javaClass<JetQualifiedExpression>()) != null &&
|
||||
descriptor.getReceiverParameter() == null) {
|
||||
return@runReadAction
|
||||
}
|
||||
|
||||
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||
ApplicationManager.getApplication()?.runWriteAction {
|
||||
val fqn = DescriptorUtils.getFqNameSafe(descriptor)
|
||||
ImportInsertHelper.addImportDirectiveIfNeeded(fqn, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,15 +37,17 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
val resolveSession: ResolveSessionForBodies,
|
||||
val visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
||||
val originalFile: JetFile) {
|
||||
|
||||
private val bindingContext = resolveSession.resolveToElement(expression)
|
||||
private val moduleDescriptor = resolveSession.getModuleDescriptor()
|
||||
private val project = expression.getProject()
|
||||
|
||||
public fun buildLookupElements(referenceVariants: Iterable<DeclarationDescriptor>): Collection<LookupElement>? {
|
||||
return buildLookupElementsInternal(referenceVariants)?.map {
|
||||
if (it.getUserData(KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY) == null) {
|
||||
object : LookupElementDecorator<LookupElement>(it) {
|
||||
public data class Result(val declarationFilter: ((DeclarationDescriptor) -> Collection<LookupElement>)?,
|
||||
val additionalItems: Collection<LookupElement>)
|
||||
|
||||
public fun execute(): Result? {
|
||||
fun postProcess(item: LookupElement): LookupElement {
|
||||
return if (item.getUserData(KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY) == null) {
|
||||
object : LookupElementDecorator<LookupElement>(item) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
if (context.getCompletionChar() == Lookup.REPLACE_SELECT_CHAR) {
|
||||
val offset = context.getOffsetMap().getOffset(SmartCompletion.OLD_ARGUMENTS_REPLACEMENT_OFFSET)
|
||||
@@ -59,14 +61,23 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
}
|
||||
}
|
||||
else {
|
||||
it
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
val result = executeInternal() ?: return null
|
||||
// TODO: code could be more simple, see KT-5726
|
||||
val additionalItems = result.additionalItems.map(::postProcess)
|
||||
val filter = result.declarationFilter
|
||||
return if (filter != null)
|
||||
Result({ filter(it).map(::postProcess) }, additionalItems)
|
||||
else
|
||||
Result(null, additionalItems)
|
||||
}
|
||||
|
||||
private fun buildLookupElementsInternal(referenceVariants: Iterable<DeclarationDescriptor>): Collection<LookupElement>? {
|
||||
val elements = buildForAsTypePosition()
|
||||
if (elements != null) return elements
|
||||
private fun executeInternal(): Result? {
|
||||
val asTypePositionResult = buildForAsTypePosition()
|
||||
if (asTypePositionResult != null) return asTypePositionResult
|
||||
|
||||
val parent = expression.getParent()
|
||||
val expressionWithType: JetExpression
|
||||
@@ -90,46 +101,47 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
else
|
||||
filteredExpectedInfos
|
||||
|
||||
val result = ArrayList<LookupElement>()
|
||||
|
||||
val typesWithAutoCasts: (DeclarationDescriptor) -> Iterable<JetType> = TypesWithAutoCasts(bindingContext).calculate(expressionWithType, receiver)
|
||||
|
||||
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
||||
|
||||
val functionExpectedInfos = expectedInfos.filter { KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(it.`type`) }
|
||||
|
||||
for (descriptor in referenceVariants) {
|
||||
if (itemsToSkip.contains(descriptor)) continue
|
||||
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
||||
val result = ArrayList<LookupElement>()
|
||||
if (!itemsToSkip.contains(descriptor)) {
|
||||
val types = typesWithAutoCasts(descriptor)
|
||||
val nonNullTypes = types.map { it.makeNotNullable() }
|
||||
val classifier = { (expectedInfo: ExpectedInfo) ->
|
||||
when {
|
||||
types.any { it.isSubtypeOf(expectedInfo.`type`) } -> ExpectedInfoClassification.MATCHES
|
||||
nonNullTypes.any { it.isSubtypeOf(expectedInfo.`type`) } -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
|
||||
else -> ExpectedInfoClassification.NOT_MATCHES
|
||||
}
|
||||
}
|
||||
result.addLookupElements(expectedInfos, classifier, { createLookupElement(descriptor, resolveSession) })
|
||||
|
||||
val types = typesWithAutoCasts(descriptor)
|
||||
val nonNullTypes = types.map { it.makeNotNullable() }
|
||||
val classifier = { (expectedInfo: ExpectedInfo) ->
|
||||
when {
|
||||
types.any { it.isSubtypeOf(expectedInfo.`type`) } -> ExpectedInfoClassification.MATCHES
|
||||
nonNullTypes.any { it.isSubtypeOf(expectedInfo.`type`) } -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
|
||||
else -> ExpectedInfoClassification.NOT_MATCHES
|
||||
if (receiver == null) {
|
||||
toFunctionReferenceLookupElement(descriptor, functionExpectedInfos)?.let { result.add(it) }
|
||||
}
|
||||
}
|
||||
result.addLookupElements(expectedInfos, classifier, { createLookupElement(descriptor, resolveSession) })
|
||||
|
||||
if (receiver == null) {
|
||||
toFunctionReferenceLookupElement(descriptor, functionExpectedInfos)?.let { result.add(it) }
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
val additionalItems = ArrayList<LookupElement>()
|
||||
if (receiver == null) {
|
||||
TypeInstantiationItems(resolveSession, visibilityFilter).addToCollection(result, expectedInfos)
|
||||
TypeInstantiationItems(resolveSession, visibilityFilter).addToCollection(additionalItems, expectedInfos)
|
||||
|
||||
StaticMembers(bindingContext, resolveSession).addToCollection(result, expectedInfos, expression, itemsToSkip)
|
||||
StaticMembers(bindingContext, resolveSession).addToCollection(additionalItems, expectedInfos, expression, itemsToSkip)
|
||||
|
||||
ThisItems(bindingContext).addToCollection(result, expressionWithType, expectedInfos)
|
||||
ThisItems(bindingContext).addToCollection(additionalItems, expressionWithType, expectedInfos)
|
||||
|
||||
LambdaItems.addToCollection(result, functionExpectedInfos)
|
||||
LambdaItems.addToCollection(additionalItems, functionExpectedInfos)
|
||||
|
||||
KeywordValues.addToCollection(result, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expressionWithType)
|
||||
KeywordValues.addToCollection(additionalItems, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expressionWithType)
|
||||
}
|
||||
|
||||
return result
|
||||
return Result(::filterDeclaration, additionalItems)
|
||||
}
|
||||
|
||||
private fun calcExpectedInfos(expression: JetExpression): Collection<ExpectedInfo>? {
|
||||
@@ -258,26 +270,23 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
return null
|
||||
}
|
||||
|
||||
private fun buildForAsTypePosition(): Collection<LookupElement>? {
|
||||
private fun buildForAsTypePosition(): Result? {
|
||||
val binaryExpression = ((expression.getParent() as? JetUserType)
|
||||
?.getParent() as? JetTypeReference)
|
||||
?.getParent() as? JetBinaryExpressionWithTypeRHS
|
||||
if (binaryExpression != null) {
|
||||
val elementType = binaryExpression.getOperationReference().getReferencedNameElementType()
|
||||
if (elementType == JetTokens.AS_KEYWORD || elementType == JetTokens.AS_SAFE) {
|
||||
val expectedInfos = calcExpectedInfos(binaryExpression) ?: return null
|
||||
?: return null
|
||||
val elementType = binaryExpression.getOperationReference().getReferencedNameElementType()
|
||||
if (elementType != JetTokens.AS_KEYWORD && elementType != JetTokens.AS_SAFE) return null
|
||||
val expectedInfos = calcExpectedInfos(binaryExpression) ?: return null
|
||||
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.`type`.makeNotNullable() }
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.`type`.makeNotNullable() }
|
||||
|
||||
val result = ArrayList<LookupElement>()
|
||||
for ((jetType, infos) in expectedInfosGrouped) {
|
||||
val lookupElement = lookupElementForType(jetType) ?: continue
|
||||
result.add(lookupElement.addTail(infos))
|
||||
}
|
||||
return result
|
||||
}
|
||||
val items = ArrayList<LookupElement>()
|
||||
for ((jetType, infos) in expectedInfosGrouped) {
|
||||
val lookupElement = lookupElementForType(jetType) ?: continue
|
||||
items.add(lookupElement.addTail(infos))
|
||||
}
|
||||
return null
|
||||
return Result(null, items)
|
||||
}
|
||||
|
||||
private fun lookupElementForType(jetType: JetType): LookupElement? {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package first
|
||||
|
||||
fun foo() {
|
||||
"".ext<caret>
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package second
|
||||
|
||||
val String.extensionProp: Int
|
||||
get() = 1
|
||||
@@ -0,0 +1,7 @@
|
||||
package first
|
||||
|
||||
import second.extensionProp
|
||||
|
||||
fun foo() {
|
||||
"".extensionProp<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package some
|
||||
|
||||
fun other() {
|
||||
somePr<caret>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package other
|
||||
|
||||
val someProp: Int = 1
|
||||
@@ -0,0 +1,7 @@
|
||||
package some
|
||||
|
||||
import other.someProp
|
||||
|
||||
fun other() {
|
||||
someProp<caret>
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fun foo(s: String): String = s.ext<caret>
|
||||
@@ -0,0 +1,3 @@
|
||||
package other
|
||||
|
||||
fun String.extensionFun() = ""
|
||||
@@ -0,0 +1,3 @@
|
||||
import other.extensionFun
|
||||
|
||||
fun foo(s: String): String = s.extensionFun()<caret>
|
||||
@@ -0,0 +1 @@
|
||||
fun foo(s: String): Int = s.ext<caret>
|
||||
@@ -0,0 +1,3 @@
|
||||
package other
|
||||
|
||||
val String.extensionProp: Int get() = this.size
|
||||
@@ -0,0 +1,3 @@
|
||||
import other.extensionProp
|
||||
|
||||
fun foo(s: String): Int = s.extensionProp<caret>
|
||||
+9
-1
@@ -22,7 +22,11 @@ import org.jetbrains.jet.plugin.PluginTestCaseBase;
|
||||
import java.io.File;
|
||||
|
||||
public class CompletionMultifileHandlerTest extends KotlinCompletionTestCase {
|
||||
public void testExtensionFunctions() throws Exception {
|
||||
public void testExtensionFunctionImport() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testExtensionPropertyImport() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@@ -42,6 +46,10 @@ public class CompletionMultifileHandlerTest extends KotlinCompletionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testTopLevelPropertyImport() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testNoParenthesisInImports() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.completion.handlers;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionType;
|
||||
import org.jetbrains.jet.plugin.KotlinCompletionTestCase;
|
||||
import org.jetbrains.jet.plugin.PluginTestCaseBase;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class SmartCompletionMultifileHandlerTest extends KotlinCompletionTestCase {
|
||||
public void testImportExtensionFunction() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testImportExtensionProperty() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
setType(CompletionType.SMART);
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public void doTest() throws Exception {
|
||||
String fileName = getTestName(false);
|
||||
|
||||
configureByFiles(null, fileName + "-1.kt", fileName + "-2.kt");
|
||||
complete(1);
|
||||
checkResultByFile(fileName + ".kt.after");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return new File(PluginTestCaseBase.getTestDataPathBase(), "/completion/handlers/multifile/smart/").getPath() + File.separator;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user