FIR IDE: Refactor variables insertion handlers

This commit is contained in:
Roman Golyshev
2021-03-15 11:50:21 +03:00
parent 84386237a6
commit 9db8bd8d38
@@ -55,10 +55,10 @@ internal class KotlinFirLookupElementFactory {
} }
} }
private sealed class FunctionImportStrategy { private sealed class CallableImportStrategy {
object DoNothing : FunctionImportStrategy() object DoNothing : CallableImportStrategy()
data class AddImport(val nameToImport: FqName) : FunctionImportStrategy() data class AddImport(val nameToImport: FqName) : CallableImportStrategy()
data class InsertFqNameAndShorten(val fqName: FqName) : FunctionImportStrategy() data class InsertFqNameAndShorten(val fqName: FqName) : CallableImportStrategy()
} }
/** /**
@@ -77,7 +77,7 @@ private data class ClassifierLookupObject(override val shortName: Name, val clas
*/ */
private data class FunctionLookupObject( private data class FunctionLookupObject(
override val shortName: Name, override val shortName: Name,
val importingStrategy: FunctionImportStrategy, val importingStrategy: CallableImportStrategy,
val inputValueArguments: Boolean, val inputValueArguments: Boolean,
val insertEmptyLambda: Boolean, val insertEmptyLambda: Boolean,
// for distinction between different overloads // for distinction between different overloads
@@ -87,7 +87,10 @@ private data class FunctionLookupObject(
/** /**
* Simplest lookup object so two lookup elements for the same property will clash. * Simplest lookup object so two lookup elements for the same property will clash.
*/ */
private data class VariableLookupObject(override val shortName: Name, val callableIdIfNonLocal: FqName?) : KotlinLookupObject private data class VariableLookupObject(
override val shortName: Name,
val importStrategy: CallableImportStrategy
) : KotlinLookupObject
private class ClassLookupElementFactory { private class ClassLookupElementFactory {
fun createLookup(symbol: KtClassLikeSymbol): LookupElementBuilder { fun createLookup(symbol: KtClassLikeSymbol): LookupElementBuilder {
@@ -104,12 +107,15 @@ private class TypeParameterLookupElementFactory {
private class VariableLookupElementFactory { private class VariableLookupElementFactory {
fun KtAnalysisSession.createLookup(symbol: KtVariableLikeSymbol): LookupElementBuilder { fun KtAnalysisSession.createLookup(symbol: KtVariableLikeSymbol): LookupElementBuilder {
val lookupObject = VariableLookupObject(symbol.name, symbol.callableIdIfExists) val lookupObject = VariableLookupObject(
symbol.name,
importStrategy = detectImportStrategy(symbol)
)
return LookupElementBuilder.create(lookupObject, symbol.name.asString()) return LookupElementBuilder.create(lookupObject, symbol.name.asString())
.withTypeText(symbol.annotatedType.type.render()) .withTypeText(symbol.annotatedType.type.render())
.markIfSyntheticJavaProperty(symbol) .markIfSyntheticJavaProperty(symbol)
.withInsertHandler(createInsertHandler(symbol)) .withInsertHandler(ShorteningVariableInsertionHandler)
} }
private fun LookupElementBuilder.markIfSyntheticJavaProperty(symbol: KtVariableLikeSymbol): LookupElementBuilder = when (symbol) { private fun LookupElementBuilder.markIfSyntheticJavaProperty(symbol: KtVariableLikeSymbol): LookupElementBuilder = when (symbol) {
@@ -125,29 +131,13 @@ private class VariableLookupElementFactory {
private fun buildSyntheticPropertyTailText(getterName: String, setterName: String?): String = private fun buildSyntheticPropertyTailText(getterName: String, setterName: String?): String =
if (setterName != null) "$getterName()/$setterName()" else "$getterName()" if (setterName != null) "$getterName()/$setterName()" else "$getterName()"
private fun createInsertHandler(symbol: KtVariableLikeSymbol): InsertHandler<LookupElement> { private fun detectImportStrategy(symbol: KtVariableLikeSymbol): CallableImportStrategy {
val callableId = symbol.callableIdIfExists ?: return QuotedNamesAwareInsertionHandler() if (symbol !is KtKotlinPropertySymbol) return CallableImportStrategy.DoNothing
if (symbol.dispatchType != null || symbol.receiverType != null) return CallableImportStrategy.DoNothing
return if (symbol.canBeCalledByFqName) { return symbol.callableIdIfNonLocal?.let(CallableImportStrategy::InsertFqNameAndShorten)
ShorteningVariableInsertionHandler(callableId) ?: CallableImportStrategy.DoNothing
} else {
SimpleVariableInsertionHandler(symbol.importableFqName)
}
} }
private val KtVariableLikeSymbol.canBeCalledByFqName: Boolean
get() = when (this) {
is KtKotlinPropertySymbol -> dispatchType == null && receiverType == null
is KtEnumEntrySymbol -> true
is KtJavaFieldSymbol,
is KtSyntheticJavaPropertySymbol,
is KtLocalVariableSymbol,
is KtFunctionParameterSymbol,
is KtConstructorParameterSymbol,
is KtSetterParameterSymbol -> false
}
} }
private class FunctionLookupElementFactory { private class FunctionLookupElementFactory {
@@ -172,13 +162,13 @@ private class FunctionLookupElementFactory {
} }
} }
private fun detectImportingStrategy(symbol: KtFunctionSymbol): FunctionImportStrategy { private fun detectImportingStrategy(symbol: KtFunctionSymbol): CallableImportStrategy {
val functionFqName = symbol.callableIdIfNonLocal val functionFqName = symbol.callableIdIfNonLocal
return when { return when {
functionFqName == null -> FunctionImportStrategy.DoNothing functionFqName == null -> CallableImportStrategy.DoNothing
symbol.dispatchType != null -> FunctionImportStrategy.DoNothing symbol.dispatchType != null -> CallableImportStrategy.DoNothing
!symbol.isExtension -> FunctionImportStrategy.InsertFqNameAndShorten(functionFqName) !symbol.isExtension -> CallableImportStrategy.InsertFqNameAndShorten(functionFqName)
else -> FunctionImportStrategy.AddImport(functionFqName) else -> CallableImportStrategy.AddImport(functionFqName)
} }
} }
@@ -299,7 +289,7 @@ private object ShorteningFunctionInsertionHandler : AbstractFunctionInsertionHan
val startOffset = context.startOffset val startOffset = context.startOffset
val element = context.file.findElementAt(startOffset) ?: return val element = context.file.findElementAt(startOffset) ?: return
if (importingStrategy is FunctionImportStrategy.InsertFqNameAndShorten) { if (importingStrategy is CallableImportStrategy.InsertFqNameAndShorten) {
context.document.replaceString( context.document.replaceString(
context.startOffset, context.startOffset,
context.tailOffset, context.tailOffset,
@@ -315,42 +305,40 @@ private object ShorteningFunctionInsertionHandler : AbstractFunctionInsertionHan
addArguments(context, element, lookupObject) addArguments(context, element, lookupObject)
context.commitDocument() context.commitDocument()
if (importingStrategy is FunctionImportStrategy.AddImport) { if (importingStrategy is CallableImportStrategy.AddImport) {
addCallableImportIfRequired(targetFile, importingStrategy.nameToImport) addCallableImportIfRequired(targetFile, importingStrategy.nameToImport)
} }
} }
} }
} }
private class ShorteningVariableInsertionHandler(private val name: FqName) : InsertHandler<LookupElement> { private object ShorteningVariableInsertionHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
val targetFile = context.file as? KtFile ?: return val targetFile = context.file as? KtFile ?: return
val lookupObject = item.`object` as VariableLookupObject
context.document.replaceString( when (val importStrategy = lookupObject.importStrategy) {
context.startOffset, is CallableImportStrategy.AddImport -> {
context.tailOffset, addCallableImportIfRequired(targetFile, importStrategy.nameToImport)
name.withRootPrefixIfNeeded().render() }
)
context.commitDocument()
shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset)) is CallableImportStrategy.InsertFqNameAndShorten -> {
} context.document.replaceString(
} context.startOffset,
context.tailOffset,
importStrategy.fqName.withRootPrefixIfNeeded().render()
)
private class SimpleVariableInsertionHandler(private val nameToImport: FqName?) : context.commitDocument()
QuotedNamesAwareInsertionHandler() { shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset))
override fun handleInsert(context: InsertionContext, item: LookupElement) { }
super.handleInsert(context, item)
val targetFile = context.file as? KtFile ?: return is CallableImportStrategy.DoNothing -> {}
if (nameToImport != null) {
addCallableImportIfRequired(targetFile, nameToImport)
} }
} }
} }
private open class QuotedNamesAwareInsertionHandler() : InsertHandler<LookupElement> { private open class QuotedNamesAwareInsertionHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
val lookupElement = item.`object` as KotlinLookupObject val lookupElement = item.`object` as KotlinLookupObject
@@ -402,19 +390,6 @@ private val KtVariableLikeSymbol.callableIdIfExists: FqName?
is KtSetterParameterSymbol -> null is KtSetterParameterSymbol -> null
} }
private val KtVariableLikeSymbol.importableFqName: FqName?
get() = when (this) {
is KtKotlinPropertySymbol -> if (dispatchType == null) callableIdIfNonLocal else null
is KtEnumEntrySymbol,
is KtJavaFieldSymbol,
is KtSyntheticJavaPropertySymbol,
is KtLocalVariableSymbol,
is KtFunctionParameterSymbol,
is KtConstructorParameterSymbol,
is KtSetterParameterSymbol -> null
}
private object ShortNamesRenderer { private object ShortNamesRenderer {
fun KtAnalysisSession.renderFunctionParameters(function: KtFunctionSymbol): String = fun KtAnalysisSession.renderFunctionParameters(function: KtFunctionSymbol): String =
function.valueParameters.joinToString(", ", "(", ")") { renderFunctionParameter(it) } function.valueParameters.joinToString(", ", "(", ")") { renderFunctionParameter(it) }