Convert to Kotlin: KotlinRefactoringUtil2 (prettify)
This commit is contained in:
@@ -24,14 +24,12 @@ import com.intellij.openapi.ui.Messages
|
|||||||
import com.intellij.openapi.ui.popup.JBPopupAdapter
|
import com.intellij.openapi.ui.popup.JBPopupAdapter
|
||||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||||
import com.intellij.openapi.ui.popup.LightweightWindowEvent
|
import com.intellij.openapi.ui.popup.LightweightWindowEvent
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||||
import com.intellij.psi.util.PsiFormatUtil
|
import com.intellij.psi.util.PsiFormatUtil
|
||||||
import com.intellij.psi.util.PsiFormatUtilBase
|
import com.intellij.psi.util.PsiFormatUtilBase
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.intellij.ui.components.JBList
|
import com.intellij.ui.components.JBList
|
||||||
import com.intellij.util.containers.ContainerUtil
|
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||||
import org.jetbrains.kotlin.asJava.unwrapped
|
import org.jetbrains.kotlin.asJava.unwrapped
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
@@ -41,16 +39,17 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.findExpressionOrStringFragment
|
import org.jetbrains.kotlin.idea.refactoring.introduce.findExpressionOrStringFragment
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
|
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
|
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
@@ -60,51 +59,44 @@ import java.util.*
|
|||||||
import javax.swing.DefaultListCellRenderer
|
import javax.swing.DefaultListCellRenderer
|
||||||
import javax.swing.DefaultListModel
|
import javax.swing.DefaultListModel
|
||||||
import javax.swing.JList
|
import javax.swing.JList
|
||||||
import javax.swing.event.ListSelectionListener
|
|
||||||
|
|
||||||
object KotlinRefactoringUtil2 {
|
object KotlinRefactoringUtil2 {
|
||||||
|
fun wrapOrSkip(s: String, inCode: Boolean) = if (inCode) "<code>$s</code>" else s
|
||||||
|
|
||||||
fun wrapOrSkip(s: String, inCode: Boolean): String {
|
fun formatClassDescriptor(classDescriptor: DeclarationDescriptor) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(classDescriptor)
|
||||||
return if (inCode) "<code>$s</code>" else s
|
|
||||||
}
|
|
||||||
|
|
||||||
fun formatClassDescriptor(classDescriptor: DeclarationDescriptor): String {
|
|
||||||
return IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(classDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun formatPsiClass(
|
fun formatPsiClass(
|
||||||
psiClass: PsiClass,
|
psiClass: PsiClass,
|
||||||
markAsJava: Boolean,
|
markAsJava: Boolean,
|
||||||
inCode: Boolean): String {
|
inCode: Boolean
|
||||||
|
): String {
|
||||||
var description: String
|
var description: String
|
||||||
|
|
||||||
val kind = if (psiClass.isInterface) "interface " else "class "
|
val kind = if (psiClass.isInterface) "interface " else "class "
|
||||||
description = kind + PsiFormatUtil.formatClass(
|
description = kind + PsiFormatUtil.formatClass(
|
||||||
psiClass,
|
psiClass,
|
||||||
PsiFormatUtilBase.SHOW_CONTAINING_CLASS or PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE)
|
PsiFormatUtilBase.SHOW_CONTAINING_CLASS or PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
|
||||||
|
)
|
||||||
description = wrapOrSkip(description, inCode)
|
description = wrapOrSkip(description, inCode)
|
||||||
|
|
||||||
return if (markAsJava) "[Java] " + description else description
|
return if (markAsJava) "[Java] $description" else description
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkSuperMethods(
|
fun checkSuperMethods(
|
||||||
declaration: KtDeclaration,
|
declaration: KtDeclaration,
|
||||||
ignore: Collection<PsiElement>?,
|
ignore: Collection<PsiElement>?,
|
||||||
actionStringKey: String): List<PsiElement> {
|
actionStringKey: String
|
||||||
val bindingContext = declaration.analyze(BodyResolveMode.FULL)
|
): List<PsiElement> {
|
||||||
|
val declarationDescriptor = declaration.resolveToDescriptor() as CallableDescriptor
|
||||||
|
|
||||||
val declarationDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) as CallableDescriptor?
|
if (declarationDescriptor is LocalVariableDescriptor) return listOf(declaration)
|
||||||
|
|
||||||
if (declarationDescriptor == null || declarationDescriptor is LocalVariableDescriptor) {
|
|
||||||
return listOf(declaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
val project = declaration.project
|
val project = declaration.project
|
||||||
val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>()
|
val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>()
|
||||||
for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors(declarationDescriptor)) {
|
for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors(declarationDescriptor)) {
|
||||||
val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, overriddenDescriptor) ?: continue
|
val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, overriddenDescriptor) ?: continue
|
||||||
if (PsiTreeUtil.instanceOf(overriddenDeclaration, KtNamedFunction::class.java, KtProperty::class.java, PsiMethod::class.java)) {
|
if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod) {
|
||||||
overriddenElementsToDescriptor.put(overriddenDeclaration, overriddenDescriptor)
|
overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ignore != null) {
|
if (ignore != null) {
|
||||||
@@ -113,30 +105,29 @@ object KotlinRefactoringUtil2 {
|
|||||||
|
|
||||||
if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration)
|
if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration)
|
||||||
|
|
||||||
val superClasses = getClassDescriptions(overriddenElementsToDescriptor)
|
return askUserForMethodsToSearch(declaration, declarationDescriptor, overriddenElementsToDescriptor, actionStringKey)
|
||||||
return askUserForMethodsToSearch(declaration, declarationDescriptor, overriddenElementsToDescriptor, superClasses, actionStringKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun askUserForMethodsToSearch(
|
private fun askUserForMethodsToSearch(
|
||||||
declaration: KtDeclaration,
|
declaration: KtDeclaration,
|
||||||
declarationDescriptor: CallableDescriptor,
|
declarationDescriptor: CallableDescriptor,
|
||||||
overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>,
|
overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>,
|
||||||
superClasses: List<String>,
|
actionStringKey: String
|
||||||
actionStringKey: String): List<PsiElement> {
|
): List<PsiElement> {
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
if (ApplicationManager.getApplication().isUnitTestMode) return overriddenElementsToDescriptor.keys.toList()
|
||||||
return ContainerUtil.newArrayList(overriddenElementsToDescriptor.keys)
|
|
||||||
}
|
val superClassDescriptions = getClassDescriptions(overriddenElementsToDescriptor)
|
||||||
|
|
||||||
val superClassesStr = "\n" + StringUtil.join(superClasses, "")
|
|
||||||
val message = KotlinBundle.message(
|
val message = KotlinBundle.message(
|
||||||
"x.overrides.y.in.class.list",
|
"x.overrides.y.in.class.list",
|
||||||
DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor),
|
DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor),
|
||||||
superClassesStr,
|
"\n${superClassDescriptions.joinToString(separator = "")}",
|
||||||
KotlinBundle.message(actionStringKey))
|
KotlinBundle.message(actionStringKey)
|
||||||
|
)
|
||||||
|
|
||||||
val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
|
val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
|
||||||
when (exitCode) {
|
when (exitCode) {
|
||||||
Messages.YES -> return ContainerUtil.newArrayList(overriddenElementsToDescriptor.keys)
|
Messages.YES -> return overriddenElementsToDescriptor.keys.toList()
|
||||||
Messages.NO -> return listOf(declaration)
|
Messages.NO -> return listOf(declaration)
|
||||||
else -> return emptyList()
|
else -> return emptyList()
|
||||||
}
|
}
|
||||||
@@ -144,51 +135,46 @@ object KotlinRefactoringUtil2 {
|
|||||||
|
|
||||||
private fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
|
private fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
|
||||||
return overriddenElementsToDescriptor.entries.map { entry ->
|
return overriddenElementsToDescriptor.entries.map { entry ->
|
||||||
val description: String
|
val (element, descriptor) = entry
|
||||||
|
val description = when (element) {
|
||||||
val element = entry.key
|
is KtNamedFunction, is KtProperty -> formatClassDescriptor(descriptor.containingDeclaration)
|
||||||
val descriptor = entry.value
|
is PsiMethod -> {
|
||||||
if (element is KtNamedFunction || element is KtProperty) {
|
val psiClass = element.containingClass ?: error("Invalid element: ${element.getText()}")
|
||||||
description = formatClassDescriptor(descriptor.getContainingDeclaration())
|
formatPsiClass(psiClass, true, false)
|
||||||
|
}
|
||||||
|
else -> error("Unexpected element: ${element.getElementTextWithContext()}")
|
||||||
}
|
}
|
||||||
else {
|
" $description\n"
|
||||||
assert(element is PsiMethod) { "Invalid element: " + element.getText() }
|
|
||||||
|
|
||||||
val psiClass = (element as PsiMethod).containingClass ?: error("Invalid element: " + element.getText())
|
|
||||||
|
|
||||||
description = formatPsiClass(psiClass, true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
" " + description + "\n"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatClass(classDescriptor: DeclarationDescriptor, inCode: Boolean): String {
|
fun formatClass(classDescriptor: DeclarationDescriptor, inCode: Boolean): String {
|
||||||
val element = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor)
|
val element = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor)
|
||||||
if (element is PsiClass) {
|
return if (element is PsiClass) {
|
||||||
return formatPsiClass((element as PsiClass?)!!, false, inCode)
|
formatPsiClass(element, false, inCode)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wrapOrSkip(formatClassDescriptor(classDescriptor), inCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapOrSkip(formatClassDescriptor(classDescriptor), inCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatFunction(functionDescriptor: DeclarationDescriptor, inCode: Boolean): String {
|
fun formatFunction(functionDescriptor: DeclarationDescriptor, inCode: Boolean): String {
|
||||||
val element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)
|
val element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)
|
||||||
if (element is PsiMethod) {
|
return if (element is PsiMethod) {
|
||||||
return formatPsiMethod((element as PsiMethod?)!!, false, inCode)
|
formatPsiMethod(element, false, inCode)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wrapOrSkip(formatFunctionDescriptor(functionDescriptor), inCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapOrSkip(formatFunctionDescriptor(functionDescriptor), inCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatFunctionDescriptor(functionDescriptor: DeclarationDescriptor): String {
|
private fun formatFunctionDescriptor(functionDescriptor: DeclarationDescriptor) = DescriptorRenderer.COMPACT.render(functionDescriptor)
|
||||||
return DescriptorRenderer.COMPACT.render(functionDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun formatPsiMethod(
|
fun formatPsiMethod(
|
||||||
psiMethod: PsiMethod,
|
psiMethod: PsiMethod,
|
||||||
showContainingClass: Boolean,
|
showContainingClass: Boolean,
|
||||||
inCode: Boolean): String {
|
inCode: Boolean
|
||||||
|
): String {
|
||||||
var options = PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
|
var options = PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
|
||||||
if (showContainingClass) {
|
if (showContainingClass) {
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
@@ -198,49 +184,30 @@ object KotlinRefactoringUtil2 {
|
|||||||
var description = PsiFormatUtil.formatMethod(psiMethod, PsiSubstitutor.EMPTY, options, PsiFormatUtilBase.SHOW_TYPE)
|
var description = PsiFormatUtil.formatMethod(psiMethod, PsiSubstitutor.EMPTY, options, PsiFormatUtilBase.SHOW_TYPE)
|
||||||
description = wrapOrSkip(description, inCode)
|
description = wrapOrSkip(description, inCode)
|
||||||
|
|
||||||
return "[Java] " + description
|
return "[Java] $description"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatJavaOrLightMethod(method: PsiMethod): String {
|
fun formatJavaOrLightMethod(method: PsiMethod): String {
|
||||||
val originalDeclaration = method.unwrapped
|
val originalDeclaration = method.unwrapped
|
||||||
if (originalDeclaration is KtDeclaration) {
|
return if (originalDeclaration is KtDeclaration) {
|
||||||
val bindingContext = originalDeclaration.analyze(BodyResolveMode.FULL)
|
formatFunctionDescriptor(originalDeclaration.resolveToDescriptor())
|
||||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, originalDeclaration)
|
}
|
||||||
|
else {
|
||||||
if (descriptor != null) return formatFunctionDescriptor(descriptor)
|
formatPsiMethod(method, false, false)
|
||||||
}
|
}
|
||||||
return formatPsiMethod(method, false, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatClass(classOrObject: KtClassOrObject): String {
|
fun formatClass(classOrObject: KtClassOrObject) = formatClassDescriptor(classOrObject.resolveToDescriptor() as ClassDescriptor)
|
||||||
val bindingContext = classOrObject.analyze(BodyResolveMode.FULL)
|
|
||||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject)
|
|
||||||
|
|
||||||
if (descriptor is ClassDescriptor) return formatClassDescriptor(descriptor)
|
|
||||||
return "class " + classOrObject.name!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkParametersInMethodHierarchy(parameter: PsiParameter): Collection<PsiElement>? {
|
fun checkParametersInMethodHierarchy(parameter: PsiParameter): Collection<PsiElement>? {
|
||||||
val method = parameter.declarationScope as PsiMethod
|
val method = parameter.declarationScope as PsiMethod
|
||||||
|
|
||||||
val parametersToDelete = collectParametersHierarchy(method, parameter)
|
val parametersToDelete = collectParametersHierarchy(method, parameter)
|
||||||
if (parametersToDelete.size > 1) {
|
if (parametersToDelete.size <= 1 || ApplicationManager.getApplication().isUnitTestMode) return parametersToDelete
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
return parametersToDelete
|
|
||||||
}
|
|
||||||
|
|
||||||
val message = KotlinBundle.message("delete.param.in.method.hierarchy", formatJavaOrLightMethod(method))
|
val message = KotlinBundle.message("delete.param.in.method.hierarchy", formatJavaOrLightMethod(method))
|
||||||
val exitCode = Messages.showOkCancelDialog(
|
val exitCode = Messages.showOkCancelDialog(parameter.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
|
||||||
parameter.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
|
return if (exitCode == Messages.OK) parametersToDelete else null
|
||||||
if (exitCode == Messages.OK) {
|
|
||||||
return parametersToDelete
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parametersToDelete
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: generalize breadth-first search
|
// TODO: generalize breadth-first search
|
||||||
@@ -253,19 +220,15 @@ object KotlinRefactoringUtil2 {
|
|||||||
while (!queue.isEmpty()) {
|
while (!queue.isEmpty()) {
|
||||||
val currentMethod = queue.poll()
|
val currentMethod = queue.poll()
|
||||||
|
|
||||||
visited.add(currentMethod)
|
visited += currentMethod
|
||||||
addParameter(currentMethod, parametersToDelete, parameter)
|
addParameter(currentMethod, parametersToDelete, parameter)
|
||||||
|
|
||||||
for (superMethod in currentMethod.findSuperMethods(true)) {
|
currentMethod.findSuperMethods(true)
|
||||||
if (!visited.contains(superMethod)) {
|
.filter { it !in visited }
|
||||||
queue.offer(superMethod)
|
.forEach { queue.offer(it) }
|
||||||
}
|
OverridingMethodsSearch.search(currentMethod)
|
||||||
}
|
.filter { it !in visited }
|
||||||
for (overrider in OverridingMethodsSearch.search(currentMethod)) {
|
.forEach { queue.offer(it) }
|
||||||
if (!visited.contains(overrider)) {
|
|
||||||
queue.offer(overrider)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return parametersToDelete
|
return parametersToDelete
|
||||||
}
|
}
|
||||||
@@ -289,9 +252,8 @@ object KotlinRefactoringUtil2 {
|
|||||||
editor: Editor,
|
editor: Editor,
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
||||||
callback: (PsiElement?) -> Unit) {
|
callback: (PsiElement?) -> Unit
|
||||||
selectElement(editor, file, true, elementKinds, callback)
|
) = selectElement(editor, file, true, elementKinds, callback)
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IntroduceRefactoringException::class)
|
@Throws(IntroduceRefactoringException::class)
|
||||||
fun selectElement(editor: Editor,
|
fun selectElement(editor: Editor,
|
||||||
@@ -306,22 +268,18 @@ object KotlinRefactoringUtil2 {
|
|||||||
var firstElement: PsiElement = file.findElementAt(selectionStart)!!
|
var firstElement: PsiElement = file.findElementAt(selectionStart)!!
|
||||||
var lastElement: PsiElement = file.findElementAt(selectionEnd - 1)!!
|
var lastElement: PsiElement = file.findElementAt(selectionEnd - 1)!!
|
||||||
|
|
||||||
if (PsiTreeUtil.getParentOfType(firstElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null && PsiTreeUtil.getParentOfType(lastElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null) {
|
if (PsiTreeUtil.getParentOfType(firstElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null
|
||||||
|
&& PsiTreeUtil.getParentOfType(lastElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null) {
|
||||||
firstElement = firstElement.getNextSiblingIgnoringWhitespaceAndComments(true)!!
|
firstElement = firstElement.getNextSiblingIgnoringWhitespaceAndComments(true)!!
|
||||||
lastElement = lastElement.getPrevSiblingIgnoringWhitespaceAndComments(true)!!
|
lastElement = lastElement.getPrevSiblingIgnoringWhitespaceAndComments(true)!!
|
||||||
selectionStart = firstElement.textRange.startOffset
|
selectionStart = firstElement.textRange.startOffset
|
||||||
selectionEnd = lastElement.textRange.endOffset
|
selectionEnd = lastElement.textRange.endOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
for (elementKind in elementKinds) {
|
val element = elementKinds.asSequence()
|
||||||
val element = findElement(file, selectionStart, selectionEnd, failOnEmptySuggestion, elementKind)
|
.mapNotNull { findElement(file, selectionStart, selectionEnd, failOnEmptySuggestion, it) }
|
||||||
if (element != null) {
|
.firstOrNull()
|
||||||
callback(element)
|
callback(element)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val offset = editor.caretModel.offset
|
val offset = editor.caretModel.offset
|
||||||
@@ -333,15 +291,13 @@ object KotlinRefactoringUtil2 {
|
|||||||
fun getSmartSelectSuggestions(
|
fun getSmartSelectSuggestions(
|
||||||
file: PsiFile,
|
file: PsiFile,
|
||||||
offset: Int,
|
offset: Int,
|
||||||
elementKind: CodeInsightUtils.ElementKind): List<KtElement> {
|
elementKind: CodeInsightUtils.ElementKind
|
||||||
if (offset < 0) {
|
): List<KtElement> {
|
||||||
return ArrayList()
|
if (offset < 0) return emptyList()
|
||||||
}
|
|
||||||
|
|
||||||
var element: PsiElement? = file.findElementAt(offset) ?: return ArrayList()
|
var element: PsiElement? = file.findElementAt(offset) ?: return emptyList()
|
||||||
if (element is PsiWhiteSpace) {
|
|
||||||
return getSmartSelectSuggestions(file, offset - 1, elementKind)
|
if (element is PsiWhiteSpace) return getSmartSelectSuggestions(file, offset - 1, elementKind)
|
||||||
}
|
|
||||||
|
|
||||||
val elements = ArrayList<KtElement>()
|
val elements = ArrayList<KtElement>()
|
||||||
while (element != null && !(element is KtBlockExpression && element.parent !is KtFunctionLiteral) &&
|
while (element != null && !(element is KtBlockExpression && element.parent !is KtFunctionLiteral) &&
|
||||||
@@ -413,31 +369,28 @@ object KotlinRefactoringUtil2 {
|
|||||||
offset: Int,
|
offset: Int,
|
||||||
failOnEmptySuggestion: Boolean,
|
failOnEmptySuggestion: Boolean,
|
||||||
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
||||||
callback: (PsiElement?) -> Unit) {
|
callback: (PsiElement?) -> Unit
|
||||||
val elements = elementKinds.flatMap { kind -> getSmartSelectSuggestions(file, offset, kind) }
|
) {
|
||||||
if (elements.size == 0) {
|
val elements = elementKinds.flatMap { getSmartSelectSuggestions(file, offset, it) }
|
||||||
if (failOnEmptySuggestion)
|
if (elements.isEmpty()) {
|
||||||
throw IntroduceRefactoringException(
|
if (failOnEmptySuggestion) throw IntroduceRefactoringException(KotlinRefactoringBundle.message("cannot.refactor.not.expression"))
|
||||||
KotlinRefactoringBundle.message("cannot.refactor.not.expression"))
|
|
||||||
callback(null)
|
callback(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elements.size == 1 || ApplicationManager.getApplication().isUnitTestMode) {
|
if (elements.size == 1 || ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
callback(elements[0])
|
callback(elements.first())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val model = DefaultListModel<PsiElement>()
|
val model = DefaultListModel<PsiElement>()
|
||||||
for (element in elements) {
|
elements.forEach { model.addElement(it) }
|
||||||
model.addElement(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
val highlighter = ScopeHighlighter(editor)
|
val highlighter = ScopeHighlighter(editor)
|
||||||
|
|
||||||
val list = JBList(model)
|
val list = JBList(model)
|
||||||
|
|
||||||
list.setCellRenderer(object : DefaultListCellRenderer() {
|
list.cellRenderer = object : DefaultListCellRenderer() {
|
||||||
override fun getListCellRendererComponent(list: JList<*>, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component {
|
override fun getListCellRendererComponent(list: JList<*>, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component {
|
||||||
val rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
|
val rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
|
||||||
val element = value as KtElement?
|
val element = value as KtElement?
|
||||||
@@ -446,16 +399,16 @@ object KotlinRefactoringUtil2 {
|
|||||||
}
|
}
|
||||||
return rendererComponent
|
return rendererComponent
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
list.addListSelectionListener(ListSelectionListener {
|
list.addListSelectionListener {
|
||||||
highlighter.dropHighlight()
|
highlighter.dropHighlight()
|
||||||
val selectedIndex = list.getSelectedIndex()
|
val selectedIndex = list.selectedIndex
|
||||||
if (selectedIndex < 0) return@ListSelectionListener
|
if (selectedIndex < 0) return@addListSelectionListener
|
||||||
val toExtract = ArrayList<PsiElement>()
|
val toExtract = ArrayList<PsiElement>()
|
||||||
toExtract.add(model.get(selectedIndex))
|
toExtract.add(model.get(selectedIndex))
|
||||||
highlighter.highlight(model.get(selectedIndex), toExtract)
|
highlighter.highlight(model.get(selectedIndex), toExtract)
|
||||||
})
|
}
|
||||||
|
|
||||||
var title = "Elements"
|
var title = "Elements"
|
||||||
if (elementKinds.size == 1) {
|
if (elementKinds.size == 1) {
|
||||||
@@ -465,11 +418,22 @@ object KotlinRefactoringUtil2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JBPopupFactory.getInstance().createListPopupBuilder(list).setTitle(title).setMovable(false).setResizable(false).setRequestFocus(true).setItemChoosenCallback { callback(list.getSelectedValue() as KtElement) }.addListener(object : JBPopupAdapter() {
|
JBPopupFactory.getInstance()
|
||||||
override fun onClosed(event: LightweightWindowEvent?) {
|
.createListPopupBuilder(list)
|
||||||
highlighter.dropHighlight()
|
.setTitle(title)
|
||||||
}
|
.setMovable(false)
|
||||||
}).createPopup().showInBestPositionFor(editor)
|
.setResizable(false)
|
||||||
|
.setRequestFocus(true)
|
||||||
|
.setItemChoosenCallback { callback(list.selectedValue as KtElement) }
|
||||||
|
.addListener(
|
||||||
|
object : JBPopupAdapter() {
|
||||||
|
override fun onClosed(event: LightweightWindowEvent?) {
|
||||||
|
highlighter.dropHighlight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.createPopup()
|
||||||
|
.showInBestPositionFor(editor)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +451,8 @@ object KotlinRefactoringUtil2 {
|
|||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
failOnNoExpression: Boolean,
|
failOnNoExpression: Boolean,
|
||||||
elementKind: CodeInsightUtils.ElementKind): PsiElement? {
|
elementKind: CodeInsightUtils.ElementKind
|
||||||
|
): PsiElement? {
|
||||||
var element = CodeInsightUtils.findElement(file, startOffset, endOffset, elementKind)
|
var element = CodeInsightUtils.findElement(file, startOffset, endOffset, elementKind)
|
||||||
if (element == null && elementKind == CodeInsightUtils.ElementKind.EXPRESSION) {
|
if (element == null && elementKind == CodeInsightUtils.ElementKind.EXPRESSION) {
|
||||||
element = findExpressionOrStringFragment(file, startOffset, endOffset)
|
element = findExpressionOrStringFragment(file, startOffset, endOffset)
|
||||||
@@ -503,8 +468,5 @@ object KotlinRefactoringUtil2 {
|
|||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
|
|
||||||
class IntroduceRefactoringException(private val myMessage: String) : RuntimeException() {
|
class IntroduceRefactoringException(message: String) : RuntimeException(message)
|
||||||
override val message: String?
|
|
||||||
get() = myMessage
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user