Smaller range + refactoring of ConvertMemberToExtension

This commit is contained in:
Valentin Kipyatkov
2015-05-11 20:09:51 +03:00
parent 04ec19cb4a
commit a23c6a2cdc
4 changed files with 85 additions and 110 deletions
@@ -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.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 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.warnings.family=Suppress Warnings
suppress.warning.for=Suppress ''{0}'' for {1} {2} suppress.warning.for=Suppress ''{0}'' for {1} {2}
@@ -17,79 +17,69 @@
package org.jetbrains.kotlin.idea.intentions.declarations package org.jetbrains.kotlin.idea.intentions.declarations
import com.intellij.codeInsight.CodeInsightUtilCore import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.codeInsight.intention.impl.BaseIntentionAction
import com.intellij.openapi.editor.Editor 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.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.Function import com.intellij.util.Function
import com.intellij.util.IncorrectOperationException
import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
public class ConvertMemberToExtension : BaseIntentionAction() { public class ConvertMemberToExtension : JetSelfTargetingRangeIntention<JetCallableDeclaration>(javaClass(), "Convert to extension") {
override fun applicabilityRange(element: JetCallableDeclaration): TextRange? {
override fun getText(): String { val classBody = element.getParent() as? JetClassBody ?: return null
return getFamilyName() 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 (element.getNameIdentifier() ?: return null).getTextRange()
} }
override fun getFamilyName(): String { override fun applyTo(element: JetCallableDeclaration, editor: Editor) {
return JetBundle.message("convert.to.extension") val descriptor = element.resolveToDescriptor()
} val containingClass = descriptor.getContainingDeclaration() as ClassDescriptor
override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean { val file = element.getContainingJetFile()
val declaration = getTarget(editor, file) val outermostParent = JetPsiUtil.getOutermostParent(element, file, false)
if (declaration is JetProperty) {
if (declaration.hasInitializer()) return false
}
return declaration != null && declaration !is JetSecondaryConstructor && declaration.getParent() is JetClassBody && declaration.getParent().getParent() is JetClass && declaration.getReceiverTypeReference() == null
}
throws(IncorrectOperationException::class) val receiver = containingClass.getDefaultType().toString() + "."
override fun invoke(project: Project, editor: Editor, file: PsiFile) { val name = element.getNameIdentifier()!!.getText()
val member = getTarget(editor, file)
assert(member != null, "Must be checked by isAvailable")
val bindingContext = member!!.analyzeFully() val valueParameterList = element.getValueParameterList()
val memberDescriptor = bindingContext.get<PsiElement, DeclarationDescriptor>(DECLARATION_TO_DESCRIPTOR, member) ?: return val returnTypeRef = element.getTypeReference()
val containingClass = memberDescriptor.getContainingDeclaration() val extensionText = modifiers(element) +
assert(containingClass is ClassDescriptor) { "Members must be contained in classes: \n" + "Descriptor: " + memberDescriptor + "\n" + "Declaration & context: " + member.getParent().getParent().getText() } memberType(element) +
" " +
typeParameters(element) +
receiver +
name +
(if (valueParameterList == null) "" else valueParameterList.getText()) +
(if (returnTypeRef != null) ": " + returnTypeRef.getText() else "") +
body(element)
val outermostParent = JetPsiUtil.getOutermostParent(member, file, false) val psiFactory = JetPsiFactory(element)
val receiver = (containingClass as ClassDescriptor).getDefaultType().toString() + "."
val identifier = member.getNameIdentifier()
val name = if (identifier == null) "" else identifier.getText()
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 extension = psiFactory.createDeclaration<JetDeclaration>(extensionText) val extension = psiFactory.createDeclaration<JetDeclaration>(extensionText)
val added = file.addAfter(extension, outermostParent) val added = file.addAfter(extension, outermostParent)
file.addAfter(psiFactory.createNewLine(), outermostParent) file.addAfter(psiFactory.createNewLine(), outermostParent)
file.addAfter(psiFactory.createNewLine(), outermostParent) file.addAfter(psiFactory.createNewLine(), outermostParent)
member.delete() element.delete()
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement<PsiElement>(added) CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement<PsiElement>(added)
val caretAnchor = added.getText().indexOf(CARET_ANCHOR) val caretAnchor = added.getText().indexOf(CARET_ANCHOR)
if (caretAnchor >= 0) { if (caretAnchor >= 0) {
val caretOffset = added.getTextRange().getStartOffset() + caretAnchor 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()) { if (anchor != null && CARET_ANCHOR == anchor.getReferencedName()) {
val throwException = psiFactory.createExpression(THROW_UNSUPPORTED_OPERATION_EXCEPTION) val throwException = psiFactory.createExpression(THROW_UNSUPPORTED_OPERATION_EXCEPTION)
val replaced = anchor.replace(throwException) val replaced = anchor.replace(throwException)
@@ -100,15 +90,8 @@ 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 { private fun memberType(member: JetCallableDeclaration): String {
if (member is JetFunction) { if (member is JetFunction) {
@@ -158,18 +141,17 @@ public class ConvertMemberToExtension : BaseIntentionAction() {
private fun getter(property: JetProperty): String { private fun getter(property: JetProperty): String {
val getter = property.getGetter() val getter = property.getGetter()
if (synthesizeBody(getter)) return "get() = " + CARET_ANCHOR if (synthesizeBody(getter)) return "get() = " + CARET_ANCHOR
return getter.getText() return getter!!.getText()
} }
private fun setter(property: JetProperty, allowCaretAnchor: Boolean): String { private fun setter(property: JetProperty, allowCaretAnchor: Boolean): String {
if (!property.isVar()) return "" if (!property.isVar()) return ""
val setter = property.getSetter() val setter = property.getSetter()
if (synthesizeBody(setter)) return "set(value) {" + (if (allowCaretAnchor) CARET_ANCHOR else THROW_UNSUPPORTED_OPERATION_EXCEPTION) + "}" if (synthesizeBody(setter)) return "set(value) {" + (if (allowCaretAnchor) CARET_ANCHOR else THROW_UNSUPPORTED_OPERATION_EXCEPTION) + "}"
return setter.getText() return setter!!.getText()
} }
private fun synthesizeBody(getter: JetPropertyAccessor?): Boolean { private fun synthesizeBody(getter: JetPropertyAccessor?): Boolean {
return getter == null || getter.getBodyExpression() == null return getter == null || getter.getBodyExpression() == null
} }
} }
}
@@ -1,3 +1,4 @@
// IS_APPLICABLE: false
// ERROR: Function declaration must have a name // ERROR: Function declaration must have a name
class Owner { class Owner {
fun <caret>() {} fun <caret>() {}
@@ -1,6 +0,0 @@
// ERROR: Function declaration must have a name
class Owner {
}
fun Owner.() {
}