mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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;
This commit is contained in:
@@ -49,10 +49,10 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
/**
|
||||
* Holds types of the elements for which <code>'align in column'</code> rule may be preserved.
|
||||
*
|
||||
* @see CodeStyleSettings#ALIGN_GROUP_FIELDS_VARIABLES
|
||||
* @see CodeStyleSettings#ALIGN_GROUP_FIELD_DECLARATIONS
|
||||
*/
|
||||
protected static final Set<IElementType> ALIGN_IN_COLUMNS_ELEMENT_TYPES = Collections.unmodifiableSet(new HashSet<IElementType>(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 <code>'align in column'</code> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class FormattingTest {
|
||||
|
||||
private static FormattingTest staticFoo;
|
||||
|
||||
private FormattingTest instanceFoo;
|
||||
|
||||
@SuppressWarnings({"AccessStaticViaInstance"})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class FormattingTest {
|
||||
|
||||
private static FormattingTest staticFoo;
|
||||
|
||||
private FormattingTest instanceFoo;
|
||||
|
||||
@SuppressWarnings({"AccessStaticViaInstance"})
|
||||
|
||||
+22
-20
@@ -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" +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 --------------------
|
||||
|
||||
@@ -36,12 +36,12 @@ class FormatProcessor {
|
||||
|
||||
private LeafBlockWrapper myCurrentBlock;
|
||||
|
||||
private Map<AbstractBlockWrapper, Block> myInfos;
|
||||
private CompositeBlockWrapper myRootBlockWrapper;
|
||||
private Map<AbstractBlockWrapper, Block> myInfos;
|
||||
private CompositeBlockWrapper myRootBlockWrapper;
|
||||
private TIntObjectHashMap<LeafBlockWrapper> 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<LeafBlockWrapper, Set<LeafBlockWrapper>> myBackwardShiftedAlignedBlocks
|
||||
= new HashMap<LeafBlockWrapper, Set<LeafBlockWrapper>>();
|
||||
|
||||
private LeafBlockWrapper myWrapCandidate = null;
|
||||
private LeafBlockWrapper myWrapCandidate = null;
|
||||
private LeafBlockWrapper myFirstWrappedBlockOnLine = null;
|
||||
|
||||
private LeafBlockWrapper myFirstTokenBlock;
|
||||
private LeafBlockWrapper myLastTokenBlock;
|
||||
|
||||
private SortedMap<TextRange,Pair<AbstractBlockWrapper, Boolean>> myPreviousDependencies =
|
||||
private SortedMap<TextRange, Pair<AbstractBlockWrapper, Boolean>> myPreviousDependencies =
|
||||
new TreeMap<TextRange, Pair<AbstractBlockWrapper, Boolean>>(new Comparator<TextRange>() {
|
||||
public int compare(final TextRange o1, final TextRange o2) {
|
||||
int offsetsDelta = o1.getEndOffset() - o2.getEndOffset();
|
||||
@@ -81,8 +81,8 @@ class FormatProcessor {
|
||||
});
|
||||
|
||||
private final HashSet<WhiteSpace> myAlignAgain = new HashSet<WhiteSpace>();
|
||||
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<AbstractBlockWrapper, Boolean>(myCurrentBlock, Boolean.TRUE));
|
||||
myPreviousDependencies.put(dependency, new Pair<AbstractBlockWrapper, Boolean>(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 <code>true</code> if wrap of the {@link #myWrapCandidate} object (if any) may be replaced by the given wrap;
|
||||
* <code>false</code> otherwise
|
||||
* @param wrap wrap candidate to check
|
||||
* @return <code>true</code> if wrap of the {@link #myWrapCandidate} object (if any) may be replaced by the given wrap;
|
||||
* <code>false</code> 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 <code>true</code> if current formatting iteration should be continued;
|
||||
* <code>false</code> otherwise (e.g. if previously processed block is shifted inside this method for example
|
||||
* because of specified alignment options)
|
||||
* @return <code>true</code> if current formatting iteration should be continued;
|
||||
* <code>false</code> 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<LeafBlockWrapper> 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; <code>null</code> otherwise
|
||||
* @return closest block to the currently processed block that contains line feeds if any; <code>null</code> 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 <code>true</code> if it's ok to wrap at the currently processed block; <code>false</code> otherwise
|
||||
* @return <code>true</code> if it's ok to wrap at the currently processed block; <code>false</code> otherwise
|
||||
*/
|
||||
private boolean positionAfterWrappingIsSuitable() {
|
||||
final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace();
|
||||
@@ -671,12 +680,12 @@ class FormatProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if {@link #myCurrentBlock currently processed wrapped block} doesn't contain line feeds and
|
||||
* exceeds right margin; <code>false</code> otherwise
|
||||
* @return <code>true</code> if {@link #myCurrentBlock currently processed wrapped block} doesn't contain line feeds and
|
||||
* exceeds right margin; <code>false</code> 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 <code>'whitespace<sub>21</sub>'</code> after its last line feed symbol plus number of symbols at
|
||||
* <code>block<sub>21</sub></code> plus number of symbols at <code>whitespace<sub>22</sub></code>.
|
||||
*
|
||||
* @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; <code>null</code> otherwise
|
||||
* @return indent of the current block if any; <code>null</code> 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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <code>'align fields in columns'</code> processing:
|
||||
* <pre>
|
||||
* public class Test {
|
||||
* private Object o;
|
||||
* private int {@code <white space to align>}i;
|
||||
* }
|
||||
* </pre>
|
||||
* 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).
|
||||
* <p/>
|
||||
* 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user