Converted ConvertMemberToExtension to Kotlin
This commit is contained in:
-211
@@ -1,211 +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.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.tree.IElementType;
|
||||
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.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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.ResolvePackage;
|
||||
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR;
|
||||
|
||||
public class ConvertMemberToExtension extends BaseIntentionAction {
|
||||
|
||||
public static final String CARET_ANCHOR = "____CARET_ANCHOR____";
|
||||
public static final String THROW_UNSUPPORTED_OPERATION_EXCEPTION = " throw UnsupportedOperationException()";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return JetBundle.message("convert.to.extension");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(
|
||||
@NotNull Project project, Editor editor, PsiFile file
|
||||
) {
|
||||
JetCallableDeclaration declaration = getTarget(editor, file);
|
||||
if (declaration instanceof JetProperty) {
|
||||
if (((JetProperty) declaration).hasInitializer()) return false;
|
||||
}
|
||||
return declaration != null
|
||||
&& !(declaration instanceof JetSecondaryConstructor)
|
||||
&& declaration.getParent() instanceof JetClassBody
|
||||
&& declaration.getParent().getParent() instanceof JetClass
|
||||
&& declaration.getReceiverTypeReference() == null;
|
||||
}
|
||||
|
||||
private static JetCallableDeclaration getTarget(Editor editor, PsiFile file) {
|
||||
PsiElement element = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
return PsiTreeUtil.getParentOfType(element, JetCallableDeclaration.class, false, JetExpression.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(
|
||||
@NotNull Project project, Editor editor, PsiFile file
|
||||
) throws IncorrectOperationException {
|
||||
JetCallableDeclaration member = getTarget(editor, file);
|
||||
assert member != null : "Must be checked by isAvailable";
|
||||
|
||||
BindingContext bindingContext = ResolvePackage.analyzeFully(member);
|
||||
DeclarationDescriptor memberDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, member);
|
||||
if (memberDescriptor == null) return;
|
||||
|
||||
DeclarationDescriptor containingClass = memberDescriptor.getContainingDeclaration();
|
||||
assert containingClass instanceof ClassDescriptor : "Members must be contained in classes: \n" +
|
||||
"Descriptor: " + memberDescriptor + "\n" +
|
||||
"Declaration & context: " + member.getParent().getParent().getText();
|
||||
|
||||
PsiElement outermostParent = JetPsiUtil.getOutermostParent(member, file, false);
|
||||
|
||||
String receiver = ((ClassDescriptor) containingClass).getDefaultType() + ".";
|
||||
PsiElement identifier = member.getNameIdentifier();
|
||||
String name = identifier == null ? "" : identifier.getText();
|
||||
|
||||
JetParameterList valueParameterList = member.getValueParameterList();
|
||||
JetTypeReference returnTypeRef = member.getTypeReference();
|
||||
|
||||
String extensionText = modifiers(member) +
|
||||
memberType(member) + " " +
|
||||
typeParameters(member) +
|
||||
receiver +
|
||||
name +
|
||||
(valueParameterList == null ? "" : valueParameterList.getText()) +
|
||||
(returnTypeRef != null ? ": " + returnTypeRef.getText() : "") +
|
||||
body(member);
|
||||
|
||||
JetPsiFactory psiFactory = JetPsiFactory(member);
|
||||
JetDeclaration extension = psiFactory.<JetDeclaration>createDeclaration(extensionText);
|
||||
|
||||
PsiElement added = file.addAfter(extension, outermostParent);
|
||||
file.addAfter(psiFactory.createNewLine(), outermostParent);
|
||||
file.addAfter(psiFactory.createNewLine(), outermostParent);
|
||||
member.delete();
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(added);
|
||||
|
||||
int caretAnchor = added.getText().indexOf(CARET_ANCHOR);
|
||||
if (caretAnchor >= 0) {
|
||||
int caretOffset = added.getTextRange().getStartOffset() + caretAnchor;
|
||||
JetSimpleNameExpression anchor = PsiTreeUtil.findElementOfClassAtOffset(file, caretOffset, JetSimpleNameExpression.class, false);
|
||||
if (anchor != null && CARET_ANCHOR.equals(anchor.getReferencedName())) {
|
||||
JetExpression throwException = psiFactory.createExpression(THROW_UNSUPPORTED_OPERATION_EXCEPTION);
|
||||
PsiElement replaced = anchor.replace(throwException);
|
||||
TextRange range = replaced.getTextRange();
|
||||
editor.getCaretModel().moveToOffset(range.getStartOffset());
|
||||
editor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String memberType(JetCallableDeclaration member) {
|
||||
if (member instanceof JetFunction) {
|
||||
return "fun";
|
||||
}
|
||||
return ((JetProperty) member).getValOrVarNode().getText();
|
||||
}
|
||||
|
||||
private static String modifiers(JetCallableDeclaration member) {
|
||||
JetModifierList modifierList = member.getModifierList();
|
||||
if (modifierList == null) return "";
|
||||
for (IElementType modifierType : JetTokens.VISIBILITY_MODIFIERS.getTypes()) {
|
||||
PsiElement modifier = modifierList.getModifier((JetModifierKeywordToken) modifierType);
|
||||
if (modifier != null) {
|
||||
return modifierType == JetTokens.PROTECTED_KEYWORD ? "" : modifier.getText() + " ";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String typeParameters(JetCallableDeclaration member) {
|
||||
PsiElement classElement = member.getParent().getParent();
|
||||
assert classElement instanceof JetClass : "Must be checked in isAvailable: " + classElement.getText();
|
||||
|
||||
List<JetTypeParameter> allTypeParameters = ContainerUtil.concat(
|
||||
((JetClass) classElement).getTypeParameters(),
|
||||
member.getTypeParameters());
|
||||
if (allTypeParameters.isEmpty()) return "";
|
||||
return "<" + StringUtil.join(allTypeParameters, new Function<JetTypeParameter, String>() {
|
||||
@Override
|
||||
public String fun(JetTypeParameter parameter) {
|
||||
return parameter.getText();
|
||||
}
|
||||
}, ", ") + "> ";
|
||||
}
|
||||
|
||||
private static String body(JetCallableDeclaration member) {
|
||||
if (member instanceof JetProperty) {
|
||||
JetProperty property = (JetProperty) member;
|
||||
return "\n" + getter(property) + "\n" + setter(property, !synthesizeBody(property.getGetter()));
|
||||
}
|
||||
else if (member instanceof JetFunction) {
|
||||
JetFunction function = (JetFunction) member;
|
||||
JetExpression bodyExpression = function.getBodyExpression();
|
||||
if (bodyExpression == null) return "{" + CARET_ANCHOR + "}";
|
||||
if (!function.hasBlockBody()) return " = " + bodyExpression.getText();
|
||||
return bodyExpression.getText();
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getter(JetProperty property) {
|
||||
JetPropertyAccessor getter = property.getGetter();
|
||||
if (synthesizeBody(getter)) return "get() = " + CARET_ANCHOR;
|
||||
return getter.getText();
|
||||
}
|
||||
|
||||
private static String setter(JetProperty property, boolean allowCaretAnchor) {
|
||||
if (!property.isVar()) return "";
|
||||
JetPropertyAccessor setter = property.getSetter();
|
||||
if (synthesizeBody(setter)) return "set(value) {" + (allowCaretAnchor ? CARET_ANCHOR : THROW_UNSUPPORTED_OPERATION_EXCEPTION) + "}";
|
||||
return setter.getText();
|
||||
}
|
||||
|
||||
private static boolean synthesizeBody(@Nullable JetPropertyAccessor getter) {
|
||||
return getter == null || getter.getBodyExpression() == null;
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.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.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.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
|
||||
}
|
||||
return declaration != null && declaration !is JetSecondaryConstructor && declaration.getParent() is JetClassBody && declaration.getParent().getParent() is JetClass && declaration.getReceiverTypeReference() == null
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
val bindingContext = member!!.analyzeFully()
|
||||
val memberDescriptor = bindingContext.get<PsiElement, DeclarationDescriptor>(DECLARATION_TO_DESCRIPTOR, member) ?: return
|
||||
|
||||
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 outermostParent = JetPsiUtil.getOutermostParent(member, file, false)
|
||||
|
||||
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 added = file.addAfter(extension, outermostParent)
|
||||
file.addAfter(psiFactory.createNewLine(), outermostParent)
|
||||
file.addAfter(psiFactory.createNewLine(), outermostParent)
|
||||
member.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)
|
||||
if (anchor != null && CARET_ANCHOR == anchor.getReferencedName()) {
|
||||
val throwException = psiFactory.createExpression(THROW_UNSUPPORTED_OPERATION_EXCEPTION)
|
||||
val replaced = anchor.replace(throwException)
|
||||
val range = replaced.getTextRange()
|
||||
editor.getCaretModel().moveToOffset(range.getStartOffset())
|
||||
editor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
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 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 ""
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user