mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
[Java. Code Formatting] Extract leading and trailing empty lines before reformatting the javadoc
IDEA-361836 GitOrigin-RevId: 707bb0081905309266aec9cc74802fe6e6092063
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7bd9f15a53
commit
91d37684a1
@@ -1208,6 +1208,7 @@ public final class JavaChangeSignatureUsageProcessor implements ChangeSignatureU
|
||||
if (changeInfo.isReturnTypeChanged() && methodDocComment != null) {
|
||||
CanonicalTypes.Type type = changeInfo.getNewReturnType();
|
||||
PsiDocTag aReturn = methodDocComment.findTagByName("return");
|
||||
PsiDocComment oldMethodDocComment = (PsiDocComment)methodDocComment.copy();
|
||||
if (PsiTypes.voidType().equalsToText(type.getTypeText())) {
|
||||
if (aReturn != null) {
|
||||
aReturn.delete();
|
||||
@@ -1219,7 +1220,7 @@ public final class JavaChangeSignatureUsageProcessor implements ChangeSignatureU
|
||||
methodDocComment.add(JavaPsiFacade.getElementFactory(method.getProject()).createDocTagFromText("@return"));
|
||||
}
|
||||
}
|
||||
CommonJavaRefactoringUtil.formatJavadocIgnoringSettings(method, methodDocComment);
|
||||
CommonJavaRefactoringUtil.formatJavadocIgnoringSettings(method, methodDocComment, oldMethodDocComment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,12 @@ public class CommentFormatter {
|
||||
private final Project myProject;
|
||||
|
||||
public CommentFormatter(@NotNull PsiFile file) {
|
||||
this(file, null);
|
||||
}
|
||||
|
||||
public CommentFormatter(@NotNull PsiFile file, @Nullable PsiDocComment oldComment) {
|
||||
mySettings = CodeStyle.getSettings(file);
|
||||
myParser = new JDParser(mySettings);
|
||||
myParser = new JDParser(mySettings, oldComment);
|
||||
myProject = file.getProject();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ public class JDParser {
|
||||
|
||||
private final JavaCodeStyleSettings mySettings;
|
||||
private final CommonCodeStyleSettings myCommonSettings;
|
||||
private final @Nullable JDPreformattingContext myPreformattingContext;
|
||||
|
||||
private static final String SNIPPET_START_REGEXP = "\\{s*@snippet[^\\}]*";
|
||||
private static final String PRE_TAG_START_REGEXP = "<pre\\s*(\\w+\\s*=.*)?>";
|
||||
@@ -68,8 +69,33 @@ public class JDParser {
|
||||
private static final String[] TAGS_TO_KEEP_INDENTS_AFTER = {"table", "ol", "ul", "div", "dl"};
|
||||
|
||||
public JDParser(@NotNull CodeStyleSettings settings) {
|
||||
this(settings, null);
|
||||
}
|
||||
|
||||
public JDParser(@NotNull CodeStyleSettings settings, @Nullable PsiDocComment oldComment) {
|
||||
mySettings = settings.getCustomSettings(JavaCodeStyleSettings.class);
|
||||
myCommonSettings = settings.getCommonSettings(JavaLanguage.INSTANCE);
|
||||
myPreformattingContext = getPreformattingContext(oldComment);
|
||||
}
|
||||
|
||||
public @Nullable JDPreformattingContext getPreformattingContext(@Nullable PsiElement element) {
|
||||
if (!(element instanceof PsiDocComment comment)) return null;
|
||||
DocTextInfo docTextInfo = getDocTextInfo(comment);
|
||||
boolean isMarkdown = comment.isMarkdownComment();
|
||||
|
||||
if (!isMarkdown && !JAVADOC_HEADER.equals(docTextInfo.commentHeader)) return null;
|
||||
|
||||
List<Boolean> markers = new ArrayList<>();
|
||||
|
||||
List<String> l = toArray(docTextInfo.comment, markers, isMarkdown);
|
||||
if (l == null) return null;
|
||||
|
||||
preprocessLines(l, markers, isMarkdown);
|
||||
|
||||
EmptyLinesInfo emptyLinesInfo = getEmptyLinesInfo(l, isMarkdown);
|
||||
|
||||
if (emptyLinesInfo == null) return null;
|
||||
return new JDPreformattingContext(emptyLinesInfo.prefixLinesCount, emptyLinesInfo.suffixLinesCount);
|
||||
}
|
||||
|
||||
public void formatCommentText(@NotNull PsiElement element, @NotNull CommentFormatter formatter) {
|
||||
@@ -83,7 +109,7 @@ public class JDParser {
|
||||
}
|
||||
|
||||
private static boolean isJavadoc(CommentInfo info) {
|
||||
return info.docComment.isMarkdownComment() || JAVADOC_HEADER.equals(info.commentHeader);
|
||||
return info.docComment.isMarkdownComment() || JAVADOC_HEADER.equals(info.docTextInfo.commentHeader);
|
||||
}
|
||||
|
||||
private static CommentInfo getElementsCommentInfo(@Nullable PsiElement psiElement) {
|
||||
@@ -110,6 +136,11 @@ public class JDParser {
|
||||
}
|
||||
|
||||
private static CommentInfo getCommentInfo(@NotNull PsiDocComment docComment, @NotNull PsiElement owner) {
|
||||
DocTextInfo docTextInfo = getDocTextInfo(docComment);
|
||||
return new CommentInfo(docComment, owner, docTextInfo);
|
||||
}
|
||||
|
||||
private static @NotNull JDParser.DocTextInfo getDocTextInfo(@NotNull PsiDocComment docComment) {
|
||||
String commentHeader = null;
|
||||
String commentFooter = null;
|
||||
|
||||
@@ -134,17 +165,17 @@ public class JDParser {
|
||||
sb.append(text);
|
||||
}
|
||||
|
||||
return new CommentInfo(docComment, owner, commentHeader, sb.toString(), commentFooter);
|
||||
return new DocTextInfo(commentHeader, sb.toString(), commentFooter);
|
||||
}
|
||||
|
||||
private @NotNull JDComment parse(@NotNull CommentInfo info, @NotNull CommentFormatter formatter) {
|
||||
JDComment comment = createComment(info.commentOwner, formatter, info.docComment.isMarkdownComment());
|
||||
parse(info.comment, comment);
|
||||
if (info.commentHeader != null) {
|
||||
comment.setFirstCommentLine(info.commentHeader);
|
||||
parse(info.docTextInfo.comment, comment);
|
||||
if (info.docTextInfo.commentHeader != null) {
|
||||
comment.setFirstCommentLine(info.docTextInfo.commentHeader);
|
||||
}
|
||||
if (info.commentFooter != null) {
|
||||
comment.setLastCommentLine(info.commentFooter);
|
||||
if (info.docTextInfo.commentFooter != null) {
|
||||
comment.setLastCommentLine(info.docTextInfo.commentFooter);
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
@@ -180,60 +211,17 @@ public class JDParser {
|
||||
int size = l.size();
|
||||
if (size == 0) return;
|
||||
|
||||
// preprocess strings - removes leading token
|
||||
for (int i = 0; i < size; i++) {
|
||||
String line = l.get(i);
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()) {
|
||||
if(!comment.getIsMarkdown()){
|
||||
if (line.charAt(0) == '*') {
|
||||
if ((markers.get(i)).booleanValue()) {
|
||||
if (line.length() > 1 && line.charAt(1) == ' ') {
|
||||
line = line.substring(2);
|
||||
}
|
||||
else {
|
||||
line = line.substring(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
line = line.substring(1).trim();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Note: Markdown comments are not trimmed like html ones, except for javadoc tags
|
||||
String newLine;
|
||||
int tagStart = CharArrayUtil.shiftForward(line, 3, " \t");
|
||||
if (tagStart != line.length() && line.charAt(tagStart) == '@') {
|
||||
newLine = line.substring(tagStart);
|
||||
} else {
|
||||
newLine = StringUtil.trimStart(line, "/// ");
|
||||
if (Strings.areSameInstance(newLine, line)) {
|
||||
newLine = StringUtil.trimStart(line, "///");
|
||||
}
|
||||
}
|
||||
line = newLine;
|
||||
}
|
||||
}
|
||||
l.set(i, line);
|
||||
preprocessLines(l, markers, comment.getIsMarkdown());
|
||||
|
||||
if (myPreformattingContext != null) {
|
||||
comment.setPrefixEmptyLineCount(myPreformattingContext.getPrefixLinesCount());
|
||||
comment.setSuffixEmptyLineCount(myPreformattingContext.getSuffixLinesCount());
|
||||
}
|
||||
|
||||
if (mySettings.shouldKeepEmptyTrailingLines()) {
|
||||
// counting for the empty lines in the prefix and the suffix of the javadoc to restore them in the future
|
||||
int prefixLineCount = 0;
|
||||
int suffixLineCount = 0;
|
||||
|
||||
while (prefixLineCount < size && l.get(prefixLineCount).isEmpty()) prefixLineCount++;
|
||||
if (prefixLineCount == size) {
|
||||
if (comment.getIsMarkdown()) prefixLineCount--;
|
||||
|
||||
comment.setPrefixEmptyLineCount(prefixLineCount);
|
||||
comment.setSuffixEmptyLineCount(0);
|
||||
}
|
||||
else {
|
||||
while (suffixLineCount < size && l.get(size - suffixLineCount - 1).isEmpty()) suffixLineCount++;
|
||||
|
||||
comment.setPrefixEmptyLineCount(prefixLineCount);
|
||||
comment.setSuffixEmptyLineCount(suffixLineCount);
|
||||
else {
|
||||
EmptyLinesInfo emptyLinesInfo = getEmptyLinesInfo(l, comment.getIsMarkdown());
|
||||
if (emptyLinesInfo != null) {
|
||||
comment.setPrefixEmptyLineCount(emptyLinesInfo.prefixLinesCount);
|
||||
comment.setSuffixEmptyLineCount(emptyLinesInfo.suffixLinesCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +283,64 @@ public class JDParser {
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable EmptyLinesInfo getEmptyLinesInfo(@NotNull List<String> l, boolean isMarkdown) {
|
||||
if (!mySettings.shouldKeepEmptyTrailingLines()) return null;
|
||||
// counting for the empty lines in the prefix and the suffix of the javadoc to restore them in the future
|
||||
int prefixLineCount = 0;
|
||||
int suffixLineCount = 0;
|
||||
int size = l.size();
|
||||
while (prefixLineCount < size && l.get(prefixLineCount).isEmpty()) prefixLineCount++;
|
||||
if (prefixLineCount == size) {
|
||||
if (isMarkdown) prefixLineCount--;
|
||||
|
||||
return new EmptyLinesInfo(prefixLineCount, 0);
|
||||
}
|
||||
else {
|
||||
while (suffixLineCount < size && l.get(size - suffixLineCount - 1).isEmpty()) suffixLineCount++;
|
||||
|
||||
return new EmptyLinesInfo(prefixLineCount, suffixLineCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static void preprocessLines(List<String> l, List<Boolean> markers, boolean isMarkdown) {
|
||||
// preprocess strings - removes leading token
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
String line = l.get(i);
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()) {
|
||||
if(!isMarkdown){
|
||||
if (line.charAt(0) == '*') {
|
||||
if ((markers.get(i)).booleanValue()) {
|
||||
if (line.length() > 1 && line.charAt(1) == ' ') {
|
||||
line = line.substring(2);
|
||||
}
|
||||
else {
|
||||
line = line.substring(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
line = line.substring(1).trim();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Note: Markdown comments are not trimmed like html ones, except for javadoc tags
|
||||
String newLine;
|
||||
int tagStart = CharArrayUtil.shiftForward(line, 3, " \t");
|
||||
if (tagStart != line.length() && line.charAt(tagStart) == '@') {
|
||||
newLine = line.substring(tagStart);
|
||||
} else {
|
||||
newLine = StringUtil.trimStart(line, "/// ");
|
||||
if (Strings.areSameInstance(newLine, line)) {
|
||||
newLine = StringUtil.trimStart(line, "///");
|
||||
}
|
||||
}
|
||||
line = newLine;
|
||||
}
|
||||
}
|
||||
l.set(i, line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks the specified string by the specified separators into array of strings
|
||||
*
|
||||
@@ -968,7 +1014,12 @@ public class JDParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
private record CommentInfo(PsiDocComment docComment, PsiElement commentOwner, String commentHeader, String comment,
|
||||
String commentFooter) {
|
||||
private record CommentInfo(PsiDocComment docComment, PsiElement commentOwner, DocTextInfo docTextInfo) {
|
||||
}
|
||||
|
||||
private record DocTextInfo(String commentHeader, String comment, String commentFooter) {
|
||||
}
|
||||
|
||||
private record EmptyLinesInfo(int prefixLinesCount, int suffixLinesCount) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.impl.source.codeStyle.javadoc
|
||||
|
||||
class JDPreformattingContext(val prefixLinesCount : Int, val suffixLinesCount : Int)
|
||||
@@ -716,6 +716,7 @@ public final class CommonJavaRefactoringUtil {
|
||||
@NotNull Condition<? super Pair<PsiParameter, String>> eqCondition,
|
||||
@NotNull Condition<? super String> matchedToOldParam) throws IncorrectOperationException {
|
||||
if (docComment == null) return;
|
||||
PsiDocComment oldDocComment = (PsiDocComment)docComment.copy();
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final PsiDocTag[] paramTags = docComment.findTagsByName("param");
|
||||
if (parameters.length > 0 && newParameters.size() < parameters.length && paramTags.length == 0) return;
|
||||
@@ -778,10 +779,14 @@ public final class CommonJavaRefactoringUtil {
|
||||
? docComment.addAfter(psiDocTag, anchor)
|
||||
: docComment.add(psiDocTag);
|
||||
}
|
||||
formatJavadocIgnoringSettings(method, docComment);
|
||||
formatJavadocIgnoringSettings(method, docComment, oldDocComment);
|
||||
}
|
||||
|
||||
public static void formatJavadocIgnoringSettings(@NotNull PsiMethod method, @NotNull PsiDocComment docComment) {
|
||||
formatJavadocIgnoringSettings(method, docComment, null);
|
||||
}
|
||||
|
||||
public static void formatJavadocIgnoringSettings(@NotNull PsiMethod method, @NotNull PsiDocComment docComment, @Nullable PsiDocComment oldComment) {
|
||||
PsiFile containingFile = method.getContainingFile();
|
||||
if (containingFile == null) {
|
||||
return;
|
||||
@@ -793,11 +798,9 @@ public final class CommonJavaRefactoringUtil {
|
||||
boolean javadocEnabled = settings.ENABLE_JAVADOC_FORMATTING;
|
||||
try {
|
||||
settings.ENABLE_JAVADOC_FORMATTING = true;
|
||||
settings.JD_KEEP_TRAILING_EMPTY_LINES = false;
|
||||
CommentFormatter formatter = new CommentFormatter(method.getContainingFile());
|
||||
CommentFormatter formatter = new CommentFormatter(method.getContainingFile(), oldComment);
|
||||
formatter.processComment(docComment.getNode());
|
||||
} finally {
|
||||
settings.JD_KEEP_TRAILING_EMPTY_LINES = true;
|
||||
settings.ENABLE_JAVADOC_FORMATTING = javadocEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
class A {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Demo.
|
||||
*
|
||||
* @param a
|
||||
* a.
|
||||
* @param b
|
||||
* b.
|
||||
* @param c
|
||||
* c.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public void <caret>demo(int a, int b, int c) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
class A {
|
||||
|
||||
///
|
||||
///
|
||||
/// Demo.
|
||||
///
|
||||
/// @param a
|
||||
/// a.
|
||||
/// @param b
|
||||
/// b.
|
||||
/// @param c
|
||||
/// c.
|
||||
///
|
||||
///
|
||||
///
|
||||
public void <caret>demo(int a, int b, int c) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class A {
|
||||
|
||||
///
|
||||
///
|
||||
/// Demo.
|
||||
///
|
||||
/// @param b b.
|
||||
/// @param a a.
|
||||
/// @param c c.
|
||||
///
|
||||
///
|
||||
///
|
||||
public void demo(int b, int a, int c) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class A {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Demo.
|
||||
*
|
||||
* @param b b.
|
||||
* @param a a.
|
||||
* @param c c.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public void demo(int b, int a, int c) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -654,6 +654,14 @@ public class ChangeSignatureTest extends ChangeSignatureBaseTest {
|
||||
}, false);
|
||||
}
|
||||
|
||||
public void testPreserveEmptyTrailingLeadingLinesJavadoc() {
|
||||
doTest(null, null, null, method -> new ParameterInfoImpl[]{
|
||||
ParameterInfoImpl.create(1).withType(PsiTypes.intType()).withName("b"),
|
||||
ParameterInfoImpl.create(0).withType(PsiTypes.intType()).withName("a"),
|
||||
ParameterInfoImpl.create(2).withType(PsiTypes.intType()).withName("c"),
|
||||
}, false);
|
||||
}
|
||||
|
||||
public void testMultilineJavadocWithoutFormatting() { // IDEA-281568
|
||||
JavaCodeStyleSettings.getInstance(getProject()).ENABLE_JAVADOC_FORMATTING = false;
|
||||
doTest(null, null, null, method -> new ParameterInfoImpl[]{
|
||||
@@ -902,6 +910,14 @@ public class ChangeSignatureTest extends ChangeSignatureBaseTest {
|
||||
}, false);
|
||||
}
|
||||
|
||||
public void testPreserveEmptyTrailingLeadingLinesJavadocMarkdown() {
|
||||
doTest(null, null, null, method -> new ParameterInfoImpl[]{
|
||||
ParameterInfoImpl.create(1).withType(PsiTypes.intType()).withName("b"),
|
||||
ParameterInfoImpl.create(0).withType(PsiTypes.intType()).withName("a"),
|
||||
ParameterInfoImpl.create(2).withType(PsiTypes.intType()).withName("c"),
|
||||
}, false);
|
||||
}
|
||||
|
||||
public void testNoGapsInParameterTagsMarkdown() { // IDEA-139879
|
||||
doTest(null, null, null, method -> new ParameterInfoImpl[]{
|
||||
ParameterInfoImpl.create(0).withType(PsiTypes.intType()).withName("b"),
|
||||
|
||||
Reference in New Issue
Block a user