diff --git a/java/java-impl/src/com/intellij/application/options/JavaIndentOptionsEditor.java b/java/java-impl/src/com/intellij/application/options/JavaIndentOptionsEditor.java index 54a551efe67d..19ae70407ef6 100644 --- a/java/java-impl/src/com/intellij/application/options/JavaIndentOptionsEditor.java +++ b/java/java-impl/src/com/intellij/application/options/JavaIndentOptionsEditor.java @@ -30,6 +30,7 @@ public class JavaIndentOptionsEditor extends SmartIndentOptionsEditor { private JCheckBox myLabelIndentAbsolute; private JCheckBox myCbDontIndentTopLevelMembers; + private JCheckBox myCbUseRelativeIndent; protected void addComponents() { super.addComponents(); @@ -42,6 +43,9 @@ public class JavaIndentOptionsEditor extends SmartIndentOptionsEditor { myCbDontIndentTopLevelMembers = new JCheckBox(ApplicationBundle.message("checkbox.do.not.indent.top.level.class.members")); add(myCbDontIndentTopLevelMembers); + + myCbUseRelativeIndent = new JCheckBox(ApplicationBundle.message("checkbox.use.relative.indents")); + add(myCbUseRelativeIndent); } public boolean isModified(final CodeStyleSettings settings, final CodeStyleSettings.IndentOptions options) { @@ -50,6 +54,7 @@ public class JavaIndentOptionsEditor extends SmartIndentOptionsEditor { isModified |= isFieldModified(myLabelIndent, options.LABEL_INDENT_SIZE); isModified |= isFieldModified(myLabelIndentAbsolute, options.LABEL_INDENT_ABSOLUTE); isModified |= isFieldModified(myCbDontIndentTopLevelMembers, settings.DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS); + isModified |= isFieldModified(myCbUseRelativeIndent, options.USE_RELATIVE_INDENTS); return isModified; } @@ -64,6 +69,7 @@ public class JavaIndentOptionsEditor extends SmartIndentOptionsEditor { } options.LABEL_INDENT_ABSOLUTE = myLabelIndentAbsolute.isSelected(); settings.DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS = myCbDontIndentTopLevelMembers.isSelected(); + options.USE_RELATIVE_INDENTS = myCbUseRelativeIndent.isSelected(); } public void reset(@NotNull final CodeStyleSettings settings, @NotNull final CodeStyleSettings.IndentOptions options) { @@ -71,6 +77,7 @@ public class JavaIndentOptionsEditor extends SmartIndentOptionsEditor { myLabelIndent.setText(Integer.toString(options.LABEL_INDENT_SIZE)); myLabelIndentAbsolute.setSelected(options.LABEL_INDENT_ABSOLUTE); myCbDontIndentTopLevelMembers.setSelected(settings.DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS); + myCbUseRelativeIndent.setSelected(options.USE_RELATIVE_INDENTS); } public void setEnabled(final boolean enabled) { diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java index 2b458cb77466..8efc372f68be 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java @@ -154,6 +154,9 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { @Override protected void reformat(PsiElement atCaret) throws IncorrectOperationException { + if (atCaret == null) { + return; + } PsiElement parent = atCaret.getParent(); if (parent instanceof PsiCodeBlock) { final PsiCodeBlock block = (PsiCodeBlock) parent; diff --git a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java index 4cb54acf4a6b..535a36da14d5 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java @@ -430,22 +430,26 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext return buffer.toString(); } - public PsiComment findExistingDocComment(final PsiComment _element) { - PsiElement parentElement = _element.getParent(); - - return parentElement instanceof PsiDocCommentOwner ? ((PsiDocCommentOwner)parentElement).getDocComment() : null; + public PsiComment findExistingDocComment(final PsiComment comment) { + if (comment instanceof PsiDocComment) { + final PsiDocCommentOwner owner = ((PsiDocComment)comment).getOwner(); + if (owner != null) { + return owner.getDocComment(); + } + } + return null; } - public String generateDocumentationContentStub(PsiComment _element) { - PsiElement parentElement = _element.getParent(); - final Project project = _element.getProject(); + public String generateDocumentationContentStub(PsiComment _comment) { + final PsiDocCommentOwner commentOwner = ((PsiDocComment)_comment).getOwner(); + final Project project = commentOwner.getProject(); final StringBuilder builder = StringBuilderSpinAllocator.alloc(); try { - if (parentElement instanceof PsiMethod) { - PsiMethod psiMethod = (PsiMethod)parentElement; + final CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter)LanguageCommenters.INSTANCE + .forLanguage(commentOwner.getLanguage()); + if (commentOwner instanceof PsiMethod) { + PsiMethod psiMethod = (PsiMethod)commentOwner; final PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); - final CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter)LanguageCommenters.INSTANCE - .forLanguage(parentElement.getLanguage()); final Map param2Description = new HashMap(); final PsiMethod[] superMethods = psiMethod.findSuperMethods(); for (PsiMethod superMethod : superMethods) { @@ -499,11 +503,9 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext builder.append(LINE_SEPARATOR); } } - else if (parentElement instanceof PsiClass) { - final PsiTypeParameterList typeParameterList = ((PsiClass)parentElement).getTypeParameterList(); + else if (commentOwner instanceof PsiClass) { + final PsiTypeParameterList typeParameterList = ((PsiClass)commentOwner).getTypeParameterList(); if (typeParameterList != null) { - final CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter)LanguageCommenters.INSTANCE - .forLanguage(parentElement.getLanguage()); createTypeParamsListComment(builder, project, commenter, typeParameterList); } } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index 698baf09ceac..d72cf39fc2fb 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -213,6 +213,10 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } } + protected static boolean isRBrace(final ASTNode child) { + return child.getElementType() == ElementType.RBRACE; + } + public Spacing getSpacing(Block child1, Block child2) { return JavaSpacePropertyProcessor.getSpacing(getTreeNode(child2), mySettings); } @@ -1080,4 +1084,32 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return JavaBlockUtil.mayShiftIndentInside(myNode); } + @Nullable + protected ASTNode composeCodeBlock(final ArrayList result, ASTNode child, final Indent indent, final int childrenIndent) { + final ArrayList localResult = new ArrayList(); + processChild(localResult, child, null, null, Indent.getNoneIndent()); + child = child.getTreeNext(); + while (child != null) { + if (!FormatterUtil.containsWhiteSpacesOnly(child)) { + final boolean rBrace = isRBrace(child); + final Indent childIndent = rBrace ? Indent.getNoneIndent() : getCodeBlockInternalIndent(childrenIndent); + child = processChild(localResult, child, null, null, childIndent); + if (rBrace) { + result.add(createCodeBlockBlock(localResult, indent, childrenIndent)); + return child; + } + } + if (child != null) { + child = child.getTreeNext(); + } + } + result.add(createCodeBlockBlock(localResult, indent, childrenIndent)); + return null; + } + + private SyntheticCodeBlock createCodeBlockBlock(final ArrayList localResult, final Indent indent, final int childrenIndent) { + final SyntheticCodeBlock result = new SyntheticCodeBlock(localResult, null, getSettings(), indent, null); + result.setChildAttributes(new ChildAttributes(getCodeBlockInternalIndent(childrenIndent), null)); + return result; + } } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java index 06b6c1603d01..5ffc1394cedb 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java @@ -17,6 +17,7 @@ package com.intellij.psi.formatter.java; import com.intellij.formatting.*; import com.intellij.lang.ASTNode; +import com.intellij.psi.JavaTokenType; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.impl.source.tree.ElementType; @@ -52,6 +53,7 @@ public class BlockContainingJavaBlock extends AbstractJavaBlock{ private void buildChildren(final ArrayList result, final Alignment childAlignment, final Wrap childWrap) { ASTNode child = myNode.getFirstChildNode(); + ASTNode prevChild = null; int state = BEFORE_FIRST; @@ -60,7 +62,26 @@ public class BlockContainingJavaBlock extends AbstractJavaBlock{ final Indent indent = calcIndent(child, state); myIndentsBefore.add(calcIndentBefore(child, state)); state = calcNewState(child, state); - child = processChild(result, child, childAlignment, childWrap, indent); + + // The general idea is that it's possible that there are comment lines before method declaration line and that they have + // different indents. Example: + // + // // This is comment before method + // void foo() {} + // + // We want to have the comment and method as distinct blocks then in order to correctly process indentation for inner method + // elements. See IDEA-53778 for example of situation when it is significant. + if (prevChild != null && myNode.getElementType() == ElementType.METHOD + && JavaTokenType.COMMENT_BIT_SET.contains(prevChild.getElementType()) + && !JavaTokenType.COMMENT_BIT_SET.contains(child.getElementType())) + { + prevChild = child; + child = composeCodeBlock(result, child, Indent.getNoneIndent(), 0); + } + else { + prevChild = child; + child = processChild(result, child, childAlignment, childWrap, indent); + } for (int i = myIndentsBefore.size(); i < result.size(); i++) { myIndentsBefore.add(Indent.getContinuationIndent()); } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java index f93962a6b00c..1e448bb8e403 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java @@ -86,7 +86,7 @@ public class CodeBlockBlock extends AbstractJavaBlock { child = processCaseAndStatementAfter(result, child, childAlignment, childWrap, indent); } else if (myNode.getElementType() == ElementType.CLASS && child.getElementType() == ElementType.LBRACE) { - child = composeCodeBlock(result, child, getCodeBlockExternalIndent()); + child = composeCodeBlock(result, child, getCodeBlockExternalIndent(), myChildrenIndent); } else { child = processChild(result, child, childAlignment, childWrap, indent); @@ -98,35 +98,6 @@ public class CodeBlockBlock extends AbstractJavaBlock { } } - @Nullable - private ASTNode composeCodeBlock(final ArrayList result, ASTNode child, final Indent indent) { - final ArrayList localResult = new ArrayList(); - processChild(localResult, child, null, null, Indent.getNoneIndent()); - child = child.getTreeNext(); - while (child != null) { - if (!FormatterUtil.containsWhiteSpacesOnly(child)) { - final boolean rBrace = isRBrace(child); - final Indent childIndent = rBrace ? Indent.getNoneIndent() : getCodeBlockInternalIndent(myChildrenIndent); - child = processChild(localResult, child, null, null, childIndent); - if (rBrace) { - result.add(createCodeBlockBlock(localResult, indent)); - return child; - } - } - if (child != null) { - child = child.getTreeNext(); - } - } - result.add(createCodeBlockBlock(localResult, indent)); - return null; - } - - private SyntheticCodeBlock createCodeBlockBlock(final ArrayList localResult, final Indent indent) { - final SyntheticCodeBlock result = new SyntheticCodeBlock(localResult, null, getSettings(), indent, null); - result.setChildAttributes(new ChildAttributes(getCodeBlockInternalIndent(myChildrenIndent), null)); - return result; - } - @Nullable private ASTNode processCaseAndStatementAfter(final ArrayList result, ASTNode child, @@ -253,10 +224,6 @@ public class CodeBlockBlock extends AbstractJavaBlock { } } - private static boolean isRBrace(final ASTNode child) { - return child.getElementType() == ElementType.RBRACE; - } - @Override @NotNull public ChildAttributes getChildAttributes(final int newChildIndex) { diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java index c8b345eb626e..cc5d98a28476 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.psi.impl.compiled; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.JavaDocElementType; import com.intellij.psi.impl.source.tree.TreeElement; @@ -26,12 +25,10 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; class ClsDocCommentImpl extends ClsElementImpl implements PsiDocComment, JavaTokenType, PsiJavaToken { - private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsDocCommentImpl"); - - private final ClsElementImpl myParent; + private final PsiDocCommentOwner myParent; private final PsiDocTag[] myTags; - ClsDocCommentImpl(ClsElementImpl parent) { + ClsDocCommentImpl(PsiDocCommentOwner parent) { myParent = parent; PsiDocTag[] tags = new PsiDocTag[1]; @@ -63,10 +60,16 @@ class ClsDocCommentImpl extends ClsElementImpl implements PsiDocComment, JavaTok return myParent; } + public PsiDocCommentOwner getOwner() { + return myParent; + } + + @NotNull public PsiElement[] getDescriptionElements() { return EMPTY_ARRAY; } + @NotNull public PsiDocTag[] getTags() { return myTags; } @@ -76,6 +79,7 @@ class ClsDocCommentImpl extends ClsElementImpl implements PsiDocComment, JavaTok return getTags()[0]; } + @NotNull public PsiDocTag[] findTagsByName(@NonNls String name) { if (!name.equals("deprecated")) return PsiDocTag.EMPTY_ARRAY; return getTags(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java index 3da36a9530bc..a061625051c5 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java @@ -18,10 +18,7 @@ package com.intellij.psi.impl.source.javadoc; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.psi.JavaElementVisitor; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.impl.source.Constants; import com.intellij.psi.impl.source.PsiElementArrayConstructor; @@ -57,18 +54,32 @@ public class PsiDocCommentImpl extends LazyParseablePsiElement implements PsiDoc super(JavaDocElementType.DOC_COMMENT, text); } + public PsiDocCommentOwner getOwner() { + final PsiElement parent = getParent(); + if (parent instanceof PsiDocCommentOwner) { + final PsiDocCommentOwner owner = (PsiDocCommentOwner)parent; + if (owner.getDocComment() == this) { + return owner; + } + } + + return null; + } + + @NotNull public PsiElement[] getDescriptionElements() { - ArrayList array = new ArrayList(); + ArrayList array = new ArrayList(); for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) { IElementType i = child.getElementType(); if (i == DOC_TAG) break; if (i != DOC_COMMENT_START && i != DOC_COMMENT_END && i != DOC_COMMENT_LEADING_ASTERISKS) { - array.add(child); + array.add(child.getPsi()); } } return array.toArray(new PsiElement[array.size()]); } + @NotNull public PsiDocTag[] getTags() { return getChildrenAsPsiElements(TAG_BIT_SET, PSI_TAG_ARRAY_CONSTRUCTOR); } @@ -92,6 +103,7 @@ public class PsiDocCommentImpl extends LazyParseablePsiElement implements PsiDoc return null; } + @NotNull public PsiDocTag[] findTagsByName(String name) { ArrayList array = new ArrayList(); PsiDocTag[] tags = getTags(); diff --git a/java/java-tests/testData/refactoring/changeSignature/SCR40895.java b/java/java-tests/testData/refactoring/changeSignature/SCR40895.java index 05e4785c2e7c..f2e1889bece5 100644 --- a/java/java-tests/testData/refactoring/changeSignature/SCR40895.java +++ b/java/java-tests/testData/refactoring/changeSignature/SCR40895.java @@ -1,7 +1,7 @@ class X { /** - * Has a method called {@link #mymethod(int,int)}. + * Has a method called {@link #mymethod(int, int)}. */ public class TestRefactorLink { public void mymethod(int y, int z) { } diff --git a/java/java-tests/testData/refactoring/changeSignature/SCR40895_after.java b/java/java-tests/testData/refactoring/changeSignature/SCR40895_after.java index 1a0291659eeb..cc8388fb3f1f 100644 --- a/java/java-tests/testData/refactoring/changeSignature/SCR40895_after.java +++ b/java/java-tests/testData/refactoring/changeSignature/SCR40895_after.java @@ -1,7 +1,7 @@ class X { /** - * Has a method called {@link #mymethod(int,boolean)}. + * Has a method called {@link #mymethod(int, boolean)}. */ public class TestRefactorLink { public void mymethod(int y, boolean b) { } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/AbstractJavaFormatterTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/AbstractJavaFormatterTest.java index 5856580ab5f6..39769d391749 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/AbstractJavaFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/AbstractJavaFormatterTest.java @@ -22,6 +22,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.impl.DocumentImpl; +import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -74,6 +75,10 @@ public abstract class AbstractJavaFormatterTest extends LightIdeaTestCase { return CodeStyleSettingsManager.getSettings(getProject()); } + public static CodeStyleSettings.IndentOptions getIndentOptions() { + return getSettings().getIndentOptions(StdFileTypes.JAVA); + } + public void doTest() throws Exception { doTest(getTestName(false) + ".java", getTestName(false) + "_after.java"); } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java index 6280a7e91a12..511450a9f617 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java @@ -173,34 +173,103 @@ public class JavaFormatterIndentationTest extends AbstractJavaFormatterTest { public void testCaseFromSwitch() throws IncorrectOperationException { // Inspired by IDEADEV-22920 getSettings().INDENT_CASE_FROM_SWITCH = false; - doTextTest("class Foo{\n" + - "void foo () {\n" + - "switch(someValue) {\n" + - " // This comment is correctly not-indented\n" + - " case 1:\n" + - " doSomething();\n" + - " break;\n" + - "\n" + - " // This comment should not be indented, but it is\n" + - " case 2:\n" + - " doSomethingElse();\n" + - " break;\n" + - "}\n" + - "}\n" + - "}", "class Foo {\n" + - " void foo() {\n" + - " switch (someValue) {\n" + - " // This comment is correctly not-indented\n" + - " case 1:\n" + - " doSomething();\n" + - " break;\n" + - "\n" + - " // This comment should not be indented, but it is\n" + - " case 2:\n" + - " doSomethingElse();\n" + - " break;\n" + - " }\n" + - " }\n" + - "}"); + doTextTest( + "class Foo{\n" + + "void foo () {\n" + + "switch(someValue) {\n" + + " // This comment is correctly not-indented\n" + + " case 1:\n" + + " doSomething();\n" + + " break;\n" + + "\n" + + " // This comment should not be indented, but it is\n" + + " case 2:\n" + + " doSomethingElse();\n" + + " break;\n" + + "}\n" + + "}\n" + + "}", + + "class Foo {\n" + + " void foo() {\n" + + " switch (someValue) {\n" + + " // This comment is correctly not-indented\n" + + " case 1:\n" + + " doSomething();\n" + + " break;\n" + + "\n" + + " // This comment should not be indented, but it is\n" + + " case 2:\n" + + " doSomethingElse();\n" + + " break;\n" + + " }\n" + + " }\n" + + "}"); + } + + public void testBinaryExpressionsWithRelativeIndents() { + // Inspired by IDEA-21795 + getIndentOptions().USE_RELATIVE_INDENTS = true; + getIndentOptions().CONTINUATION_INDENT_SIZE = 4; + + doTextTest( + "public class FormattingTest {\n" + + "\n" + + " public boolean test1(int a, int b, int c, int d) {\n" + + " return a == 1 &&\n" + + " b == 2;\n" + + " }\n" + + "\n" + + " public boolean multilineSignOnCurrent(int a, int b, int c, int d) {\n" + + " return a == 0 &&\n" + + " (b == 0 ||\n" + + " c == 0) &&\n" + + " d == 0;\n" + + " }\n" + + "\n" + + " public boolean multilineSignOnNext(int a, int b, int c, int d) {\n" + + " return a == 0\n" + + " && (b == 0\n" + + " || c == 0)\n" + + " && d == 0;\n" + + " }\n" + + "\n" + + " public boolean expectedMultilineSignOnNext(int a, int b, int c, int d) {\n" + + " return a == 0\n" + + " && (b == 0\n" + + " || c == 0)\n" + + " && d == 0;\n" + + " }\n" + + "}", + + "public class FormattingTest {\n" + + "\n" + + " public boolean test1(int a, int b, int c, int d) {\n" + + " return a == 1 &&\n" + + " b == 2;\n" + + " }\n" + + "\n" + + " public boolean multilineSignOnCurrent(int a, int b, int c, int d) {\n" + + " return a == 0 &&\n" + + " (b == 0 ||\n" + + " c == 0) &&\n" + + " d == 0;\n" + + " }\n" + + "\n" + + " public boolean multilineSignOnNext(int a, int b, int c, int d) {\n" + + " return a == 0\n" + + " && (b == 0\n" + + " || c == 0)\n" + + " && d == 0;\n" + + " }\n" + + "\n" + + " public boolean expectedMultilineSignOnNext(int a, int b, int c, int d) {\n" + + " return a == 0\n" + + " && (b == 0\n" + + " || c == 0)\n" + + " && d == 0;\n" + + " }\n" + + "}" + ); } } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/AutoIndentLinesTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaIndenterTest.java similarity index 57% rename from java/java-tests/testSrc/com/intellij/psi/formatter/java/AutoIndentLinesTest.java rename to java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaIndenterTest.java index c1a1936ae0d2..6961877d7bae 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/AutoIndentLinesTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaIndenterTest.java @@ -22,7 +22,9 @@ import com.intellij.util.IncorrectOperationException; * @author Denis Zhdanov * @since May 11, 2010 5:30:28 PM */ -public class AutoIndentLinesTest extends AbstractJavaFormatterTest { +public class JavaIndenterTest extends AbstractJavaFormatterTest { + + private static final String CARET_TOKEN = ""; public void testIndentAlignedMethodParameter() { // Inspired by IDEA-22020 @@ -41,7 +43,7 @@ public class AutoIndentLinesTest extends AbstractJavaFormatterTest { int end = initial.indexOf("\n", start); myTextRange = new TextRange(start, end); - doTextTest(Action.INDENT, initial, + doTextTest(initial, "class BrokenAlignment {\n" + " public\n" + "\tstatic int foo(String a, String b, String c,\n" + @@ -52,8 +54,41 @@ public class AutoIndentLinesTest extends AbstractJavaFormatterTest { ); } + public void testMethodBodyShiftedToComment() { + // Inspired by IDEA-53778 + + doTextTest( + "class Test {\n" + + " // some comment\n" + + " public void doSmth(int[] p) {\n" + + "\n" + + " }" + + "}", + + "class Test {\n" + + " // some comment\n" + + " public void doSmth(int[] p) {\n" + + " \n" + + " }" + + "}"); + } + @Override public void doTextTest(String text, String textAfter) throws IncorrectOperationException { - doTextTest(Action.INDENT, text, textAfter); + doTextTest(Action.INDENT, adjustTextIfNecessary(text), textAfter); + } + + private String adjustTextIfNecessary(String text) { + int caretIndex = text.indexOf(CARET_TOKEN); + if (caretIndex < 0) { + return text; + } + + if (caretIndex < text.length() && text.indexOf(CARET_TOKEN, caretIndex + 1) >= 0) { + fail(String.format("Invalid indentation test 'before' text - it contains more than one caret meta-token (%s). Text: %n%s", + CARET_TOKEN, text)); + } + myTextRange = new TextRange(caretIndex, caretIndex); + return text.substring(0, caretIndex) + text.substring(caretIndex + CARET_TOKEN.length()); } } diff --git a/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspManager.java b/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspManager.java index 14e8472811fc..4d8e88f29b2b 100644 --- a/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspManager.java +++ b/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspManager.java @@ -40,8 +40,6 @@ import java.util.Set; */ public abstract class JspManager implements IBaseJspManager { - public static final Key DIRECTORIES_KEY = Key.create("TagDirOriginalDirs"); - public static JspManager getInstance(@NotNull Project project) { return ServiceManager.getService(project, JspManager.class); } diff --git a/java/openapi/src/com/intellij/psi/javadoc/PsiDocComment.java b/java/openapi/src/com/intellij/psi/javadoc/PsiDocComment.java index 7710a8530059..83132393c6f8 100644 --- a/java/openapi/src/com/intellij/psi/javadoc/PsiDocComment.java +++ b/java/openapi/src/com/intellij/psi/javadoc/PsiDocComment.java @@ -16,13 +16,25 @@ package com.intellij.psi.javadoc; import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiDocCommentOwner; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public interface PsiDocComment extends PsiComment { + @Nullable + PsiDocCommentOwner getOwner(); + + @NotNull PsiElement[] getDescriptionElements(); + + @NotNull PsiDocTag[] getTags(); + @Nullable PsiDocTag findTagByName(@NonNls String name); + + @NotNull PsiDocTag[] findTagsByName(@NonNls String name); } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java index 804170323dfa..95116298fa59 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java @@ -199,6 +199,7 @@ public class CodeStyleSettings implements Cloneable, JDOMExternalizable { public boolean SMART_TABS = false; public int LABEL_INDENT_SIZE = 0; public boolean LABEL_INDENT_ABSOLUTE = false; + public boolean USE_RELATIVE_INDENTS = false; public void readExternal(Element element) throws InvalidDataException { DefaultJDOMExternalizer.readExternal(this, element); @@ -228,6 +229,7 @@ public class CodeStyleSettings implements Cloneable, JDOMExternalizable { if (CONTINUATION_INDENT_SIZE != that.CONTINUATION_INDENT_SIZE) return false; if (INDENT_SIZE != that.INDENT_SIZE) return false; if (LABEL_INDENT_ABSOLUTE != that.LABEL_INDENT_ABSOLUTE) return false; + if (USE_RELATIVE_INDENTS != that.USE_RELATIVE_INDENTS) return false; if (LABEL_INDENT_SIZE != that.LABEL_INDENT_SIZE) return false; if (SMART_TABS != that.SMART_TABS) return false; if (TAB_SIZE != that.TAB_SIZE) return false; @@ -245,6 +247,7 @@ public class CodeStyleSettings implements Cloneable, JDOMExternalizable { result = 31 * result + (SMART_TABS ? 1 : 0); result = 31 * result + LABEL_INDENT_SIZE; result = 31 * result + (LABEL_INDENT_ABSOLUTE ? 1 : 0); + result = 31 * result + (USE_RELATIVE_INDENTS ? 1 : 0); return result; } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java index d1a884083fc8..46ed4d5f7801 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java @@ -15,27 +15,14 @@ */ package com.intellij.application.options.codeStyle; -import com.intellij.application.options.CodeStyleAbstractPanel; -import com.intellij.ide.DataManager; -import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.LanguageFileType; -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.containers.HashMap; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.*; @@ -46,7 +33,6 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Arrays; /** * @author max @@ -54,8 +40,8 @@ import java.util.Arrays; public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleAbstractPanel { private static final Logger LOG = Logger.getInstance("#com.intellij.application.options.CodeStyleSpacesPanel"); private final JTree myOptionsTree; - private final HashMap myKeyToFieldMap = new HashMap(); - private final ArrayList myKeys = new ArrayList(); + private final HashMap myKeyToFieldMap = new HashMap(); + private final ArrayList myKeys = new ArrayList(); private final JPanel myPanel = new JPanel(new GridBagLayout()); public OptionTreeWithPreviewPanel(CodeStyleSettings settings) { @@ -84,32 +70,14 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); String groupName = ""; DefaultMutableTreeNode groupNode = null; - for (int i = 0; i < myKeys.size(); i++) { - if (myKeys.get(i) instanceof BooleanOptionKey) { - BooleanOptionKey key = (BooleanOptionKey)myKeys.get(i); - String newGroupName = key.groupName; - if (!newGroupName.equals(groupName) || groupNode == null) { - groupName = newGroupName; - groupNode = new DefaultMutableTreeNode(newGroupName); - rootNode.add(groupNode); - } - groupNode.add(new MyToggleTreeNode(key, key.cbName)); - } - else if (myKeys.get(i) instanceof IntSelectionOptionKey) { - IntSelectionOptionKey key = (IntSelectionOptionKey)myKeys.get(i); - String newGroupName = key.groupName; - if (!newGroupName.equals(groupName) || groupNode == null) { - groupName = newGroupName; - groupNode = new DefaultMutableTreeNode(newGroupName); - rootNode.add(groupNode); - } - MyToggleTreeNode[] nodes = new MyToggleTreeNode[key.rbNames.length]; - for (int j = 0; j < nodes.length; j++) { - nodes[j] = new MyToggleTreeNode(key, key.rbNames[j]); - groupNode.add(nodes[j]); - } - key.setCreatedNodes(nodes); + for (BooleanOptionKey key: myKeys) { + String newGroupName = key.groupName; + if (!newGroupName.equals(groupName) || groupNode == null) { + groupName = newGroupName; + groupNode = new DefaultMutableTreeNode(newGroupName); + rootNode.add(groupNode); } + groupNode.add(new MyToggleTreeNode(key, key.cbName)); } DefaultTreeModel model = new DefaultTreeModel(rootNode); @@ -156,19 +124,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA Object o = treePath.getLastPathComponent(); if (o instanceof MyToggleTreeNode) { MyToggleTreeNode node = (MyToggleTreeNode)o; - if (node.isCheckbox()) { - node.setSelected(!node.isSelected()); - } - else { - MyToggleTreeNode[] group = node.getGroup(); - for (int i = 0; i < group.length; i++) { - MyToggleTreeNode groupNode = group[i]; - groupNode.setSelected(false); - int row = myOptionsTree.getRowForPath(new TreePath(groupNode.getPath())); - myOptionsTree.repaint(myOptionsTree.getRowBounds(row)); - } - node.setSelected(true); - } + node.setSelected(!node.isSelected()); int row = myOptionsTree.getRowForPath(treePath); myOptionsTree.repaint(myOptionsTree.getRowBounds(row)); //updatePreview(); @@ -201,21 +157,9 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA private void resetMyTreeNode(MyToggleTreeNode childNode, final CodeStyleSettings settings) { try { - if (childNode.getKey() instanceof BooleanOptionKey) { - BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); - Field field = (Field)myKeyToFieldMap.get(key); - childNode.setSelected(field.getBoolean(settings)); - } - else if (childNode.getKey() instanceof IntSelectionOptionKey) { - IntSelectionOptionKey key = (IntSelectionOptionKey)childNode.getKey(); - Field field = (Field)myKeyToFieldMap.get(key); - int fieldValue = field.getInt(settings); - for (int i = 0; i < key.rbNames.length; i++) { - if (childNode.getText().equals(key.rbNames[i])) { - childNode.setSelected(fieldValue == key.values[i]); - } - } - } + BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); + Field field = myKeyToFieldMap.get(key); + childNode.setSelected(field.getBoolean(settings)); } catch (IllegalArgumentException e) { LOG.error(e); @@ -244,22 +188,9 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA private void applyToggleNode(MyToggleTreeNode childNode, final CodeStyleSettings settings) { try { - if (childNode.getKey() instanceof BooleanOptionKey) { - BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); - Field field = (Field)myKeyToFieldMap.get(key); - field.set(settings, childNode.isSelected() ? Boolean.TRUE : Boolean.FALSE); - } - else if (childNode.getKey() instanceof IntSelectionOptionKey) { - if (!childNode.isSelected()) return; - IntSelectionOptionKey key = (IntSelectionOptionKey)childNode.getKey(); - Field field = (Field)myKeyToFieldMap.get(key); - for (int i = 0; i < key.rbNames.length; i++) { - if (childNode.getText().equals(key.rbNames[i])) { - field.set(settings, new Integer(key.values[i])); - break; - } - } - } + BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); + Field field = myKeyToFieldMap.get(key); + field.set(settings, childNode.isSelected() ? Boolean.TRUE : Boolean.FALSE); } catch (IllegalArgumentException e) { LOG.error(e); @@ -296,21 +227,9 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA private boolean isToggleNodeModified(MyToggleTreeNode childNode, final CodeStyleSettings settings) { try { - if (childNode.getKey() instanceof BooleanOptionKey) { - BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); - Field field = (Field)myKeyToFieldMap.get(key); - return childNode.isSelected() != field.getBoolean(settings); - } - else if (childNode.getKey() instanceof IntSelectionOptionKey) { - if (!childNode.isSelected()) return false; - IntSelectionOptionKey key = (IntSelectionOptionKey)childNode.getKey(); - Field field = (Field)myKeyToFieldMap.get(key); - for (int i = 0; i < key.rbNames.length; i++) { - if (childNode.getText().equals(key.rbNames[i])) { - return field.getInt(settings) != key.values[i]; - } - } - } + BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); + Field field = myKeyToFieldMap.get(key); + return childNode.isSelected() != field.getBoolean(settings); } catch (IllegalArgumentException e) { LOG.error(e); @@ -335,20 +254,6 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA } } - protected void initRadioGroupField(String fieldName, String groupName, String[] rbNames, int[] values) { - try { - Class styleSettingsClass = CodeStyleSettings.class; - Field field = styleSettingsClass.getField(fieldName); - IntSelectionOptionKey key = new IntSelectionOptionKey(groupName, rbNames, values); - myKeyToFieldMap.put(key, field); - myKeys.add(key); - } - catch (NoSuchFieldException e) { - } - catch (SecurityException e) { - } - } - protected void prepareForReformat(final PsiFile psiFile) { //psiFile.putUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY, LanguageLevel.HIGHEST); } @@ -356,12 +261,10 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA protected class MyTreeCellRenderer implements TreeCellRenderer { private final MyLabelPanel myLabel; private final JCheckBox myCheckBox; - private final JRadioButton myRadioButton; public MyTreeCellRenderer() { myLabel = new MyLabelPanel(); myCheckBox = new JCheckBox(); - myRadioButton = new JRadioButton(); myCheckBox.setMargin(new Insets(0, 0, 0, 0)); } @@ -370,7 +273,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA if (value instanceof MyToggleTreeNode) { MyToggleTreeNode treeNode = (MyToggleTreeNode)value; - JToggleButton button = treeNode.isCheckbox() ? (JToggleButton)myCheckBox : (JToggleButton)myRadioButton; + JToggleButton button = myCheckBox; button.setText(treeNode.getText()); button.setSelected(treeNode.isSelected); if (isSelected) { @@ -473,64 +376,23 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA } } - private static class IntSelectionOptionKey { - final String groupName; - final String[] rbNames; - final int[] values; - private MyToggleTreeNode[] myNodes; - - public IntSelectionOptionKey(String groupName, String[] rbNames, int[] values) { - this.groupName = groupName; - this.rbNames = rbNames; - this.values = values; - } - - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof IntSelectionOptionKey)) return false; - - final IntSelectionOptionKey intSelectionOptionKey = (IntSelectionOptionKey)o; - - if (!groupName.equals(intSelectionOptionKey.groupName)) return false; - if (!Arrays.equals(rbNames, intSelectionOptionKey.rbNames)) return false; - - return true; - } - - public int hashCode() { - return groupName.hashCode() + rbNames[0].hashCode() * 29; - } - - public void setCreatedNodes(MyToggleTreeNode[] nodes) { myNodes = nodes; } - - public MyToggleTreeNode[] getNodes() { return myNodes; } - } - private static class MyToggleTreeNode extends DefaultMutableTreeNode { private final Object myKey; private final String myText; private boolean isSelected; - private final boolean isCheckbox; public MyToggleTreeNode(Object key, String text) { myKey = key; myText = text; - isCheckbox = true; } public Object getKey() { return myKey; } public String getText() { return myText; } - public boolean isCheckbox() { return isCheckbox; } - public void setSelected(boolean val) { isSelected = val; } public boolean isSelected() { return isSelected; } - - public MyToggleTreeNode[] getGroup() { - return ((IntSelectionOptionKey)myKey).getNodes(); - } } public JComponent getInternalPanel() { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java index 3dcebcb7127c..d09e0fbeaba3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java @@ -222,6 +222,35 @@ public class CustomTemplateCallback { myBuilder.insertText(offset, text); } + public void insertNewLine() { + myBuilder.insertText(myOffset, "\n"); + myOffset++; + } + + public boolean newLineBefore() { + int i = myOffset - 1; + CharSequence text = myBuilder.getText(); + while (i >= 0 && Character.isWhitespace(text.charAt(i))) { + if (text.charAt(i) == '\n') { + return true; + } + i--; + } + return i < 0; + } + + public boolean newLineAfter() { + int i = myOffset; + CharSequence text = myBuilder.getText(); + while (i < text.length() && Character.isWhitespace(text.charAt(i))) { + if (text.charAt(i) == '\n') { + return true; + } + i++; + } + return i == text.length(); + } + public void deleteTemplateKey(String key) { int caretAt = myEditor.getCaretModel().getOffset(); myEditor.getDocument().deleteString(caretAt - key.length(), caretAt); diff --git a/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java index 6b3bddec876c..eb3260c8287a 100644 --- a/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java @@ -21,11 +21,24 @@ import com.intellij.psi.codeStyle.CodeStyleSettings; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import static java.util.Arrays.asList; /** * @author lesya */ public abstract class AbstractBlockWrapper { + + private static final Set RELATIVE_INDENT_TYPES = new HashSet(asList( + IndentImpl.Type.NORMAL, IndentImpl.Type.CONTINUATION, IndentImpl.Type.CONTINUATION_WITHOUT_FIRST + )); + + private static final Set CONTINUATION_INDENT_TYPES = new HashSet(asList( + IndentImpl.Type.CONTINUATION, IndentImpl.Type.CONTINUATION_WITHOUT_FIRST + )); + protected WhiteSpace myWhiteSpace; protected CompositeBlockWrapper myParent; protected int myStart; @@ -154,36 +167,38 @@ public abstract class AbstractBlockWrapper { } - public IndentData getChildOffset(AbstractBlockWrapper child, CodeStyleSettings.IndentOptions options, final int tokenBlockStartOffset) { - final boolean childOnNewLine = child.getWhiteSpace().containsLineFeeds(); + public IndentData getChildOffset(AbstractBlockWrapper child, CodeStyleSettings.IndentOptions options, int targetBlockStartOffset) { + final boolean childStartsNewLine = child.getWhiteSpace().containsLineFeeds(); + IndentImpl.Type childIndentType = child.getIndent().getType(); final IndentData childIndent; // Calculate child indent. - if (childOnNewLine) { - childIndent = getIndent(options, child, tokenBlockStartOffset); + if (childStartsNewLine + || (!getWhiteSpace().containsLineFeeds() && RELATIVE_INDENT_TYPES.contains(childIndentType) && indentAlreadyUsedBefore(child))) + { + childIndent = getIndent(options, child, targetBlockStartOffset); } else { - IndentImpl.Type type = child.getIndent().getType(); - if (!getWhiteSpace().containsLineFeeds() - && (type == IndentImpl.Type.NORMAL || type == IndentImpl.Type.CONTINUATION || type == IndentImpl.Type.CONTINUATION_WITHOUT_FIRST) - && indentAlreadyUsedBefore(child)) - { - childIndent = getIndent(options, child, tokenBlockStartOffset); - } - else { - childIndent = new IndentData(0); - } + childIndent = new IndentData(0); } + // Use child indent if it's absolute and the child is contained on new line. - if (childOnNewLine && child.getIndent().isAbsolute()) { - myFlags &= ~CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT; - AbstractBlockWrapper current = this; - while (current != null && current.getStartOffset() == getStartOffset()) { - current.myFlags &= ~CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT; - current = current.myParent; + if (childStartsNewLine) { + if (child.getIndent().isAbsolute()) { + myFlags &= ~CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT; + AbstractBlockWrapper current = this; + while (current != null && current.getStartOffset() == getStartOffset()) { + current.myFlags &= ~CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT; + current = current.myParent; + } + return childIndent; + } + else if (options.USE_RELATIVE_INDENTS && child.getStartOffset() > getStartOffset() + && (CONTINUATION_INDENT_TYPES.contains(childIndentType))) + { + return childIndent.add(getNumberOfSymbolsBeforeBlock()); } - return childIndent; } if (child.getStartOffset() == getStartOffset()) { @@ -192,25 +207,25 @@ public abstract class AbstractBlockWrapper { setCanUseFirstChildIndentAsBlockIndent(newValue); } - if (getStartOffset() == tokenBlockStartOffset) { + if (getStartOffset() == targetBlockStartOffset) { if (myParent == null) { return childIndent; } else { - return childIndent.add(myParent.getChildOffset(this, options, tokenBlockStartOffset)); + return childIndent.add(myParent.getChildOffset(this, options, targetBlockStartOffset)); } } else if (!getWhiteSpace().containsLineFeeds()) { if (isIndentAffectedAlignment(child)) { return createAlignmentIndent(childIndent, child); } else { - return childIndent.add(myParent.getChildOffset(this, options, tokenBlockStartOffset)); + return childIndent.add(myParent.getChildOffset(this, options, targetBlockStartOffset)); } } else { if (myParent == null) return childIndent.add(getWhiteSpace()); if (getIndent().isAbsolute()) { if (myParent.myParent != null) { - return childIndent.add(myParent.myParent.getChildOffset(myParent, options, tokenBlockStartOffset)); + return childIndent.add(myParent.myParent.getChildOffset(myParent, options, targetBlockStartOffset)); } else { return childIndent.add(getWhiteSpace()); @@ -225,7 +240,7 @@ public abstract class AbstractBlockWrapper { } } else { - return childIndent.add(myParent.getChildOffset(this, options, tokenBlockStartOffset)); + return childIndent.add(myParent.getChildOffset(this, options, targetBlockStartOffset)); } } } @@ -252,7 +267,7 @@ public abstract class AbstractBlockWrapper { * @return previous block for the current block if any; null otherwise */ @Nullable - protected abstract AbstractBlockWrapper getPreviousBlock(); + protected abstract LeafBlockWrapper getPreviousBlock(); protected final void setCanUseFirstChildIndentAsBlockIndent(final boolean newValue) { if (newValue) myFlags |= CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT; @@ -287,10 +302,12 @@ public abstract class AbstractBlockWrapper { } /** - * Allows to answer if indent for the given child block should be calculated + * Allows to answer if indent for the given child block should be calculated taking into consideration alignment + * of the text at current block start. * - * @param child - * @return + * @param child child block to check + * @return true if indent should be calculated taking into consideration alignment of the text at current + * block start; false otherwise */ private boolean isIndentAffectedAlignment(AbstractBlockWrapper child) { if (!child.getWhiteSpace().containsLineFeeds()) { diff --git a/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java index 805bbf49aff0..dca827b624f4 100644 --- a/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java @@ -75,7 +75,7 @@ public class CompositeBlockWrapper extends AbstractBlockWrapper{ } @Override - protected AbstractBlockWrapper getPreviousBlock() { + protected LeafBlockWrapper getPreviousBlock() { if (myChildren == null || myChildren.isEmpty()) { return null; } diff --git a/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java index 4bd3df4d8489..99b38484192a 100644 --- a/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java @@ -151,8 +151,6 @@ class LeafBlockWrapper extends AbstractBlockWrapper { return new IndentData(indentSpaces, spaces); } - - public void dispose() { super.dispose(); myPreviousBlock = null; @@ -230,9 +228,4 @@ class LeafBlockWrapper extends AbstractBlockWrapper { public TextRange getTextRange() { return new TextRange(myStart, myEnd); } - - private void calculateNumberOfSymbolsAtLastLine() { - - } - } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 35484a910385..e2ff7ecda4a9 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -340,6 +340,7 @@ editbox.indent.continuation.indent=Continuation indent editbox.indent.label.indent=Label indent checkbox.indent.absolute.label.indent=Absolute label indent checkbox.do.not.indent.top.level.class.members=Do not indent top level class members +checkbox.use.relative.indents=Use indents relative to expression start tooltip.indent.must.be.multiple.of.tab.size.for.smart.tabs.to.operate=Indent must be multiple of tab size for smart tabs to operate combobox.crlf.system.dependent=System Dependent combobox.crlf.unix=Unix \n diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/SMTestRunnerConnectionUtil.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/SMTestRunnerConnectionUtil.java index dc71ad9d28cc..6a7f3ee1e1e2 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/SMTestRunnerConnectionUtil.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/SMTestRunnerConnectionUtil.java @@ -60,19 +60,20 @@ public class SMTestRunnerConnectionUtil { * consistency of test events communication protocol and throw assertion errors. To enable debug mode * please set system property idea.smrunner.debug=true * - * @param processHandler Process handler - * @param consoleProperties Console properties for test console actions * @param testFrameworkName Is used to store(project level) latest value of testTree/consoleTab splitter and other settings * and also will be mentioned in debug diagnostics + * @param processHandler Process handler + * @param consoleProperties Console properties for test console actions * @return Console view * @throws ExecutionException If IDEA cannot execute process this Exception will * be caught and shown in error message box */ - public static BaseTestsOutputConsoleView attachRunner(@NotNull final ProcessHandler processHandler, + public static BaseTestsOutputConsoleView attachRunner(@NotNull final String testFrameworkName, + @NotNull final ProcessHandler processHandler, @NotNull final TestConsoleProperties consoleProperties, final RunnerSettings runnerSettings, - final ConfigurationPerRunnerSettings configurationSettings, - @NotNull final String testFrameworkName) throws ExecutionException { + final ConfigurationPerRunnerSettings configurationSettings + ) throws ExecutionException { // Console final String splitterPropertyName = testFrameworkName + ".Splitter.Proportion"; @@ -126,24 +127,24 @@ public class SMTestRunnerConnectionUtil { * consistency of test events communication protocol and throw assertion errors. To enable debug mode * please set system property idea.smrunner.debug=true * + * @param testFrameworkName Is used to store(project level) latest value of testTree/consoleTab splitter and other settings * @param processHandler Process handler * @param commandLineState Command line state * @param config User run configuration settings - * @param testFrameworkName Is used to store(project level) latest value of testTree/consoleTab splitter and other settings * @return Console view * @throws ExecutionException If IDEA cannot execute process this Exception will * be caught and shown in error message box */ - public static ConsoleView attachRunner(@NotNull final ProcessHandler processHandler, + public static ConsoleView attachRunner(@NotNull final String testFrameworkName, @NotNull final ProcessHandler processHandler, @NotNull final CommandLineState commandLineState, - @NotNull final RuntimeConfiguration config, - @NotNull final String testFrameworkName) throws ExecutionException { + @NotNull final RuntimeConfiguration config + ) throws ExecutionException { // final String testFrameworkName final TestConsoleProperties consoleProperties = new SMTRunnerConsoleProperties(config, testFrameworkName); - return attachRunner(processHandler, consoleProperties, + return attachRunner(testFrameworkName, processHandler, consoleProperties, commandLineState.getRunnerSettings(), - commandLineState.getConfigurationSettings(), testFrameworkName); + commandLineState.getConfigurationSettings()); } /** diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 1ac5c1f664e8..5ad9423a6169 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -642,7 +642,7 @@ public class ContainerUtil { return result; } - public static List mapNotNull(T[] array, Function mapping) { + public static List mapNotNull(@NotNull T[] array, Function mapping) { return mapNotNull(Arrays.asList(array), mapping); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocCommentImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocCommentImpl.java index a71b2b1342b0..a7507ea5b86b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocCommentImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocCommentImpl.java @@ -98,6 +98,7 @@ public class GrDocCommentImpl extends LazyParseablePsiElement implements GroovyD return list.toArray(new GrDocTag[list.size()]); } + @NotNull public PsiElement[] getDescriptionElements() { ArrayList array = new ArrayList(); for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgIncomingStatusUpdater.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgIncomingStatusUpdater.java index b52cf875ee56..63d5ef6cfae0 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgIncomingStatusUpdater.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgIncomingStatusUpdater.java @@ -46,6 +46,7 @@ class HgIncomingStatusUpdater implements HgUpdater { public void run() { new Task.Backgroundable(project, "Checking Incoming Changesets", true) { public void run(@NotNull ProgressIndicator indicator) { + if (project.isDisposed()) return; HgIncomingCommand command = new HgIncomingCommand(project); VcsRoot[] roots = ProjectLevelVcsManager.getInstance(project).getAllVcsRoots(); List changesets = new LinkedList(); @@ -57,7 +58,7 @@ class HgIncomingStatusUpdater implements HgUpdater { } }.queue(); } - }); + }, project.getDisposed()); } private final class IncomingChangesetFormatter implements HgChangesetStatus.ChangesetWriter { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgOutgoingStatusUpdater.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgOutgoingStatusUpdater.java index 0cb3d58e7785..6cf9d12d8e6b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgOutgoingStatusUpdater.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgOutgoingStatusUpdater.java @@ -46,6 +46,7 @@ class HgOutgoingStatusUpdater implements HgUpdater { public void run() { new Task.Backgroundable(project, "Checking Outgoing Changesets", true) { public void run(@NotNull ProgressIndicator indicator) { + if (project.isDisposed()) return; HgOutgoingCommand command = new HgOutgoingCommand(project); VcsRoot[] roots = ProjectLevelVcsManager.getInstance(project).getAllVcsRoots(); List outgoing = new LinkedList(); @@ -57,7 +58,7 @@ class HgOutgoingStatusUpdater implements HgUpdater { } }.queue(); } - }); + }, project.getDisposed()); } private final class OutgoingChangesetFormatter implements HgChangesetStatus.ChangesetWriter { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java index 22f056150310..01152182d8b1 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java @@ -189,16 +189,21 @@ public class HgVcs extends AbstractVcs { } @Override - protected void start() throws VcsException { + protected void activate() { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { - HgExecutableValidator validator = new HgExecutableValidator(myProject); - started = validator.check(globalSettings); + if (ApplicationManager.getApplication().isUnitTestMode()) { + started = true; + } + else { + HgExecutableValidator validator = new HgExecutableValidator(myProject); + started = validator.check(globalSettings); + } if (isStarted()) { addListeners(); } } - }); + }, myProject.getDisposed()); } @Override diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java index 3600106a09cf..21274dd639f9 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java @@ -29,6 +29,7 @@ import com.intellij.psi.xml.XmlTokenType; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import com.intellij.util.containers.IntArrayList; +import com.intellij.xml.util.HtmlUtil; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -310,27 +311,38 @@ class XmlZenCodingInterpreter { if (token instanceof XmlTemplateToken && token.getTemplate() != null) { XmlTemplateToken xmlTemplateToken = (XmlTemplateToken)token; List> attr2value = new ArrayList>(xmlTemplateToken.getAttribute2Value()); - if (attr2value.size() > 0 || XmlZenCodingTemplate.isTrueXml(callback)) { - TemplateImpl modifiedTemplate = token.getTemplate().copy(); - XmlTag tag = xmlTemplateToken.getTag(); - if (tag != null) { - for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { - Pair pair = iterator.next(); - if (tag.getAttribute(pair.first) != null) { - tag.setAttribute(pair.first, ZenCodingUtil.getValue(pair, numberInIteration)); - iterator.remove(); - } + TemplateImpl modifiedTemplate = token.getTemplate().copy(); + XmlTag tag = xmlTemplateToken.getTag(); + if (tag != null) { + for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + if (tag.getAttribute(pair.first) != null) { + tag.setAttribute(pair.first, ZenCodingUtil.getValue(pair, numberInIteration)); + iterator.remove(); } - String s = filterXml(tag, callback, filter); - assert s != null; - modifiedTemplate.setString(s); - removeVariablesWhichHasNoSegment(modifiedTemplate); - Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration, callback); - callback.expandTemplate(modifiedTemplate, predefinedValues); - return; } + String s = filterXml(tag, callback, filter); + assert s != null; + if (HtmlUtil.isHtmlBlockTagL(tag.getName())) { + boolean newLineBefore = callback.newLineBefore(); + boolean newLineAfter = callback.newLineAfter(); + if (!newLineBefore || !newLineAfter) { + StringBuilder builder = new StringBuilder(); + if (!newLineBefore) { + builder.append('\n'); + } + builder.append(s); + if (!newLineAfter) { + builder.append('\n'); + } + s = builder.toString(); + } + } + modifiedTemplate.setString(s); + removeVariablesWhichHasNoSegment(modifiedTemplate); + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration, callback); + callback.expandTemplate(modifiedTemplate, predefinedValues); } - callback.expandTemplate(token.getTemplate(), null); } else { // for CSS