Smaller range + refactoring of ConvertMemberToExtension
This commit is contained in:
@@ -330,8 +330,6 @@ hierarchy.legend.member.is.defined.in.class=Member is defined in the class
|
||||
hierarchy.legend.member.defined.in.superclass=Member is not defined in the class but defined in superclass
|
||||
hierarchy.legend.member.should.be.defined=Member should be defined since the class is not abstract
|
||||
|
||||
convert.to.extension=Convert to extension
|
||||
|
||||
suppress.warnings.family=Suppress Warnings
|
||||
suppress.warning.for=Suppress ''{0}'' for {1} {2}
|
||||
|
||||
|
||||
+84
-102
@@ -17,79 +17,69 @@
|
||||
package org.jetbrains.kotlin.idea.intentions.declarations
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtilCore
|
||||
import com.intellij.codeInsight.intention.impl.BaseIntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.Function
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention
|
||||
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
|
||||
|
||||
public class ConvertMemberToExtension : BaseIntentionAction() {
|
||||
|
||||
override fun getText(): String {
|
||||
return getFamilyName()
|
||||
}
|
||||
|
||||
override fun getFamilyName(): String {
|
||||
return JetBundle.message("convert.to.extension")
|
||||
}
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
|
||||
val declaration = getTarget(editor, file)
|
||||
if (declaration is JetProperty) {
|
||||
if (declaration.hasInitializer()) return false
|
||||
public class ConvertMemberToExtension : JetSelfTargetingRangeIntention<JetCallableDeclaration>(javaClass(), "Convert to extension") {
|
||||
override fun applicabilityRange(element: JetCallableDeclaration): TextRange? {
|
||||
val classBody = element.getParent() as? JetClassBody ?: return null
|
||||
if (classBody.getParent() !is JetClass) return null
|
||||
if (element.getReceiverTypeReference() != null) return null
|
||||
when (element) {
|
||||
is JetProperty -> if (element.hasInitializer()) return null
|
||||
is JetSecondaryConstructor -> return null
|
||||
}
|
||||
return declaration != null && declaration !is JetSecondaryConstructor && declaration.getParent() is JetClassBody && declaration.getParent().getParent() is JetClass && declaration.getReceiverTypeReference() == null
|
||||
return (element.getNameIdentifier() ?: return null).getTextRange()
|
||||
}
|
||||
|
||||
throws(IncorrectOperationException::class)
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile) {
|
||||
val member = getTarget(editor, file)
|
||||
assert(member != null, "Must be checked by isAvailable")
|
||||
override fun applyTo(element: JetCallableDeclaration, editor: Editor) {
|
||||
val descriptor = element.resolveToDescriptor()
|
||||
val containingClass = descriptor.getContainingDeclaration() as ClassDescriptor
|
||||
|
||||
val bindingContext = member!!.analyzeFully()
|
||||
val memberDescriptor = bindingContext.get<PsiElement, DeclarationDescriptor>(DECLARATION_TO_DESCRIPTOR, member) ?: return
|
||||
val file = element.getContainingJetFile()
|
||||
val outermostParent = JetPsiUtil.getOutermostParent(element, file, false)
|
||||
|
||||
val containingClass = memberDescriptor.getContainingDeclaration()
|
||||
assert(containingClass is ClassDescriptor) { "Members must be contained in classes: \n" + "Descriptor: " + memberDescriptor + "\n" + "Declaration & context: " + member.getParent().getParent().getText() }
|
||||
val receiver = containingClass.getDefaultType().toString() + "."
|
||||
val name = element.getNameIdentifier()!!.getText()
|
||||
|
||||
val outermostParent = JetPsiUtil.getOutermostParent(member, file, false)
|
||||
val valueParameterList = element.getValueParameterList()
|
||||
val returnTypeRef = element.getTypeReference()
|
||||
|
||||
val receiver = (containingClass as ClassDescriptor).getDefaultType().toString() + "."
|
||||
val identifier = member.getNameIdentifier()
|
||||
val name = if (identifier == null) "" else identifier.getText()
|
||||
val extensionText = modifiers(element) +
|
||||
memberType(element) +
|
||||
" " +
|
||||
typeParameters(element) +
|
||||
receiver +
|
||||
name +
|
||||
(if (valueParameterList == null) "" else valueParameterList.getText()) +
|
||||
(if (returnTypeRef != null) ": " + returnTypeRef.getText() else "") +
|
||||
body(element)
|
||||
|
||||
val valueParameterList = member.getValueParameterList()
|
||||
val returnTypeRef = member.getTypeReference()
|
||||
|
||||
val extensionText = modifiers(member) + memberType(member) + " " + typeParameters(member) + receiver + name + (if (valueParameterList == null) "" else valueParameterList.getText()) + (if (returnTypeRef != null) ": " + returnTypeRef.getText() else "") + body(member)
|
||||
|
||||
val psiFactory = JetPsiFactory(member)
|
||||
val psiFactory = JetPsiFactory(element)
|
||||
val extension = psiFactory.createDeclaration<JetDeclaration>(extensionText)
|
||||
|
||||
val added = file.addAfter(extension, outermostParent)
|
||||
file.addAfter(psiFactory.createNewLine(), outermostParent)
|
||||
file.addAfter(psiFactory.createNewLine(), outermostParent)
|
||||
member.delete()
|
||||
element.delete()
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement<PsiElement>(added)
|
||||
|
||||
val caretAnchor = added.getText().indexOf(CARET_ANCHOR)
|
||||
if (caretAnchor >= 0) {
|
||||
val caretOffset = added.getTextRange().getStartOffset() + caretAnchor
|
||||
val anchor = PsiTreeUtil.findElementOfClassAtOffset<JetSimpleNameExpression>(file, caretOffset, javaClass<JetSimpleNameExpression>(), false)
|
||||
val anchor = PsiTreeUtil.findElementOfClassAtOffset(file, caretOffset, javaClass<JetSimpleNameExpression>(), false)
|
||||
if (anchor != null && CARET_ANCHOR == anchor.getReferencedName()) {
|
||||
val throwException = psiFactory.createExpression(THROW_UNSUPPORTED_OPERATION_EXCEPTION)
|
||||
val replaced = anchor.replace(throwException)
|
||||
@@ -100,76 +90,68 @@ public class ConvertMemberToExtension : BaseIntentionAction() {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val CARET_ANCHOR = "____CARET_ANCHOR____"
|
||||
private val THROW_UNSUPPORTED_OPERATION_EXCEPTION = " throw UnsupportedOperationException()"
|
||||
|
||||
public val CARET_ANCHOR: String = "____CARET_ANCHOR____"
|
||||
public val THROW_UNSUPPORTED_OPERATION_EXCEPTION: String = " throw UnsupportedOperationException()"
|
||||
|
||||
private fun getTarget(editor: Editor, file: PsiFile): JetCallableDeclaration? {
|
||||
val element = file.findElementAt(editor.getCaretModel().getOffset())
|
||||
return PsiTreeUtil.getParentOfType<JetCallableDeclaration>(element, javaClass<JetCallableDeclaration>(), false, javaClass<JetExpression>())
|
||||
private fun memberType(member: JetCallableDeclaration): String {
|
||||
if (member is JetFunction) {
|
||||
return "fun"
|
||||
}
|
||||
return (member as JetProperty).getValOrVarNode().getText()
|
||||
}
|
||||
|
||||
private fun memberType(member: JetCallableDeclaration): String {
|
||||
if (member is JetFunction) {
|
||||
return "fun"
|
||||
private fun modifiers(member: JetCallableDeclaration): String {
|
||||
val modifierList = member.getModifierList() ?: return ""
|
||||
for (modifierType in JetTokens.VISIBILITY_MODIFIERS.getTypes()) {
|
||||
val modifier = modifierList.getModifier(modifierType as JetModifierKeywordToken)
|
||||
if (modifier != null) {
|
||||
return if (modifierType == JetTokens.PROTECTED_KEYWORD) "" else modifier.getText() + " "
|
||||
}
|
||||
return (member as JetProperty).getValOrVarNode().getText()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
private fun modifiers(member: JetCallableDeclaration): String {
|
||||
val modifierList = member.getModifierList() ?: return ""
|
||||
for (modifierType in JetTokens.VISIBILITY_MODIFIERS.getTypes()) {
|
||||
val modifier = modifierList.getModifier(modifierType as JetModifierKeywordToken)
|
||||
if (modifier != null) {
|
||||
return if (modifierType == JetTokens.PROTECTED_KEYWORD) "" else modifier.getText() + " "
|
||||
}
|
||||
private fun typeParameters(member: JetCallableDeclaration): String {
|
||||
val classElement = member.getParent().getParent()
|
||||
assert(classElement is JetClass) { "Must be checked in isAvailable: " + classElement.getText() }
|
||||
|
||||
val allTypeParameters = ContainerUtil.concat<JetTypeParameter>((classElement as JetClass).getTypeParameters(), member.getTypeParameters())
|
||||
if (allTypeParameters.isEmpty()) return ""
|
||||
return "<" + StringUtil.join<JetTypeParameter>(allTypeParameters, object : Function<JetTypeParameter, String> {
|
||||
override fun `fun`(parameter: JetTypeParameter): String {
|
||||
return parameter.getText()
|
||||
}
|
||||
}, ", ") + "> "
|
||||
}
|
||||
|
||||
private fun body(member: JetCallableDeclaration): String {
|
||||
if (member is JetProperty) {
|
||||
return "\n" + getter(member) + "\n" + setter(member, !synthesizeBody(member.getGetter()))
|
||||
}
|
||||
else if (member is JetFunction) {
|
||||
val bodyExpression = member.getBodyExpression() ?: return "{" + CARET_ANCHOR + "}"
|
||||
if (!member.hasBlockBody()) return " = " + bodyExpression.getText()
|
||||
return bodyExpression.getText()
|
||||
}
|
||||
else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun typeParameters(member: JetCallableDeclaration): String {
|
||||
val classElement = member.getParent().getParent()
|
||||
assert(classElement is JetClass) { "Must be checked in isAvailable: " + classElement.getText() }
|
||||
private fun getter(property: JetProperty): String {
|
||||
val getter = property.getGetter()
|
||||
if (synthesizeBody(getter)) return "get() = " + CARET_ANCHOR
|
||||
return getter!!.getText()
|
||||
}
|
||||
|
||||
val allTypeParameters = ContainerUtil.concat<JetTypeParameter>((classElement as JetClass).getTypeParameters(), member.getTypeParameters())
|
||||
if (allTypeParameters.isEmpty()) return ""
|
||||
return "<" + StringUtil.join<JetTypeParameter>(allTypeParameters, object : Function<JetTypeParameter, String> {
|
||||
override fun `fun`(parameter: JetTypeParameter): String {
|
||||
return parameter.getText()
|
||||
}
|
||||
}, ", ") + "> "
|
||||
}
|
||||
private fun setter(property: JetProperty, allowCaretAnchor: Boolean): String {
|
||||
if (!property.isVar()) return ""
|
||||
val setter = property.getSetter()
|
||||
if (synthesizeBody(setter)) return "set(value) {" + (if (allowCaretAnchor) CARET_ANCHOR else THROW_UNSUPPORTED_OPERATION_EXCEPTION) + "}"
|
||||
return setter!!.getText()
|
||||
}
|
||||
|
||||
private fun body(member: JetCallableDeclaration): String {
|
||||
if (member is JetProperty) {
|
||||
return "\n" + getter(member) + "\n" + setter(member, !synthesizeBody(member.getGetter()))
|
||||
}
|
||||
else if (member is JetFunction) {
|
||||
val bodyExpression = member.getBodyExpression() ?: return "{" + CARET_ANCHOR + "}"
|
||||
if (!member.hasBlockBody()) return " = " + bodyExpression.getText()
|
||||
return bodyExpression.getText()
|
||||
}
|
||||
else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun getter(property: JetProperty): String {
|
||||
val getter = property.getGetter()
|
||||
if (synthesizeBody(getter)) return "get() = " + CARET_ANCHOR
|
||||
return getter.getText()
|
||||
}
|
||||
|
||||
private fun setter(property: JetProperty, allowCaretAnchor: Boolean): String {
|
||||
if (!property.isVar()) return ""
|
||||
val setter = property.getSetter()
|
||||
if (synthesizeBody(setter)) return "set(value) {" + (if (allowCaretAnchor) CARET_ANCHOR else THROW_UNSUPPORTED_OPERATION_EXCEPTION) + "}"
|
||||
return setter.getText()
|
||||
}
|
||||
|
||||
private fun synthesizeBody(getter: JetPropertyAccessor?): Boolean {
|
||||
return getter == null || getter.getBodyExpression() == null
|
||||
}
|
||||
private fun synthesizeBody(getter: JetPropertyAccessor?): Boolean {
|
||||
return getter == null || getter.getBodyExpression() == null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Function declaration must have a name
|
||||
class Owner {
|
||||
fun <caret>() {}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// ERROR: Function declaration must have a name
|
||||
class Owner {
|
||||
}
|
||||
|
||||
fun Owner.() {
|
||||
}
|
||||
Reference in New Issue
Block a user