Refactoring: Move jetRefactoringUtil.kt to idea module. Move compareDescriptors() and comparePossiblyOverridingDescriptors() to descriptorUtils.kt
This commit is contained in:
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.core
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
@@ -53,3 +55,32 @@ fun DeclarationDescriptorWithVisibility.isVisible(
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public fun compareDescriptors(project: Project, d1: DeclarationDescriptor?, d2: DeclarationDescriptor?): Boolean {
|
||||
if (d1 == d2) return true
|
||||
if (d1 == null || d2 == null) return false
|
||||
if (DescriptorToSourceUtilsIde.getAllDeclarations(project, d1) == DescriptorToSourceUtilsIde.getAllDeclarations(project, d2)) return true
|
||||
return DescriptorRenderer.FQ_NAMES_IN_TYPES.render(d1) == DescriptorRenderer.FQ_NAMES_IN_TYPES.render(d2)
|
||||
}
|
||||
|
||||
public fun comparePossiblyOverridingDescriptors(project: Project,
|
||||
currentDescriptor: DeclarationDescriptor?,
|
||||
originalDescriptor: DeclarationDescriptor?): Boolean {
|
||||
if (compareDescriptors(project, currentDescriptor, originalDescriptor)) return true
|
||||
if (originalDescriptor is CallableDescriptor) {
|
||||
if (!OverridingUtil.traverseOverridenDescriptors(originalDescriptor) { !compareDescriptors(project, currentDescriptor, it) }) return true
|
||||
if (originalDescriptor !is CallableMemberDescriptor || currentDescriptor !is CallableMemberDescriptor) return false
|
||||
val kind = originalDescriptor.getKind()
|
||||
if (kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE && kind != CallableMemberDescriptor.Kind.DELEGATION) return false
|
||||
if (currentDescriptor.getKind() != kind) return false
|
||||
|
||||
val originalOverriddenDescriptors = originalDescriptor.getOverriddenDescriptors()
|
||||
val currentOverriddenDescriptors = currentDescriptor.getOverriddenDescriptors()
|
||||
if (originalOverriddenDescriptors.size() != currentOverriddenDescriptors.size()) return false
|
||||
return (currentOverriddenDescriptors zip originalOverriddenDescriptors ).all {
|
||||
comparePossiblyOverridingDescriptors(project, it.first, it.second)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.refactoring
|
||||
|
||||
import com.intellij.psi.PsiElementFactory
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import com.intellij.psi.PsiReferenceList
|
||||
import com.intellij.psi.PsiReferenceList.Role
|
||||
|
||||
public fun PsiElementFactory.createReferenceListWithRole(
|
||||
references: Array<PsiJavaCodeReferenceElement>,
|
||||
role: Role
|
||||
): PsiReferenceList? {
|
||||
val refsText = references.map { it.getCanonicalText() }
|
||||
val refListText = if (refsText.isNotEmpty()) refsText.joinToString() else return null
|
||||
return when (role) {
|
||||
Role.THROWS_LIST -> createMethodFromText("void foo() throws $refListText {}", null).getThrowsList()
|
||||
Role.EXTENDS_LIST -> createClassFromText("class Foo extends $refListText {}", null).getInnerClasses()[0].getExtendsList()
|
||||
Role.IMPLEMENTS_LIST -> createClassFromText("class Foo implements $refListText {}", null).getInnerClasses()[0].getImplementsList()
|
||||
Role.EXTENDS_BOUNDS_LIST -> createTypeParameterFromText("T extends $refListText", null).getExtendsList()
|
||||
}
|
||||
}
|
||||
@@ -1,597 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.refactoring
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.openapi.roots.JavaProjectRootsUtil
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.refactoring.util.ConflictsUtil
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import org.jetbrains.kotlin.idea.JetFileType
|
||||
import com.intellij.openapi.project.Project
|
||||
import java.io.File
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import java.util.ArrayList
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
|
||||
import com.intellij.refactoring.ui.ConflictsDialog
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import com.intellij.ide.util.PsiElementListCellRenderer
|
||||
import com.intellij.openapi.ui.popup.JBPopup
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.codeInsight.unwrap.RangeSplitter
|
||||
import com.intellij.codeInsight.unwrap.UnwrapHandler
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import java.util.Collections
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.openapi.ui.popup.JBPopupAdapter
|
||||
import com.intellij.openapi.ui.popup.LightweightWindowEvent
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import javax.swing.Icon
|
||||
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightMethod
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import com.intellij.psi.PsiPackage
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
||||
import com.intellij.psi.PsiMember
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor
|
||||
import com.intellij.psi.PsiElementFactory
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.PsiTypeParameterList
|
||||
import com.intellij.refactoring.changeSignature.ChangeSignatureUtil
|
||||
import com.intellij.psi.PsiModifierList
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import com.intellij.psi.PsiField
|
||||
import com.intellij.util.VisibilityUtil
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import com.intellij.psi.PsiReferenceList
|
||||
import com.intellij.psi.PsiTypeParameter
|
||||
import com.intellij.psi.PsiTypeParameterListOwner
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
|
||||
fun <T: Any> PsiElement.getAndRemoveCopyableUserData(key: Key<T>): T? {
|
||||
val data = getCopyableUserData(key)
|
||||
putCopyableUserData(key, null)
|
||||
return data
|
||||
}
|
||||
|
||||
fun getOrCreateKotlinFile(fileName: String, targetDir: PsiDirectory): JetFile? =
|
||||
(targetDir.findFile(fileName) ?: createKotlinFile(fileName, targetDir)) as? JetFile
|
||||
|
||||
fun createKotlinFile(fileName: String, targetDir: PsiDirectory): JetFile {
|
||||
val packageName = targetDir.getPackage()?.getQualifiedName()
|
||||
|
||||
targetDir.checkCreateFile(fileName)
|
||||
val file = PsiFileFactory.getInstance(targetDir.getProject()).createFileFromText(
|
||||
fileName, JetFileType.INSTANCE, if (packageName != null && packageName.isNotEmpty()) "package $packageName \n\n" else ""
|
||||
)
|
||||
|
||||
return targetDir.add(file) as JetFile
|
||||
}
|
||||
|
||||
public fun File.toVirtualFile(): VirtualFile? = LocalFileSystem.getInstance().findFileByIoFile(this)
|
||||
|
||||
public fun File.toPsiFile(project: Project): PsiFile? {
|
||||
return toVirtualFile()?.let { vfile -> PsiManager.getInstance(project).findFile(vfile) }
|
||||
}
|
||||
|
||||
public fun File.toPsiDirectory(project: Project): PsiDirectory? {
|
||||
return toVirtualFile()?.let { vfile -> PsiManager.getInstance(project).findDirectory(vfile) }
|
||||
}
|
||||
|
||||
public fun PsiElement.getUsageContext(): PsiElement {
|
||||
return when (this) {
|
||||
is JetElement -> PsiTreeUtil.getParentOfType(this, javaClass<JetNamedDeclaration>(), javaClass<JetFile>())!!
|
||||
else -> ConflictsUtil.getContainer(this)
|
||||
}
|
||||
}
|
||||
|
||||
public fun PsiElement.isInJavaSourceRoot(): Boolean =
|
||||
!JavaProjectRootsUtil.isOutsideJavaSourceRoot(getContainingFile())
|
||||
|
||||
public inline fun JetFile.createTempCopy(textTransform: (String) -> String): JetFile {
|
||||
val tmpFile = JetPsiFactory(this).createAnalyzableFile(getName(), textTransform(getText() ?: ""), this)
|
||||
tmpFile.setOriginalFile(this)
|
||||
tmpFile.suppressDiagnosticsInDebugMode = suppressDiagnosticsInDebugMode
|
||||
return tmpFile
|
||||
}
|
||||
|
||||
public fun PsiElement.getAllExtractionContainers(strict: Boolean = true): List<JetElement> {
|
||||
val containers = ArrayList<JetElement>()
|
||||
|
||||
var element: PsiElement? = if (strict) getParent() else this
|
||||
while (element != null) {
|
||||
when (element) {
|
||||
is JetBlockExpression, is JetClassBody, is JetFile -> containers.add(element as JetElement)
|
||||
}
|
||||
|
||||
element = element!!.getParent()
|
||||
}
|
||||
|
||||
return containers
|
||||
}
|
||||
|
||||
public fun PsiElement.getExtractionContainers(strict: Boolean = true, includeAll: Boolean = false): List<JetElement> {
|
||||
fun getEnclosingDeclaration(element: PsiElement, strict: Boolean): PsiElement? {
|
||||
return element.parents(!strict)
|
||||
.filter {
|
||||
(it is JetDeclarationWithBody && it !is JetFunctionLiteral)
|
||||
|| it is JetClassInitializer
|
||||
|| it is JetClassBody
|
||||
|| it is JetFile
|
||||
}
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
if (includeAll) return getAllExtractionContainers(strict)
|
||||
|
||||
val enclosingDeclaration = getEnclosingDeclaration(this, strict)?.let {
|
||||
if (it is JetDeclarationWithBody || it is JetClassInitializer) getEnclosingDeclaration(it, true) else it
|
||||
}
|
||||
|
||||
return when (enclosingDeclaration) {
|
||||
is JetFile -> Collections.singletonList(enclosingDeclaration)
|
||||
is JetClassBody -> getAllExtractionContainers(strict).filterIsInstance<JetClassBody>()
|
||||
else -> {
|
||||
val targetContainer = when (enclosingDeclaration) {
|
||||
is JetDeclarationWithBody -> enclosingDeclaration.getBodyExpression()
|
||||
is JetClassInitializer -> enclosingDeclaration.getBody()
|
||||
else -> null
|
||||
}
|
||||
if (targetContainer is JetBlockExpression) Collections.singletonList(targetContainer) else Collections.emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun Project.checkConflictsInteractively(conflicts: MultiMap<PsiElement, String>, onAccept: () -> Unit) {
|
||||
if (!conflicts.isEmpty()) {
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode()) throw ConflictsInTestsException(conflicts.values())
|
||||
|
||||
val dialog = ConflictsDialog(this, conflicts, onAccept)
|
||||
dialog.show()
|
||||
if (!dialog.isOK()) return
|
||||
}
|
||||
|
||||
onAccept()
|
||||
}
|
||||
|
||||
public fun reportDeclarationConflict(
|
||||
conflicts: MultiMap<PsiElement, String>,
|
||||
declaration: PsiElement,
|
||||
message: (renderedDeclaration: String) -> String
|
||||
) {
|
||||
conflicts.putValue(declaration, message(RefactoringUIUtil.getDescription(declaration, true).capitalize()))
|
||||
}
|
||||
|
||||
public fun <T, E: PsiElement> getPsiElementPopup(
|
||||
editor: Editor,
|
||||
elements: List<T>,
|
||||
renderer: PsiElementListCellRenderer<E>,
|
||||
title: String?,
|
||||
highlightSelection: Boolean,
|
||||
toPsi: (T) -> E,
|
||||
processor: (T) -> Boolean): JBPopup {
|
||||
val highlighter = if (highlightSelection) SelectionAwareScopeHighlighter(editor) else null
|
||||
|
||||
val list = JBList(elements.map(toPsi))
|
||||
list.setCellRenderer(renderer)
|
||||
list.addListSelectionListener { e ->
|
||||
highlighter?.dropHighlight()
|
||||
val index = list.getSelectedIndex()
|
||||
if (index >= 0) {
|
||||
highlighter?.highlight(list.getModel()!!.getElementAt(index) as PsiElement)
|
||||
}
|
||||
}
|
||||
|
||||
return with(PopupChooserBuilder(list)) {
|
||||
title?.let { setTitle(it) }
|
||||
renderer.installSpeedSearch(this, true)
|
||||
setItemChoosenCallback {
|
||||
val index = list.getSelectedIndex()
|
||||
if (index >= 0) {
|
||||
processor(elements[index])
|
||||
}
|
||||
}
|
||||
addListener(object: JBPopupAdapter() {
|
||||
override fun onClosed(event: LightweightWindowEvent?) {
|
||||
highlighter?.dropHighlight();
|
||||
}
|
||||
})
|
||||
|
||||
createPopup()
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectionAwareScopeHighlighter(val editor: Editor) {
|
||||
private val highlighters = ArrayList<RangeHighlighter>()
|
||||
|
||||
private fun addHighlighter(r: TextRange, attr: TextAttributes) {
|
||||
highlighters.add(
|
||||
editor.getMarkupModel().addRangeHighlighter(
|
||||
r.getStartOffset(),
|
||||
r.getEndOffset(),
|
||||
UnwrapHandler.HIGHLIGHTER_LEVEL,
|
||||
attr,
|
||||
HighlighterTargetArea.EXACT_RANGE
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public fun highlight(wholeAffected: PsiElement) {
|
||||
dropHighlight()
|
||||
|
||||
val attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)!!
|
||||
val selectedRange = with(editor.getSelectionModel()) { TextRange(getSelectionStart(), getSelectionEnd()) }
|
||||
for (r in RangeSplitter.split(wholeAffected.getTextRange()!!, Collections.singletonList(selectedRange))) {
|
||||
addHighlighter(r, attributes)
|
||||
}
|
||||
}
|
||||
|
||||
public fun dropHighlight() {
|
||||
highlighters.forEach { it.dispose() }
|
||||
highlighters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiElement.getLineCount(): Int {
|
||||
val doc = getContainingFile()?.let { file -> PsiDocumentManager.getInstance(getProject()).getDocument(file) }
|
||||
if (doc != null) {
|
||||
val spaceRange = getTextRange() ?: TextRange.EMPTY_RANGE
|
||||
|
||||
val startLine = doc.getLineNumber(spaceRange.getStartOffset())
|
||||
val endLine = doc.getLineNumber(spaceRange.getEndOffset())
|
||||
|
||||
return endLine - startLine
|
||||
}
|
||||
|
||||
return (getText() ?: "").count { it == '\n' } + 1
|
||||
}
|
||||
|
||||
fun PsiElement.isMultiLine(): Boolean = getLineCount() > 1
|
||||
|
||||
public fun JetElement.getContextForContainingDeclarationBody(): BindingContext? {
|
||||
val enclosingDeclaration = getStrictParentOfType<JetDeclaration>()
|
||||
val bodyElement = when (enclosingDeclaration) {
|
||||
is JetDeclarationWithBody -> enclosingDeclaration.getBodyExpression()
|
||||
is JetWithExpressionInitializer -> enclosingDeclaration.getInitializer()
|
||||
is JetMultiDeclaration -> enclosingDeclaration.getInitializer()
|
||||
is JetParameter -> enclosingDeclaration.getDefaultValue()
|
||||
is JetClassInitializer -> enclosingDeclaration.getBody()
|
||||
is JetClass -> {
|
||||
val delegationSpecifierList = enclosingDeclaration.getDelegationSpecifierList()
|
||||
if (delegationSpecifierList.isAncestor(this)) this else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
return bodyElement?.let { it.analyze() }
|
||||
}
|
||||
|
||||
public fun chooseContainerElement<T>(
|
||||
containers: List<T>,
|
||||
editor: Editor,
|
||||
title: String,
|
||||
highlightSelection: Boolean,
|
||||
toPsi: (T) -> PsiElement,
|
||||
onSelect: (T) -> Unit) {
|
||||
return getPsiElementPopup(
|
||||
editor,
|
||||
containers,
|
||||
object : PsiElementListCellRenderer<PsiElement>() {
|
||||
private fun PsiElement.renderName(): String {
|
||||
if (this is JetPropertyAccessor) {
|
||||
return (getParent() as JetProperty).renderName() + if (isGetter()) ".get" else ".set"
|
||||
}
|
||||
if (this is JetObjectDeclaration && this.isCompanion()) {
|
||||
return "Companion object of ${getStrictParentOfType<JetClassOrObject>()?.renderName() ?: "<anonymous>"}"
|
||||
}
|
||||
return (this as? PsiNamedElement)?.getName() ?: "<anonymous>"
|
||||
}
|
||||
|
||||
private fun PsiElement.renderDeclaration(): String? {
|
||||
val descriptor = when {
|
||||
this is JetFile -> getName()
|
||||
this is JetElement -> analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, this]
|
||||
this is PsiMember -> getJavaMemberDescriptor()
|
||||
else -> null
|
||||
} ?: return null
|
||||
val name = renderName()
|
||||
val params = (descriptor as? FunctionDescriptor)?.let { descriptor ->
|
||||
descriptor.getValueParameters()
|
||||
.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it.getType()) }
|
||||
.joinToString(", ", "(", ")")
|
||||
} ?: ""
|
||||
return "$name$params"
|
||||
}
|
||||
|
||||
private fun PsiElement.renderText(): String {
|
||||
return StringUtil.shortenTextWithEllipsis(getText()!!.collapseSpaces(), 53, 0)
|
||||
}
|
||||
|
||||
private fun PsiElement.getRepresentativeElement(): PsiElement {
|
||||
return when (this) {
|
||||
is JetBlockExpression -> (getParent() as? JetDeclarationWithBody) ?: this
|
||||
is JetClassBody -> getParent() as JetClassOrObject
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
override fun getElementText(element: PsiElement): String? {
|
||||
val representativeElement = element.getRepresentativeElement()
|
||||
return representativeElement.renderDeclaration() ?: representativeElement.renderText()
|
||||
}
|
||||
|
||||
override fun getContainerText(element: PsiElement, name: String?): String? = null
|
||||
|
||||
override fun getIconFlags(): Int = 0
|
||||
|
||||
override fun getIcon(element: PsiElement): Icon? =
|
||||
super.getIcon(element.getRepresentativeElement())
|
||||
},
|
||||
title,
|
||||
highlightSelection,
|
||||
toPsi,
|
||||
{
|
||||
onSelect(it)
|
||||
true
|
||||
}
|
||||
).showInBestPositionFor(editor)
|
||||
}
|
||||
|
||||
public fun chooseContainerElementIfNecessary<T>(
|
||||
containers: List<T>,
|
||||
editor: Editor,
|
||||
title: String,
|
||||
highlightSelection: Boolean,
|
||||
toPsi: (T) -> PsiElement,
|
||||
onSelect: (T) -> Unit
|
||||
) {
|
||||
when {
|
||||
containers.isEmpty() -> return
|
||||
containers.size() == 1 || ApplicationManager.getApplication()!!.isUnitTestMode() -> onSelect(containers.first())
|
||||
else -> chooseContainerElement(containers, editor, title, highlightSelection, toPsi, onSelect)
|
||||
}
|
||||
}
|
||||
|
||||
public fun PsiElement.isTrueJavaMethod(): Boolean = this is PsiMethod && this !is KotlinLightMethod
|
||||
|
||||
fun compareDescriptors(project: Project, d1: DeclarationDescriptor?, d2: DeclarationDescriptor?): Boolean {
|
||||
if (d1 == d2) return true
|
||||
if (d1 == null || d2 == null) return false
|
||||
if (DescriptorToSourceUtilsIde.getAllDeclarations(project, d1) == DescriptorToSourceUtilsIde.getAllDeclarations(project, d2)) return true
|
||||
return DescriptorRenderer.FQ_NAMES_IN_TYPES.render(d1) == DescriptorRenderer.FQ_NAMES_IN_TYPES.render(d2)
|
||||
}
|
||||
|
||||
public fun comparePossiblyOverridingDescriptors(project: Project,
|
||||
currentDescriptor: DeclarationDescriptor?,
|
||||
originalDescriptor: DeclarationDescriptor?): Boolean {
|
||||
if (compareDescriptors(project, currentDescriptor, originalDescriptor)) return true
|
||||
if (originalDescriptor is CallableDescriptor) {
|
||||
if (!OverridingUtil.traverseOverridenDescriptors(originalDescriptor) { !compareDescriptors(project, currentDescriptor, it) }) return true
|
||||
if (originalDescriptor !is CallableMemberDescriptor || currentDescriptor !is CallableMemberDescriptor) return false
|
||||
val kind = originalDescriptor.getKind()
|
||||
if (kind != Kind.FAKE_OVERRIDE && kind != Kind.DELEGATION) return false
|
||||
if (currentDescriptor.getKind() != kind) return false
|
||||
|
||||
val originalOverriddenDescriptors = originalDescriptor.getOverriddenDescriptors()
|
||||
val currentOverriddenDescriptors = currentDescriptor.getOverriddenDescriptors()
|
||||
if (originalOverriddenDescriptors.size() != currentOverriddenDescriptors.size()) return false
|
||||
return (currentOverriddenDescriptors zip originalOverriddenDescriptors ).all {
|
||||
comparePossiblyOverridingDescriptors(project, it.first, it.second)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public fun PsiElement.canRefactor(): Boolean {
|
||||
return when {
|
||||
this is PsiPackage ->
|
||||
getDirectories().any { it.canRefactor() }
|
||||
this is JetElement,
|
||||
this is PsiMember && getLanguage() == JavaLanguage.INSTANCE,
|
||||
this is PsiDirectory ->
|
||||
isWritable() && ProjectRootsUtil.isInProjectSource(this)
|
||||
else ->
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyModifierListItems(from: PsiModifierList, to: PsiModifierList, withPsiModifiers: Boolean = true) {
|
||||
if (withPsiModifiers) {
|
||||
for (modifier in PsiModifier.MODIFIERS) {
|
||||
if (from.hasExplicitModifier(modifier)) {
|
||||
to.setModifierProperty(modifier, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (annotation in from.getAnnotations()) {
|
||||
to.addAnnotation(annotation.getQualifiedName())
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyTypeParameters<T: PsiTypeParameterListOwner>(
|
||||
from: T,
|
||||
to: T,
|
||||
inserter: (T, PsiTypeParameterList) -> Unit
|
||||
) where T : PsiNameIdentifierOwner {
|
||||
val factory = PsiElementFactory.SERVICE.getInstance((from : PsiElement).getProject())
|
||||
val templateTypeParams = from.getTypeParameterList()?.getTypeParameters() ?: PsiTypeParameter.EMPTY_ARRAY
|
||||
if (templateTypeParams.isNotEmpty()) {
|
||||
inserter(to, factory.createTypeParameterList())
|
||||
val targetTypeParamList = to.getTypeParameterList()
|
||||
val newTypeParams = templateTypeParams.map {
|
||||
factory.createTypeParameter(it.getName(), it.getExtendsList().getReferencedTypes())
|
||||
}
|
||||
ChangeSignatureUtil.synchronizeList(
|
||||
targetTypeParamList,
|
||||
newTypeParams,
|
||||
{ it.getTypeParameters().toList() },
|
||||
BooleanArray(newTypeParams.size())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public fun createJavaMethod(function: JetFunction, targetClass: PsiClass): PsiMethod {
|
||||
val template = LightClassUtil.getLightClassMethod(function)
|
||||
?: throw AssertionError("Can't generate light method: ${function.getElementTextWithContext()}")
|
||||
return createJavaMethod(template, targetClass)
|
||||
}
|
||||
|
||||
public fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMethod {
|
||||
val factory = PsiElementFactory.SERVICE.getInstance(template.getProject())
|
||||
val methodToAdd = if (template.isConstructor()) {
|
||||
factory.createConstructor(template.getName())
|
||||
}
|
||||
else {
|
||||
factory.createMethod(template.getName(), template.getReturnType())
|
||||
}
|
||||
val method = targetClass.add(methodToAdd) as PsiMethod
|
||||
|
||||
copyModifierListItems(template.getModifierList(), method.getModifierList())
|
||||
|
||||
copyTypeParameters(template, method) { method, typeParameterList ->
|
||||
method.addAfter(typeParameterList, method.getModifierList())
|
||||
}
|
||||
|
||||
val targetParamList = method.getParameterList()
|
||||
val newParams = template.getParameterList().getParameters().map {
|
||||
val param = factory.createParameter(it.getName(), it.getType())
|
||||
copyModifierListItems(it.getModifierList(), param.getModifierList())
|
||||
param
|
||||
}
|
||||
ChangeSignatureUtil.synchronizeList(
|
||||
targetParamList,
|
||||
newParams,
|
||||
{ it.getParameters().toList() },
|
||||
BooleanArray(newParams.size())
|
||||
)
|
||||
|
||||
if (template.getModifierList().hasModifierProperty(PsiModifier.ABSTRACT) || targetClass.isInterface()) {
|
||||
method.getBody().delete()
|
||||
}
|
||||
else if (!template.isConstructor()) {
|
||||
CreateFromUsageUtils.setupMethodBody(method)
|
||||
}
|
||||
|
||||
return method
|
||||
}
|
||||
|
||||
fun createJavaField(property: JetProperty, targetClass: PsiClass): PsiField {
|
||||
val template = LightClassUtil.getLightClassPropertyMethods(property).getGetter()
|
||||
?: throw AssertionError("Can't generate light method: ${property.getElementTextWithContext()}")
|
||||
|
||||
val factory = PsiElementFactory.SERVICE.getInstance(template.getProject())
|
||||
val field = targetClass.add(factory.createField(property.getName(), template.getReturnType())) as PsiField
|
||||
|
||||
with(field.getModifierList()) {
|
||||
val templateModifiers = template.getModifierList()
|
||||
setModifierProperty(VisibilityUtil.getVisibilityModifier(templateModifiers), true)
|
||||
if (!property.isVar() || targetClass.isInterface()) {
|
||||
setModifierProperty(PsiModifier.FINAL, true)
|
||||
}
|
||||
copyModifierListItems(templateModifiers, this, false)
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
fun createJavaClass(klass: JetClass, targetClass: PsiClass): PsiMember {
|
||||
val kind = (klass.resolveToDescriptor() as ClassDescriptor).getKind()
|
||||
|
||||
val factory = PsiElementFactory.SERVICE.getInstance(klass.getProject())
|
||||
val javaClassToAdd = when (kind) {
|
||||
ClassKind.CLASS -> factory.createClass(klass.getName())
|
||||
ClassKind.TRAIT -> factory.createInterface(klass.getName())
|
||||
ClassKind.ANNOTATION_CLASS -> factory.createAnnotationType(klass.getName())
|
||||
ClassKind.ENUM_CLASS -> factory.createEnum(klass.getName())
|
||||
else -> throw AssertionError("Unexpected class kind: ${klass.getElementTextWithContext()}")
|
||||
}
|
||||
val javaClass = targetClass.add(javaClassToAdd) as PsiClass
|
||||
|
||||
val template = LightClassUtil.getPsiClass(klass)
|
||||
?: throw AssertionError("Can't generate light class: ${klass.getElementTextWithContext()}")
|
||||
|
||||
copyModifierListItems(template.getModifierList(), javaClass.getModifierList())
|
||||
if (template.isInterface()) {
|
||||
javaClass.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false)
|
||||
}
|
||||
|
||||
copyTypeParameters(template, javaClass) { klass, typeParameterList ->
|
||||
klass.addAfter(typeParameterList, klass.getNameIdentifier())
|
||||
}
|
||||
|
||||
val extendsList = factory.createReferenceListWithRole(
|
||||
template.getExtendsList()?.getReferenceElements() ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY,
|
||||
PsiReferenceList.Role.EXTENDS_LIST
|
||||
)
|
||||
extendsList?.let { javaClass.getExtendsList()?.replace(it) }
|
||||
|
||||
val implementsList = factory.createReferenceListWithRole(
|
||||
template.getImplementsList()?.getReferenceElements() ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY,
|
||||
PsiReferenceList.Role.IMPLEMENTS_LIST
|
||||
)
|
||||
implementsList?.let { javaClass.getImplementsList()?.replace(it) }
|
||||
|
||||
for (method in template.getMethods()) {
|
||||
val hasParams = method.getParameterList().getParametersCount() > 0
|
||||
val needSuperCall = !template.isEnum() &&
|
||||
(template.getSuperClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY).all {
|
||||
it.getParameterList().getParametersCount() > 0
|
||||
}
|
||||
if (method.isConstructor() && !(hasParams || needSuperCall)) continue
|
||||
with(createJavaMethod(method, javaClass)) {
|
||||
if (isConstructor() && needSuperCall) {
|
||||
getBody().add(factory.createStatementFromText("super();", this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return javaClass
|
||||
}
|
||||
Reference in New Issue
Block a user