FIR IDE: Add extensions function completion from indices
The insertion handling test is not correct, we need to check file imports before adding new ones
This commit is contained in:
-1
@@ -1,4 +1,3 @@
|
|||||||
// FIR_COMPARISON
|
|
||||||
fun String.foo() {
|
fun String.foo() {
|
||||||
val v = ::xxx_<caret>
|
val v = ::xxx_<caret>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
interface TestedTrait() {
|
interface TestedTrait() {
|
||||||
|
|||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package second
|
package second
|
||||||
|
|
||||||
import first.FirstClass
|
import first.FirstClass
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
class FirstClass() {
|
class FirstClass() {
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
// For KT-3102
|
// For KT-3102
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
fun firstFun(p: () -> Unit) {
|
fun firstFun(p: () -> Unit) {
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
fun firstFun(p: String.() -> Unit) {
|
fun firstFun(p: String.() -> Unit) {
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
fun firstFun(x: third.Dependency) {
|
fun firstFun(x: third.Dependency) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
fun firstFun() {
|
fun firstFun() {
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
import second.helloFun
|
import second.helloFun
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
import second.extensionProp
|
import second.extensionProp
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
package first
|
package first
|
||||||
|
|
||||||
import second.`package`
|
import second.`package`
|
||||||
|
|||||||
+51
-5
@@ -16,14 +16,17 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper
|
|||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalKtFile
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalKtFile
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeContext
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.getAnalysisSessionFor
|
import org.jetbrains.kotlin.idea.frontend.api.getAnalysisSessionFor
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtCompositeScope
|
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtCompositeScope
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScope
|
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScope
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScopeNameFilter
|
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScopeNameFilter
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.isExtension
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.isExtension
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
@@ -89,6 +92,7 @@ private class KotlinCommonCompletionProvider(
|
|||||||
private val indexHelper: IndexHelper
|
private val indexHelper: IndexHelper
|
||||||
) {
|
) {
|
||||||
private val lookupElementFactory = KotlinFirLookupElementFactory()
|
private val lookupElementFactory = KotlinFirLookupElementFactory()
|
||||||
|
private val typeNamesProvider = TypeNamesProvider(indexHelper)
|
||||||
|
|
||||||
private val scopeNameFilter: KtScopeNameFilter =
|
private val scopeNameFilter: KtScopeNameFilter =
|
||||||
{ name -> !name.isSpecial && prefixMatcher.prefixMatches(name.identifier) }
|
{ name -> !name.isSpecial && prefixMatcher.prefixMatches(name.identifier) }
|
||||||
@@ -132,21 +136,21 @@ private class KotlinCommonCompletionProvider(
|
|||||||
with(getAnalysisSessionFor(originalFile).createContextDependentCopy(originalFile, nameExpression)) {
|
with(getAnalysisSessionFor(originalFile).createContextDependentCopy(originalFile, nameExpression)) {
|
||||||
val expectedType = nameExpression.getExpectedType()
|
val expectedType = nameExpression.getExpectedType()
|
||||||
|
|
||||||
val (implicitScopes, _) = originalFile.getScopeContextForPosition(nameExpression)
|
val scopesContext = originalFile.getScopeContextForPosition(nameExpression)
|
||||||
|
|
||||||
fun KtCallableSymbol.hasSuitableExtensionReceiver(): Boolean =
|
fun KtCallableSymbol.hasSuitableExtensionReceiver(): Boolean =
|
||||||
checkExtensionIsSuitable(originalFile, nameExpression, explicitReceiver)
|
checkExtensionIsSuitable(originalFile, nameExpression, explicitReceiver)
|
||||||
|
|
||||||
when {
|
when {
|
||||||
nameExpression.parent is KtUserType -> collectTypesCompletion(result, implicitScopes, expectedType)
|
nameExpression.parent is KtUserType -> collectTypesCompletion(result, scopesContext.scopes, expectedType)
|
||||||
explicitReceiver != null -> collectDotCompletion(
|
explicitReceiver != null -> collectDotCompletion(
|
||||||
result,
|
result,
|
||||||
implicitScopes,
|
scopesContext.scopes,
|
||||||
explicitReceiver,
|
explicitReceiver,
|
||||||
expectedType,
|
expectedType,
|
||||||
KtCallableSymbol::hasSuitableExtensionReceiver
|
KtCallableSymbol::hasSuitableExtensionReceiver
|
||||||
)
|
)
|
||||||
else -> collectDefaultCompletion(result, implicitScopes, expectedType, KtCallableSymbol::hasSuitableExtensionReceiver)
|
else -> collectDefaultCompletion(result, scopesContext, expectedType, KtCallableSymbol::hasSuitableExtensionReceiver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,14 +187,20 @@ private class KotlinCommonCompletionProvider(
|
|||||||
|
|
||||||
nonExtensionMembers.forEach { addSymbolToCompletion(result, expectedType, it) }
|
nonExtensionMembers.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||||
extensionNonMembers.forEach { addSymbolToCompletion(result, expectedType, it) }
|
extensionNonMembers.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||||
|
|
||||||
|
collectTopLevelExtensionsFromIndices(listOf(typeOfPossibleReceiver), hasSuitableExtensionReceiver)
|
||||||
|
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtAnalysisSession.collectDefaultCompletion(
|
private fun KtAnalysisSession.collectDefaultCompletion(
|
||||||
result: CompletionResultSet,
|
result: CompletionResultSet,
|
||||||
implicitScopes: KtCompositeScope,
|
implicitScopesContext: KtScopeContext,
|
||||||
expectedType: KtType?,
|
expectedType: KtType?,
|
||||||
hasSuitableExtensionReceiver: KtCallableSymbol.() -> Boolean,
|
hasSuitableExtensionReceiver: KtCallableSymbol.() -> Boolean,
|
||||||
) {
|
) {
|
||||||
|
val (implicitScopes, implicitReceiversTypes) = implicitScopesContext
|
||||||
|
|
||||||
val availableNonExtensions = implicitScopes
|
val availableNonExtensions = implicitScopes
|
||||||
.getCallableSymbols(scopeNameFilter)
|
.getCallableSymbols(scopeNameFilter)
|
||||||
.filterNot { it.isExtension }
|
.filterNot { it.isExtension }
|
||||||
@@ -209,7 +219,43 @@ private class KotlinCommonCompletionProvider(
|
|||||||
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collectTopLevelExtensionsFromIndices(implicitReceiversTypes, hasSuitableExtensionReceiver)
|
||||||
|
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||||
|
|
||||||
collectTypesCompletion(result, implicitScopes, expectedType)
|
collectTypesCompletion(result, implicitScopes, expectedType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtAnalysisSession.collectTopLevelExtensionsFromIndices(
|
||||||
|
receiverTypes: List<KtType>,
|
||||||
|
hasSuitableExtensionReceiver: KtCallableSymbol.() -> Boolean
|
||||||
|
): Sequence<KtCallableSymbol> {
|
||||||
|
val implicitReceiverNames = findAllNamesOfTypes(receiverTypes)
|
||||||
|
val topLevelExtensions = indexHelper.getTopLevelExtensions(scopeNameFilter, implicitReceiverNames)
|
||||||
|
|
||||||
|
return topLevelExtensions.asSequence()
|
||||||
|
.map { it.getSymbol() as KtCallableSymbol }
|
||||||
|
.filter(hasSuitableExtensionReceiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtAnalysisSession.findAllNamesOfTypes(implicitReceiversTypes: List<KtType>) =
|
||||||
|
implicitReceiversTypes.flatMapTo(hashSetOf()) { with(typeNamesProvider) { findAllNames(it) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TypeNamesProvider(private val indexHelper: IndexHelper) {
|
||||||
|
fun KtAnalysisSession.findAllNames(type: KtType): Set<String> {
|
||||||
|
if (type !is KtClassType) return emptySet()
|
||||||
|
|
||||||
|
val result = hashSetOf<String>()
|
||||||
|
val typeName = type.classId.shortClassName.identifier
|
||||||
|
|
||||||
|
result += typeName
|
||||||
|
result += indexHelper.getPossibleTypeAliasExpansionNames(typeName)
|
||||||
|
|
||||||
|
val superTypes = (type.classSymbol as? KtClassOrObjectSymbol)?.superTypes
|
||||||
|
superTypes?.forEach { superType ->
|
||||||
|
result += findAllNames(superType.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+33
-6
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.idea.core.withRootPrefixIfNeeded
|
|||||||
import org.jetbrains.kotlin.idea.frontend.api.HackToForceAllowRunningAnalyzeOnEDT
|
import org.jetbrains.kotlin.idea.frontend.api.HackToForceAllowRunningAnalyzeOnEDT
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.analyze
|
import org.jetbrains.kotlin.idea.frontend.api.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.addImportToFile
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.hackyAllowRunningOnEdt
|
import org.jetbrains.kotlin.idea.frontend.api.hackyAllowRunningOnEdt
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
||||||
@@ -115,10 +116,13 @@ private class VariableLookupElementFactory {
|
|||||||
if (setterName != null) "$getterName()/$setterName()" else "$getterName()"
|
if (setterName != null) "$getterName()/$setterName()" else "$getterName()"
|
||||||
|
|
||||||
private fun createInsertHandler(symbol: KtVariableLikeSymbol): InsertHandler<LookupElement> {
|
private fun createInsertHandler(symbol: KtVariableLikeSymbol): InsertHandler<LookupElement> {
|
||||||
val callableId = symbol.callableIdIfExists
|
val callableId = symbol.callableIdIfExists ?: return QuotedNamesAwareInsertionHandler(symbol.name)
|
||||||
if (callableId == null || !symbol.canBeCalledByFqName) return QuotedNamesAwareInsertionHandler(symbol.name)
|
|
||||||
|
|
||||||
return ShorteningVariableInsertionHandler(callableId)
|
return if (symbol.canBeCalledByFqName) {
|
||||||
|
ShorteningVariableInsertionHandler(callableId)
|
||||||
|
} else {
|
||||||
|
SimpleVariableInsertionHandler(callableId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val KtVariableLikeSymbol.callableIdIfExists: FqName?
|
private val KtVariableLikeSymbol.callableIdIfExists: FqName?
|
||||||
@@ -181,7 +185,6 @@ private class FunctionLookupElementFactory {
|
|||||||
|
|
||||||
private fun KtAnalysisSession.createInsertHandler(symbol: KtFunctionSymbol): InsertHandler<LookupElement> {
|
private fun KtAnalysisSession.createInsertHandler(symbol: KtFunctionSymbol): InsertHandler<LookupElement> {
|
||||||
val functionFqName = symbol.callableIdIfNonLocal
|
val functionFqName = symbol.callableIdIfNonLocal
|
||||||
|
|
||||||
return if (functionFqName != null && canBeCalledByFqName(symbol)) {
|
return if (functionFqName != null && canBeCalledByFqName(symbol)) {
|
||||||
ShorteningFunctionInsertionHandler(
|
ShorteningFunctionInsertionHandler(
|
||||||
functionFqName,
|
functionFqName,
|
||||||
@@ -192,11 +195,15 @@ private class FunctionLookupElementFactory {
|
|||||||
SimpleFunctionInsertionHandler(
|
SimpleFunctionInsertionHandler(
|
||||||
symbol.name,
|
symbol.name,
|
||||||
inputValueArguments = symbol.valueParameters.isNotEmpty(),
|
inputValueArguments = symbol.valueParameters.isNotEmpty(),
|
||||||
insertEmptyLambda = insertLambdaBraces(symbol)
|
insertEmptyLambda = insertLambdaBraces(symbol),
|
||||||
|
nameToImport = symbol.importableFqName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val KtFunctionSymbol.importableFqName: FqName?
|
||||||
|
get() = if (dispatchType == null) callableIdIfNonLocal else null
|
||||||
|
|
||||||
private fun canBeCalledByFqName(symbol: KtFunctionSymbol): Boolean {
|
private fun canBeCalledByFqName(symbol: KtFunctionSymbol): Boolean {
|
||||||
return !symbol.isExtension && symbol.dispatchType == null
|
return !symbol.isExtension && symbol.dispatchType == null
|
||||||
}
|
}
|
||||||
@@ -299,15 +306,22 @@ private abstract class AbstractFunctionInsertionHandler(
|
|||||||
private class SimpleFunctionInsertionHandler(
|
private class SimpleFunctionInsertionHandler(
|
||||||
name: Name,
|
name: Name,
|
||||||
inputValueArguments: Boolean,
|
inputValueArguments: Boolean,
|
||||||
insertEmptyLambda: Boolean
|
insertEmptyLambda: Boolean,
|
||||||
|
private val nameToImport: FqName?
|
||||||
) : AbstractFunctionInsertionHandler(name, inputValueArguments, insertEmptyLambda) {
|
) : AbstractFunctionInsertionHandler(name, inputValueArguments, insertEmptyLambda) {
|
||||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||||
super.handleInsert(context, item)
|
super.handleInsert(context, item)
|
||||||
|
|
||||||
|
val targetFile = context.file as? KtFile ?: return
|
||||||
|
|
||||||
val startOffset = context.startOffset
|
val startOffset = context.startOffset
|
||||||
val element = context.file.findElementAt(startOffset) ?: return
|
val element = context.file.findElementAt(startOffset) ?: return
|
||||||
|
|
||||||
addArguments(context, element)
|
addArguments(context, element)
|
||||||
|
|
||||||
|
if (nameToImport != null && targetFile.importDirectives.none { it.importPath?.fqName == nameToImport }) {
|
||||||
|
addImportToFile(context.project, targetFile, nameToImport)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,6 +366,19 @@ private class ShorteningVariableInsertionHandler(private val name: FqName) : Ins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SimpleVariableInsertionHandler(private val nameToImport: FqName) :
|
||||||
|
QuotedNamesAwareInsertionHandler(nameToImport.shortName()) {
|
||||||
|
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||||
|
super.handleInsert(context, item)
|
||||||
|
|
||||||
|
val targetFile = context.file as? KtFile ?: return
|
||||||
|
|
||||||
|
if (targetFile.importDirectives.none { it.importPath?.fqName == nameToImport }) {
|
||||||
|
addImportToFile(context.project, targetFile, nameToImport)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private open class QuotedNamesAwareInsertionHandler(private val name: Name) : InsertHandler<LookupElement> {
|
private open class QuotedNamesAwareInsertionHandler(private val name: Name) : InsertHandler<LookupElement> {
|
||||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||||
val startOffset = context.startOffset
|
val startOffset = context.startOffset
|
||||||
|
|||||||
+27
@@ -98,6 +98,33 @@ public class IndexHelper(val project: Project, private val scope: GlobalSearchSc
|
|||||||
return (functions + properties).toList()
|
return (functions + properties).toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getTopLevelExtensions(nameFilter: (Name) -> Boolean, receiverTypeNames: Set<String>): Collection<KtCallableDeclaration> {
|
||||||
|
val index = KotlinTopLevelExtensionsByReceiverTypeIndex.INSTANCE
|
||||||
|
|
||||||
|
return index.getAllKeys(project).asSequence()
|
||||||
|
.onEach { ProgressManager.checkCanceled() }
|
||||||
|
.filter { KotlinTopLevelExtensionsByReceiverTypeIndex.receiverTypeNameFromKey(it) in receiverTypeNames }
|
||||||
|
.filter { nameFilter(Name.identifier(KotlinTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))) }
|
||||||
|
.flatMap { key -> index[key, project, scope] }
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPossibleTypeAliasExpansionNames(originalTypeName: String): Set<String> {
|
||||||
|
val index = KotlinTypeAliasByExpansionShortNameIndex.INSTANCE
|
||||||
|
val out = mutableSetOf<String>()
|
||||||
|
|
||||||
|
fun searchRecursively(typeName: String) {
|
||||||
|
ProgressManager.checkCanceled()
|
||||||
|
index[typeName, project, scope].asSequence()
|
||||||
|
.mapNotNull { it.name }
|
||||||
|
.filter { out.add(it) }
|
||||||
|
.forEach(::searchRecursively)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchRecursively(originalTypeName)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun CallableId.asStringForIndexes(): String =
|
private fun CallableId.asStringForIndexes(): String =
|
||||||
(if (packageName.isRoot) callableName.asString() else toString()).replace('/', '.')
|
(if (packageName.isRoot) callableName.asString() else toString()).replace('/', '.')
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ private val SimpleImportPathComparator: Comparator<ImportPath> = compareBy(Impor
|
|||||||
*
|
*
|
||||||
* We want it as a copy because we do not yet care about imports ordering, so we do not need a fancy comparator.
|
* We want it as a copy because we do not yet care about imports ordering, so we do not need a fancy comparator.
|
||||||
*/
|
*/
|
||||||
internal fun addImportToFile(
|
fun addImportToFile(
|
||||||
project: Project,
|
project: Project,
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
fqName: FqName,
|
fqName: FqName,
|
||||||
|
|||||||
Reference in New Issue
Block a user