From feb72f4b67b550ef45ddbab9c6bb0015e108de73 Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Mon, 30 Aug 2010 12:14:14 +0400 Subject: [PATCH] IDEA-56242 Align When Multiline > Fields/variables groups does not align correctly when using tabs 1. Removed 'Align in columns' option processing for local variables; 2. Corrected white space string generation during 'align fields in columns' processing (tabs are not used for non-first block on a line alignment now); 3. Green code policy is applied whenever possible; 4. Corresponding tests are added; --- .../psi/formatter/java/AbstractJavaBlock.java | 14 +-- .../java/ChainedMethodsAlignment.java | 1 + .../java/ChainedMethodsAlignment_after.java | 1 + .../java/JavaFormatterAlignmentTest.java | 42 ++++---- .../psi/formatter/java/JavaFormatterTest.java | 35 ++++--- .../psi/codeStyle/CodeStyleSettings.java | 4 +- .../intellij/formatting/FormatProcessor.java | 99 ++++++++++--------- .../com/intellij/formatting/IndentInfo.java | 44 ++------- .../com/intellij/formatting/WhiteSpace.java | 29 +++++- .../src/messages/ApplicationBundle.properties | 2 +- 10 files changed, 143 insertions(+), 128 deletions(-) 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 d2e7d5863c2b..e9aff65950ba 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 @@ -49,10 +49,10 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo /** * Holds types of the elements for which 'align in column' rule may be preserved. * - * @see CodeStyleSettings#ALIGN_GROUP_FIELDS_VARIABLES + * @see CodeStyleSettings#ALIGN_GROUP_FIELD_DECLARATIONS */ protected static final Set ALIGN_IN_COLUMNS_ELEMENT_TYPES = Collections.unmodifiableSet(new HashSet(asList( - JavaElementType.FIELD, JavaElementType.DECLARATION_STATEMENT, JavaElementType.LOCAL_VARIABLE + JavaElementType.FIELD ))); private static final Logger LOG = Logger.getInstance("#com.intellij.psi.formatter.java.AbstractJavaBlock"); @@ -60,11 +60,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo /** * Shared thread-safe config object to use during 'align in column' processing. * - * @see CodeStyleSettings#ALIGN_GROUP_FIELDS_VARIABLES + * @see CodeStyleSettings#ALIGN_GROUP_FIELD_DECLARATIONS */ private static final AlignmentInColumnsConfig ALIGNMENT_IN_COLUMNS_CONFIG = new AlignmentInColumnsConfig( JavaTokenType.IDENTIFIER, ElementType.WHITE_SPACE_BIT_SET, ElementType.JAVA_COMMENT_BIT_SET, JavaTokenType.EQ, - JavaElementType.FIELD, JavaElementType.LOCAL_VARIABLE + JavaElementType.FIELD ); /** @@ -874,14 +874,14 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo * * @param child variable declaration child node which alignment is to be defined * @return alignment to use for the given node - * @see CodeStyleSettings#ALIGN_GROUP_FIELDS_VARIABLES + * @see CodeStyleSettings#ALIGN_GROUP_FIELD_DECLARATIONS */ @Nullable private Alignment getVariableDeclarationSubElementAlignment(ASTNode child) { // The whole idea of variable declarations alignment is that complete declaration blocks which children are to be aligned hold // reference to the same AlignmentStrategy object, hence, reuse the same Alignment objects. So, there is no point in checking // if it's necessary to align sub-blocks if shared strategy is not defined. - if (myAlignmentStrategy == null || !mySettings.ALIGN_GROUP_FIELDS_VARIABLES) { + if (myAlignmentStrategy == null || !mySettings.ALIGN_GROUP_FIELD_DECLARATIONS) { return null; } @@ -1265,7 +1265,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo * @return */ protected boolean shouldUseVarDeclarationAlignment(ASTNode node) { - return mySettings.ALIGN_GROUP_FIELDS_VARIABLES && ALIGN_IN_COLUMNS_ELEMENT_TYPES.contains(node.getElementType()) + return mySettings.ALIGN_GROUP_FIELD_DECLARATIONS && ALIGN_IN_COLUMNS_ELEMENT_TYPES.contains(node.getElementType()) && !myAlignmentInColumnsHelper.useDifferentVarDeclarationAlignment(node, ALIGNMENT_IN_COLUMNS_CONFIG); } diff --git a/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment.java b/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment.java index 158695bf92fe..47c1abe3b84f 100644 --- a/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment.java +++ b/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment.java @@ -1,6 +1,7 @@ class FormattingTest { private static FormattingTest staticFoo; + private FormattingTest instanceFoo; @SuppressWarnings({"AccessStaticViaInstance"}) diff --git a/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment_after.java b/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment_after.java index eef0a90e838e..0b21b924963a 100644 --- a/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment_after.java +++ b/java/java-tests/testData/psi/formatter/java/ChainedMethodsAlignment_after.java @@ -1,6 +1,7 @@ class FormattingTest { private static FormattingTest staticFoo; + private FormattingTest instanceFoo; @SuppressWarnings({"AccessStaticViaInstance"}) diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java index 85d00cb23db1..a8365efd152e 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java @@ -150,9 +150,9 @@ public class JavaFormatterAlignmentTest extends AbstractJavaFormatterTest { doMethodTest(method, method); } - public void testVariableDeclarationAlignment() { + public void testFieldInColumnsAlignment() { // Inspired by IDEA-55147 - getSettings().ALIGN_GROUP_FIELDS_VARIABLES = true; + getSettings().ALIGN_GROUP_FIELD_DECLARATIONS = true; getSettings().FIELD_ANNOTATION_WRAP = CodeStyleSettings.DO_NOT_WRAP; getSettings().VARIABLE_ANNOTATION_WRAP = CodeStyleSettings.DO_NOT_WRAP; @@ -179,15 +179,6 @@ public class JavaFormatterAlignmentTest extends AbstractJavaFormatterTest { " private transient int iiii5 = 1;\n" + " /*sdf*/\n" + " @MyAnnotation(value = 1, text = 2) float f5 = 1;\n" + - "\n" + - " public void foo() {\n" + - " int start = 1;\n" + - " int start2 = 1;\n" + - " @NotNull int end = 2;\n" + - " @NotNull long longValue = 1;\n" + - " Serializable serializable;\n" + - " Object o;\n" + - " }\n" + "}", "public class FormattingTest {\n" + @@ -212,15 +203,26 @@ public class JavaFormatterAlignmentTest extends AbstractJavaFormatterTest { " private transient int iiii5 = 1;\n" + " /*sdf*/\n" + " @MyAnnotation(value = 1, text = 2) float f5 = 1;\n" + - "\n" + - " public void foo() {\n" + - " int start = 1;\n" + - " int start2 = 1;\n" + - " @NotNull int end = 2;\n" + - " @NotNull long longValue = 1;\n" + - " Serializable serializable;\n" + - " Object o;\n" + - " }\n" + + "}" + ); + } + + public void testTabsAndFieldsInColumnsAlignment() throws Exception { + // Inspired by IDEA-56242 + getSettings().ALIGN_GROUP_FIELD_DECLARATIONS = true; + getIndentOptions().USE_TAB_CHARACTER = true; + + doTextTest( + "public class Test {\n" + + "\tprivate Long field2 = null;\n" + + "\tprivate final Object field1 = null;\n" + + "\tprivate int i = 1;\n" + + "}", + + "public class Test {\n" + + "\tprivate Long field2 = null;\n" + + "\tprivate final Object field1 = null;\n" + + "\tprivate int i = 1;\n" + "}" ); } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java index 3698345f77d9..4eccb0bbf6e7 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java @@ -730,21 +730,26 @@ public class JavaFormatterTest extends AbstractJavaFormatterTest { } public void testComment1() throws Exception { - doTextTest("class Foo {\n" + - " public boolean mErrorFlage;\n" + - " /**\n" + - " * Reference to New Member Message Source\n" + - " */\n" + - " private NewMemberMessageSource newMemberMessageSource;" + - "\n" + -"}", "class Foo {\n" + - " public boolean mErrorFlage;\n" + - " /**\n" + - " * Reference to New Member Message Source\n" + - " */\n" + - " private NewMemberMessageSource newMemberMessageSource;" + - "\n" + - "}"); + doTextTest( + "class Foo {\n" + + " public boolean mErrorFlage;\n" + + "\n" + + " /**\n" + + " * Reference to New Member Message Source\n" + + " */\n" + + " private NewMemberMessageSource newMemberMessageSource;" + + "\n" + + "}", + + "class Foo {\n" + + " public boolean mErrorFlage;\n" + + "\n" + + " /**\n" + + " * Reference to New Member Message Source\n" + + " */\n" + + " private NewMemberMessageSource newMemberMessageSource;" + + "\n" + + "}"); } public void testElseAfterComment() throws Exception { 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 27311cd1300b..4384d27f1cb8 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java @@ -434,12 +434,10 @@ public class CodeStyleSettings implements Cloneable, JDOMExternalizable { //----------------- Group alignments --------------- /** - * Specifies if subsequent fields/variables declarations and initialisations should be aligned in columns like below: + * Specifies if subsequent fields declarations and initialisations should be aligned in columns like below: * int start = 1; * int end = 10; */ - public boolean ALIGN_GROUP_FIELDS_VARIABLES = false; - public boolean ALIGN_GROUP_FIELD_DECLARATIONS = false; //----------------- BLANK LINES -------------------- diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index bc8d78d106d9..196620602b0e 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -36,12 +36,12 @@ class FormatProcessor { private LeafBlockWrapper myCurrentBlock; - private Map myInfos; - private CompositeBlockWrapper myRootBlockWrapper; + private Map myInfos; + private CompositeBlockWrapper myRootBlockWrapper; private TIntObjectHashMap myTextRangeToWrapper; private final CodeStyleSettings.IndentOptions myIndentOption; - private final CodeStyleSettings mySettings; + private final CodeStyleSettings mySettings; /** * Remembers mappings between backward-shifted aligned block and blocks that cause that shift in order to detect @@ -62,13 +62,13 @@ class FormatProcessor { private final Map> myBackwardShiftedAlignedBlocks = new HashMap>(); - private LeafBlockWrapper myWrapCandidate = null; + private LeafBlockWrapper myWrapCandidate = null; private LeafBlockWrapper myFirstWrappedBlockOnLine = null; private LeafBlockWrapper myFirstTokenBlock; private LeafBlockWrapper myLastTokenBlock; - private SortedMap> myPreviousDependencies = + private SortedMap> myPreviousDependencies = new TreeMap>(new Comparator() { public int compare(final TextRange o1, final TextRange o2) { int offsetsDelta = o1.getEndOffset() - o2.getEndOffset(); @@ -81,8 +81,8 @@ class FormatProcessor { }); private final HashSet myAlignAgain = new HashSet(); - private WhiteSpace myLastWhiteSpace; - private boolean myDisposed; + private WhiteSpace myLastWhiteSpace; + private boolean myDisposed; private CodeStyleSettings.IndentOptions myJavaIndentOptions; public FormatProcessor(final FormattingDocumentModel docModel, @@ -183,7 +183,7 @@ class FormatProcessor { myJavaIndentOptions = mySettings.getIndentOptions(StdFileTypes.JAVA); } - doModify(blocksToModify, model,myIndentOption, myJavaIndentOptions); + doModify(blocksToModify, model, myIndentOption, myJavaIndentOptions); } public void setJavaIndentOptions(final CodeStyleSettings.IndentOptions javaIndentOptions) { @@ -195,14 +195,14 @@ class FormatProcessor { final int blocksToModifyCount = blocksToModify.size(); final boolean bulkReformat = blocksToModifyCount > 50; final DocumentEx updatedDocument = bulkReformat ? getAffectedDocument(model) : null; - if(updatedDocument != null) { + if (updatedDocument != null) { updatedDocument.setInBulkUpdate(true); } try { int shift = 0; for (int i = 0; i < blocksToModifyCount; ++i) { final LeafBlockWrapper block = blocksToModify.get(i); - shift = replaceWhiteSpace(model, block, shift, block.getWhiteSpace().generateWhiteSpace(indentOption),javaOptions); + shift = replaceWhiteSpace(model, block, shift, block.getWhiteSpace().generateWhiteSpace(indentOption), javaOptions); // block could be gc'd block.getParent().dispose(); @@ -222,7 +222,7 @@ class FormatProcessor { private static DocumentEx getAffectedDocument(final FormattingModel model) { if (model instanceof DocumentBasedFormattingModel) { final Document document = ((DocumentBasedFormattingModel)model).getDocument(); - if (document instanceof DocumentEx) return (DocumentEx)document; + if (document instanceof DocumentEx) return (DocumentEx)document; }/* else if (false) { // till issue with persistent range markers dropped fixed Document document = model.getDocumentModel().getDocument(); if (document instanceof DocumentEx) return (DocumentEx)document; @@ -231,11 +231,11 @@ class FormatProcessor { } private static int replaceWhiteSpace(final FormattingModel model, - @NotNull final LeafBlockWrapper block, - int shift, - final CharSequence _newWhiteSpace, - final CodeStyleSettings.IndentOptions options - ) { + @NotNull final LeafBlockWrapper block, + int shift, + final CharSequence _newWhiteSpace, + final CodeStyleSettings.IndentOptions options + ) { final WhiteSpace whiteSpace = block.getWhiteSpace(); final TextRange textRange = whiteSpace.getTextRange(); final TextRange wsRange = shiftRange(textRange, shift); @@ -298,7 +298,7 @@ class FormatProcessor { } if (!adjustIndent()) { - return; + return; } defineAlignOffset(myCurrentBlock); @@ -344,7 +344,7 @@ class FormatProcessor { final DependantSpacingImpl dependantSpaceProperty = (DependantSpacingImpl)spaceProperty; final TextRange dependency = dependantSpaceProperty.getDependency(); if (dependantSpaceProperty.wasLFUsed()) { - myPreviousDependencies.put(dependency,new Pair(myCurrentBlock, Boolean.TRUE)); + myPreviousDependencies.put(dependency, new Pair(myCurrentBlock, Boolean.TRUE)); } else { final boolean value = containsLineFeeds(dependency); @@ -454,9 +454,9 @@ class FormatProcessor { /** * Allows to answer if wrap of the {@link #myWrapCandidate} object (if any) may be replaced by the given wrap. * - * @param wrap wrap candidate to check - * @return true if wrap of the {@link #myWrapCandidate} object (if any) may be replaced by the given wrap; - * false otherwise + * @param wrap wrap candidate to check + * @return true if wrap of the {@link #myWrapCandidate} object (if any) may be replaced by the given wrap; + * false otherwise */ private boolean canReplaceWrapCandidate(WrapImpl wrap) { if (myWrapCandidate == null) return true; @@ -468,8 +468,8 @@ class FormatProcessor { private boolean isCandidateToBeWrapped(final WrapImpl wrap) { return isSuitableInTheCurrentPosition(wrap) && - (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED || wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED) && - !myCurrentBlock.getWhiteSpace().isReadOnly(); + (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED || wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED) && + !myCurrentBlock.getWhiteSpace().isReadOnly(); } private void onCurrentLineChanged() { @@ -479,9 +479,9 @@ class FormatProcessor { /** * Adjusts indent of the current block. * - * @return true if current formatting iteration should be continued; - * false otherwise (e.g. if previously processed block is shifted inside this method for example - * because of specified alignment options) + * @return true if current formatting iteration should be continued; + * false otherwise (e.g. if previously processed block is shifted inside this method for example + * because of specified alignment options) */ private boolean adjustIndent() { IndentData alignOffset = getAlignOffset(); @@ -509,7 +509,12 @@ class FormatProcessor { } if (diff > 0) { - whiteSpace.setSpaces(whiteSpace.getSpaces(), whiteSpace.getIndentSpaces() + diff); + whiteSpace.setSpaces(whiteSpace.getSpaces() + diff, whiteSpace.getIndentSpaces()); + + // Avoid tabulations usage for aligning blocks that are not the first blocks on a line. + if (!whiteSpace.containsLineFeeds()) { + whiteSpace.setForceSkipTabulationsUsage(true); + } return true; } AlignmentImpl alignment = myCurrentBlock.getAlignmentAtStartOffset(); @@ -545,8 +550,8 @@ class FormatProcessor { Set blocksCausedRealignment = myBackwardShiftedAlignedBlocks.get(offsetResponsibleBlock); if (blocksCausedRealignment != null && blocksCausedRealignment.contains(myCurrentBlock)) { LOG.error(String.format("Formatting error - code block %s is set to be shifted right because of its alignment with " - + "block %s more than once. I.e. moving the former block because of alignment algorithm causes " - + "subsequent block to be shifted right as well - cyclic dependency", + + "block %s more than once. I.e. moving the former block because of alignment algorithm causes " + + "subsequent block to be shifted right as well - cyclic dependency", offsetResponsibleBlock.getTextRange(), myCurrentBlock.getTextRange())); blocksCausedRealignment.add(myCurrentBlock); return true; @@ -556,7 +561,11 @@ class FormatProcessor { blocksCausedRealignment.add(myCurrentBlock); WhiteSpace previousWhiteSpace = offsetResponsibleBlock.getWhiteSpace(); - previousWhiteSpace.setSpaces(previousWhiteSpace.getSpaces(), previousWhiteSpace.getIndentOffset() - diff); + previousWhiteSpace.setSpaces(previousWhiteSpace.getSpaces() - diff, previousWhiteSpace.getIndentOffset()); + // Avoid tabulations usage for aligning blocks that are not the first blocks on a line. + if (!previousWhiteSpace.containsLineFeeds()) { + previousWhiteSpace.setForceSkipTabulationsUsage(true); + } myCurrentBlock = offsetResponsibleBlock.getNextBlock(); onCurrentLineChanged(); @@ -586,7 +595,7 @@ class FormatProcessor { /** * Tries to find the closest block that starts before the {@link #myCurrentBlock currently processed block} and contains line feeds. * - * @return closest block to the currently processed block that contains line feeds if any; null otherwise + * @return closest block to the currently processed block that contains line feeds if any; null otherwise */ @Nullable private AbstractBlockWrapper getPreviousIndentedBlock() { @@ -626,7 +635,7 @@ class FormatProcessor { /** * Ensures that offset of the {@link #myCurrentBlock currently processed block} is not increased if we make a wrap on it. * - * @return true if it's ok to wrap at the currently processed block; false otherwise + * @return true if it's ok to wrap at the currently processed block; false otherwise */ private boolean positionAfterWrappingIsSuitable() { final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); @@ -671,12 +680,12 @@ class FormatProcessor { } /** - * @return true if {@link #myCurrentBlock currently processed wrapped block} doesn't contain line feeds and - * exceeds right margin; false otherwise + * @return true if {@link #myCurrentBlock currently processed wrapped block} doesn't contain line feeds and + * exceeds right margin; false otherwise */ private boolean lineOver() { return !myCurrentBlock.containsLineFeeds() && - getOffsetBefore(myCurrentBlock) + myCurrentBlock.getLength() > mySettings.RIGHT_MARGIN; + getOffsetBefore(myCurrentBlock) + myCurrentBlock.getLength() > mySettings.RIGHT_MARGIN; } /** @@ -693,8 +702,8 @@ class FormatProcessor { * from 'whitespace21' after its last line feed symbol plus number of symbols at * block21 plus number of symbols at whitespace22. * - * @param info target wrapped block to be used at a boundary during counting non-line feed symbols to the left of it - * @return non-line feed symbols to the left of the given wrapped block + * @param info target wrapped block to be used at a boundary during counting non-line feed symbols to the left of it + * @return non-line feed symbols to the left of the given wrapped block */ private static int getOffsetBefore(LeafBlockWrapper info) { if (info != null) { @@ -733,7 +742,7 @@ class FormatProcessor { /** * Tries to get align-implied indent of the current block. * - * @return indent of the current block if any; null otherwise + * @return indent of the current block if any; null otherwise */ @Nullable private IndentData getAlignOffset() { @@ -829,8 +838,8 @@ class FormatProcessor { static class ChildAttributesInfo { public final AbstractBlockWrapper parent; - final ChildAttributes attributes; - final int index; + final ChildAttributes attributes; + final int index; public ChildAttributesInfo(final AbstractBlockWrapper parent, final ChildAttributes attributes, final int index) { this.parent = parent; @@ -845,11 +854,11 @@ class FormatProcessor { if (parent == null) { final LeafBlockWrapper previousBlock = myCurrentBlock.getPreviousBlock(); if (previousBlock != null) parent = getParentFor(offset, previousBlock); - if (parent == null) return new IndentInfo(0,0,0); + if (parent == null) return new IndentInfo(0, 0, 0); } int index = getNewChildPosition(parent, offset); final Block block = myInfos.get(parent); - + if (block == null) { return new IndentInfo(0, 0, 0); } @@ -970,8 +979,8 @@ class FormatProcessor { if (current.getEndOffset() <= offset) { while (!current.isIncomplete() && - current.getParent() != null && - current.getParent().getEndOffset() <= offset) { + current.getParent() != null && + current.getParent().getEndOffset() <= offset) { current = current.getParent(); } if (current.isIncomplete()) return current; @@ -1031,7 +1040,7 @@ class FormatProcessor { private static int calcShift(final IndentInside lastLineIndent, final IndentInside whiteSpaceIndent, final CodeStyleSettings.IndentOptions options - ) { + ) { if (lastLineIndent.equals(whiteSpaceIndent)) return 0; if (options.USE_TAB_CHARACTER) { if (lastLineIndent.whiteSpaces > 0) { diff --git a/platform/lang-impl/src/com/intellij/formatting/IndentInfo.java b/platform/lang-impl/src/com/intellij/formatting/IndentInfo.java index c2a279258152..070b5c981dcf 100644 --- a/platform/lang-impl/src/com/intellij/formatting/IndentInfo.java +++ b/platform/lang-impl/src/com/intellij/formatting/IndentInfo.java @@ -17,38 +17,36 @@ package com.intellij.formatting; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.CodeStyleSettings; public class IndentInfo { + private final int mySpaces; private final int myIndentSpaces; private final int myLineFeeds; - private boolean myIsChanged = true; - private TextRange myInitialTextRange; + + /** @see WhiteSpace#setForceSkipTabulationsUsage(boolean) */ + private boolean myForceSkipTabulationsUsage; public IndentInfo(final int lineFeeds, final int indentSpaces, final int spaces) { + this(lineFeeds, indentSpaces, spaces, false); + } + + public IndentInfo(final int lineFeeds, final int indentSpaces, final int spaces, final boolean forceSkipTabulationsUsage) { mySpaces = spaces; myIndentSpaces = indentSpaces; myLineFeeds = lineFeeds; + myForceSkipTabulationsUsage = forceSkipTabulationsUsage; } public int getSpaces() { return mySpaces; } - public void setInitialTextRange(final TextRange initialTextRange) { - myInitialTextRange = initialTextRange; - } - public int getIndentSpaces() { return myIndentSpaces; } - public int getLineFeeds() { - return myLineFeeds; - } - /** * Builds string that contains line feeds, white spaces and tabulation symbols known to the current {@link IndentInfo} object. * @@ -58,7 +56,7 @@ public class IndentInfo { StringBuffer buffer = new StringBuffer(); StringUtil.repeatSymbol(buffer, '\n', myLineFeeds); - if (options.USE_TAB_CHARACTER) { + if (options.USE_TAB_CHARACTER && !myForceSkipTabulationsUsage) { if (options.SMART_TABS) { int tabCount = myIndentSpaces / options.TAB_SIZE; int leftSpaces = myIndentSpaces - tabCount * options.TAB_SIZE; @@ -90,26 +88,4 @@ public class IndentInfo { public int getTotalSpaces() { return myIndentSpaces + mySpaces; } - - public int getIndentCount(final CodeStyleSettings.IndentOptions indentOptions) { - return myIndentSpaces/indentOptions.INDENT_SIZE; - } - - public int getSpacesCount(final CodeStyleSettings.IndentOptions indentOptions) { - final int indentSpaces = getIndentCount(indentOptions); - return myIndentSpaces - indentSpaces * indentOptions.INDENT_SIZE + mySpaces; - - } - - public boolean isChanged() { - return myIsChanged; - } - - public void setIsChanged(final boolean value) { - myIsChanged = value; - } - - public TextRange getInitialTextRange() { - return myInitialTextRange; - } } diff --git a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java index d9b5eeb20cc3..274fbb049a28 100644 --- a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java +++ b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java @@ -48,13 +48,14 @@ class WhiteSpace { private static final char LINE_FEED = '\n'; private final int myStart; - private int myEnd; + private int myEnd; private int mySpaces; private int myIndentSpaces; private CharSequence myInitial; - private int myFlags; + private int myFlags; + private boolean myForceSkipTabulationsUsage; private static final byte FIRST = 1; private static final byte SAFE = 0x2; @@ -238,7 +239,7 @@ class WhiteSpace { * {@link WhiteSpace} object */ public String generateWhiteSpace(CodeStyleSettings.IndentOptions options) { - return new IndentInfo(getLineFeeds(), myIndentSpaces, mySpaces).generateNewWhiteSpace(options); + return new IndentInfo(getLineFeeds(), myIndentSpaces, mySpaces, myForceSkipTabulationsUsage).generateNewWhiteSpace(options); } /** @@ -401,6 +402,28 @@ class WhiteSpace { } + /** + * There is a possible case that particular indent info is applied to the code block that is not the first block on a line. + * E.g. we may want to align field name during 'align fields in columns' processing: + *
+   *         public class Test {
+   *             private Object o;
+   *             private int {@code }i;
+   *         }
+   *     
+ * We may not want to use tabulation characters then even if user configured + * {@link CodeStyleSettings.IndentOptions#USE_TAB_CHARACTER their usage} because we can't be sure how many visual columns + * will be used for tab representation if there are non-white space symbols before it (IJ editor may use different number of columns + * for single tabulation symbol representation). + *

+ * Hence, we can ask current white space object to avoid using tabulation symbols. + * + * @param skip indicates if tabulations symbols usage should be suppressed + */ + public void setForceSkipTabulationsUsage(boolean skip) { + myForceSkipTabulationsUsage = skip; + } + /** * Allows to get information if target text document continuous 'white space' represented by the current object contained line feed * symbol(s) initially or contains line feed(s) now. diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 5254cb771e7a..e8b5e729f8a7 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -209,7 +209,7 @@ checkbox.align.multiline.method.parameters=Method parameters checkbox.align.multiline.call.arguments=Call arguments checkbox.align.multiline.extends.list=Extends list checkbox.align.multiline.throws.list=Throws list -checkbox.align.multiline.fields.variables.groups=Fields/variables groups +checkbox.align.multiline.fields.groups=Field groups checkbox.align.multiline.parenthesized.expression=Parenthesized expression checkbox.align.multiline.binary.operation=Binary operation checkbox.align.multiline.ternary.operation=Ternary operation