From da559b446adb604ef132ddc74e453e99981e37d6 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 24 May 2017 20:40:18 +0300 Subject: [PATCH] external annotations: bring external annotation to source code (IDEA-163179) --- ...ExternalAnnotationsLineMarkerProvider.java | 1 + .../MakeExternalAnnotationExplicit.java | 138 ++++++++++++++++++ .../after.java.template | 10 ++ .../before.java.template | 7 + .../description.html | 6 + resources/src/META-INF/IdeaPlugin.xml | 5 + 6 files changed, 167 insertions(+) create mode 100644 java/java-impl/src/com/intellij/codeInsight/MakeExternalAnnotationExplicit.java create mode 100644 resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/after.java.template create mode 100644 resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/before.java.template create mode 100644 resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/description.html diff --git a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java index d71631b96e0f..c29a51c1895e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java @@ -212,6 +212,7 @@ public class ExternalAnnotationsLineMarkerProvider extends LineMarkerProviderDes action instanceof EditContractIntention || action instanceof ToggleSourceInferredAnnotations || action instanceof MakeInferredAnnotationExplicit || + action instanceof MakeExternalAnnotationExplicit || action instanceof IntentionActionDelegate && shouldShowInGutterPopup(((IntentionActionDelegate)action).getDelegate()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/MakeExternalAnnotationExplicit.java b/java/java-impl/src/com/intellij/codeInsight/MakeExternalAnnotationExplicit.java new file mode 100644 index 000000000000..a31d730b7af8 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/MakeExternalAnnotationExplicit.java @@ -0,0 +1,138 @@ +/* + * Copyright 2000-2017 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 com.intellij.codeInsight; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class MakeExternalAnnotationExplicit extends BaseIntentionAction { + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Make External Annotations Explicit"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + final PsiElement leaf = file.findElementAt(editor.getCaretModel().getOffset()); + final PsiModifierListOwner owner = ExternalAnnotationsLineMarkerProvider.getAnnotationOwner(leaf); + if (owner != null && owner.getLanguage().isKindOf(JavaLanguage.INSTANCE) && isWritable(owner) && + ModuleUtilCore.findModuleForPsiElement(file) != null && + PsiUtil.getLanguageLevel(file).isAtLeast(LanguageLevel.JDK_1_5)) { + final PsiAnnotation[] annotations = getAnnotations(project, owner); + if (annotations.length > 0) { + final String annos = StringUtil.join(annotations, annotation -> { + final PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement(); + final String name = nameRef != null ? nameRef.getReferenceName() : annotation.getQualifiedName(); + return "@" + name + annotation.getParameterList().getText(); + }, " "); + setText("Insert '" + annos + "'"); + return true; + } + } + + return false; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PsiElement leaf = file.findElementAt(editor.getCaretModel().getOffset()); + final PsiModifierListOwner owner = ExternalAnnotationsLineMarkerProvider.getAnnotationOwner(leaf); + assert owner != null; + final PsiModifierList modifierList = owner.getModifierList(); + assert modifierList != null; + final Module module = ModuleUtilCore.findModuleForPsiElement(file); + assert module != null; + + ExternalAnnotationsManager externalAnnotationsManager = ExternalAnnotationsManager.getInstance(project); + + if (!FileModificationService.getInstance().preparePsiElementsForWrite(getFilesToWrite(file, owner, externalAnnotationsManager))) return; + + for (PsiAnnotation anno : getAnnotations(project, owner)) { + final String qname = anno.getQualifiedName(); + assert qname != null; + externalAnnotationsManager.deannotate(owner, qname); + + WriteCommandAction.runWriteCommandAction(project, () -> DumbService.getInstance(project).withAlternativeResolveEnabled( + () -> JavaCodeStyleManager.getInstance(project).shortenClassReferences(modifierList.addAfter(anno, null)))); + } + + + } + + private static List getFilesToWrite(PsiFile file, + PsiModifierListOwner owner, + ExternalAnnotationsManager externalAnnotationsManager) { + List files = externalAnnotationsManager.findExternalAnnotationsFiles(owner); + if (files != null) { + List elements = new ArrayList<>(); + elements.addAll(files); + elements.add(file); + return elements; + } + return Collections.singletonList(file); + } + + @NotNull + private PsiAnnotation[] getAnnotations(@NotNull Project project, PsiModifierListOwner owner) { + PsiAnnotation[] annotations = ExternalAnnotationsManager.getInstance(project).findExternalAnnotations(owner); + if (annotations == null) { + return PsiAnnotation.EMPTY_ARRAY; + } + else { + JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + return Arrays.stream(annotations).filter(anno -> { + String qualifiedName = anno.getQualifiedName(); + return qualifiedName != null && facade.findClass(qualifiedName, owner.getResolveScope()) != null; + }).toArray(PsiAnnotation[]::new); + } + } + + @Override + public boolean startInWriteAction() { + return false; + } + + private static boolean isWritable(PsiModifierListOwner owner) { + if (owner instanceof PsiCompiledElement) return false; + + VirtualFile vFile = PsiUtilCore.getVirtualFile(owner); + return vFile != null && vFile.isInLocalFileSystem(); + } +} diff --git a/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/after.java.template b/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/after.java.template new file mode 100644 index 000000000000..adbc1afe33a4 --- /dev/null +++ b/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/after.java.template @@ -0,0 +1,10 @@ +import org.jetbrains.annotations.Nullable; + +class A { + + @Nullable + Object getObject() { + //do smth + return null; + } +} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/before.java.template b/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/before.java.template new file mode 100644 index 000000000000..affc2590041d --- /dev/null +++ b/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/before.java.template @@ -0,0 +1,7 @@ +class A { + + Object getObject() { + //do smth + return null; + } +} ] \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/description.html b/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/description.html new file mode 100644 index 000000000000..fb6b2897e818 --- /dev/null +++ b/resources-en/src/intentionDescriptions/MakeExternalAnnotationExplicit/description.html @@ -0,0 +1,6 @@ + + +This intention brings external annotations to the code explicitly. + + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 41e0aee5cea0..c9bc06500fb1 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1030,6 +1030,11 @@ Java/Annotations MakeInferredAnnotationExplicit + + com.intellij.codeInsight.MakeExternalAnnotationExplicit + Java/Annotations + MakeExternalAnnotationExplicit + com.intellij.codeInsight.ToggleSourceInferredAnnotations Java/Annotations