From 7fccf1632730c44531f6f7a4e2ae69d796f361cc Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Fri, 21 Sep 2012 17:26:25 +0400 Subject: [PATCH] IDEA-76469 Regenerate the javadoc All javadoc fixes except ordering --- .../documentation/JavaDocCommentFixer.java | 267 ++++++++++++ .../javaDoc/JavaDocLocalInspection.java | 75 ++-- .../javadoc/JavadocNavigationDelegate.java | 4 +- .../psi/impl/source/PsiJavaFileBaseImpl.java | 7 +- .../editorActions/FixDocCommentTest.groovy | 409 ++++++++++++++++++ .../documentation/DocCommentFixer.java | 35 ++ .../editorActions/FixDocCommentAction.java | 40 +- .../src/META-INF/LangExtensionPoints.xml | 2 + resources/src/META-INF/IdeaPlugin.xml | 1 + 9 files changed, 785 insertions(+), 55 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/documentation/JavaDocCommentFixer.java create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/documentation/DocCommentFixer.java diff --git a/java/java-impl/src/com/intellij/codeInsight/documentation/JavaDocCommentFixer.java b/java/java-impl/src/com/intellij/codeInsight/documentation/JavaDocCommentFixer.java new file mode 100644 index 000000000000..eaca9f997748 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/documentation/JavaDocCommentFixer.java @@ -0,0 +1,267 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.documentation; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.QuickFix; +import com.intellij.codeInspection.javaDoc.JavaDocLocalInspection; +import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection; +import com.intellij.javadoc.JavadocNavigationDelegate; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.psi.javadoc.PsiDocTagValue; +import com.intellij.util.containers.ContainerUtilRt; +import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * @author Denis Zhdanov + * @since 9/20/12 8:44 PM + */ +public class JavaDocCommentFixer implements DocCommentFixer { + + @NotNull private static final String PARAM_TAG = "@param"; + + /** + * Lists tags eligible for moving caret to after javadoc fixing. The main idea is that we want to locate caret at the + * incomplete tag description after fixing the doc comment. + *

+ * Example: + *

+   *   class Test {
+   *     /**
+   *      * Method description
+   *      *
+   *      * @param i    'i' argument
+   *      * @param j    [we want to move the caret here because j's description is missing]
+   *      */
+   *     void test(int i, int j) {
+   *     }
+   *   }
+   * 
+ */ + @NotNull private static final Set CARET_ANCHOR_TAGS = ContainerUtilRt.newHashSet(PARAM_TAG, "@throws", "@return"); + + @NotNull private static final List TAGS_ORDER = new ArrayList(); + static { + String tags = System.getProperty("java.doc.comment.fix.tags.order"); + if (tags == null) { + tags = "@param:@return:@throws"; + } + + for (String s : tags.split(":")) { + String tagName = s.trim(); + if (!tagName.isEmpty()) { + TAGS_ORDER.add("@" + tagName); + } + } + } + + private static final Comparator COMPARATOR = new Comparator() { + @Override + public int compare(PsiElement e1, PsiElement e2) { + return e2.getTextRange().getEndOffset() - e1.getTextRange().getEndOffset(); + } + }; + + @Override + public void fixComment(@NotNull Project project, @NotNull Editor editor, @NotNull PsiComment comment) { + if (!(comment instanceof PsiDocComment)) { + return; + } + + PsiDocCommentOwner owner = ((PsiDocComment)comment).getOwner(); + if (owner == null) { + return; + } + + JavaDocReferenceInspection referenceInspection = new JavaDocReferenceInspection(); + JavaDocLocalInspection localInspection = getDocLocalInspection(); + + InspectionManager inspectionManager = InspectionManager.getInstance(project); + ProblemDescriptor[] referenceProblems = null; + ProblemDescriptor[] otherProblems = null; + if (owner instanceof PsiClass) { + referenceProblems = referenceInspection.checkClass(((PsiClass)owner), inspectionManager, false); + otherProblems = localInspection.checkClass(((PsiClass)owner), inspectionManager, false); + } + else if (owner instanceof PsiField) { + referenceProblems = referenceInspection.checkField(((PsiField)owner), inspectionManager, false); + otherProblems = localInspection.checkField(((PsiField)owner), inspectionManager, false); + } + else if (owner instanceof PsiMethod) { + referenceProblems = referenceInspection.checkMethod((PsiMethod)owner, inspectionManager, false); + otherProblems = localInspection.checkMethod((PsiMethod)owner, inspectionManager, false); + } + + if (referenceProblems != null) { + fixReferenceProblems(referenceProblems, project); + } + if (otherProblems != null) { + fixCommonProblems(otherProblems, comment, editor.getDocument(), project); + } + + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); + locateCaret((PsiDocComment)comment, editor); + } + + @NotNull + private static JavaDocLocalInspection getDocLocalInspection() { + JavaDocLocalInspection localInspection = new JavaDocLocalInspection(); + + //region visibility + localInspection.TOP_LEVEL_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR = PsiModifier.PRIVATE; + localInspection.INNER_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR = PsiModifier.PRIVATE; + localInspection.FIELD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR = PsiModifier.PRIVATE; + localInspection.METHOD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR = PsiModifier.PRIVATE; + //endregion + + localInspection.IGNORE_EMPTY_DESCRIPTIONS = true; + + //region class type arguments + if (!localInspection.TOP_LEVEL_CLASS_OPTIONS.REQUIRED_TAGS.contains(PARAM_TAG)) { + localInspection.TOP_LEVEL_CLASS_OPTIONS.REQUIRED_TAGS += PARAM_TAG; + } + if (!localInspection.INNER_CLASS_OPTIONS.REQUIRED_TAGS.contains(PARAM_TAG)) { + localInspection.INNER_CLASS_OPTIONS.REQUIRED_TAGS += PARAM_TAG; + } + //endregion + + return localInspection; + } + + @SuppressWarnings("unchecked") + private static void fixReferenceProblems(@NotNull ProblemDescriptor[] problems, @NotNull Project project) { + for (ProblemDescriptor problem : problems) { + QuickFix[] fixes = problem.getFixes(); + if (fixes != null) { + fixes[0].applyFix(project, problem); + } + } + } + + // TODO den add doc + @SuppressWarnings("unchecked") + private static void fixCommonProblems(@NotNull ProblemDescriptor[] problems, + @NotNull PsiComment comment, + @NotNull final Document document, + @NotNull Project project) + { + List toRemove = new ArrayList(); + for (ProblemDescriptor problem : problems) { + PsiElement element = problem.getPsiElement(); + if (element == null) { + continue; + } + if (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) { + toRemove.add(e); + break; + } + } + } + else { + // Problems like 'missing @param'. + QuickFix[] fixes = problem.getFixes(); + if (fixes != null && fixes.length > 0) { + fixes[0].applyFix(project, problem); + } + } + } + + if (toRemove.isEmpty()) { + return; + } + if (toRemove.size() > 1) { + Collections.sort(toRemove, COMPARATOR); + } + + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); + CharSequence text = document.getCharsSequence(); + for (PsiElement element : toRemove) { + int startOffset = element.getTextRange().getStartOffset(); + int startLine = document.getLineNumber(startOffset); + 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 (i >= 0 && text.charAt(i) == '\n') { + startOffset = Math.max(i, document.getLineStartOffset(startLine) - 1); + } + + int endOffset = element.getTextRange().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*"); + if (i > 0 && text.charAt(i) == '\n') { + endOffset = i; + } + document.deleteString(startOffset, endOffset); + } + } + + private static void locateCaret(@NotNull PsiDocComment comment, @NotNull Editor editor) { + Document document = editor.getDocument(); + int lineToNavigate = -1; + for (PsiDocTag tag : comment.getTags()) { + PsiElement nameElement = tag.getNameElement(); + if (nameElement == null || !CARET_ANCHOR_TAGS.contains(nameElement.getText())) { + continue; + } + boolean good = false; + PsiElement[] dataElements = tag.getDataElements(); + if (dataElements != null) { + PsiDocTagValue valueElement = tag.getValueElement(); + for (PsiElement element : dataElements) { + if (element == valueElement) { + continue; + } + if (!StringUtil.isEmptyOrSpaces(element.getText())) { + good = true; + break; + } + } + } + if (!good) { + int offset = tag.getTextRange().getEndOffset(); + CharSequence text = document.getCharsSequence(); + int i = CharArrayUtil.shiftBackward(text, offset - 1, " \t*"); + if (i > 0 && text.charAt(i) == '\n') { + offset = i - 1; + } + lineToNavigate = document.getLineNumber(offset); + break; + } + } + + if (lineToNavigate >= 0) { + editor.getCaretModel().moveToOffset(document.getLineEndOffset(lineToNavigate)); + JavadocNavigationDelegate.navigateToLineEnd(editor, comment.getContainingFile()); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspection.java b/java/java-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspection.java index e9963add3b43..682268b983d5 100644 --- a/java/java-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspection.java @@ -92,15 +92,16 @@ public class JavaDocLocalInspection extends BaseLocalInspectionTool { } } - @NonNls public Options TOP_LEVEL_CLASS_OPTIONS = new Options("none", ""); - @NonNls public Options INNER_CLASS_OPTIONS = new Options("none", ""); - @NonNls public Options METHOD_OPTIONS = new Options("none", "@return@param@throws or @exception"); - @NonNls public Options FIELD_OPTIONS = new Options("none", ""); - public boolean IGNORE_DEPRECATED = false; - public boolean IGNORE_JAVADOC_PERIOD = true; - public boolean IGNORE_DUPLICATED_THROWS = false; - public boolean IGNORE_POINT_TO_ITSELF = false; - public String myAdditionalJavadocTags = ""; + @NonNls public Options TOP_LEVEL_CLASS_OPTIONS = new Options("none", ""); + @NonNls public Options INNER_CLASS_OPTIONS = new Options("none", ""); + @NonNls public Options METHOD_OPTIONS = new Options("none", "@return@param@throws or @exception"); + @NonNls public Options FIELD_OPTIONS = new Options("none", ""); + public boolean IGNORE_DEPRECATED = false; + public boolean IGNORE_JAVADOC_PERIOD = true; + public boolean IGNORE_DUPLICATED_THROWS = false; + public boolean IGNORE_POINT_TO_ITSELF = false; + public boolean IGNORE_EMPTY_DESCRIPTIONS = false; + public String myAdditionalJavadocTags = ""; private static final Logger LOG = Logger.getInstance("com.intellij.codeInspection.javaDoc.JavaDocLocalInspection"); @@ -145,8 +146,8 @@ public class JavaDocLocalInspection extends BaseLocalInspectionTool { private class MyChangeListener implements ChangeListener { private final JCheckBox myCheckBox; - private final Options myOptions; - private final String myTagName; + private final Options myOptions; + private final String myTagName; public MyChangeListener(JCheckBox checkBox, Options options, String tagName) { myCheckBox = checkBox; @@ -156,7 +157,7 @@ public class JavaDocLocalInspection extends BaseLocalInspectionTool { public void stateChanged(ChangeEvent e) { if (myCheckBox.isSelected()) { - if (!isTagRequired(myOptions,myTagName)) { + if (!isTagRequired(myOptions, myTagName)) { myOptions.REQUIRED_TAGS += myTagName; } } @@ -594,27 +595,29 @@ public class JavaDocLocalInspection extends BaseLocalInspectionTool { } } - for (PsiDocTag tag : tags) { - if ("param".equals(tag.getName())) { - final PsiElement[] dataElements = tag.getDataElements(); - final PsiDocTagValue valueElement = tag.getValueElement(); - boolean hasProblemsWithTag = dataElements.length < 2; - if (!hasProblemsWithTag) { - final StringBuilder buf = new StringBuilder(); - for (PsiElement element : dataElements) { - if (element != valueElement){ - buf.append(element.getText()); + if (!IGNORE_EMPTY_DESCRIPTIONS) { + for (PsiDocTag tag : tags) { + if ("param".equals(tag.getName())) { + final PsiElement[] dataElements = tag.getDataElements(); + final PsiDocTagValue valueElement = tag.getValueElement(); + boolean hasProblemsWithTag = dataElements.length < 2; + if (!hasProblemsWithTag) { + final StringBuilder buf = new StringBuilder(); + for (PsiElement element : dataElements) { + if (element != valueElement){ + buf.append(element.getText()); + } } + hasProblemsWithTag = buf.toString().trim().length() == 0; } - hasProblemsWithTag = buf.toString().trim().length() == 0; - } - if (hasProblemsWithTag) { - if (valueElement != null) { - problems.add(createDescriptor(valueElement, - InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "@param " + valueElement.getText() + ""), - manager, isOnTheFly)); + if (hasProblemsWithTag) { + if (valueElement != null) { + problems.add(createDescriptor(valueElement, + InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "@param " + valueElement.getText() + ""), + manager, isOnTheFly)); + } + } - } } } @@ -661,7 +664,7 @@ public class JavaDocLocalInspection extends BaseLocalInspectionTool { } } else - if ("return".equals(tag.getName())) { + if ("return".equals(tag.getName()) && !IGNORE_EMPTY_DESCRIPTIONS) { if (extractTagDescription(tag).length() == 0) { String message = InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "@return"); ProblemDescriptor descriptor = manager.createProblemDescriptor(tag.getNameElement(), message, (LocalQuickFix)null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, @@ -694,10 +697,10 @@ public class JavaDocLocalInspection extends BaseLocalInspectionTool { return false; } - private static void processThrowsTags(final PsiDocTag[] tags, - final Map declaredExceptions, - final InspectionManager mananger, - @NotNull final ArrayList problems, boolean isOnTheFly) { + private void processThrowsTags(final PsiDocTag[] tags, + final Map declaredExceptions, + final InspectionManager mananger, + @NotNull final ArrayList problems, boolean isOnTheFly) { for (PsiDocTag tag : tags) { if ("throws".equals(tag.getName()) || "exception".equals(tag.getName())) { final PsiDocTagValue value = tag.getValueElement(); @@ -714,7 +717,7 @@ public class JavaDocLocalInspection extends BaseLocalInspectionTool { PsiClassType classType = it.next(); final PsiClass psiClass = declaredExceptions.get(classType); if (InheritanceUtil.isInheritorOrSelf(exceptionClass, psiClass, true)) { - if (extractThrowsTagDescription(tag).length() == 0) { + if (!IGNORE_EMPTY_DESCRIPTIONS && extractThrowsTagDescription(tag).length() == 0) { problems.add(createDescriptor(tag.getNameElement(), InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "" + tag.getName() + ""), mananger, isOnTheFly)); } diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java b/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java index 4a92f5bc8175..20d8005282a1 100644 --- a/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java +++ b/java/java-impl/src/com/intellij/javadoc/JavadocNavigationDelegate.java @@ -94,10 +94,10 @@ public class JavadocNavigationDelegate extends EditorNavigationDelegateAdapter { return Result.CONTINUE; } - return navigateToLineEnd(editor, project, psiFile); + return navigateToLineEnd(editor, psiFile); } - public static Result navigateToLineEnd(@NotNull Editor editor, @NotNull Project project, @NotNull PsiFile psiFile) { + public static Result navigateToLineEnd(@NotNull Editor editor, @NotNull PsiFile psiFile) { final Document document = editor.getDocument(); final CaretModel caretModel = editor.getCaretModel(); final int offset = caretModel.getOffset(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java index 1c104816b082..da11f11ae200 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java @@ -272,8 +272,11 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava PsiElement lastParent, @NotNull PsiElement place) { assert isValid(); - - if (processor instanceof ClassResolverProcessor && isPhysical() && + + // TODO den remove + boolean allowCaching = true; + + if (allowCaching && processor instanceof ClassResolverProcessor && isPhysical() && (getUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING) == Boolean.TRUE || myResolveCache.hasUpToDateValue())) { final ClassResolverProcessor hint = (ClassResolverProcessor)processor; String name = hint.getName(state); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/editorActions/FixDocCommentTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/editorActions/FixDocCommentTest.groovy index ef0a53746c16..f3495c1e0438 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/editorActions/FixDocCommentTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/editorActions/FixDocCommentTest.groovy @@ -80,9 +80,418 @@ class Test { }''' ) } + + void testRemoveOneParameterFromMany() { + doTest( + initial: '''\ +class Test { + /** + * @param i + * @param j + * @param k + */ + void test(int i, int j) { + } +}''', + expected: '''\ +class Test { + /** + * @param i + * @param j + */ + void test(int i, int j) { + } +}''' + ) + } + + void testRemoveTheOnlyParameter() { + doTest( + initial: '''\ +class Test { + /** + * My description + * @param i + */ + void test() { + } +}''', + expected: '''\ +class Test { + /** + * My description + */ + void test() { + } +}''' + ) + } + + void testRemoveReturn() { + doTest( + initial: '''\ +class Test { + /** + * My description + * @return data + */ + void test() { + } +}''', + expected: '''\ +class Test { + /** + * My description + */ + void test() { + } +}''' + ) + } + + void testRemoveOneThrowsFromMany() { + doTest( + 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 { + } +}''', + expected: '''\ +class MyException1 extends Exception {} +class MyException2 extends Exception {} + +class Test { + /** + * @param i my arg + * @throws MyException2 text2 + */ + void test(int i) throws MyException2 { + } +}''' + ) + } + + void testRemoveTheOnlyThrows() { + doTest( + initial: '''\ +class MyException extends Exception {} + +class Test { + /** + * @param i my arg + * @throws MyException text + */ + void test(int i) { + } +}''', + expected: '''\ +class MyException extends Exception {} + +class Test { + /** + * @param i my arg + */ + void test(int i) { + } +}''' + ) + } + + void testRemoveOneTypeParameterFromMany() { + doTest( + initial: '''\ +/** + * @param tDescription + * @param vDescription + */ +class Test { +}''', + expected: '''\ +/** + * @param vDescription + */ +class Test { +}''' + ) + } + + void testRemoveMultipleTypeParameter() { + doTest( + initial: '''\ +/** + * @param tDescription + * @param vDescription + */ +class Test { +}''', + expected: '''\ +/** + */ +class Test { +}''' + ) + } + + void testAddFirstParameter() { + doTest( + initial: '''\ +class Test { + /** + */ + void test(int i) { + } +}''', + expected: '''\ +class Test { + /** + * @param i + */ + void test(int i) { + } +}''' + ) + } + + void testAddMultipleParameter() { + doTest( + initial: '''\ +class Test { + /** + * @param i + */ + void test(int i, int j, int k) { + } +}''', + expected: '''\ +class Test { + /** + * @param i + * @param j + * @param k + */ + void test(int i, int j, int k) { + } +}''' + ) + } + + void testAddReturn() { + doTest( + initial: '''\ +class Test { + /** + */ + int test() { + } +}''', + expected: '''\ +class Test { + /** + * @return + */ + int test() { + } +}''' + ) + } + + void testAddFirstThrows() { + doTest( + initial: '''\ +class MyException extends Exception {} + +class Test { + /** + * @param i my arg + */ + void test(int i) throws MyException { + } +}''', + expected: '''\ +class MyException extends Exception {} + +class Test { + /** + * @param i my arg + * @throws MyException + */ + void test(int i) throws MyException { + } +}''') + } + + void testAddNonFirstThrows() { + doTest( + 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 { + } +}''', + expected: '''\ +class MyException1 extends Exception {} +class MyException2 extends Exception {} +class MyException3 extends Exception {} + +class Test { + /** + * @param i my arg + * @throws MyException1 + * @throws MyException2 + * @throws MyException3 + */ + void test(int i) throws MyException1, MyException2, MyException3 { + } +}''') + } + + void testAddFirstThrowsWhenEmptyReturnIsAvailable() { + doTest( + initial: '''\ +class MyException extends Exception {} + +class Test { + /** + * @return + */ + int test() throws MyException { + return 1; + } +}''', + expected: '''\ +class MyException extends Exception {} + +class Test { + /** + * @return + * @throws MyException + */ + int test() throws MyException { + return 1; + } +}''') + } + void testAddFirstTypeParameter() { + doTest( + initial: '''\ +/** + * My description + * @author me + */ +class Test { +}''', + expected: '''\ +/** + * My description + * @author me + * @param + */ +class Test { +}''') + } + + void testAddNonFirstTypeParameter() { + doTest( + initial: '''\ +/** + * My description + * @author me + * @param type description + */ +class Test { +}''', + expected: '''\ +/** + * My description + * @author me + * @param type description + * @param + */ +class Test { +}''') + } + + void _testCorrectParametersOrder() { + doTest( + initial: '''\ +class Test { + /** + * @param j + * @param k k description + * @param i + */ + public void test(int i, int j, int k) { + } +}''', + expected: '''\ +class Test { + /** + * @param i + * @param j + * @param k k description + */ + public void test(int i, int j, int k) { + } +}''' + ) + } + + void testCorrectTypeParametersOrder() { + // TODO den implement + } + + void testAllesZusammen() { + // TODO den implement + } + + void testNavigateToMissingParamDescription() { + doTest( + initial: '''\ +class Test { + /** + * @param i + */ + void test(int i) { + } +}''', + expected: '''\ +class Test { + /** + * @param i + */ + void test(int i) { + } +}''' + ) + } + + void testNavigateToMissingReturnDescription() { + // TODO den implement + } + + void testNavigateToMissingThrowsDescription() { + // TODO den implement + } + private def doTest(Map args) { configureFromFileText("${getTestName(false)}.java", args.initial) + myEditor.settings.virtualSpace = false executeAction(FixDocCommentAction.ACTION_ID) checkResultByText(args.expected) } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocCommentFixer.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocCommentFixer.java new file mode 100644 index 000000000000..923f6adfc5e0 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocCommentFixer.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.documentation; + +import com.intellij.lang.LanguageExtension; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiComment; +import org.jetbrains.annotations.NotNull; + +/** + * @author Denis Zhdanov + * @since 9/20/12 8:37 PM + */ +public interface DocCommentFixer { + + LanguageExtension EXTENSION = new LanguageExtension("com.intellij.lang.documentationFixer"); + + // TODO den add doc + void fixComment(@NotNull Project project, @NotNull Editor editor, @NotNull PsiComment comment); +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/FixDocCommentAction.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/FixDocCommentAction.java index 353ea0cbe38a..cb3de1cb6270 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/FixDocCommentAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/FixDocCommentAction.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.editorActions; +import com.intellij.codeInsight.documentation.DocCommentFixer; import com.intellij.lang.*; import com.intellij.lang.documentation.CodeDocumentationProvider; import com.intellij.lang.documentation.CompositeDocumentationProvider; @@ -104,20 +105,33 @@ public class FixDocCommentAction extends EditorAction { return; } final CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter)c; + final Runnable task; + if (pair.second == null || pair.second.getTextRange().isEmpty()) { + task = new Runnable() { + @Override + public void run() { + generateComment(pair.first, editor, docProvider, commenter, project); + } + }; + } + else { + final DocCommentFixer fixer = DocCommentFixer.EXTENSION.forLanguage(language); + if (fixer == null) { + return; + } + else { + task = new Runnable() { + @Override + public void run() { + fixer.fixComment(project, editor, pair.second); + } + }; + } + } final Runnable command = new Runnable() { @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - if (pair.second == null || pair.second.getTextRange().isEmpty()) { - generateComment(pair.first, editor, docProvider, commenter, project); - } - else { - fixCommentIfNecessary(pair.second); - } - } - }); + ApplicationManager.getApplication().runWriteAction(task); } }; CommandProcessor.getInstance().executeCommand(project, command, "Fix documentation", null); @@ -246,8 +260,4 @@ public class FixDocCommentAction extends EditorAction { } return result; } - - private static void fixCommentIfNecessary(@NotNull PsiComment docComment) { - // TODO den implement - } } diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 10bb417a1494..937ea57a09fd 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -472,6 +472,8 @@ + + diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 93c161b20774..94e5f7dd7ebc 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -791,6 +791,7 @@ +