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