From 80f46af6ad9ed9fd3de2ff4a4325e191e8563e9f Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Thu, 16 Aug 2012 22:16:35 +0400 Subject: [PATCH] Added "Specify Kotlin Signature" intention. --- .../after.kt.template | 8 ++ .../before.kt.template | 5 + .../description.html | 5 + idea/src/META-INF/plugin.xml | 5 + .../jetbrains/jet/plugin/JetBundle.properties | 3 + .../AddKotlinSignatureAnnotation.java | 125 ++++++++++++++++++ .../codeInsight/EditSignatureBalloon.java | 14 +- .../KotlinSignatureInJavaMarkerProvider.java | 2 +- 8 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/after.kt.template create mode 100644 idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/before.kt.template create mode 100644 idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/description.html create mode 100644 idea/src/org/jetbrains/jet/plugin/codeInsight/AddKotlinSignatureAnnotation.java diff --git a/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/after.kt.template b/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/after.kt.template new file mode 100644 index 00000000000..9375f4ba83a --- /dev/null +++ b/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/after.kt.template @@ -0,0 +1,8 @@ +import jet.runtime.typeinfo.KotlinSignature; + +public class MyClass { + @KotlinSignature("fun method() : jet.String?") + public String method() { + return ""; + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/before.kt.template b/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/before.kt.template new file mode 100644 index 00000000000..d8550fadf73 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/before.kt.template @@ -0,0 +1,5 @@ +public class MyClass { + public String method() { + return ""; + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/description.html b/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/description.html new file mode 100644 index 00000000000..d2d96cb7f34 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddKotlinSignatureAnnotation/description.html @@ -0,0 +1,5 @@ + + +This intention adds Kotlin signature for Java method. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 434b3f38bd4..b8b806e8e02 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -228,6 +228,11 @@ Kotlin + + org.jetbrains.jet.plugin.codeInsight.AddKotlinSignatureAnnotation + Kotlin + + diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index ee407a64047..8bdad14cc43 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -36,6 +36,9 @@ specify.type.explicitly.add.return.type.action.name=Specify Return Type Explicit specify.type.explicitly.add.action.name=Specify Type Explicitly specify.type.explicitly.remove.action.name=Remove Explicitly Specified Type +add.kotlin.signature.action.family.name=Specify Custom Kotlin Signature +add.kotlin.signature.action.text=Specify Custom Kotlin Signature + goto.super.function.chooser.title=Choose super function goto.super.property.chooser.title=Choose super property goto.super.class.chooser.title=Choose super class or interface diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/AddKotlinSignatureAnnotation.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/AddKotlinSignatureAnnotation.java new file mode 100644 index 00000000000..8daafaba225 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/AddKotlinSignatureAnnotation.java @@ -0,0 +1,125 @@ +/* + * Copyright 2010-2012 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.jet.plugin.codeInsight; + +import com.intellij.codeInsight.intention.AddAnnotationFix; +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.psi.PsiCodeBlock; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.di.InjectorForJavaSemanticServices; +import org.jetbrains.jet.lang.BuiltinsScopeExtensionMode; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.plugin.JetBundle; +import org.jetbrains.jet.resolve.DescriptorRenderer; + +/** + * @author Evgeny Gerashchenko + * @since 16 Aug 2012 + */ +public class AddKotlinSignatureAnnotation extends BaseIntentionAction { + private static final DescriptorRenderer RENDERER = new DescriptorRenderer() { + @Override + protected boolean shouldRenderDefinedIn() { + return false; + } + + @Override + public String renderType(JetType type) { + return renderTypeWithShortNames(type); + } + + @Override + protected boolean shouldRenderModifiers() { + return false; + } + }; + + public AddKotlinSignatureAnnotation() { + setText(JetBundle.message("add.kotlin.signature.action.text")); + } + + @NotNull + @Override + public String getFamilyName() { + return JetBundle.message("add.kotlin.signature.action.family.name"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + PsiMethod method = findMethod(file, editor.getCaretModel().getOffset()); + if (method == null) return false; + if (KotlinSignatureInJavaMarkerProvider.findKotlinSignatureAnnotation(method) != null) return false; + return createFix(method, "").isAvailable(project, editor, file); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PsiMethod method = (PsiMethod) findMethod(file, editor.getCaretModel().getOffset()).getOriginalElement(); + String signature = getDefaultSignature(project, method); + if (signature == null) { + return; + } + createFix(method, signature).invoke(project, editor, file); + KotlinSignatureInJavaMarkerProvider.refresh(project); + } + + @NotNull + private static AddAnnotationFix createFix(@NotNull PsiMethod method, @NotNull String signature) { + return new AddAnnotationFix(KotlinSignatureInJavaMarkerProvider.KOTLIN_SIGNATURE_ANNOTATION, method, + EditSignatureBalloon.signatureToNameValuePairs(method.getProject(), signature)); + } + + private static String getDefaultSignature(@NotNull Project project, @NotNull PsiMethod method) { + InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices(BuiltinsScopeExtensionMode.ALL, project); + JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver(); + FqName classFqName = new FqName(method.getContainingClass().getQualifiedName()); + ClassDescriptor classDescriptor = javaDescriptorResolver.resolveClass(classFqName); + if (classDescriptor == null) return null; + classDescriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier(method.getName())); + SimpleFunctionDescriptor functionDescriptor = injector.getBindingTrace().getBindingContext().get(BindingContext.FUNCTION, method); + assert functionDescriptor != null: "Couldn't find function descriptor for " + method.getName() + " in " + classFqName; + return RENDERER.render(functionDescriptor); + } + + private static PsiMethod findMethod(PsiFile file, int offset) { + PsiElement element = file.findElementAt(offset); + PsiMethod res = PsiTreeUtil.getParentOfType(element, PsiMethod.class); + if (res == null) return null; + + //Not available in method's body + PsiCodeBlock body = res.getBody(); + if (body == null) return null; + TextRange textRange = body.getTextRange(); + if (textRange == null || textRange.getStartOffset() <= offset) return null; + + return res; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/EditSignatureBalloon.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/EditSignatureBalloon.java index bc090c75d7e..34d74d509ee 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/EditSignatureBalloon.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/EditSignatureBalloon.java @@ -195,12 +195,8 @@ class EditSignatureBalloon { } private void saveAndHide() { - String newSignature = editor.getDocument().getText(); + final String newSignature = editor.getDocument().getText(); if (!previousSignature.equals(newSignature)) { - final PsiNameValuePair[] nameValuePairs = JavaPsiFacade.getElementFactory(project).createAnnotationFromText( - "@" + KotlinSignatureInJavaMarkerProvider.KOTLIN_SIGNATURE_ANNOTATION + "(value=\"" + newSignature + "\")", null) - .getParameterList().getAttributes(); - new WriteCommandAction(project) { @Override protected void run(final Result result) throws Throwable { @@ -208,11 +204,17 @@ class EditSignatureBalloon { .getInstance(project).deannotate(method, KotlinSignatureInJavaMarkerProvider.KOTLIN_SIGNATURE_ANNOTATION); ExternalAnnotationsManager.getInstance(project).annotateExternally( method, KotlinSignatureInJavaMarkerProvider.KOTLIN_SIGNATURE_ANNOTATION, method.getContainingFile(), - nameValuePairs); + signatureToNameValuePairs(project, newSignature)); } }.execute(); } balloon.hide(); } + + static PsiNameValuePair[] signatureToNameValuePairs(@NotNull Project project, @NotNull String signature) { + return JavaPsiFacade.getElementFactory(project).createAnnotationFromText( + "@" + KotlinSignatureInJavaMarkerProvider.KOTLIN_SIGNATURE_ANNOTATION + "(value=\"" + signature + "\")", null) + .getParameterList().getAttributes(); + } } diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinSignatureInJavaMarkerProvider.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinSignatureInJavaMarkerProvider.java index a2d02c52dd9..42120403f58 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinSignatureInJavaMarkerProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/KotlinSignatureInJavaMarkerProvider.java @@ -93,7 +93,7 @@ public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider { static final String KOTLIN_SIGNATURE_ANNOTATION = JvmStdlibNames.KOTLIN_SIGNATURE.getFqName().getFqName(); @Nullable - private static PsiAnnotation findKotlinSignatureAnnotation(@NotNull PsiElement element) { + static PsiAnnotation findKotlinSignatureAnnotation(@NotNull PsiElement element) { if (!(element instanceof PsiMethod)) return null; PsiMethod annotationOwner = element.getOriginalElement() instanceof PsiMethod ? (PsiMethod) element.getOriginalElement()