From 1ccb5ea4e3b3701ce46fee7eff44e642c15b3825 Mon Sep 17 00:00:00 2001 From: Nikita Eshkeev Date: Tue, 6 Apr 2021 22:47:18 +0300 Subject: [PATCH] [javadoc] IDEA-224332 Replace line comment before a method with a Javadoc comment Restore tags of a javadoc after the intention is applied. The intention used to lose tags of a javadoc when comments are merged into it, this patch fixes this problem GitOrigin-RevId: 683a564fa446d110baf6fb342322cee8f4763a29 --- .../to_javadoc/afterJavadocWithTags.java | 14 ++++++ .../to_javadoc/beforeJavadocWithTags.java | 15 ++++++ .../comment/ReplaceWithJavadocIntention.java | 50 ++++++++++++++----- 3 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 java/java-tests/testData/comment/to_javadoc/afterJavadocWithTags.java create mode 100644 java/java-tests/testData/comment/to_javadoc/beforeJavadocWithTags.java diff --git a/java/java-tests/testData/comment/to_javadoc/afterJavadocWithTags.java b/java/java-tests/testData/comment/to_javadoc/afterJavadocWithTags.java new file mode 100644 index 000000000000..14c2e50a61f0 --- /dev/null +++ b/java/java-tests/testData/comment/to_javadoc/afterJavadocWithTags.java @@ -0,0 +1,14 @@ +// "Replace with javadoc" "true" + +class Main { + /** + * 1 + * 2 + * 3 + * @since 2021.2 + * @author Nikita Eshkeev + */ + public /*4*/ void f// 5 + () { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/comment/to_javadoc/beforeJavadocWithTags.java b/java/java-tests/testData/comment/to_javadoc/beforeJavadocWithTags.java new file mode 100644 index 000000000000..ec939ee11f0a --- /dev/null +++ b/java/java-tests/testData/comment/to_javadoc/beforeJavadocWithTags.java @@ -0,0 +1,15 @@ +// "Replace with javadoc" "true" + +class Main { + /** + * 1 + * + * @since 2021.2 + * @author Nikita Eshkeev + */ + // 2 + /* 3 */ + public /*4*/ void f// 5 + () { + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/comment/ReplaceWithJavadocIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/comment/ReplaceWithJavadocIntention.java index 8408833a6285..9c487e11c27f 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/comment/ReplaceWithJavadocIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/comment/ReplaceWithJavadocIntention.java @@ -5,11 +5,14 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.psi.javadoc.PsiDocTagValue; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -32,17 +35,16 @@ public class ReplaceWithJavadocIntention extends Intention { final PsiElement child = method.getFirstChild(); if (!(child instanceof PsiComment)) return; - final PsiComment comment = (PsiComment)child; - - // a set will contain nodes to remove: all the right and left sibling nodes of the PsiComment type - final Set commentNodes = new HashSet<>(); - final List rightSiblingsComments = siblingsComments(comment, commentNodes); + final PsiComment firstCommentOfMethod = (PsiComment)child; final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(element.getProject()); final PsiElementFactory factory = psiFacade.getElementFactory(); - final String javadoc = prepareJavadocComment(rightSiblingsComments); - final PsiDocComment docComment = factory.createDocCommentFromText(javadoc); + // the set will contain all the comment nodes that are directly before the method's modifier list + final Set commentNodes = new HashSet<>(); + + final String javadocText = prepareJavadocComment(firstCommentOfMethod, commentNodes); + final PsiDocComment javadoc = factory.createDocCommentFromText(javadocText); if (commentNodes.isEmpty()) { LOGGER.error("The set of visited node comments is empty"); @@ -55,7 +57,7 @@ public class ReplaceWithJavadocIntention extends Intention { } final PsiComment item = ContainerUtil.getFirstItem(commentNodes); - item.replace(docComment); + item.replace(javadoc); } /** @@ -84,6 +86,17 @@ public class ReplaceWithJavadocIntention extends Intention { return result; } + @NotNull + @NonNls + private static String extractJavadocTagContent(PsiDocTag tag) { + final PsiDocTagValue element = tag.getValueElement(); + final String result = "@" + tag.getName(); + + if (element == null) return result; + + return result + " " + element.getText(); + } + /** * Combines the collection of strings into a string in the javadoc format: *
    @@ -91,11 +104,11 @@ public class ReplaceWithJavadocIntention extends Intention { *
  • Each line from the collection is precluded with '*'
  • *
  • The resulting string ends with */
  • *
- * @param commentContent the collection of strings to form the javadoc's content * @return the string in the javadoc format. */ - @Contract(pure = true) - private static @NotNull String prepareJavadocComment(final @NotNull Collection commentContent) { + @Contract(mutates = "param2") + private static @NotNull String prepareJavadocComment(PsiComment comment, @NotNull Set<@NotNull PsiComment> visited) { + final @NotNull Collection commentContent = siblingsComments(comment, visited); final StringBuilder sb = new StringBuilder("/**\n"); for (String string : commentContent) { @@ -105,6 +118,18 @@ public class ReplaceWithJavadocIntention extends Intention { sb.append(line); sb.append("\n"); } + + if (comment instanceof PsiDocComment) { + PsiDocComment javadoc = (PsiDocComment)comment; + final PsiDocTag[] tags = javadoc.getTags(); + if (tags.length > 0) { + final int start = tags[0].getStartOffsetInParent(); + final int end = tags[tags.length - 1].getTextRangeInParent().getEndOffset(); + sb.append("* "); + sb.append(comment.getText(), start, end); + sb.append("\n"); + } + } sb.append("*/"); return sb.toString(); @@ -133,8 +158,7 @@ public class ReplaceWithJavadocIntention extends Intention { .filter(Predicate.not(String::isEmpty)) .map(line -> line.startsWith("*") ? line.substring(1) : line) .map(line -> StringUtil.replace(line, "*/", "*/")) - .collect(Collectors.toList()) - ; + .collect(Collectors.toList()); } /**