From 7f42c13987f98247d84a48bc806dbb4a73251bfd Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 7 May 2025 13:45:58 +0200 Subject: [PATCH] [javadoc] Remove redundant abstract classes; make methods static, minor cleaup GitOrigin-RevId: 3c5af00a75d01c9eba13bb148a33c6d0246e03c9 --- .../AbstractBasicJavaSmartEnterProcessor.java | 14 +- .../smartEnter/AbstractBasicJavadocFixer.java | 112 --------- .../smartEnter/JavadocFixer.java | 98 +++++++- .../smartEnter/MissingLambdaBodyFixer.java | 2 +- .../javadoc/AbstractBasicJavadocHelper.java | 230 ------------------ .../com/intellij/javadoc/JavadocHelper.java | 228 ++++++++++++++++- ...EnterInJavadocParamDescriptionHandler.java | 7 +- .../javadoc/JavadocNavigationDelegate.java | 9 +- 8 files changed, 326 insertions(+), 374 deletions(-) delete mode 100644 java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavadocFixer.java delete mode 100644 java/java-frontback-impl/src/com/intellij/javadoc/AbstractBasicJavadocHelper.java diff --git a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavaSmartEnterProcessor.java b/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavaSmartEnterProcessor.java index c2c1293da416..146eb5f5a5d4 100644 --- a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavaSmartEnterProcessor.java +++ b/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavaSmartEnterProcessor.java @@ -69,12 +69,12 @@ public abstract class AbstractBasicJavaSmartEnterProcessor extends SmartEnterPro private static class TooManyAttemptsException extends Exception { } - private final AbstractBasicJavadocFixer myJavadocFixer; + private final JavadocFixer myJavadocFixer; protected AbstractBasicJavaSmartEnterProcessor(@NotNull List fixers, EnterProcessor @NotNull [] enterProcessors, EnterProcessor @NotNull [] afterCompletionEnterProcessors, - @NotNull AbstractBasicJavadocFixer thinJavadocFixer, + @NotNull JavadocFixer thinJavadocFixer, @NotNull EnterProcessor breakerEnterProcessor) { myBreakerEnterProcessor = breakerEnterProcessor; ourFixers = fixers; @@ -106,7 +106,7 @@ public abstract class AbstractBasicJavaSmartEnterProcessor extends SmartEnterPro document.replaceString(0, document.getTextLength(), textForRollback); } catch (Exception e) { - e.printStackTrace(); + LOG.error(e); } finally { editor.putUserData(SMART_ENTER_TIMESTAMP, null); @@ -206,12 +206,12 @@ public abstract class AbstractBasicJavaSmartEnterProcessor extends SmartEnterPro if (myFirstErrorOffset != Integer.MAX_VALUE) { editor.getCaretModel().moveToOffset(myFirstErrorOffset); - reformat(editor, atCaret); + reformat(atCaret); return; } final RangeMarker rangeMarker = createRangeMarker(atCaret); - reformat(editor, atCaret); + reformat(atCaret); commit(editor); if (!mySkipEnter) { @@ -357,10 +357,6 @@ public abstract class AbstractBasicJavaSmartEnterProcessor extends SmartEnterPro editor.getCaretModel().moveToOffset(caretOffset - 1); } - protected void reformat(@NotNull Editor editor, @Nullable PsiElement elt) { - reformat(elt); - } - private void reformatBlockParentIfNeeded(@NotNull Editor editor, @NotNull PsiFile file) { commit(editor); ASTNode block = diff --git a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavadocFixer.java b/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavadocFixer.java deleted file mode 100644 index 0583073fdc7a..000000000000 --- a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/AbstractBasicJavadocFixer.java +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.codeInsight.editorActions.smartEnter; - -import com.intellij.javadoc.AbstractBasicJavadocHelper; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.PsiFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.List; - -/** - * Serves as a facade for javadoc smart completion. - *

- * Thread-safe. - */ -public abstract class AbstractBasicJavadocFixer { - - private final AbstractBasicJavadocHelper myHelper; - - public AbstractBasicJavadocFixer(@NotNull AbstractBasicJavadocHelper helper) { myHelper = helper; } - - /** - * Checks if caret of the given editor is located inside javadoc and tries to perform smart completion there in case of the positive - * answer. - * - * @param editor target editor - * @param psiFile PSI file for the document exposed via the given editor - * @return {@code true} if smart completion was performed; {@code false} otherwise - */ - public boolean process(@NotNull Editor editor, @NotNull PsiFile psiFile) { - // Check parameter description completion. - final CaretModel caretModel = editor.getCaretModel(); - final Pair> pair = - myHelper.parse(psiFile, editor, caretModel.getOffset()); - - if (pair.first == null) { - return false; - } - - final AbstractBasicJavadocHelper.JavadocParameterInfo next = findNext(pair.second, pair.first); - if (next == null) { - final int line = pair.first.lastLine + 1; - final Document document = editor.getDocument(); - if (line < document.getLineCount()) { - StringBuilder indent = new StringBuilder(); - boolean insertIndent = true; - final CharSequence text = document.getCharsSequence(); - for (int i = document.getLineStartOffset(line), max = document.getLineEndOffset(line); i < max; i++) { - final char c = text.charAt(i); - if (c == ' ' || c == '\t') { - indent.append(c); - continue; - } - else if (c == '*') { - indent.append("* "); - if (i < max - 1 && text.charAt(i + 1) != '/') { - insertIndent = false; - } - } - indent.append("\n"); - break; - } - if (insertIndent) { - document.insertString(document.getLineStartOffset(line), indent); - } - } - moveCaretToTheLineEndIfPossible(editor, line); - return true; - } - - if (next.parameterDescriptionStartPosition != null) { - myHelper.navigate(next.parameterDescriptionStartPosition, editor, psiFile.getProject()); - } - else { - final LogicalPosition position = myHelper.calculateDescriptionStartPosition(psiFile, pair.second, next); - myHelper.navigate(position, editor, psiFile.getProject()); - } - return true; - } - - private static void moveCaretToTheLineEndIfPossible(@NotNull Editor editor, int line) { - final Document document = editor.getDocument(); - final CaretModel caretModel = editor.getCaretModel(); - int offset; - if (line >= document.getLineCount()) { - offset = document.getTextLength(); - } - else { - offset = document.getLineEndOffset(line); - } - caretModel.moveToOffset(offset); - } - - private static @Nullable AbstractBasicJavadocHelper.JavadocParameterInfo findNext(@NotNull Collection data, - @NotNull AbstractBasicJavadocHelper.JavadocParameterInfo anchor) - { - boolean returnNow = false; - for (AbstractBasicJavadocHelper.JavadocParameterInfo info : data) { - if (returnNow) { - return info; - } - returnNow = info == anchor; - } - return null; - } -} diff --git a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavadocFixer.java b/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavadocFixer.java index 9b1da9fea99c..9616c88ec16a 100644 --- a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavadocFixer.java +++ b/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavadocFixer.java @@ -1,15 +1,107 @@ package com.intellij.codeInsight.editorActions.smartEnter; import com.intellij.javadoc.JavadocHelper; +import com.intellij.openapi.editor.CaretModel; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; /** * Serves as a facade for javadoc smart completion. *

* Thread-safe. */ -public class JavadocFixer extends AbstractBasicJavadocFixer { +public class JavadocFixer { - public JavadocFixer() { - super( new JavadocHelper()); + /** + * Checks if caret of the given editor is located inside javadoc and tries to perform smart completion there in case of the positive + * answer. + * + * @param editor target editor + * @param psiFile PSI file for the document exposed via the given editor + * @return {@code true} if smart completion was performed; {@code false} otherwise + */ + public boolean process(@NotNull Editor editor, @NotNull PsiFile psiFile) { + // Check parameter description completion. + final CaretModel caretModel = editor.getCaretModel(); + final Pair> pair = + JavadocHelper.parse(psiFile, editor, caretModel.getOffset()); + + if (pair.first == null) { + return false; + } + + final JavadocHelper.JavadocParameterInfo next = findNext(pair.second, pair.first); + if (next == null) { + final int line = pair.first.lastLine + 1; + final Document document = editor.getDocument(); + if (line < document.getLineCount()) { + StringBuilder indent = new StringBuilder(); + boolean insertIndent = true; + final CharSequence text = document.getCharsSequence(); + for (int i = document.getLineStartOffset(line), max = document.getLineEndOffset(line); i < max; i++) { + final char c = text.charAt(i); + if (c == ' ' || c == '\t') { + indent.append(c); + continue; + } + else if (c == '*') { + indent.append("* "); + if (i < max - 1 && text.charAt(i + 1) != '/') { + insertIndent = false; + } + } + indent.append("\n"); + break; + } + if (insertIndent) { + document.insertString(document.getLineStartOffset(line), indent); + } + } + moveCaretToTheLineEndIfPossible(editor, line); + return true; + } + + if (next.parameterDescriptionStartPosition != null) { + JavadocHelper.navigate(next.parameterDescriptionStartPosition, editor, psiFile.getProject()); + } + else { + final LogicalPosition position = JavadocHelper.calculateDescriptionStartPosition(psiFile, pair.second, next); + JavadocHelper.navigate(position, editor, psiFile.getProject()); + } + return true; + } + + private static void moveCaretToTheLineEndIfPossible(@NotNull Editor editor, int line) { + final Document document = editor.getDocument(); + final CaretModel caretModel = editor.getCaretModel(); + int offset; + if (line >= document.getLineCount()) { + offset = document.getTextLength(); + } + else { + offset = document.getLineEndOffset(line); + } + caretModel.moveToOffset(offset); + } + + private static @Nullable JavadocHelper.JavadocParameterInfo findNext(@NotNull Collection data, + @NotNull JavadocHelper.JavadocParameterInfo anchor) + { + boolean returnNow = false; + for (JavadocHelper.JavadocParameterInfo info : data) { + if (returnNow) { + return info; + } + returnNow = info == anchor; + } + return null; } } diff --git a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MissingLambdaBodyFixer.java b/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MissingLambdaBodyFixer.java index 3ca5fda74c80..dca421be45ab 100644 --- a/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MissingLambdaBodyFixer.java +++ b/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MissingLambdaBodyFixer.java @@ -56,7 +56,7 @@ public class MissingLambdaBodyFixer implements Fixer { processor.insertBracesWithNewLine(editor, offset); editor.getCaretModel().moveToOffset(offset + 1); processor.commit(editor); - processor.reformat(editor, psiElement); + processor.reformat(psiElement); processor.setSkipEnter(BasicJavaAstTreeUtil.is(astNode, BASIC_LAMBDA_EXPRESSION)); } } \ No newline at end of file diff --git a/java/java-frontback-impl/src/com/intellij/javadoc/AbstractBasicJavadocHelper.java b/java/java-frontback-impl/src/com/intellij/javadoc/AbstractBasicJavadocHelper.java deleted file mode 100644 index 5d7fd9e1cf05..000000000000 --- a/java/java-frontback-impl/src/com/intellij/javadoc/AbstractBasicJavadocHelper.java +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.javadoc; - -import com.intellij.lang.ASTNode; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.impl.source.BasicJavaAstTreeUtil; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.ParentAwareTokenSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import static com.intellij.psi.impl.source.BasicJavaDocElementType.*; - -public abstract class AbstractBasicJavadocHelper { - private static final String PARAM_TEXT = "param"; - - public static final Pair> EMPTY - = new Pair<>(null, Collections.emptyList()); - private static final @NotNull ParentAwareTokenSet TAG_TOKEN_SET = ParentAwareTokenSet.create(BASIC_DOC_TAG, - BASIC_DOC_SNIPPET_TAG, - BASIC_DOC_INLINE_TAG); - - protected AbstractBasicJavadocHelper() { - } - - /** - * Tries to navigate caret at the given editor to the target position inserting missing white spaces if necessary. - * - * @param position target caret position - * @param editor target editor - * @param project target project - */ - public void navigate(@NotNull LogicalPosition position, @NotNull Editor editor, final @NotNull Project project) { - final Document document = editor.getDocument(); - final CaretModel caretModel = editor.getCaretModel(); - final int endLineOffset = document.getLineEndOffset(position.line); - final LogicalPosition endLinePosition = editor.offsetToLogicalPosition(endLineOffset); - if (endLinePosition.column < position.column && !editor.getSettings().isVirtualSpace() && !editor.isViewer()) { - final String toInsert = StringUtil.repeat(" ", position.column - endLinePosition.column); - ApplicationManager.getApplication().runWriteAction(() -> { - document.insertString(endLineOffset, toInsert); - PsiDocumentManager.getInstance(project).commitDocument(document); - }); - } - caretModel.moveToLogicalPosition(position); - } - - /** - * Calculates desired position of target javadoc parameter's description start. - * - * @param psiFile PSI holder - * @param data parsed adjacent javadoc parameters - * @param anchor descriptor for the target parameter - * @return logical position that points to the desired parameter description start location - */ - public @NotNull LogicalPosition calculateDescriptionStartPosition(@NotNull PsiFile psiFile, - @NotNull Collection data, - @NotNull JavadocParameterInfo anchor) { - int descriptionStartColumn = -1; - int parameterNameEndColumn = -1; - for (JavadocParameterInfo parameterInfo : data) { - parameterNameEndColumn = Math.max(parameterNameEndColumn, parameterInfo.parameterNameEndPosition.column); - if (parameterInfo.parameterDescriptionStartPosition != null) { - descriptionStartColumn = Math.max(descriptionStartColumn, parameterInfo.parameterDescriptionStartPosition.column); - } - } - - int column; - - if (getJdAlignParamComments(psiFile)) { - column = Math.max(descriptionStartColumn, parameterNameEndColumn); - if (column <= parameterNameEndColumn) { - column = parameterNameEndColumn + 1; - } - } - else { - column = anchor.parameterNameEndPosition.column + 1; - } - return new LogicalPosition(anchor.parameterNameEndPosition.line, column); - } - - protected abstract boolean getJdAlignParamComments(@NotNull PsiFile psiFile); - - /** - * Returns information about all lines that contain javadoc parameters and are adjacent to the one that holds given offset. - * - * @param psiFile PSI holder for the document exposed the given editor - * @param editor target editor - * @param offset target offset that identifies anchor line to check - * @return pair like (javadoc info for the line identified by the given offset; list of javadoc parameter infos for - * adjacent lines if any - */ - public @NotNull Pair> parse(@NotNull PsiFile psiFile, - @NotNull Editor editor, - int offset) { - List result = new ArrayList<>(); - PsiDocumentManager.getInstance(psiFile.getProject()).commitDocument(editor.getDocument()); - final PsiElement elementAtCaret = psiFile.findElementAt(offset); - if (elementAtCaret == null) { - return EMPTY; - } - - ASTNode nodeAtCaret = BasicJavaAstTreeUtil.toNode(elementAtCaret); - ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(nodeAtCaret, TAG_TOKEN_SET); - if (tag == null) { - // Due to javadoc PSI specifics. - if (BasicJavaAstTreeUtil.isWhiteSpace(nodeAtCaret)) { - for (ASTNode e = nodeAtCaret.getTreePrev(); e != null && tag == null; e = e.getTreePrev()) { - tag = BasicJavaAstTreeUtil.getParentOfType(e, TAG_TOKEN_SET, false); - if (e instanceof PsiWhiteSpace - || (e.getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) { - continue; - } - break; - } - } - } - if (tag == null) { - return EMPTY; - } - - JavadocParameterInfo anchorInfo = parse(tag, editor); - if (anchorInfo == null) { - return EMPTY; - } - - // Parse previous parameters. - for (ASTNode n = tag.getTreePrev(); n != null; n = n.getTreePrev()) { - JavadocParameterInfo info = parse(n, editor); - if (info == null) { - break; - } - result.add(0, info); - } - - result.add(anchorInfo); - - // Parse subsequent parameters. - for (ASTNode n = tag.getTreeNext(); n != null; n = n.getTreeNext()) { - JavadocParameterInfo info = parse(n, editor); - if (info == null) { - break; - } - result.add(info); - } - - return Pair.create(anchorInfo, result); - } - - private static @Nullable JavadocParameterInfo parse(@NotNull ASTNode astNode, @NotNull Editor editor) { - final ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(astNode, TAG_TOKEN_SET, false); - if (tag == null || !PARAM_TEXT.equals(BasicJavaAstTreeUtil.getTagName(tag))) { - return null; - } - - final ASTNode paramRef = BasicJavaAstTreeUtil.findChildByType(tag, BASIC_DOC_TAG_VALUE_ELEMENT, - BASIC_DOC_METHOD_OR_FIELD_REF, - BASIC_DOC_PARAMETER_REF, - BASIC_DOC_SNIPPET_TAG_VALUE); - if (paramRef == null) { - return null; - } - - for (ASTNode node = paramRef.getTreeNext(); node != null; node = node.getTreeNext()) { - final IElementType elementType = node.getElementType(); - if (elementType == JavaDocTokenType.DOC_COMMENT_DATA) { - return new JavadocParameterInfo( - editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()), - editor.offsetToLogicalPosition(node.getTextRange().getStartOffset()), - editor.getDocument().getLineNumber(node.getTextRange().getEndOffset()) - ); - } - else if (elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) { - break; - } - } - return new JavadocParameterInfo( - editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()), - null, - editor.getDocument().getLineNumber(paramRef.getTextRange().getEndOffset()) - ); - } - - /** - * Encapsulates information about source code line that holds javadoc parameter. - */ - public static class JavadocParameterInfo { - - /** - * Logical position that points to location just after javadoc parameter name. - *

- * Example: - *

-     *   /**
-     *    * @param i[X]  description
-     *    */
-     * 
- */ - public final @NotNull LogicalPosition parameterNameEndPosition; - public final @Nullable LogicalPosition parameterDescriptionStartPosition; - /** Last logical line occupied by the current javadoc parameter. */ - public final int lastLine; - - public JavadocParameterInfo(@NotNull LogicalPosition parameterNameEndPosition, - @Nullable LogicalPosition parameterDescriptionStartPosition, - int lastLine) { - this.parameterNameEndPosition = parameterNameEndPosition; - this.parameterDescriptionStartPosition = parameterDescriptionStartPosition; - this.lastLine = lastLine; - } - - @Override - public String toString() { - return "name end: " + parameterNameEndPosition + ", description start: " + parameterDescriptionStartPosition; - } - } -} diff --git a/java/java-frontback-impl/src/com/intellij/javadoc/JavadocHelper.java b/java/java-frontback-impl/src/com/intellij/javadoc/JavadocHelper.java index 6a7ea54ef3d3..7c0fd09c84d2 100644 --- a/java/java-frontback-impl/src/com/intellij/javadoc/JavadocHelper.java +++ b/java/java-frontback-impl/src/com/intellij/javadoc/JavadocHelper.java @@ -16,24 +16,236 @@ package com.intellij.javadoc; import com.intellij.application.options.CodeStyle; -import com.intellij.psi.PsiFile; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.CaretModel; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; +import com.intellij.psi.impl.source.BasicJavaAstTreeUtil; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.ParentAwareTokenSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.intellij.psi.impl.source.BasicJavaDocElementType.*; /** - * This class is not singleton but provides {@link #getInstance() single-point-of-usage field}. + * Utility methods to support some Javadoc-related operations like formatting or navigation */ -public final class JavadocHelper extends AbstractBasicJavadocHelper { - private static final JavadocHelper INSTANCE = new JavadocHelper(); +public final class JavadocHelper { + private static final Pair> EMPTY + = new Pair<>(null, Collections.emptyList()); + private static final String PARAM_TEXT = "param"; + private static final @NotNull ParentAwareTokenSet TAG_TOKEN_SET = + ParentAwareTokenSet.create(BASIC_DOC_TAG, BASIC_DOC_SNIPPET_TAG, BASIC_DOC_INLINE_TAG); - @Override - protected boolean getJdAlignParamComments(@NotNull PsiFile psiFile) { + private JavadocHelper() { + } + + private static boolean getJdAlignParamComments(@NotNull PsiFile psiFile) { final CodeStyleSettings codeStyleSettings = CodeStyle.getSettings(psiFile); return (codeStyleSettings.getCustomSettings(JavaCodeStyleSettings.class).JD_ALIGN_PARAM_COMMENTS); } - public static @NotNull JavadocHelper getInstance() { - return INSTANCE; + /** + * Tries to navigate caret at the given editor to the target position, inserting missing white spaces if necessary. + * + * @param position target caret position + * @param editor target editor + * @param project target project + */ + public static void navigate(@NotNull LogicalPosition position, @NotNull Editor editor, final @NotNull Project project) { + final Document document = editor.getDocument(); + final CaretModel caretModel = editor.getCaretModel(); + final int endLineOffset = document.getLineEndOffset(position.line); + final LogicalPosition endLinePosition = editor.offsetToLogicalPosition(endLineOffset); + if (endLinePosition.column < position.column && !editor.getSettings().isVirtualSpace() && !editor.isViewer()) { + final String toInsert = StringUtil.repeat(" ", position.column - endLinePosition.column); + ApplicationManager.getApplication().runWriteAction(() -> { + document.insertString(endLineOffset, toInsert); + PsiDocumentManager.getInstance(project).commitDocument(document); + }); + } + caretModel.moveToLogicalPosition(position); + } + + /** + * Calculates desired position of target javadoc parameter's description start. + * + * @param psiFile PSI holder + * @param data parsed adjacent javadoc parameters + * @param anchor descriptor for the target parameter + * @return logical position that points to the desired parameter description start location + */ + public static @NotNull LogicalPosition calculateDescriptionStartPosition(@NotNull PsiFile psiFile, + @NotNull Collection data, + @NotNull JavadocParameterInfo anchor) { + int descriptionStartColumn = -1; + int parameterNameEndColumn = -1; + for (JavadocParameterInfo parameterInfo : data) { + parameterNameEndColumn = Math.max(parameterNameEndColumn, parameterInfo.parameterNameEndPosition.column); + if (parameterInfo.parameterDescriptionStartPosition != null) { + descriptionStartColumn = Math.max(descriptionStartColumn, parameterInfo.parameterDescriptionStartPosition.column); + } + } + + int column; + + if (getJdAlignParamComments(psiFile)) { + column = Math.max(descriptionStartColumn, parameterNameEndColumn); + if (column <= parameterNameEndColumn) { + column = parameterNameEndColumn + 1; + } + } + else { + column = anchor.parameterNameEndPosition.column + 1; + } + return new LogicalPosition(anchor.parameterNameEndPosition.line, column); + } + + /** + * Returns information about all lines that contain javadoc parameters and are adjacent to the one that holds given offset. + * + * @param psiFile PSI holder for the document exposed the given editor + * @param editor target editor + * @param offset target offset that identifies anchor line to check + * @return pair like (javadoc info for the line identified by the given offset; list of javadoc parameter infos for + * adjacent lines if any + */ + public static @NotNull Pair> parse(@NotNull PsiFile psiFile, + @NotNull Editor editor, + int offset) { + List result = new ArrayList<>(); + PsiDocumentManager.getInstance(psiFile.getProject()).commitDocument(editor.getDocument()); + final PsiElement elementAtCaret = psiFile.findElementAt(offset); + if (elementAtCaret == null) { + return EMPTY; + } + + ASTNode nodeAtCaret = BasicJavaAstTreeUtil.toNode(elementAtCaret); + ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(nodeAtCaret, TAG_TOKEN_SET); + if (tag == null) { + // Due to javadoc PSI specifics. + if (BasicJavaAstTreeUtil.isWhiteSpace(nodeAtCaret)) { + for (ASTNode e = nodeAtCaret.getTreePrev(); e != null && tag == null; e = e.getTreePrev()) { + tag = BasicJavaAstTreeUtil.getParentOfType(e, TAG_TOKEN_SET, false); + if (e instanceof PsiWhiteSpace + || (e.getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) { + continue; + } + break; + } + } + } + if (tag == null) { + return EMPTY; + } + + JavadocParameterInfo anchorInfo = parse(tag, editor); + if (anchorInfo == null) { + return EMPTY; + } + + // Parse previous parameters. + for (ASTNode n = tag.getTreePrev(); n != null; n = n.getTreePrev()) { + JavadocParameterInfo info = parse(n, editor); + if (info == null) { + break; + } + result.add(0, info); + } + + result.add(anchorInfo); + + // Parse subsequent parameters. + for (ASTNode n = tag.getTreeNext(); n != null; n = n.getTreeNext()) { + JavadocParameterInfo info = parse(n, editor); + if (info == null) { + break; + } + result.add(info); + } + + return Pair.create(anchorInfo, result); + } + + private static @Nullable JavadocParameterInfo parse(@NotNull ASTNode astNode, @NotNull Editor editor) { + final ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(astNode, TAG_TOKEN_SET, false); + if (tag == null || !PARAM_TEXT.equals(BasicJavaAstTreeUtil.getTagName(tag))) { + return null; + } + + final ASTNode paramRef = BasicJavaAstTreeUtil.findChildByType(tag, BASIC_DOC_TAG_VALUE_ELEMENT, + BASIC_DOC_METHOD_OR_FIELD_REF, + BASIC_DOC_PARAMETER_REF, + BASIC_DOC_SNIPPET_TAG_VALUE); + if (paramRef == null) { + return null; + } + + for (ASTNode node = paramRef.getTreeNext(); node != null; node = node.getTreeNext()) { + final IElementType elementType = node.getElementType(); + if (elementType == JavaDocTokenType.DOC_COMMENT_DATA) { + return new JavadocParameterInfo( + editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()), + editor.offsetToLogicalPosition(node.getTextRange().getStartOffset()), + editor.getDocument().getLineNumber(node.getTextRange().getEndOffset()) + ); + } + else if (elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) { + break; + } + } + return new JavadocParameterInfo( + editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()), + null, + editor.getDocument().getLineNumber(paramRef.getTextRange().getEndOffset()) + ); + } + + /** + * Encapsulates information about source code line that holds javadoc parameter. + */ + public static class JavadocParameterInfo { + + /** + * Logical position that points to location just after javadoc parameter name. + *

+ * Example: + *

+     *   /**
+     *    * @param i[X]  description
+     *    */
+     * 
+ */ + public final @NotNull LogicalPosition parameterNameEndPosition; + public final @Nullable LogicalPosition parameterDescriptionStartPosition; + /** Last logical line occupied by the current javadoc parameter. */ + public final int lastLine; + + public JavadocParameterInfo(@NotNull LogicalPosition parameterNameEndPosition, + @Nullable LogicalPosition parameterDescriptionStartPosition, + int lastLine) { + this.parameterNameEndPosition = parameterNameEndPosition; + this.parameterDescriptionStartPosition = parameterDescriptionStartPosition; + this.lastLine = lastLine; + } + + @Override + public String toString() { + return "name end: " + parameterNameEndPosition + ", description start: " + parameterDescriptionStartPosition; + } } } diff --git a/java/java-impl/src/com/intellij/javadoc/EnterInJavadocParamDescriptionHandler.java b/java/java-impl/src/com/intellij/javadoc/EnterInJavadocParamDescriptionHandler.java index 43670035ee79..c4dd05cb09bb 100644 --- a/java/java-impl/src/com/intellij/javadoc/EnterInJavadocParamDescriptionHandler.java +++ b/java/java-impl/src/com/intellij/javadoc/EnterInJavadocParamDescriptionHandler.java @@ -22,9 +22,6 @@ import org.jetbrains.annotations.NotNull; import java.util.List; public final class EnterInJavadocParamDescriptionHandler implements EnterHandlerDelegate { - - private final JavadocHelper myHelper = JavadocHelper.getInstance(); - @Override public Result postProcessEnter(final @NotNull PsiFile file, @NotNull Editor editor, @NotNull DataContext dataContext) { if (!(file instanceof PsiJavaFile) @@ -39,7 +36,7 @@ public final class EnterInJavadocParamDescriptionHandler implements EnterHandler } final Pair> pair - = myHelper.parse(file, editor, caretOffset); + = JavadocHelper.parse(file, editor, caretOffset); if (pair.first == null || pair.first.parameterDescriptionStartPosition == null) { return Result.Continue; } @@ -64,7 +61,7 @@ public final class EnterInJavadocParamDescriptionHandler implements EnterHandler }); } - myHelper.navigate(desiredPosition, editor, file.getProject()); + JavadocHelper.navigate(desiredPosition, editor, file.getProject()); return Result.Stop; } diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java b/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java index 80a084f7099c..860aa52b1cd6 100644 --- a/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java +++ b/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java @@ -18,9 +18,6 @@ import java.util.List; * Holds javadoc-specific navigation logic. */ public final class JavadocNavigationDelegate extends EditorNavigationDelegateAdapter { - - private static final JavadocHelper ourHelper = JavadocHelper.getInstance(); - /** * Improves navigation in case of incomplete javadoc parameter descriptions. *

@@ -94,13 +91,13 @@ public final class JavadocNavigationDelegate extends EditorNavigationDelegateAda return Result.CONTINUE; } - final Pair> pair = ourHelper.parse(psiFile, editor, offset); + final Pair> pair = JavadocHelper.parse(psiFile, editor, offset); if (pair.first == null || pair.first.parameterDescriptionStartPosition != null) { return Result.CONTINUE; } - final LogicalPosition position = ourHelper.calculateDescriptionStartPosition(psiFile, pair.second, pair.first); - ourHelper.navigate(position, editor, psiFile.getProject()); + final LogicalPosition position = JavadocHelper.calculateDescriptionStartPosition(psiFile, pair.second, pair.first); + JavadocHelper.navigate(position, editor, psiFile.getProject()); return Result.STOP; } }