From debb180ea497b2de992979779f6a777ca1a563bd Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Mon, 25 Jun 2012 16:59:40 +0400 Subject: [PATCH] IDEA-87315 The freemarker code style should respect the XML identation setting instead of forcing 4 space identation Block language-specific indent settings are used now whenever possible by the formatter core --- .../formatting/AbstractBlockWrapper.java | 58 +++++++++++++++--- .../intellij/formatting/FormatProcessor.java | 61 +++++++++++++------ .../formatting/InitialInfoBuilder.java | 23 +++---- 3 files changed, 102 insertions(+), 40 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java index 77908a96df12..7a5d366fada7 100644 --- a/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java @@ -16,8 +16,12 @@ package com.intellij.formatting; +import com.intellij.lang.ASTNode; +import com.intellij.lang.Language; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -36,31 +40,53 @@ public abstract class AbstractBlockWrapper { Indent.Type.NORMAL, Indent.Type.CONTINUATION, Indent.Type.CONTINUATION_WITHOUT_FIRST )); - protected WhiteSpace myWhiteSpace; + protected WhiteSpace myWhiteSpace; protected CompositeBlockWrapper myParent; - protected int myStart; - protected int myEnd; - protected int myFlags; + protected int myStart; + protected int myEnd; + protected int myFlags; static int CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT = 1; - static int INCOMPLETE = 2; + static int INCOMPLETE = 2; + + private final Language myLanguage; protected IndentInfo myIndentFromParent = null; - private IndentImpl myIndent = null; + private IndentImpl myIndent = null; private AlignmentImpl myAlignment; - private WrapImpl myWrap; + private WrapImpl myWrap; - public AbstractBlockWrapper(final Block block, final WhiteSpace whiteSpace, final CompositeBlockWrapper parent, final TextRange textRange) { + public AbstractBlockWrapper(final Block block, + final WhiteSpace whiteSpace, + final CompositeBlockWrapper parent, + final TextRange textRange) { myWhiteSpace = whiteSpace; myParent = parent; myStart = textRange.getStartOffset(); myEnd = textRange.getEndOffset(); - myFlags = CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT | (block.isIncomplete() ? INCOMPLETE:0); + myFlags = CAN_USE_FIRST_CHILD_INDENT_AS_BLOCK_INDENT | (block.isIncomplete() ? INCOMPLETE : 0); myAlignment = (AlignmentImpl)block.getAlignment(); myWrap = (WrapImpl)block.getWrap(); + myLanguage = deriveLanguage(block); } + @Nullable + private static Language deriveLanguage(@NotNull Block block) { + if (block instanceof ASTBlock) { + final ASTNode node = ((ASTBlock)block).getNode(); + if (node == null) { + return null; + } + final PsiElement psi = node.getPsi(); + if (psi == null) { + return null; + } + return psi.getLanguage(); + } + return null; + } + public WhiteSpace getWhiteSpace() { return myWhiteSpace; } @@ -89,6 +115,20 @@ public abstract class AbstractBlockWrapper { return myEnd - myStart; } + /** + * There is a possible case that particular block's language differs from the language implied by the file type. We need to + * distinguish such a situation because, for example in case of indent calculation (code style settings for different languages + * may have different indent values). + *

+ * This method allows to retrieve the language associated with the current block (if provided). + * + * @return current block's language (if provided) + */ + @Nullable + public Language getLanguage() { + return myLanguage; + } + /** * Applies given start offset to the current block wrapper and recursively calls this method on parent block wrapper * if it starts at the same place as the current one. diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index baf5f788838c..6216c10580d0 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -16,6 +16,7 @@ package com.intellij.formatting; +import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.TextChange; @@ -42,7 +43,7 @@ class FormatProcessor { ALIGNMENT_PROCESSORS.put(Alignment.Anchor.LEFT, new LeftEdgeAlignmentProcessor()); ALIGNMENT_PROCESSORS.put(Alignment.Anchor.RIGHT, new RightEdgeAlignmentProcessor()); } - + /** * There is a possible case that formatting introduced big number of changes to the underlying document. That number may be * big enough for that their subsequent appliance is much slower than direct replacing of the whole document text. @@ -50,7 +51,7 @@ class FormatProcessor { * Current constant holds minimum number of changes that should trigger such 'replace whole text' optimization. */ private static final int BULK_REPLACE_OPTIMIZATION_CRITERIA = 3000; - + private static final Logger LOG = Logger.getInstance("#com.intellij.formatting.FormatProcessor"); private LeafBlockWrapper myCurrentBlock; @@ -59,9 +60,9 @@ class FormatProcessor { private CompositeBlockWrapper myRootBlockWrapper; private TIntObjectHashMap myTextRangeToWrapper; - private final CommonCodeStyleSettings.IndentOptions myIndentOption; - private final CodeStyleSettings mySettings; - private final Document myDocument; + private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; + private final CodeStyleSettings mySettings; + private final Document myDocument; /** * Remembers mappings between backward-shifted aligned block and blocks that cause that shift in order to detect @@ -82,7 +83,7 @@ class FormatProcessor { private final Map> myBackwardShiftedAlignedBlocks = new HashMap>(); - private final Map> myAlignmentMappings + private final Map> myAlignmentMappings = new HashMap>(); /** @@ -167,7 +168,7 @@ class FormatProcessor { @NotNull FormattingProgressCallback progressCallback) { myProgressCallback = progressCallback; - myIndentOption = indentOptions; + myDefaultIndentOption = indentOptions; mySettings = settings; myDocument = docModel.getDocument(); myCurrentState = new WrapBlocksState(rootBlock, docModel, affectedRanges, interestingOffset); @@ -320,8 +321,8 @@ class FormatProcessor { * false otherwise */ @SuppressWarnings({"deprecation"}) - private static boolean applyChangesAtBulkMode(final List blocksToModify, final FormattingModel model, - CommonCodeStyleSettings.IndentOptions indentOption) + private boolean applyChangesAtBulkMode(final List blocksToModify, final FormattingModel model, + @NotNull CommonCodeStyleSettings.IndentOptions indentOption) { FormattingDocumentModel documentModel = model.getDocumentModel(); Document document = documentModel.getDocument(); @@ -335,7 +336,8 @@ class FormatProcessor { for (LeafBlockWrapper block : blocksToModify) { WhiteSpace whiteSpace = block.getWhiteSpace(); CharSequence newWs = documentModel.adjustWhiteSpaceIfNecessary( - whiteSpace.generateWhiteSpace(indentOption), whiteSpace.getStartOffset(), whiteSpace.getEndOffset(), false + whiteSpace.generateWhiteSpace(getIndentOptionsToUse(block, indentOption)), whiteSpace.getStartOffset(), + whiteSpace.getEndOffset(), false ); if (changes.size() > 10000) { CharSequence mergeResult = BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); @@ -408,7 +410,7 @@ class FormatProcessor { for (LeafBlockWrapper block = myFirstTokenBlock; block != null; block = block.getNextBlock()) { final WhiteSpace whiteSpace = block.getWhiteSpace(); if (!whiteSpace.isReadOnly()) { - final String newWhiteSpace = whiteSpace.generateWhiteSpace(myIndentOption); + final String newWhiteSpace = whiteSpace.generateWhiteSpace(getIndentOptionsToUse(block, myDefaultIndentOption)); if (!whiteSpace.equalsToString(newWhiteSpace)) { blocksToModify.add(block); } @@ -417,6 +419,22 @@ class FormatProcessor { return blocksToModify; } + @NotNull + private CommonCodeStyleSettings.IndentOptions getIndentOptionsToUse(@NotNull AbstractBlockWrapper block, + @NotNull CommonCodeStyleSettings.IndentOptions fallbackIndentOptions) + { + final Language language = block.getLanguage(); + if (language == null) { + return fallbackIndentOptions; + } + final CommonCodeStyleSettings commonSettings = mySettings.getCommonSettings(language); + if (commonSettings == null) { + return fallbackIndentOptions; + } + final CommonCodeStyleSettings.IndentOptions result = commonSettings.getIndentOptions(); + return result == null ? fallbackIndentOptions : result; + } + private static TextRange shiftRange(final TextRange textRange, final int shift) { return new TextRange(textRange.getStartOffset() + shift, textRange.getEndOffset() + shift); } @@ -629,7 +647,8 @@ class FormatProcessor { } BlockAlignmentProcessor.Context context = new BlockAlignmentProcessor.Context( - myDocument, alignment, myCurrentBlock, myAlignmentMappings, myBackwardShiftedAlignedBlocks, myIndentOption + myDocument, alignment, myCurrentBlock, myAlignmentMappings, myBackwardShiftedAlignedBlocks, + getIndentOptionsToUse(myCurrentBlock, myDefaultIndentOption) ); BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock); @@ -704,7 +723,7 @@ class FormatProcessor { } private void adjustSpacingByIndentOffset() { - IndentData offset = myCurrentBlock.calculateOffset(myIndentOption); + IndentData offset = myCurrentBlock.calculateOffset(getIndentOptionsToUse(myCurrentBlock, myDefaultIndentOption)); myCurrentBlock.getWhiteSpace().setSpaces(offset.getSpaces(), offset.getIndentSpaces()); } @@ -972,7 +991,7 @@ class FormatProcessor { private IndentInfo adjustLineIndent(final AbstractBlockWrapper parent, final ChildAttributes childAttributes, final int index) { int alignOffset = getAlignOffsetBefore(childAttributes.getAlignment(), null); if (alignOffset == -1) { - return parent.calculateChildOffset(myIndentOption, childAttributes, index).createIndentInfo(); + return parent.calculateChildOffset(getIndentOptionsToUse(parent, myDefaultIndentOption), childAttributes, index).createIndentInfo(); } else { AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(myCurrentBlock); @@ -1247,7 +1266,7 @@ class FormatProcessor { super(FormattingStateId.WRAPPING_BLOCKS); myModel = model; myWrapper = InitialInfoBuilder.prepareToBuildBlocksSequentially( - root, model, affectedRanges, myIndentOption, interestingOffset, myProgressCallback + root, model, affectedRanges, myDefaultIndentOption, interestingOffset, myProgressCallback ); } @@ -1273,7 +1292,7 @@ class FormatProcessor { myCurrentBlock = myFirstTokenBlock; myTextRangeToWrapper = buildTextRangeToInfoMap(myFirstTokenBlock); myLastWhiteSpace = new WhiteSpace(getLastBlock().getEndOffset(), false); - myLastWhiteSpace.append(myModel.getTextLength(), myModel, myIndentOption); + myLastWhiteSpace.append(myModel.getTextLength(), myModel, myDefaultIndentOption); } } @@ -1358,7 +1377,9 @@ class FormatProcessor { updatedDocument.setInBulkUpdate(true); myResetBulkUpdateState = true; } - if (blocksToModifyCount > BULK_REPLACE_OPTIMIZATION_CRITERIA && applyChangesAtBulkMode(myBlocksToModify, myModel, myIndentOption)) { + if (blocksToModifyCount > BULK_REPLACE_OPTIMIZATION_CRITERIA + && applyChangesAtBulkMode(myBlocksToModify, myModel, myDefaultIndentOption)) + { setDone(true); } } @@ -1367,7 +1388,11 @@ class FormatProcessor { protected void doIteration() { LeafBlockWrapper blockWrapper = myBlocksToModify.get(myIndex); myShift = replaceWhiteSpace( - myModel, blockWrapper, myShift, blockWrapper.getWhiteSpace().generateWhiteSpace(myIndentOption), myJavaIndentOptions + myModel, + blockWrapper, + myShift, + blockWrapper.getWhiteSpace().generateWhiteSpace(getIndentOptionsToUse(blockWrapper, myDefaultIndentOption)), + myJavaIndentOptions ); myProgressCallback.afterApplyingChange(blockWrapper); // block could be gc'd diff --git a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java index 1ee76f43d19a..71855056673c 100644 --- a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java +++ b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java @@ -185,11 +185,10 @@ class InitialInfoBuilder { } private CompositeBlockWrapper buildCompositeBlock(final Block rootBlock, - final CompositeBlockWrapper parent, - final int index, - final WrapImpl currentWrapParent, - boolean rootBlockIsRightBlock) - { + final CompositeBlockWrapper parent, + final int index, + final WrapImpl currentWrapParent, + boolean rootBlockIsRightBlock) { final CompositeBlockWrapper wrappedRootBlock = new CompositeBlockWrapper(rootBlock, myCurrentWhiteSpace, parent); if (index == 0) { wrappedRootBlock.arrangeParentTextRange(); @@ -206,7 +205,7 @@ class InitialInfoBuilder { blocksMayBeOfInterest = true; } final boolean blocksAreReadOnly = rootBlock instanceof ReadOnlyBlockContainer || blocksMayBeOfInterest; - + State state = new State(rootBlock, wrappedRootBlock, currentWrapParent, blocksAreReadOnly, rootBlockIsRightBlock); myStates.push(state); return wrappedRootBlock; @@ -237,14 +236,14 @@ class InitialInfoBuilder { if (!state.readOnly && !(subBlocks instanceof Unmodifiable) && !(subBlocks instanceof ImmutableCollection)) { subBlocks.set(childBlockIndex, null); // to prevent extra strong refs during model building } - + if (state.childBlockProcessed(block, wrapper)) { while (!myStates.isEmpty() && myStates.peek().isProcessed()) { myStates.pop(); } } } - + private void setDefaultIndents(final List list) { if (!list.isEmpty()) { for (AbstractBlockWrapper wrapper : list) { @@ -259,8 +258,7 @@ class InitialInfoBuilder { final CompositeBlockWrapper parent, final boolean readOnly, final int index, - Block parentBlock) - { + Block parentBlock) { LeafBlockWrapper result = doProcessSimpleBlock(rootBlock, parent, readOnly, index, parentBlock); myProgressCallback.afterWrappingBlock(result); return result; @@ -270,8 +268,7 @@ class InitialInfoBuilder { final CompositeBlockWrapper parent, final boolean readOnly, final int index, - Block parentBlock) - { + Block parentBlock) { if (!INLINE_TABS_ENABLED && !myCurrentWhiteSpace.containsLineFeeds()) { myCurrentWhiteSpace.setForceSkipTabulationsUsage(true); } @@ -363,7 +360,7 @@ class InitialInfoBuilder { buffer.append("Affected text fragment:[").append(minOffset).append(",").append(maxOffset).append("] - '") .append(model.getText(new TextRange(minOffset, maxOffset))).append("'\n"); - final StringBuilder messageBuffer = new StringBuilder(); + final StringBuilder messageBuffer = new StringBuilder(); messageBuffer.append("Invalid ranges during formatting"); if (model instanceof FormattingDocumentModelImpl) { messageBuffer.append(" in ").append(((FormattingDocumentModelImpl)model).getFile().getLanguage());