Improve: intention "Introduce Import Alias" should suggest new names for the new alias
#KT-30456 Fixed
This commit is contained in:
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
import org.jetbrains.kotlin.lexer.KotlinLexer
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getOutermostParenthesizerOrThis
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
|
||||
@@ -32,6 +33,8 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
|
||||
import java.util.*
|
||||
|
||||
@@ -107,6 +110,35 @@ object KotlinNameSuggester {
|
||||
return result
|
||||
}
|
||||
|
||||
fun suggestNamesByFqName(
|
||||
fqName: FqName,
|
||||
ignoreCompanion: Boolean = true,
|
||||
validator: (String) -> Boolean = { true },
|
||||
defaultName: () -> String? = { null }
|
||||
): Collection<String> {
|
||||
val result = LinkedHashSet<String>()
|
||||
|
||||
var name = ""
|
||||
fqName.asString().split('.').asReversed().forEach {
|
||||
if (ignoreCompanion && it == "Companion") return@forEach
|
||||
name = name.withPrefix(it)
|
||||
result.addName(name, validator)
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result.addName(defaultName(), validator)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun String.withPrefix(prefix: String): String {
|
||||
if (isEmpty()) return prefix
|
||||
val c = this[0]
|
||||
return (if (c in 'a'..'z') prefix.decapitalizeAsciiOnly()
|
||||
else prefix.capitalizeAsciiOnly()) + capitalizeAsciiOnly()
|
||||
}
|
||||
|
||||
private val COMMON_TYPE_PARAMETER_NAMES = listOf("T", "U", "V", "W", "X", "Y", "Z")
|
||||
private const val MAX_NUMBER_OF_SUGGESTED_NAME_CHECKS = 1000
|
||||
|
||||
|
||||
+35
-33
@@ -5,25 +5,22 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceImportAlias
|
||||
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinRenameDispatcherHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.findElementForRename
|
||||
import org.jetbrains.kotlin.idea.refactoring.selectElement
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
@@ -33,22 +30,24 @@ import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinFunctionShortNameIndex
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinPropertyShortNameIndex
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelperImpl
|
||||
import org.jetbrains.kotlin.idea.util.getAllAccessibleFunctions
|
||||
import org.jetbrains.kotlin.idea.util.getAllAccessibleVariables
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtUserType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.resolve.PropertyImportedFromObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findFunction
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findPackage
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
|
||||
|
||||
@@ -70,13 +69,27 @@ object KotlinIntroduceImportAliasHandler : RefactoringActionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
val suggestedName = suggestedName(element.mainReference.value, file.getResolutionScope())
|
||||
ImportInsertHelperImpl.addImport(project, file, fqName, false, Name.identifier(suggestedName))
|
||||
replaceUsages(usages, suggestedName)
|
||||
val oldName = element.mainReference.value
|
||||
val scope = file.getResolutionScope()
|
||||
val validator = fun(name: String): Boolean {
|
||||
if (oldName == name) return false
|
||||
val identifier = Name.identifier(name)
|
||||
|
||||
return scope.getAllAccessibleFunctions(identifier).isEmpty()
|
||||
&& scope.getAllAccessibleVariables(identifier).isEmpty()
|
||||
&& scope.findClassifier(identifier, NoLookupLocation.FROM_IDE) == null
|
||||
&& scope.findPackage(identifier) == null
|
||||
}
|
||||
|
||||
val suggestionsName = KotlinNameSuggester.suggestNamesByFqName(fqName, validator = validator)
|
||||
val newName = suggestionsName.first()
|
||||
val newDirective = ImportInsertHelperImpl.addImport(project, file, fqName, false, Name.identifier(newName))
|
||||
|
||||
replaceUsages(usages, newName)
|
||||
cleanImport(file, fqName)
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
invokeRename(project, editor, file)
|
||||
invokeRename(project, editor, newDirective.alias, suggestionsName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +101,7 @@ object KotlinIntroduceImportAliasHandler : RefactoringActionHandler {
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
|
||||
throw AssertionError("${KotlinIntroduceImportAliasHandler.REFACTORING_NAME} can only be invoked from editor")
|
||||
throw AssertionError("$REFACTORING_NAME can only be invoked from editor")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,26 +123,15 @@ private fun findPsiElements(project: Project, file: KtFile, descriptor: Declarat
|
||||
}.filter { fqName == it.fqName }
|
||||
}
|
||||
|
||||
private fun suggestedName(oldName: String, scope: LexicalScope): String =
|
||||
KotlinNameSuggester.suggestNameByName(oldName, fun(name: String): Boolean {
|
||||
if (oldName == name) return false
|
||||
val identifier = Name.identifier(name)
|
||||
return scope.findVariable(identifier, NoLookupLocation.FROM_IDE) == null
|
||||
&& scope.findFunction(identifier, NoLookupLocation.FROM_IDE) == null
|
||||
&& scope.findClassifier(identifier, NoLookupLocation.FROM_IDE) == null
|
||||
&& scope.findPackage(identifier) == null
|
||||
})
|
||||
|
||||
private fun invokeRename(project: Project, editor: Editor, file: KtFile) {
|
||||
val elementToRename = file.findElementForRename<KtSimpleNameExpression>(editor.caretModel.offset) ?: return
|
||||
val dataContext = SimpleDataContext.getSimpleContext(
|
||||
CommonDataKeys.PSI_ELEMENT.name,
|
||||
elementToRename,
|
||||
(editor as? EditorEx)?.dataContext
|
||||
)
|
||||
|
||||
private fun invokeRename(
|
||||
project: Project,
|
||||
editor: Editor,
|
||||
elementToRename: PsiNamedElement?,
|
||||
suggestionsName: Collection<String>
|
||||
) {
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
KotlinRenameDispatcherHandler().invoke(project, editor, file, dataContext)
|
||||
val rename = VariableInplaceRenamer(elementToRename, editor, project)
|
||||
rename.performInplaceRefactoring(LinkedHashSet(suggestionsName))
|
||||
}
|
||||
|
||||
private fun replaceUsages(usages: List<UsageContext>, newName: String) {
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import kotlin.Int.Companion as Companion1
|
||||
import kotlin.Int.Companion as Int1
|
||||
|
||||
// WITH_RUNTIME
|
||||
fun foo() {
|
||||
val max = Companion1.MAX_VALUE
|
||||
val max2 = Companion1.MAX_VALUE
|
||||
val max = Int1.MAX_VALUE
|
||||
val max2 = Int1.MAX_VALUE
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user