From f49eb6eaadafa5a8c5d816c859188432df09c9f1 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 25 Oct 2017 16:28:34 +0300 Subject: [PATCH] AddJavadocIntention: fix exception when the intention invoked for package-info or module-info EA-109542 --- .../intention/impl/AddJavadocIntention.java | 44 +++++++++---------- .../com/intellij/ide/util/PackageUtil.java | 20 +++------ .../lang/java/JavaDocumentationProvider.java | 30 +++++++------ .../AddJavadocToModuleAndPackageTest.java | 32 ++++++++++++++ 4 files changed, 74 insertions(+), 52 deletions(-) create mode 100644 java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddJavadocToModuleAndPackageTest.java diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddJavadocIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddJavadocIntention.java index 4f2e4fab8fe8..596a5dea0cd4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddJavadocIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddJavadocIntention.java @@ -1,45 +1,41 @@ -/* - * Copyright 2000-2014 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInsight.intention.impl; import com.intellij.codeInsight.editorActions.FixDocCommentAction; import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction; import com.intellij.codeInsight.intention.LowPriorityAction; +import com.intellij.ide.util.PackageUtil; +import com.intellij.lang.java.JavaDocumentationProvider; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class AddJavadocIntention extends BaseElementAtCaretIntentionAction implements LowPriorityAction { @Override public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException { - final PsiDocCommentOwner docCommentOwner = (PsiDocCommentOwner)element.getParent(); - FixDocCommentAction.generateOrFixComment(docCommentOwner, project, editor); + FixDocCommentAction.generateOrFixComment(element, project, editor); } @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { - if (!(element instanceof PsiIdentifier)) { - return false; + public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull PsiElement element) { + PsiElement targetElement = PsiTreeUtil.skipParentsOfType(element, PsiIdentifier.class, PsiJavaCodeReferenceElement.class, PsiJavaModuleReferenceElement.class); + if (targetElement instanceof PsiJavaDocumentedElement && + !(targetElement instanceof PsiTypeParameter) && + !(targetElement instanceof PsiAnonymousClass)) { + return ((PsiJavaDocumentedElement)targetElement).getDocComment() == null; } - final PsiElement parent = element.getParent(); - return parent instanceof PsiDocCommentOwner && - ((PsiDocCommentOwner)parent).getDocComment() == null && - !(parent instanceof PsiTypeParameter); + + if (targetElement instanceof PsiPackageStatement) { + PsiFile file = targetElement.getContainingFile(); + return PackageUtil.isPackageInfoFile(file) && JavaDocumentationProvider.getPackageInfoComment(file) == null; + } + else if (PackageUtil.isPackageInfoFile(targetElement)) { + return JavaDocumentationProvider.getPackageInfoComment(targetElement) == null; + } + return false; } @NotNull diff --git a/java/java-impl/src/com/intellij/ide/util/PackageUtil.java b/java/java-impl/src/com/intellij/ide/util/PackageUtil.java index 4d0081fe7461..fefa43c330bf 100644 --- a/java/java-impl/src/com/intellij/ide/util/PackageUtil.java +++ b/java/java-impl/src/com/intellij/ide/util/PackageUtil.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ide.util; import com.intellij.ide.IdeBundle; @@ -398,4 +384,8 @@ public class PackageUtil { public static PsiDirectory findOrCreateSubdirectory(@NotNull PsiDirectory directory, @NotNull String directoryName) { return CreateFileAction.findOrCreateSubdirectory(directory, directoryName); } + + public static boolean isPackageInfoFile(@Nullable PsiElement element) { + return element instanceof PsiJavaFile && PsiPackage.PACKAGE_INFO_FILE.equals(((PsiJavaFile)element).getName()); + } } diff --git a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java index 0caf4fa38b5d..81bddcbdf93d 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java @@ -1,6 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. -// Use of this source code is governed by the Apache 2.0 license that can be -// found in the LICENSE file. +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.lang.java; import com.intellij.codeInsight.CodeInsightBundle; @@ -12,6 +10,7 @@ import com.intellij.codeInsight.javadoc.JavaDocExternalFilter; import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator; import com.intellij.codeInsight.javadoc.JavaDocInfoGeneratorFactory; import com.intellij.codeInsight.javadoc.JavaDocUtil; +import com.intellij.ide.util.PackageUtil; import com.intellij.lang.CodeDocumentationAwareCommenter; import com.intellij.lang.LangBundle; import com.intellij.lang.LanguageCommenters; @@ -392,18 +391,18 @@ public class JavaDocumentationProvider extends DocumentationProviderEx implement @Nullable @Override public Pair parseContext(@NotNull PsiElement startPoint) { - PsiElement docCommentOwner = PsiTreeUtil.findFirstParent(startPoint, e -> { - if (e instanceof PsiDocCommentOwner && !(e instanceof PsiTypeParameter) && !(e instanceof PsiAnonymousClass)) { - return true; + PsiElement current = startPoint; + while (current != null) { + if (current instanceof PsiJavaDocumentedElement && !(current instanceof PsiTypeParameter) && !(current instanceof PsiAnonymousClass)) { + PsiDocComment comment = ((PsiJavaDocumentedElement)current).getDocComment(); + return Pair.create(current instanceof PsiField ? ((PsiField)current).getModifierList() : current, comment); } - return false; - }); - if (docCommentOwner == null) return null; - PsiDocComment comment = ((PsiDocCommentOwner)docCommentOwner).getDocComment(); - if (docCommentOwner instanceof PsiField) { - docCommentOwner = ((PsiField)docCommentOwner).getModifierList(); + else if (PackageUtil.isPackageInfoFile(current)) { + return Pair.create(current, getPackageInfoComment(current)); + } + current = current.getParent(); } - return Pair.create(docCommentOwner, comment); + return null; } @Override @@ -730,6 +729,11 @@ public class JavaDocumentationProvider extends DocumentationProviderEx implement return signature; } + @Nullable + public static PsiDocComment getPackageInfoComment(@NotNull PsiElement packageInfoFile) { + return PsiTreeUtil.getChildOfType(packageInfoFile, PsiDocComment.class); + } + @Nullable public static List findUrlForClass(@NotNull PsiClass aClass) { String qName = aClass.getQualifiedName(); diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddJavadocToModuleAndPackageTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddJavadocToModuleAndPackageTest.java new file mode 100644 index 000000000000..d975f041c5ae --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/AddJavadocToModuleAndPackageTest.java @@ -0,0 +1,32 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.codeInsight.intention; + +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +public class AddJavadocToModuleAndPackageTest extends LightCodeInsightFixtureTestCase { + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_9; + } + + public void testPackageInfo() { + myFixture.configureByText("package-info.java", "package org.some.awesome;"); + myFixture.launchAction(myFixture.findSingleIntention("Add Javadoc")); + myFixture.checkResult("/**\n" + + " * \n" + + " */\n" + + "package org.some.awesome;"); + } + + public void testModuleInfo() { + myFixture.configureByText("module-info.java", "module org.some.awesome{}"); + myFixture.launchAction(myFixture.findSingleIntention("Add Javadoc")); + myFixture.checkResult("/**\n" + + " * \n" + + " */\n" + + "module org.some.awesome{}"); + } +}