IDEA-364895 IDEA-380136 IJPL-162444 Javadoc: "first class" Markdown comment support

GitOrigin-RevId: 79d05829f03f4fa0ae8179627beac219ed69d7e6
This commit is contained in:
Mathias
2026-05-28 16:32:42 +00:00
committed by intellij-monorepo-bot
parent 4662def90f
commit 32766e9b18
111 changed files with 2116 additions and 286 deletions
@@ -135,7 +135,13 @@ public class JavaTypedHandlerBase extends TypedHandlerDelegate {
final @NotNull FileType fileType) {
if (!isJavaFile(file)) return Result.CONTINUE;
if (c == '@') {
if (c == '/') {
PsiElement element = file.findElementAt(editor.getCaretModel().getOffset() - 1);
if (element != null && element.getNode().getElementType() == JavaTokenType.END_OF_LINE_COMMENT && element.getTextLength() == 2) {
autoPopupJavadocLookup(project, editor);
}
}
else if (c == '@') {
autoPopupJavadocLookup(project, editor);
}
else if (c == '#' || c == '.') {
@@ -350,7 +350,8 @@ public final class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeSty
"LINE_COMMENT_ADD_SPACE_ON_REFORMAT",
"LINE_COMMENT_AT_FIRST_COLUMN",
"BLOCK_COMMENT_AT_FIRST_COLUMN",
"BLOCK_COMMENT_ADD_SPACE"
"BLOCK_COMMENT_ADD_SPACE",
"DOCUMENTATION_LINE_COMMENT_PREFERRED"
);
}
else if (settingsType == SettingsType.LANGUAGE_SPECIFIC) {
@@ -1,6 +1,6 @@
#foreach($param in $RECORD_COMPONENTS)
* @param $param
$COMMENT_PREFIX @param $param
#end
#foreach($param in $TYPE_PARAMS)
* @param <$param>
$COMMENT_PREFIX @param <$param>
#end
@@ -19,6 +19,13 @@
<td colspan="3"><font face="verdana" size="-1">Predefined variables take the following values:</font></td>
</tr>
<tr>
<td valign="top">
<nobr><font face="verdana" size="-2"><b>${COMMENT_PREFIX}</b></font></nobr>
</td>
<td width="10">&nbsp;</td>
<td width="100%" valign="top"><font face="verdana" size="-1">The prefix on each comment line (/// or *)</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2"><b>${RECORD_COMPONENTS}</b></font></nobr></td>
<td width="10">&nbsp;</td>
@@ -1,9 +1,9 @@
#foreach($param in $PARAMS)
* @param $param
$COMMENT_PREFIX @param $param
#end
#foreach($param in $TYPE_PARAMS)
* @param <$param>
$COMMENT_PREFIX @param <$param>
#end
#foreach($exception in $THROWS)
* @throws $exception
$COMMENT_PREFIX @throws $exception
#end
@@ -19,6 +19,13 @@
<td colspan="3"><font face="verdana" size="-1">Predefined variables take the following values:</font></td>
</tr>
<tr>
<td valign="top">
<nobr><font face="verdana" size="-2"><b>${COMMENT_PREFIX}</b></font></nobr>
</td>
<td width="10">&nbsp;</td>
<td width="100%" valign="top"><font face="verdana" size="-1">The prefix on each comment line (/// or *)</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2"><b>${PARAMS}</b></font></nobr></td>
<td width="10">&nbsp;</td>
@@ -1,12 +1,12 @@
#foreach($param in $PARAMS)
* @param $param
$COMMENT_PREFIX @param $param
#end
#if($RETURN_TYPE != "void")
* @return
$COMMENT_PREFIX @return
#end
#foreach($param in $TYPE_PARAMS)
* @param <$param>
$COMMENT_PREFIX @param <$param>
#end
#foreach($exception in $THROWS)
* @throws $exception
$COMMENT_PREFIX @throws $exception
#end
@@ -19,6 +19,13 @@
<td colspan="3"><font face="verdana" size="-1">Predefined variables take the following values:</font></td>
</tr>
<tr>
<td valign="top">
<nobr><font face="verdana" size="-2"><b>${COMMENT_PREFIX}</b></font></nobr>
</td>
<td width="10">&nbsp;</td>
<td width="100%" valign="top"><font face="verdana" size="-1">The prefix on each comment line (/// or *)</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2"><b>${PARAMS}</b></font></nobr></td>
<td width="10">&nbsp;</td>
@@ -1,17 +1,17 @@
#foreach ($param in $PARAMS_INHERITED)
* @param $param
$COMMENT_PREFIX @param $param
#end
#if (!$PARAMS_INHERITED)
#foreach ($param in $PARAMS)
* @param $param
$COMMENT_PREFIX @param $param
#end
#end
#if($RETURN_TYPE != "void")
* @return
$COMMENT_PREFIX @return
#end
#foreach($param in $TYPE_PARAMS)
* @param <$param>
$COMMENT_PREFIX @param <$param>
#end
#foreach($exception in $THROWS)
* @throws $exception
$COMMENT_PREFIX @throws $exception
#end
@@ -19,6 +19,13 @@
<td colspan="3"><font face="verdana" size="-1">Predefined variables take the following values:</font></td>
</tr>
<tr>
<td valign="top">
<nobr><font face="verdana" size="-2"><b>${COMMENT_PREFIX}</b></font></nobr>
</td>
<td width="10">&nbsp;</td>
<td width="100%" valign="top"><font face="verdana" size="-1">The prefix on each comment line (/// or *)</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2"><b>${PARAMS}</b></font></nobr></td>
<td width="10">&nbsp;</td>
@@ -44,3 +44,5 @@ codestyle.settings.extractor.command.name=CodeStyleSettings Extractor
nullaway.suppress.command.name=Suppress NullAway Problem
nullaway.suppress.inlay.text=Add @SuppressWarnings
nullaway.suppress.error.no.target=Cannot find element to suppress
insert.javadoc.template=Insert Javadoc Stub
@@ -25,6 +25,7 @@ import com.intellij.codeInspection.javaDoc.JavadocDeclarationInspection;
import com.intellij.codeInspection.javaDoc.MissingJavadocInspection;
import com.intellij.icons.AllIcons;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
@@ -64,6 +65,7 @@ import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiTypeParameter;
import com.intellij.psi.TokenType;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.filters.TrueFilter;
@@ -84,6 +86,7 @@ import com.intellij.psi.javadoc.PsiSnippetAttribute;
import com.intellij.psi.javadoc.PsiSnippetAttributeList;
import com.intellij.psi.javadoc.PsiSnippetAttributeValue;
import com.intellij.psi.javadoc.PsiSnippetDocTagValue;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
@@ -147,9 +150,20 @@ public final class JavaDocCompletionContributor extends CompletionContributor im
@NotNull ProcessingContext context,
@NotNull CompletionResultSet result) {
final PsiElement position = parameters.getPosition();
if (isStartOfMarkdownComment(position)) {
JavadocMarkdownTemplateLookupElement documentationElement = new JavadocMarkdownTemplateLookupElement(
((PsiDocComment)position.getParent()).getOwner());
if (documentationElement.isAvailable()) {
result.addElement(documentationElement);
// Unusual early return, but only the template should be shown
return;
}
}
boolean isArg = PsiJavaPatterns.psiElement().afterLeaf("(").accepts(position);
PsiDocTag tag = PsiTreeUtil.getParentOfType(position, PsiDocTag.class);
boolean onlyConstants = !isArg && tag != null && tag.getName().equals(VALUE_TAG);
final PsiReference ref = position.getContainingFile().findReferenceAt(parameters.getOffset());
PsiElement refElement = ref == null ? null : ref.getElement();
@@ -165,6 +179,7 @@ public final class JavaDocCompletionContributor extends CompletionContributor im
result = JavaCompletionSorting.addJavaSorting(parameters, result);
result.stopHere();
boolean onlyConstants = !isArg && tag != null && tag.getName().equals(VALUE_TAG);
for (LookupElement item : completeJavadocReference(position, (PsiJavaReference)ref)) {
if (onlyConstants) {
if (!(item.getObject() instanceof PsiField field)) continue;
@@ -192,7 +207,6 @@ public final class JavaDocCompletionContributor extends CompletionContributor im
}
}
}
if (tag != null && "author".equals(tag.getName())) {
result.addElement(LookupElementBuilder.create(SystemProperties.getUserName()));
}
@@ -282,6 +296,76 @@ public final class JavaDocCompletionContributor extends CompletionContributor im
}
}
});
extend(CompletionType.BASIC, psiElement(PsiSnippetDocTagValue.class), new CompletionProvider<>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
@NotNull ProcessingContext context,
@NotNull CompletionResultSet result) {
PsiElement position = parameters.getPosition();
PsiElement parent = position.getParent();
if (parent instanceof PsiDocTagValue && !(parent instanceof PsiDocParamRef) && !(parent instanceof PsiDocMethodOrFieldRef)) {
PsiDocTag docTag = ObjectUtils.tryCast(parent.getParent(), PsiDocTag.class);
if (docTag != null) {
JavadocManager docManager = JavadocManager.getInstance(parameters.getOriginalFile().getProject());
JavadocTagInfo info = docManager.getTagInfo(docTag.getName());
if (info != null) {
// Avoid suggesting standard tags inside custom tag value, as custom tag may require custom value (e.g., reference)
suggestTags(parameters, result, position, true);
}
}
}
}
});
extend(CompletionType.BASIC, psiElement(JavaDocTokenType.DOC_COMMENT_DATA), new CompletionProvider<>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
@NotNull ProcessingContext context,
@NotNull CompletionResultSet result) {
PsiElement position = parameters.getPosition();
if (isStartOfMarkdownComment(position)) return;
final PsiParameter param = getDocTagParam(position.getParent());
if (param != null) {
suggestSimilarParameterDescriptions(result, position, param);
}
if (!(position.getParent() instanceof PsiInlineDocTag)) {
suggestLinkWrappingVariants(parameters, result.withPrefixMatcher(CompletionUtil.findJavaIdentifierPrefix(parameters)), position);
}
suggestCodeLiterals(result, position);
boolean forceInlineTags = position.getParent() instanceof PsiDocTag &&
!(position.getPrevSibling() instanceof PsiDocToken token &&
token.getTokenType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS);
suggestTags(parameters, result, position, forceInlineTags);
}
});
}
/// Returns `true` if the element is at the start of the Markdown comment.
/// Note that if the comment isn't empty, `false` will be returned.
private static boolean isStartOfMarkdownComment(@NotNull PsiElement element) {
PsiElement prev = element.getPrevSibling();
if (prev == null) return false;
ASTNode prevNode = prev.getNode();
IElementType prevType = prevNode.getElementType();
PsiDocComment comment = PsiTreeUtil.getParentOfType(prev, PsiDocComment.class);
if (comment == null || !comment.isMarkdownComment()) return false;
PsiElement[] children = comment.getChildren();
if (children.length > 3) return false;
// The identifier gets merged into the existing comment data (if it exists)
if (element.getTextLength() > CompletionUtilCore.DUMMY_IDENTIFIER.length()) return false;
if (prevType == TokenType.WHITE_SPACE) return true;
if (prev == comment.getFirstChild()) return true;
return false;
}
private @Unmodifiable @NotNull List<LookupElement> completeJavadocReference(PsiElement position, PsiJavaReference ref) {
@@ -326,51 +410,6 @@ public final class JavaDocCompletionContributor extends CompletionContributor im
return null;
}
@Override
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
PsiElement position = parameters.getPosition();
if (position.getParent() instanceof PsiDocFragmentName) {
super.fillCompletionVariants(parameters, result);
return;
}
if (PsiDocToken.isDocToken(position, JavaDocTokenType.DOC_TAG_VALUE_TOKEN)) {
PsiElement parent = position.getParent();
if (parent instanceof PsiDocTagValue && !(parent instanceof PsiDocParamRef) && !(parent instanceof PsiDocMethodOrFieldRef)) {
PsiDocTag docTag = ObjectUtils.tryCast(parent.getParent(), PsiDocTag.class);
if (docTag != null) {
JavadocManager docManager = JavadocManager.getInstance(parameters.getOriginalFile().getProject());
JavadocTagInfo info = docManager.getTagInfo(docTag.getName());
if (info != null) {
// Avoid suggesting standard tags inside custom tag value, as custom tag may require custom value (e.g., reference)
suggestTags(parameters, result, position, true);
}
}
}
}
if (PsiJavaPatterns.psiElement(JavaDocTokenType.DOC_COMMENT_DATA).accepts(position)) {
final PsiParameter param = getDocTagParam(position.getParent());
if (param != null) {
suggestSimilarParameterDescriptions(result, position, param);
}
if (!(position.getParent() instanceof PsiInlineDocTag)) {
suggestLinkWrappingVariants(parameters, result.withPrefixMatcher(CompletionUtil.findJavaIdentifierPrefix(parameters)), position);
}
suggestCodeLiterals(result, position);
boolean forceInlineTags = position.getParent() instanceof PsiDocTag &&
!(position.getPrevSibling() instanceof PsiDocToken token
&& token.getTokenType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS);
suggestTags(parameters, result, position, forceInlineTags);
}
super.fillCompletionVariants(parameters, result);
}
private static void suggestTags(@NotNull CompletionParameters parameters,
@NotNull CompletionResultSet result,
@NotNull PsiElement position,
@@ -44,6 +44,9 @@ public final class JavadocCompletionConfidence extends CompletionConfidence {
return ThreeState.NO;
}
}
if(PsiDocToken.isDocToken(contextElement, JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) {
return ThreeState.NO;
}
return ThreeState.UNSURE;
}
@@ -0,0 +1,72 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.editorActions.CodeDocumentationUtil;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementCustomPreviewHolder;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.documentation.CodeDocumentationProvider;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.modcommand.ActionContext;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiJavaDocumentedElement;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.RefactorJBundle;
import org.jetbrains.annotations.NotNull;
/// [LookupElement] to handle Markdown Javadoc template preview and insertion
final class JavadocMarkdownTemplateLookupElement extends LookupElement implements LookupElementCustomPreviewHolder {
private final PsiJavaDocumentedElement targetElement;
private String cachedTemplate = null;
JavadocMarkdownTemplateLookupElement(PsiJavaDocumentedElement element) { targetElement = element; }
@Override
public @NotNull String getLookupString() {
return RefactorJBundle.message("insert.javadoc.template");
}
@Override
public @NotNull IntentionPreviewInfo preview(@NotNull ActionContext ctx) {
return new IntentionPreviewInfo.Snippet(JavaFileType.INSTANCE, "/// \n" + getTemplate(),
targetElement.getContainingFile().getFileDocument().getLineNumber(ctx.offset()));
}
private String getTemplate() {
if (cachedTemplate == null) {
final CodeDocumentationProvider langDocumentationProvider = CodeDocumentationUtil.getCodeProvider(JavaLanguage.INSTANCE);
assert langDocumentationProvider != null;
String potentialDocs = langDocumentationProvider.generateDocumentationContentStub(targetElement.getDocComment());
cachedTemplate = potentialDocs == null ? "" : potentialDocs.stripTrailing().stripIndent();
}
return cachedTemplate;
}
public boolean isAvailable() {
return !StringUtil.isEmptyOrSpaces(getTemplate());
}
@Override
public void handleInsert(@NotNull InsertionContext context) {
String docs = "\n" + getTemplate();
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), docs);
context.commitDocument();
PsiComment comment =
PsiTreeUtil.getParentOfType(context.getFile().findElementAt(context.getTailOffset() - 1), PsiComment.class);
if (comment == null) return;
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(context.getProject());
CodeDocumentationUtil.formatComment(context.getFile(), comment, codeStyleManager);
PsiDocumentManager.getInstance(context.getProject()).doPostponedOperationsAndUnblockDocument(context.getDocument());
context.getDocument().insertString(comment.getTextRange().getStartOffset() + 3, " ");
context.getEditor().getCaretModel().moveToOffset(comment.getTextRange().getStartOffset() + 4);
}
}
@@ -17,7 +17,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
@@ -31,7 +30,6 @@ import com.intellij.psi.PsiTypeParameterListOwner;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.util.PairProcessor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
@@ -43,6 +41,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public final class JavaDocCommentFixer implements DocCommentFixer {
@@ -99,12 +98,12 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
}
Document document = file.getFileDocument();
if (!commonProblems.isEmpty()) {
fixCommonProblems(ContainerUtil.flatten(commonProblems.values()), comment, document, project);
fixCommonProblems(ContainerUtil.flatten(commonProblems.values()), owner, document, project);
}
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
ensureContentOrdered(docComment, document);
locateCaret(docComment, editor, file);
ensureContentOrdered(Objects.requireNonNull(owner.getDocComment()), document);
locateCaret(Objects.requireNonNull(owner.getDocComment()), editor, file);
}
private static @NotNull MissingJavadocInspection getMissingJavadocInspection() {
@@ -151,15 +150,18 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
* parameter doesn't have a description etc). This method handles result of the second exception
*
* @param problems detected problems
* @param comment target comment to fix
* @param commentOwner owner of the comment to fix
* @param document target document which contains text of the comment being fixed
* @param project current project
*/
@SuppressWarnings("unchecked")
private static void fixCommonProblems(@NotNull List<? extends ProblemDescriptor> problems,
@NotNull PsiComment comment,
@NotNull PsiJavaDocumentedElement commentOwner,
final @NotNull Document document,
@NotNull Project project) {
PsiDocComment comment = commentOwner.getDocComment();
if (comment == null) return;
List<RangeMarker> toRemove = new ArrayList<>();
List<ProblemDescriptor> problemsToApply = new ArrayList<>();
for (ProblemDescriptor problem : problems) {
@@ -167,7 +169,7 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
if (element == null) {
continue;
}
if (!PsiDocToken.isDocToken(element, JavaDocTokenType.DOC_COMMENT_START) && comment.getTextRange().contains(element.getTextRange())) {
if ((element != comment.getFirstChild()) && comment.getTextRange().contains(element.getTextRange())) {
// Unnecessary element like '@return' at the void method's javadoc.
for (PsiElement e = element; e != null; e = e.getParent()) {
if (e instanceof PsiDocTag) {
@@ -188,6 +190,7 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
fixes[0].applyFix(project, problem);
}
}
comment = commentOwner.getDocComment();
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
if (toRemove.isEmpty()) {
@@ -205,8 +208,15 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
int i = CharArrayUtil.shiftBackward(text, startOffset - 1, " \t");
if (i >= 0) {
char c = text.charAt(i);
if (c == '*') {
i = CharArrayUtil.shiftBackward(text, i - 1, " \t");
if (!comment.isMarkdownComment()) {
if (c == '*') {
i = CharArrayUtil.shiftBackward(text, i - 1, " \t");
}
}
else {
if (c == '/') {
i = CharArrayUtil.shiftBackward(text, i - 1, " \t/");
}
}
}
if (i >= 0 && text.charAt(i) == '\n') {
@@ -215,7 +225,7 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
int endOffset = range.getEndOffset();
// Javadoc PSI is awkward, it includes next line text before the next tag. That's why we need to strip it.
i = CharArrayUtil.shiftBackward(text, endOffset - 1, " \t*");
i = findBackwardOffset(text, endOffset - 1, comment.isMarkdownComment());
if (i > 0 && text.charAt(i) == '\n') {
endOffset = i;
}
@@ -246,7 +256,7 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
String paramName = valueElement.getText();
if (paramName != null) {
current.add(paramName);
tagInfoByName.put(paramName, parseTagValue(tag, document));
tagInfoByName.put(paramName, parseTagValue(comment, tag, document));
}
}
//endregion
@@ -294,7 +304,9 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
//endregion
}
private static @NotNull Pair<TextRange, String> parseTagValue(@NotNull PsiDocTag tag, @NotNull Document document) {
private static @NotNull Pair<TextRange, String> parseTagValue(@NotNull PsiDocComment comment,
@NotNull PsiDocTag tag,
@NotNull Document document) {
PsiDocTagValue valueElement = tag.getValueElement();
assert valueElement != null;
@@ -302,7 +314,7 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
int endOffset = tag.getTextRange().getEndOffset();
// Javadoc PSI is rather weird...
CharSequence text = document.getCharsSequence();
int i = CharArrayUtil.shiftBackward(text, endOffset - 1, " \t*");
int i = findBackwardOffset(text, endOffset - 1, comment.isMarkdownComment());
if (i > 0 && text.charAt(i) == '\n') {
endOffset = i;
}
@@ -333,7 +345,7 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
if (!good) {
int offset = tag.getTextRange().getEndOffset();
CharSequence text = document.getCharsSequence();
int i = CharArrayUtil.shiftBackward(text, offset - 1, " \t*");
int i = findBackwardOffset(text, offset - 1, comment.isMarkdownComment());
if (i > 0 && text.charAt(i) == '\n') {
offset = i - 1;
}
@@ -347,4 +359,9 @@ public final class JavaDocCommentFixer implements DocCommentFixer {
JavadocNavigationDelegate.navigateToLineEnd(editor, file);
}
}
/// @return the new offset, taking into account the start of a comment line
private static int findBackwardOffset(@NotNull CharSequence text, int offset, boolean isMarkdown) {
return CharArrayUtil.shiftBackward(text, offset, isMarkdown ? " \t/" : " \t*");
}
}
@@ -218,7 +218,11 @@ public final class JavaTypedHandler extends JavaTypedHandlerBase {
int offset = editor.getCaretModel().getOffset();
PsiElement lastElement = file.findElementAt(offset - 1);
return lastElement != null && StringUtil.endsWithChar(lastElement.getText(), '@');
if (lastElement != null) {
String lastElementText = lastElement.getText();
return StringUtil.endsWithChar(lastElementText, '@') || StringUtil.endsWith(lastElementText, "///");
}
return false;
});
}
@@ -637,6 +637,11 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
attributes.put("CONTAINING_CLASS", psiClass.getName());
}
}
if (commentOwner.getDocComment() != null) {
attributes.put("COMMENT_PREFIX", commentOwner.getDocComment().isMarkdownComment() ? "///" : "*");
}
return attributes;
}
@@ -697,16 +702,16 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
final PsiTypeParameterList typeParameterList = psiMethod.getTypeParameterList();
if (typeParameterList != null) {
createTypeParamsListComment(builder, commenter, typeParameterList);
createTypeParamsListComment(builder, commenter, typeParameterList, _comment);
}
if (psiMethod.getReturnType() != null && !PsiTypes.voidType().equals(psiMethod.getReturnType())) {
builder.append(CodeDocumentationUtil.createDocCommentLine(RETURN_TAG, _comment.getContainingFile(), commenter));
builder.append(CodeDocumentationUtil.createDocCommentLine(RETURN_TAG, _comment, commenter));
builder.append(LINE_SEPARATOR);
}
final PsiJavaCodeReferenceElement[] references = psiMethod.getThrowsList().getReferenceElements();
for (PsiJavaCodeReferenceElement reference : references) {
builder.append(CodeDocumentationUtil.createDocCommentLine(THROWS_TAG, _comment.getContainingFile(), commenter));
builder.append(CodeDocumentationUtil.createDocCommentLine(THROWS_TAG, _comment, commenter));
builder.append(reference.getText());
builder.append(LINE_SEPARATOR);
}
@@ -714,14 +719,14 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
else if (commentOwner instanceof PsiClass) {
if (((PsiClass)commentOwner).isRecord()) {
for (PsiRecordComponent component : ((PsiClass)commentOwner).getRecordComponents()) {
builder.append(CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, commentOwner.getContainingFile(), commenter));
builder.append(CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, _comment, commenter));
builder.append(component.getName());
builder.append(LINE_SEPARATOR);
}
}
final PsiTypeParameterList typeParameterList = ((PsiClass)commentOwner).getTypeParameterList();
if (typeParameterList != null) {
createTypeParamsListComment(builder, commenter, typeParameterList);
createTypeParamsListComment(builder, commenter, typeParameterList, _comment);
}
}
return !builder.isEmpty() ? builder.toString() : null;
@@ -732,10 +737,16 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
PsiMethod psiMethod) {
PsiParameterList parameterList = psiMethod.getParameterList();
final PsiParameter[] parameters = parameterList.getParameters();
PsiDocComment docComment = psiMethod.getDocComment();
final Map<Integer, String> index2Description = collectParentParameterDescriptions(psiMethod, parameters);
for (int i = 0; i < parameters.length; i++) {
builder.append(CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, psiMethod.getContainingFile(), commenter));
builder.append(
docComment == null
? CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, psiMethod.getContainingFile(), commenter)
: CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, docComment, commenter)
);
builder.append(parameters[i].getName());
String description = index2Description.get(i);
if (description != null) {
@@ -745,8 +756,10 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
}
}
/// @return A "sparse array" of each parameter that is documented
private static @NotNull Map<Integer, String> collectParentParameterDescriptions(PsiMethod psiMethod, PsiParameter[] parameters) {
final Map<Integer, String> index2Description = new HashMap<>();
CharSequence prefix = CodeDocumentationUtil.preferredDocumentationLinePrefix(psiMethod.getContainingFile(), psiMethod.getDocComment());
for (int i = 0; i < parameters.length; i++) {
PsiDocTag param = JavaDocInfoGenerator.findInheritDocTag(psiMethod, i);
@@ -763,7 +776,9 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
}
}
if (paramName != null) {
String description = param.getText().substring(endOffset).replaceFirst("(\\s*\\*)?\\s*$", "");
String description = param.getText().substring(endOffset).replaceFirst("(\\s*(?:\\*|///))?\\s*$", "");
// Remove the leading chars, since we don't know the final comment type.
description = description.replaceAll("(\\n\\s*)(?:\\*|///)", "$1" + prefix);
index2Description.put(i, description);
}
}
@@ -772,10 +787,11 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
public static void createTypeParamsListComment(final StringBuilder buffer,
final CodeDocumentationAwareCommenter commenter,
final PsiTypeParameterList typeParameterList) {
final PsiTypeParameterList typeParameterList,
final PsiComment comment) {
final PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters();
for (PsiTypeParameter typeParameter : typeParameters) {
buffer.append(CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, typeParameterList.getContainingFile(), commenter));
buffer.append(CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, comment, commenter));
buffer.append("<").append(typeParameter.getName()).append(">");
buffer.append(LINE_SEPARATOR);
}
@@ -74,10 +74,25 @@ public final class JavaCommenter implements CodeDocumentationAwareCommenterEx {
return element instanceof PsiDocComment;
}
@Override
public boolean isDocumentationLineComment(PsiComment element) {
return isDocumentationComment(element) && ((PsiDocComment)element).isMarkdownComment();
}
@Override
public boolean isDocumentationCommentText(final PsiElement element) {
if (element == null) return false;
final ASTNode node = element.getNode();
return node != null && (node.getElementType() == JavaDocTokenType.DOC_COMMENT_DATA || node.getElementType() == JavaDocTokenType.DOC_TAG_VALUE_TOKEN);
}
@Override
public IElementType getDocumentationLineCommentTokenType() {
return JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS;
}
@Override
public String getDocumentationLineCommentPrefix() {
return "///";
}
}
@@ -14,6 +14,7 @@ import com.intellij.lang.java.parser.JavaParserUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.syntax.SyntaxElementType;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.impl.source.javadoc.PsiDocCommentImpl;
import com.intellij.psi.impl.source.javadoc.PsiDocFragmentNameImpl;
@@ -185,12 +186,21 @@ public interface JavaDocElementType {
@NotNull CharSequence newText,
@NotNull Language fileLanguage,
@NotNull Project project) {
if (!StringUtil.startsWith(newText, "/**") || !StringUtil.endsWith(newText, "*/")) return false;
SyntaxElementType targetType;
if (StringUtil.startsWith(newText, "/**") && StringUtil.endsWith(newText, "*/")) {
targetType = JavaDocSyntaxElementType.DOC_COMMENT;
}
else if (StringUtil.startsWith(newText, "///")) {
targetType = JavaDocSyntaxElementType.DOC_MARKDOWN_COMMENT;
}
else {
return false;
}
LanguageLevel level = LanguageLevelProjectExtension.getInstance(project).getLanguageLevel();
JavaLexer lexer = new JavaLexer(level);
lexer.start(newText);
if (lexer.getTokenType() == JavaDocSyntaxElementType.DOC_COMMENT) {
if (lexer.getTokenType() == targetType) {
lexer.advance();
return lexer.getTokenType() == null;
}
@@ -0,0 +1,13 @@
// "Add Javadoc" "true-preview"
class A {
/// @param a Very beautiful param
/// from mk jdoc
void test(int a) {}
}
class B extends A {
/**
* @param a Very beautiful param
* from mk jdoc
*/
void test(int a) {}
}
@@ -0,0 +1,9 @@
// "Add Javadoc" "true-preview"
class A {
/// @param a Very beautiful param
/// from mk jdoc
void test(int a) {}
}
class B extends A {
void test<caret>(int a) {}
}
@@ -0,0 +1,6 @@
// "Add Javadoc" "true-preview"
///
class Foo {
}
@@ -0,0 +1,25 @@
// "Add Javadoc" "true-preview"
class A {
/// @param b it's a description
/// for the
/// second param
/// @param a it&apos;s &#97; description for the first param
/// @param blah it's a description
/// for a nonexistent parameter
/// @param c it's a description
/// for the third param
void test(int a, int b, int c, int d) {}
}
class B extends A {
/// @param x it&apos;s &#97; description for the first param
/// @param y it's a description
/// for the
/// second param
/// @param z it's a description
/// for the third param
/// @param w
@Override
void test(final int x, int y, int z, int w) {}
}
@@ -0,0 +1,8 @@
// "Add Javadoc" "true-preview"
class A {
///
private int myFoo;
}
@@ -0,0 +1,9 @@
// "Add Javadoc" "true-preview"
class Test {
///
@XXX
int xxx = 1;
}
@@ -0,0 +1,8 @@
// "Add Javadoc" "true-preview"
class Test {
///
private @XXX int xxx = 1;
}
@@ -0,0 +1,14 @@
// "Add Javadoc" "true-preview"
class A {
/**
* @param a Very beautiful param
* From the classic javadoc
*/
void test(int a) {}
}
class B extends A {
/// @param a Very beautiful param
/// From the classic javadoc
void test(int a) {}
}
@@ -0,0 +1,8 @@
// "Add Javadoc" "true-preview"
class A {
///
public void foo() {
}
}
@@ -0,0 +1,10 @@
// "Add Javadoc" "true-preview"
class A {
/// @param s <caret>
/// @return
/// @throws Exception
public int foo(final String s) throws Exception {
}
}
@@ -0,0 +1,128 @@
// "Add Javadoc" "true-preview"
//Method Comments Algorithm
//
// If a method does not have a documentation comment, or has an {@inheritDoc} tag, then the standard doclet uses Automatic Supertype Search.
interface ClazzInterface1Interface2 {
/// @param h must be copied to 8th place. Just
/// a few unicode symbols: &lt;&quest;&gt;
/// @param f blah blah blah
/// @param c blah blah blah
/// @param d blah blah blah
/// @param g blah blah blah
void foo(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j);
}
interface ClazzInterface1Interface1 {
/// @param dd blah blah blah
/// @param ff blah blah blah
/// @param cc blah blah blah
/// @param gg must be copied to 7th place
void foo(int aa, int bb, int cc, int dd, int ee, int ff, int gg, int hh, int ii, int jj);
}
class ClazzClazz {
/// @param fff must be copied to 6th place
/// @param ccc blah blah blah
void foo(int aaa, int bbb, int ccc, int ddd, int eee, int fff, int ggg, int hhh, int iii, int jjj) {
}
}
interface ClazzInterface1 extends ClazzInterface1Interface1, ClazzInterface1Interface2 {
/// @param ffff blah blah blah
/// @param dddd must be copied to 4th place
/// @param cccc blah blah blah
void foo(int aaaa, int bbbb, int cccc, int dddd, int eeee, int ffff, int gggg, int hhhh, int iiii, int jjjj);
}
interface ClazzInterface2 {
/// @param ddddd blah blah blah
/// @param hhhhh blah blah blah
/// @param fffff blah blah blah
/// @param ccccc blah blah blah
/// @param ggggg blah blah blah
/// @param eeeee must be copied to 5th place
void foo(int aaaaa, int bbbbb, int ccccc, int ddddd, int eeeee, int fffff, int ggggg, int hhhhh, int iiiii, int jjjjj);
}
class Clazz extends ClazzClazz implements ClazzInterface1, ClazzInterface2 {
/// @param cccccc must be copied to 3rd place
public void foo(int aaaaaa, int bbbbbb, int cccccc, int dddddd, int eeeeee, int ffffff, int gggggg, int hhhhhh, int iiiiii, int jjjjjj) {
}
}
interface Interface1Interface1 {
/// @param x1 blah blah blah
/// @param x3 blah blah blah
/// @param x4 blah blah blah
/// @param x5 blah blah blah
/// @param x6 blah blah blah
/// @param x7 blah blah blah
/// @param x8 blah blah blah
/// @param x10 must be copied to 10th place
void foo(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10);
}
interface Interface1Interface2 {
/// @param b1 blah blah blah
/// @param b3 blah blah blah
/// @param b4 blah blah blah
/// @param b5 blah blah blah
/// @param b6 blah blah blah
/// @param b7 blah blah blah
/// @param b8 blah blah blah
/// @param b9 must be copied to 9th place
/// @param b10 blah blah blah
void foo(int b1, int b2, int b3, int b4, int b5, int b6, int b7, int b8, int b9, int b10);
}
interface Interface1 extends Interface1Interface1, Interface1Interface2 {
/// @param parameter1 must be copied to 1st place
/// @param parameter3 blah blah blah
/// @param parameter4 blah blah blah
/// @param parameter5 blah blah blah
/// @param parameter6 blah blah blah
/// @param parameter7 blah blah blah
/// @param parameter8 blah blah blah
void foo(int parameter1,
int parameter2,
int parameter3,
int parameter4,
int parameter5,
int parameter6,
int parameter7,
int parameter8,
int parameter9,
int parameter10);
}
interface Interface2 {
/// @param param1 blah blah blah
/// @param param2 must be copied to 2nd place
/// @param param3 blah blah blah
/// @param param4 blah blah blah
/// @param param5 blah blah blah
/// @param param6 blah blah blah
/// @param param7 blah blah blah
/// @param param8 blah blah blah
/// @param param9 blah blah blah
/// @param param10 blah blah blah
void foo(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10);
}
class Test extends Clazz implements Interface1, Interface2 {
/// @param p1 must be copied to 1st place <caret>
/// @param p2 must be copied to 2nd place
/// @param p3 must be copied to 3rd place
/// @param p4 must be copied to 4th place
/// @param p5 must be copied to 5th place
/// @param p6 must be copied to 6th place
/// @param p7 must be copied to 7th place
/// @param p8 must be copied to 8th place. Just
/// a few unicode symbols: &lt;&quest;&gt;
/// @param p9 must be copied to 9th place
/// @param p10 must be copied to 10th place
public void foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10) {
}
}
@@ -0,0 +1,5 @@
// "Add Javadoc" "true-preview"
class Foo {
int x = 2, ///
y = 1;
}
@@ -0,0 +1,5 @@
// "Add Javadoc" "true-preview"
class Foo {
///
int x = 2, y = 1;
}
@@ -0,0 +1,6 @@
// "Add Javadoc" "true-preview"
/// @param u
record Test(int u) {
}
@@ -0,0 +1,13 @@
// "Add Javadoc" "false"
class A {
void m() {
new Ru<caret>nnable() {
@Override
public void run() {
}
}
}
}
@@ -0,0 +1,5 @@
// "Add Javadoc" "true-preview"
class Fo<caret>o {
}
@@ -0,0 +1,13 @@
// "Add Javadoc" "false"
class A {
void m() {
Object o<caret> = new Object() {
}
}
}
@@ -0,0 +1,18 @@
// "Add Javadoc" "true-preview"
class A {
/// @param b it's a description
/// for the
/// second param
/// @param a it&apos;s &#97; description for the first param
/// @param blah it's a description
/// for a nonexistent parameter
/// @param c it's a description
/// for the third param
void test(int a, int b, int c, int d) {}
}
class B extends A {
@Override
void test<caret>(final int x, int y, int z, int w) {}
}
@@ -0,0 +1,7 @@
// "Add Javadoc" "true-preview"
class A {
private int myFo<caret>o;
}
@@ -0,0 +1,8 @@
// "Add Javadoc" "true-preview"
class Test {
@XXX
int x<caret>xx = 1;
}
@@ -0,0 +1,7 @@
// "Add Javadoc" "true-preview"
class Test {
private @XXX int x<caret>xx = 1;
}
@@ -0,0 +1,8 @@
// "Add Javadoc" "false"
class Test {
String foo = "xyz";
String bar = f<caret>oo;
}
@@ -0,0 +1,12 @@
// "Add Javadoc" "true-preview"
class A {
/**
* @param a Very beautiful param
* From the classic javadoc
*/
void test(int a) {}
}
class B extends A {
void test<caret>(int a) {}
}
@@ -0,0 +1,5 @@
// "Add Javadoc" "false"
/// javadoc
class F<caret>oo {
}
@@ -0,0 +1,9 @@
// "Add Javadoc" "false"
class Test {
void x() {
class <caret>Local {}
}
}
@@ -0,0 +1,7 @@
// "Add Javadoc" "true-preview"
class A {
public void f<caret>oo() {
}
}
@@ -0,0 +1,7 @@
// "Add Javadoc" "true-preview"
class A {
public int foo<caret>(final String s) throws Exception {
}
}
@@ -0,0 +1,117 @@
// "Add Javadoc" "true-preview"
//Method Comments Algorithm
//
// If a method does not have a documentation comment, or has an {@inheritDoc} tag, then the standard doclet uses Automatic Supertype Search.
interface ClazzInterface1Interface2 {
/// @param h must be copied to 8th place. Just
/// a few unicode symbols: &lt;&quest;&gt;
/// @param f blah blah blah
/// @param c blah blah blah
/// @param d blah blah blah
/// @param g blah blah blah
void foo(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j);
}
interface ClazzInterface1Interface1 {
/// @param dd blah blah blah
/// @param ff blah blah blah
/// @param cc blah blah blah
/// @param gg must be copied to 7th place
void foo(int aa, int bb, int cc, int dd, int ee, int ff, int gg, int hh, int ii, int jj);
}
class ClazzClazz {
/// @param fff must be copied to 6th place
/// @param ccc blah blah blah
void foo(int aaa, int bbb, int ccc, int ddd, int eee, int fff, int ggg, int hhh, int iii, int jjj) {
}
}
interface ClazzInterface1 extends ClazzInterface1Interface1, ClazzInterface1Interface2 {
/// @param ffff blah blah blah
/// @param dddd must be copied to 4th place
/// @param cccc blah blah blah
void foo(int aaaa, int bbbb, int cccc, int dddd, int eeee, int ffff, int gggg, int hhhh, int iiii, int jjjj);
}
interface ClazzInterface2 {
/// @param ddddd blah blah blah
/// @param hhhhh blah blah blah
/// @param fffff blah blah blah
/// @param ccccc blah blah blah
/// @param ggggg blah blah blah
/// @param eeeee must be copied to 5th place
void foo(int aaaaa, int bbbbb, int ccccc, int ddddd, int eeeee, int fffff, int ggggg, int hhhhh, int iiiii, int jjjjj);
}
class Clazz extends ClazzClazz implements ClazzInterface1, ClazzInterface2 {
/// @param cccccc must be copied to 3rd place
public void foo(int aaaaaa, int bbbbbb, int cccccc, int dddddd, int eeeeee, int ffffff, int gggggg, int hhhhhh, int iiiiii, int jjjjjj) {
}
}
interface Interface1Interface1 {
/// @param x1 blah blah blah
/// @param x3 blah blah blah
/// @param x4 blah blah blah
/// @param x5 blah blah blah
/// @param x6 blah blah blah
/// @param x7 blah blah blah
/// @param x8 blah blah blah
/// @param x10 must be copied to 10th place
void foo(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10);
}
interface Interface1Interface2 {
/// @param b1 blah blah blah
/// @param b3 blah blah blah
/// @param b4 blah blah blah
/// @param b5 blah blah blah
/// @param b6 blah blah blah
/// @param b7 blah blah blah
/// @param b8 blah blah blah
/// @param b9 must be copied to 9th place
/// @param b10 blah blah blah
void foo(int b1, int b2, int b3, int b4, int b5, int b6, int b7, int b8, int b9, int b10);
}
interface Interface1 extends Interface1Interface1, Interface1Interface2 {
/// @param parameter1 must be copied to 1st place
/// @param parameter3 blah blah blah
/// @param parameter4 blah blah blah
/// @param parameter5 blah blah blah
/// @param parameter6 blah blah blah
/// @param parameter7 blah blah blah
/// @param parameter8 blah blah blah
void foo(int parameter1,
int parameter2,
int parameter3,
int parameter4,
int parameter5,
int parameter6,
int parameter7,
int parameter8,
int parameter9,
int parameter10);
}
interface Interface2 {
/// @param param1 blah blah blah
/// @param param2 must be copied to 2nd place
/// @param param3 blah blah blah
/// @param param4 blah blah blah
/// @param param5 blah blah blah
/// @param param6 blah blah blah
/// @param param7 blah blah blah
/// @param param8 blah blah blah
/// @param param9 blah blah blah
/// @param param10 blah blah blah
void foo(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10);
}
class Test extends Clazz implements Interface1, Interface2 {
public void foo<caret>(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10) {
}
}
@@ -0,0 +1,4 @@
// "Add Javadoc" "true-preview"
class Foo {
int x = 2, <caret>y = 1;
}
@@ -0,0 +1,4 @@
// "Add Javadoc" "true-preview"
class Foo {
int <caret>x = 2, y = 1;
}
@@ -0,0 +1,5 @@
// "Add Javadoc" "true-preview"
record Te<caret>st(int u) {
}
@@ -0,0 +1,8 @@
// "Add Javadoc" "false"
class A {
<T<caret>TTT> void mmmm() {
}
}
@@ -0,0 +1,2 @@
/// <caret>
class MarkdownGeneration {}
@@ -0,0 +1,2 @@
/// <caret>
class MarkdownGeneration {}
@@ -0,0 +1,3 @@
/// <caret>
/// @param <T>
interface MarkdownFirstClassParam<T> {}
@@ -0,0 +1,2 @@
/// <caret>
interface MarkdownFirstClassParam<T> {}
@@ -0,0 +1,5 @@
class MarkdownFirstClass {
/// some text <caret>
int methodFull(String param1, String param2);
}
@@ -0,0 +1,5 @@
class MarkdownFirstClass {
/// some text <caret>
int methodFull(String param1, String param2);
}
@@ -0,0 +1,5 @@
class MarkdownFirstClass {
/// <caret>some text
int methodFull(String param1, String param2);
}
@@ -0,0 +1,5 @@
class MarkdownFirstClass {
/// <caret>some text
int methodFull(String param1, String param2);
}
@@ -0,0 +1,5 @@
class MarkdownFirstClass {
/// z <caret>ome text
int methodFull(String param1, String param2);
}
@@ -0,0 +1,5 @@
class MarkdownFirstClass {
/// z<caret>ome text
int methodFull(String param1, String param2);
}
@@ -0,0 +1,7 @@
abstract class MarkdownFirstClassParam<T> {
/// <caret>
/// @param param1
/// @param param2
/// @return
abstract String methodAbstract(String param1, String param2);
}
@@ -0,0 +1,4 @@
abstract class MarkdownFirstClassParam<T> {
/// <caret>
abstract String methodAbstract(String param1, String param2);
}
@@ -0,0 +1,4 @@
interface MarkdownFirstClass {
/// <caret>
void methodEmpty();
}
@@ -0,0 +1,4 @@
interface MarkdownFirstClass {
/// <caret>
void methodEmpty();
}
@@ -0,0 +1,8 @@
interface MarkdownFirstClass {
/// <caret>
/// @param param1
/// @param param2
/// @return
/// @throws RuntimeException
int methodFull(String param1, String param2) throws RuntimeException;
}
@@ -0,0 +1,4 @@
interface MarkdownFirstClass {
/// <caret>
int methodFull(String param1, String param2) throws RuntimeException;
}
@@ -0,0 +1,8 @@
abstract class MarkdownFirstClassParam<T> {
/// <caret>
/// @param param1
/// @param param2
/// @return
/// @throws RuntimeException
abstract String methodAbstract(String param1, String param2) throws RuntimeException;
}
@@ -0,0 +1,4 @@
abstract class MarkdownFirstClassParam<T> {
///<caret>
abstract String methodAbstract(String param1, String param2) throws RuntimeException;
}
@@ -0,0 +1,5 @@
interface MarkdownFirstClass {
/// <caret>
/// @param param
void methodParam(String param);
}
@@ -0,0 +1,4 @@
interface MarkdownFirstClass {
/// <caret>
void methodParam(String param);
}
@@ -0,0 +1,5 @@
interface MarkdownFirstClass {
/// <caret>
/// @return
String methodReturn();
}
@@ -0,0 +1,4 @@
interface MarkdownFirstClass {
/// <caret>
String methodReturn();
}
@@ -0,0 +1,5 @@
interface MarkdownFirstClass {
/// <caret>
/// @throws RuntimeException
void methodReturn() throws RuntimeException;
}
@@ -0,0 +1,4 @@
interface MarkdownFirstClass {
/// <caret>
void methodReturn() throws RuntimeException;
}
@@ -92,6 +92,7 @@
"doc_param_description_on_new_line": false,
"doc_preserve_line_breaks": false,
"doc_use_throws_not_exception_tag": true,
"documentation_line_comment_preferred": false,
"else_on_new_line": false,
"enum_constants_wrap": "off",
"enum_field_annotation_wrap": "off",
@@ -92,6 +92,7 @@
"doc_param_description_on_new_line": false,
"doc_preserve_line_breaks": false,
"doc_use_throws_not_exception_tag": true,
"documentation_line_comment_preferred": false,
"else_on_new_line": false,
"enum_constants_wrap": "off",
"enum_field_annotation_wrap": "off",
@@ -92,6 +92,7 @@
"doc_param_description_on_new_line": false,
"doc_preserve_line_breaks": false,
"doc_use_throws_not_exception_tag": true,
"documentation_line_comment_preferred": false,
"else_on_new_line": false,
"enum_constants_wrap": "off",
"enum_field_annotation_wrap": "off",
@@ -4,4 +4,5 @@
/// ~~~
/// squigly code block
/// ~~~
/// out of code block
/// out of code block
class X {}
@@ -1,30 +1,46 @@
PsiJavaFile:CodeBlockMarkdown01.java
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('```')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' backtick Code block')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_CODE_FENCE('```')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' squigly code block')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_CODE_FENCE('~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' out of code block')
PsiImportList
<empty list>
<empty list>
PsiClass:X
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('```')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' backtick Code block')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_CODE_FENCE('```')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' squigly code block')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_CODE_FENCE('~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' out of code block')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:X('X')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -1,30 +1,46 @@
java.FILE
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
IMPORT_LIST
<empty list>
<empty list>
CLASS
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
MODIFIER_LIST
<empty list>
CLASS_KEYWORD
WHITE_SPACE
IDENTIFIER
TYPE_PARAMETER_LIST
<empty list>
EXTENDS_LIST
<empty list>
IMPLEMENTS_LIST
<empty list>
WHITE_SPACE
LBRACE
RBRACE
@@ -1,4 +1,5 @@
/// ```
/// ~~~
/// Inside squigly code block, but the backtick codeblock has priority
/// ~~~
/// ~~~
class X {}
@@ -1,21 +1,37 @@
PsiJavaFile:CodeBlockMarkdown02.java
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('```')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_COMMENT_DATA('~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' Inside squigly code block')
PsiDocToken:DOC_COMMENT_DATA(',')
PsiDocToken:DOC_COMMENT_DATA(' but the backtick codeblock has priority')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_COMMENT_DATA('~~~')
PsiImportList
<empty list>
<empty list>
PsiClass:X
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('```')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_COMMENT_DATA('~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' Inside squigly code block')
PsiDocToken:DOC_COMMENT_DATA(',')
PsiDocToken:DOC_COMMENT_DATA(' but the backtick codeblock has priority')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_COMMENT_DATA('~~~')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:X('X')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -1,21 +1,37 @@
java.FILE
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_COMMENT_DATA
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_COMMENT_DATA
IMPORT_LIST
<empty list>
<empty list>
CLASS
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_COMMENT_DATA
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_COMMENT_DATA
WHITE_SPACE
MODIFIER_LIST
<empty list>
CLASS_KEYWORD
WHITE_SPACE
IDENTIFIER
TYPE_PARAMETER_LIST
<empty list>
EXTENDS_LIST
<empty list>
IMPLEMENTS_LIST
<empty list>
WHITE_SPACE
LBRACE
RBRACE
@@ -1,3 +1,4 @@
/// ~~~~~~~~
/// Codeblock with more than 3 chars
/// ~~~~
/// ~~~~
class X {}
@@ -1,15 +1,31 @@
PsiJavaFile:CodeBlockMarkdown03.java
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('~~~~~~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' Codeblock with more than 3 chars')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_CODE_FENCE('~~~~')
PsiImportList
<empty list>
<empty list>
PsiClass:X
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_CODE_FENCE('~~~~~~~~')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' Codeblock with more than 3 chars')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_CODE_FENCE('~~~~')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:X('X')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -1,15 +1,31 @@
java.FILE
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_FENCE
IMPORT_LIST
<empty list>
<empty list>
CLASS
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_CODE_FENCE
WHITE_SPACE
MODIFIER_LIST
<empty list>
CLASS_KEYWORD
WHITE_SPACE
IDENTIFIER
TYPE_PARAMETER_LIST
<empty list>
EXTENDS_LIST
<empty list>
IMPLEMENTS_LIST
<empty list>
WHITE_SPACE
LBRACE
RBRACE
@@ -1 +1,2 @@
/// `Single line codeblock`
class X {}
@@ -1,10 +1,26 @@
PsiJavaFile:CodeBlockMarkdown04.java
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiDocToken:DOC_COMMENT_DATA('Single line codeblock')
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiImportList
<empty list>
<empty list>
PsiClass:X
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiDocToken:DOC_COMMENT_DATA('Single line codeblock')
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:X('X')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -1,10 +1,26 @@
java.FILE
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_INLINE_CODE_FENCE
DOC_COMMENT_DATA
DOC_INLINE_CODE_FENCE
IMPORT_LIST
<empty list>
<empty list>
CLASS
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_INLINE_CODE_FENCE
DOC_COMMENT_DATA
DOC_INLINE_CODE_FENCE
WHITE_SPACE
MODIFIER_LIST
<empty list>
CLASS_KEYWORD
WHITE_SPACE
IDENTIFIER
TYPE_PARAMETER_LIST
<empty list>
EXTENDS_LIST
<empty list>
IMPLEMENTS_LIST
<empty list>
WHITE_SPACE
LBRACE
RBRACE
@@ -1,3 +1,4 @@
/// `
/// According to markdown rules, this is inline
/// `
class X {}
@@ -1,15 +1,31 @@
PsiJavaFile:CodeBlockMarkdown05.java
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' According to markdown rules, this is inline')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiImportList
<empty list>
<empty list>
PsiClass:X
PsiDocComment
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiWhiteSpace(' ')
PsiMarkdownCodeBlock:
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' According to markdown rules, this is inline')
PsiWhiteSpace('\n')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiDocToken:DOC_INLINE_CODE_FENCE('`')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:X('X')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -1,15 +1,31 @@
java.FILE
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_INLINE_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_INLINE_CODE_FENCE
IMPORT_LIST
<empty list>
<empty list>
CLASS
DOC_MARKDOWN_COMMENT
DOC_COMMENT_LEADING_ASTERISKS
WHITE_SPACE
DOC_CODE_BLOCK
DOC_INLINE_CODE_FENCE
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
WHITE_SPACE
DOC_COMMENT_LEADING_ASTERISKS
DOC_COMMENT_DATA
DOC_INLINE_CODE_FENCE
WHITE_SPACE
MODIFIER_LIST
<empty list>
CLASS_KEYWORD
WHITE_SPACE
IDENTIFIER
TYPE_PARAMETER_LIST
<empty list>
EXTENDS_LIST
<empty list>
IMPLEMENTS_LIST
<empty list>
WHITE_SPACE
LBRACE
RBRACE
@@ -0,0 +1,64 @@
// Copyright 2000-2020 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;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.completion.LightFixtureCompletionTestCase;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryTestUtil;
import com.intellij.testFramework.HeavyPlatformTestCase;
import kotlin.Unit;
/// Variant of [GenerateJavadocTest] to handle Markdown comment generation
/// Since the workflow for Markdown comments is different ([com.intellij.codeInsight.completion.CompletionContributor]-based),
/// it is a separate class
@HeavyPlatformTestCase.WrapInCommand
public class GenerateJavadocMarkdownTest extends LightFixtureCompletionTestCase {
@Override
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/generateJavadocMarkdown/";
}
public void testClass() { doTestWithTemplates(); }
public void testClassParam() { doTestWithTemplates(); }
public void testMethodEmpty() { doTestWithTemplates(); }
public void testMethodParam() { doTestWithTemplates(); }
public void testMethodReturn() { doTestWithTemplates(); }
public void testMethodThrows() { doTestWithTemplates(); }
public void testMethodFull() { doTestWithTemplates(); }
public void testMethodAbstract() { doTestWithTemplates(); }
public void testGenerationDisabled01(){ doTestWithTemplates(); }
public void testGenerationDisabled02(){ doTestWithTemplates(); }
public void testGenerationDisabled03(){ doTestWithTemplates(); }
/// Sanity check: verify template-less code path doesn't explode
public void testMethodFullNoTemplate() { doTestNoTemplates(); }
private void doTestNoTemplates() {
RegistryTestUtil.withValue(Registry.get("java.javadoc.use.templates"), Boolean.FALSE, ()-> {
doTest();
return Unit.INSTANCE;
});
}
private void doTestWithTemplates() {
RegistryTestUtil.withValue(Registry.get("java.javadoc.use.templates"), Boolean.TRUE, ()-> {
doTest();
return Unit.INSTANCE;
});
}
private void doTest() {
String name = getTestName(true);
configureByFile(name + ".before.java");
performAction();
checkResultByFile(name + ".after.java");
}
private void performAction() {
myFixture.completeBasic();
if (myItems.length <= 1)
myFixture.type("\t");
}
}
@@ -0,0 +1,528 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.editorActions;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.editorActions.FixDocCommentAction;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.impl.AbstractEditorTest;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
public class FixDocMarkdownCommentTest extends AbstractEditorTest {
@Override
protected void setUp() throws Exception {
super.setUp();
CodeStyleSettings settings = CodeStyle.getSettings(getProject());
settings.getCommonSettings(JavaLanguage.INSTANCE).DOCUMENTATION_LINE_COMMENT_PREFERRED = true;
CodeStyle.setTemporarySettings(getProject(), settings);
}
public void testGenerateMethodDoc() {
String initial = """
class Test {
String test(int i) {
return "s";<caret>
}
}""";
String expected = """
class Test {
/// @param i <caret>
/// @return
String test(int i) {
return "s";
}
}""";
doTest(initial, expected);
}
public void testGenerateFieldDoc() {
String initial = """
class Test {
int <caret>i;
}""";
String expected = """
class Test {
/// <caret>
int i;
}""";
doTest(initial, expected);
}
public void testGenerateClassDoc() {
String initial = """
class Test {
void test1() {}
<caret>
void test2() {}
}""";
String expected = """
/// <caret>
class Test {
void test1() {}
void test2() {}
}""";
doTest(initial, expected);
}
public void testRemoveOneParameterFromMany() {
String initial = """
class Test {
/// @param i
/// @param j
/// @param k
void test(int i, int j) {<caret>
}
}""";
String expected = """
class Test {
/// @param i <caret>
/// @param j
void test(int i, int j) {
}
}""";
doTest(initial, expected);
}
public void testRemoveTheOnlyParameter() {
String initial = """
class Test {
/// My description
/// @param i
void test() {<caret>
}
}""";
String expected = """
class Test {
/// My description
void test() {<caret>
}
}""";
doTest(initial, expected);
}
public void testRemoveReturn() {
String initial = """
class Test {
/// My description
/// @return data
void test() {<caret>
}
}""";
String expected = """
class Test {
/// My description
void test() {<caret>
}
}""";
doTest(initial, expected);
}
public void testRemoveOneThrowsFromMany() {
String initial = """
class MyException1 extends Exception {}
class MyException2 extends Exception {}
class Test {
/// @param i my arg
/// @throws MyException1 text1
/// @throws MyException2 text2
void test(int i) throws MyException2 {<caret>
}
}""";
String expected = """
class MyException1 extends Exception {}
class MyException2 extends Exception {}
class Test {
/// @param i my arg
/// @throws MyException2 text2
void test(int i) throws MyException2 {
}
}""";
doTest(initial, expected);
}
public void testRemoveTheOnlyThrows() {
String initial = """
class MyException extends Exception {}
class Test {
/// @param i my arg
/// @throws MyException text
void test(int i) {<caret>
}
}""";
String expected = """
class MyException extends Exception {}
class Test {
/// @param i my arg
void test(int i) {
}
}""";
doTest(initial, expected);
}
public void testRemoveOneTypeParameterFromMany() {
String initial = """
/// @param <T> tDescription
/// @param <V> vDescription
class Test<V> {<caret>
}""";
String expected = """
/// @param <V> vDescription
class Test<V> {<caret>
}""";
doTest(initial, expected);
}
public void testRemoveMultipleTypeParameter() {
String initial = """
/// @param <T> tDescription
/// @param <V> vDescription
class Test {<caret>
}""";
String expected = """
///
class Test {<caret>
}""";
doTest(initial, expected);
}
public void testAddFirstParameter() {
String initial = """
class Test {
void test(int i) {<caret>
}
}""";
String expected = """
class Test {
/// @param i <caret>
void test(int i) {
}
}""";
doTest(initial, expected);
}
public void testAddMultipleParameter() {
String initial = """
class Test {
/// @param i
void test(int i, int j, int k) {<caret>
}
}""";
String expected = """
class Test {
/// @param i <caret>
/// @param j
/// @param k
void test(int i, int j, int k) {
}
}""";
doTest(initial, expected);
}
public void testAddReturn() {
String initial = """
class Test {
int test() {<caret>
}
}""";
String expected = """
class Test {
/// @return <caret>
int test() {
}
}""";
doTest(initial, expected);
}
public void testAddFirstThrows() {
String initial = """
class MyException extends Exception {}
class Test {
/// @param i my arg
void test(int i) throws MyException {<caret>
}
}""";
String expected = """
class MyException extends Exception {}
class Test {
/// @param i my arg
/// @throws MyException<caret>
void test(int i) throws MyException {
}
}""";
doTest(initial, expected);
}
public void testAddNonFirstThrows() {
String initial = """
class MyException1 extends Exception {}
class MyException2 extends Exception {}
class MyException3 extends Exception {}
class Test {
/// @param i my arg
/// @throws MyException1
void test(int i) throws MyException1, MyException2, MyException3 {<caret>
}
}""";
String expected = """
class MyException1 extends Exception {}
class MyException2 extends Exception {}
class MyException3 extends Exception {}
class Test {
/// @param i my arg
/// @throws MyException1<caret>
/// @throws MyException2
/// @throws MyException3
void test(int i) throws MyException1, MyException2, MyException3 {
}
}""";
doTest(initial, expected);
}
public void testAddFirstThrowsWhenEmptyReturnIsAvailable() {
String initial = """
class MyException extends Exception {}
class Test {
/// @return
int test() throws MyException {<caret>
return 1;
}
}""";
String expected = """
class MyException extends Exception {}
class Test {
/// @return<caret>
/// @throws MyException
int test() throws MyException {
return 1;
}
}""";
doTest(initial, expected);
}
public void testAddFirstTypeParameter() {
String initial = """
/// My description
/// @author me
class Test<T> {<caret>
}""";
String expected = """
/// My description
/// @author me
/// @param <T> <caret>
class Test<T> {
}""";
doTest(initial, expected);
}
public void testAddNonFirstTypeParameter() {
String initial = """
/// My description
/// @author me
/// @param <T> type description<caret>
class Test<T, V> {
}""";
String expected = """
/// My description
/// @author me
/// @param <T> type description
/// @param <V> <caret>
class Test<T, V> {
}""";
doTest(initial, expected);
}
public void testCorrectParametersOrder() {
String initial = """
class Test {
/// @param j
/// @param k single line description
/// @param i multi-line
/// description
public void test(int i, int j, int k) {<caret>
}
}""";
String expected = """
class Test {
/// @param i multi-line
/// description
/// @param j <caret>
/// @param k single line description
public void test(int i, int j, int k) {
}
}""";
doTest(initial, expected);
}
public void testCorrectParametersDescriptionWhenIndentIsDefines() {
String initial = """
class Test {
/// @param j \s
/// @param i
public void test(int i, int j) {<caret>
}
}""";
String expected = """
class Test {
/// @param i <caret>
/// @param j \s
public void test(int i, int j) {
}
}""";
doTest(initial, expected);
}
public void testCorrectMethodTypeParametersOrder() {
String initial = """
class Test {
/// @param <B>
/// @param <A> A description
<A, B> void test() {<caret>
}
}""";
String expected = """
class Test {
/// @param <A> A description
/// @param <B> <caret>
<A, B> void test() {
}
}""";
doTest(initial, expected);
}
public void testCorrectClassTypeParametersOrder() {
String initial = """
/// Class description
/// @author Zigmund
/// @param <B> multi-line
/// description
/// @param <A>
class Test<A, B> {<caret>
}""";
String expected = """
/// Class description
/// @author Zigmund
/// @param <A> <caret>
/// @param <B> multi-line
/// description
class Test<A, B> {
}""";
doTest(initial, expected);
}
public void testAllesZusammen() {
String initial = """
class MyException1 extends Exception {}
class MyException2 extends Exception {}
class Test {
/// Method description
/// @param j j description (single line)
/// @param s s description
/// @param k
/// k description (single line but located at another line)
/// @throws MyException2
/// @return some value
void test(int i, int j, int k) throws MyException1 {<caret>
}
}""";
String expected = """
class MyException1 extends Exception {}
class MyException2 extends Exception {}
class Test {
/// Method description
/// @param i <caret>
/// @param j j description (single line)
/// @param k
/// k description (single line but located at another line)
/// @throws MyException1
void test(int i, int j, int k) throws MyException1 {
}
}""";
doTest(initial, expected);
}
public void testNavigateToMissingParamDescription() {
String initial = """
class Test {
/// @param i
void test(int i) {<caret>
}
}""";
String expected = """
class Test {
/// @param i <caret>
void test(int i) {
}
}""";
doTest(initial, expected);
}
public void test_many_newlines_before_interface() {
String initial = """
interface <caret>I {}""";
String expected = """
///\s
interface I {}""";
doTest(initial, expected);
}
public void testWithEmptyTagsRemovalOption() {
JavaCodeStyleSettings settings = getCustomSettings(JavaCodeStyleSettings.class);
settings.JD_KEEP_EMPTY_PARAMETER = false;
settings.JD_KEEP_EMPTY_RETURN = false;
settings.JD_KEEP_EMPTY_EXCEPTION = false;
String initial = """
package com.company;
public class Test
{
int foo<caret>(String s, int i, double d) throws Exception
{
return 0;
}
}
""";
String expected = """
package com.company;
public class Test
{
/// @param s <caret>
/// @param i
/// @param d
/// @return
/// @throws Exception
int foo(String s, int i, double d) throws Exception
{
return 0;
}
}
""";
doTest(initial, expected);
}
private void doTest(String initial, String expected) {
configureFromFileText(getTestName(false) + ".java", initial);
getEditor().getSettings().setVirtualSpace(false);
executeAction(FixDocCommentAction.ACTION_ID);
checkResultByText(expected);
}
}
@@ -0,0 +1,24 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.intention;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.codeStyle.CodeStyleSettings;
public class AddJavadocMarkdownIntentionTest extends LightIntentionActionTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
CodeStyleSettings settings = CodeStyle.getSettings(getProject());
settings.getCommonSettings(JavaLanguage.INSTANCE).DOCUMENTATION_LINE_COMMENT_PREFERRED = true;
CodeStyle.setTemporarySettings(getProject(), settings);
}
@Override
protected String getBasePath() {
return "/codeInsight/addJavadocMarkdown";
}
}
@@ -1,6 +1,9 @@
// 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.application.options.CodeStyle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
@@ -31,4 +34,31 @@ public class AddJavadocToModuleAndPackageTest extends LightJavaCodeInsightFixtur
*/
module org.some.awesome{}""");
}
public void testPackageInfoMarkdown() {
myFixture.configureByText("package-info.java", "package org.some.awe<caret>some;");
myFixture.launchAction(myFixture.findSingleIntention("Add Javadoc"));
myFixture.checkResult("""
/// <caret>
package org.some.awesome;""");
}
public void testModuleInfoMarkdown() {
myFixture.configureByText("module-info.java", "module org.some.awe<caret>some{}");
myFixture.launchAction(myFixture.findSingleIntention("Add Javadoc"));
myFixture.checkResult("""
/// <caret>
module org.some.awesome{}""");
}
@Override
protected void setUp() throws Exception {
super.setUp();
if (getQualifiedTestMethodName().endsWith("Markdown")) {
CodeStyleSettings settings = CodeStyle.getSettings(myFixture.getProject());
settings.getCommonSettings(JavaLanguage.INSTANCE).DOCUMENTATION_LINE_COMMENT_PREFERRED = true;
CodeStyle.setTemporarySettings(getProject(), settings);
}
}
}
@@ -9,7 +9,7 @@ import org.jetbrains.annotations.ApiStatus
/**
* Represents an entity that can provide or indicate the availability of a custom preview for a lookup element.
*
* @see LookupElement
*/
@ApiStatus.Experimental
interface LookupElementCustomPreviewHolder {
+1
View File
@@ -685,6 +685,7 @@ c:com.intellij.psi.codeStyle.CommonCodeStyleSettings
- CATCH_ON_NEW_LINE:Z
- CLASS_ANNOTATION_WRAP:I
- CLASS_BRACE_STYLE:I
- DOCUMENTATION_LINE_COMMENT_PREFERRED:Z
- DOWHILE_BRACE_FORCE:I
- sf:DO_NOT_FORCE:I
- DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS:Z
@@ -261,6 +261,9 @@ public class CommonCodeStyleSettings implements CommentStyleSettings {
public boolean LINE_COMMENT_ADD_SPACE_ON_REFORMAT = false;
public boolean LINE_COMMENT_ADD_SPACE_IN_SUPPRESSION = false;
/// Whether to prefer documentation line comments (In languages like Java, Rust, Dart)
public boolean DOCUMENTATION_LINE_COMMENT_PREFERRED = false;
public boolean KEEP_LINE_BREAKS = true;
/**

Some files were not shown because too many files have changed in this diff Show More