[formatter-core] minor refactoring

This commit is contained in:
Yaroslav Lepenkin
2016-06-22 17:46:46 +03:00
parent 49af7c5c2d
commit 3226369d60
6 changed files with 157 additions and 212 deletions
@@ -16,7 +16,6 @@
package com.intellij.formatting;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -26,22 +25,10 @@ public class CompositeBlockWrapper extends AbstractBlockWrapper{
private List<AbstractBlockWrapper> myChildren;
private ProbablyIncreasingLowerboundAlgorithm<AbstractBlockWrapper> myPrevBlockCalculator = null;
/**
* Shortcut for calling {@link #CompositeBlockWrapper(Block, WhiteSpace, CompositeBlockWrapper, TextRange)} with
* {@link Block#getTextRange() text range associated with the given block}.
*
* @param block block to wrap
* @param whiteSpaceBefore white space before the block
* @param parent wrapped block parent
*/
public CompositeBlockWrapper(final Block block, final WhiteSpace whiteSpaceBefore, @Nullable final CompositeBlockWrapper parent) {
super(block, whiteSpaceBefore, parent, block.getTextRange());
}
public CompositeBlockWrapper(final Block block, final WhiteSpace whiteSpaceBefore, final CompositeBlockWrapper parent, TextRange textRange) {
super(block, whiteSpaceBefore, parent, textRange);
}
public List<AbstractBlockWrapper> getChildren() {
return myChildren;
}
@@ -135,14 +122,4 @@ public class CompositeBlockWrapper extends AbstractBlockWrapper{
}
return null;
}
/*
@Override
public String toString() {
StringBuilder result = new StringBuilder();
for (AbstractBlockWrapper child : myChildren) {
result.append(child.getWhiteSpace().generateWhiteSpace(DEF_OPTIONS)).append(child.toString());
}
return result.toString();
} */
}
@@ -0,0 +1,79 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.formatting;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.Nullable;
public class FormatTextRange {
private TextRange myRange;
private final boolean myProcessHeadingWhitespace;
public FormatTextRange(TextRange range, boolean processHeadingWhitespace) {
myRange = range;
myProcessHeadingWhitespace = processHeadingWhitespace;
}
public boolean isProcessHeadingWhitespace() {
return myProcessHeadingWhitespace;
}
public boolean isWhitespaceReadOnly(@Nullable TextRange range) {
if (myRange == null) {
return false;
}
if (range == null || range.getStartOffset() >= myRange.getEndOffset()) return true;
if (myProcessHeadingWhitespace) {
return range.getEndOffset() < myRange.getStartOffset();
}
else {
return range.getEndOffset() <= myRange.getStartOffset();
}
}
public int getStartOffset() {
return myRange.getStartOffset();
}
public boolean isReadOnly(TextRange range, boolean rootIsRightBlock) {
if (myRange == null || myRange.getStartOffset() >= range.getEndOffset() && rootIsRightBlock) {
return false;
}
return range.getStartOffset() > myRange.getEndOffset() || range.getEndOffset() < myRange.getStartOffset();
}
public TextRange getTextRange() {
return myRange;
}
public void setTextRange(TextRange range) {
myRange = range;
}
public TextRange getNonEmptyTextRange() {
int endOffset = myRange.getStartOffset() == myRange.getEndOffset()
? myRange.getEndOffset() + 1
: myRange.getEndOffset();
return new TextRange(myRange.getStartOffset(), endOffset);
}
@Override
public String toString() {
return myRange.toString() + (myProcessHeadingWhitespace ? "+" : "");
}
}
@@ -17,110 +17,13 @@ package com.intellij.formatting;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Collection of {@link FormatTextRange} objects with utility methods for batch processing on aggregated objects.
*
* @see FormatTextRange
* @author yole
*/
public class FormatTextRanges {
/**
* Wraps {@link TextRange} object with {@link #myProcessHeadingWhitespace} flag and provides convenient services for checking if wrapped range intersects
* with the given one.
*/
public static class FormatTextRange {
private TextRange myRange;
private final boolean myProcessHeadingWhitespace;
public FormatTextRange(TextRange range, boolean processHeadingWhitespace) {
myRange = range;
myProcessHeadingWhitespace = processHeadingWhitespace;
}
/**
* Allows to answer if given range has intersections with the range wrapped by the current {@link FormatTextRange} object.
* <p/>
* I.e. this method returns <code>true</code> (no intersections) if any of the statements below is true:
* <ul>
* <li>given range starts after wrapped range;</li>
* <li>
* given range ends before wrapped range start (given range ends before or at the wrapped range start if
* <code>'processHeadingWhitespace'</code> flag is set to <code>false</code>);
* </li>
* </ul>
*
* @param range range to check
* @return <code>true</code> if given range has no intersections with the wrapped range; <code>false</code> otherwise
*/
public boolean isWhitespaceReadOnly(@Nullable TextRange range) {
if (myRange == null) {
return false;
}
if (range == null || range.getStartOffset() >= myRange.getEndOffset()) return true;
if (myProcessHeadingWhitespace) {
return range.getEndOffset() < myRange.getStartOffset();
}
else {
return range.getEndOffset() <= myRange.getStartOffset();
}
}
public int getStartOffset() {
return myRange.getStartOffset();
}
/**
* Allows to check if given range has intersections with the range wrapped by the current {@link FormatTextRange} object.
* <p/>
* I.e. this method returns <code>true</code> (no intersections) if and only if any of conditions below is satisfied:
* <ul>
* <li>given range starts after end of the wrapped range;</li>
* <li>
* given range ends before start of the wrapped range (there is a special case when given <code>'rootIsRightBlock'</code> flag
* is <code>true</code> - <code>false</code> is returned if given range ends before or at start of the wrapped range);
* </li>
* </ul>
*
* @param range range to check
* @param rootIsRightBlock meta-information about given range that is used during final answer calculation
* @return <code>true</code> if there are no intersections between given and wrapped ranges;
* <code>false</code> otherwise
*/
public boolean isReadOnly(TextRange range, boolean rootIsRightBlock) {
if (myRange == null || myRange.getStartOffset() >= range.getEndOffset() && rootIsRightBlock) {
return false;
}
return range.getStartOffset() > myRange.getEndOffset() || range.getEndOffset() < myRange.getStartOffset();
}
public TextRange getTextRange() {
return myRange;
}
public void setTextRange(TextRange range) {
myRange = range;
}
public TextRange getNonEmptyTextRange() {
return new TextRange(myRange.getStartOffset(), myRange.getStartOffset() == myRange.getEndOffset()
? myRange.getEndOffset()+1
: myRange.getEndOffset());
}
@Override
public String toString() {
return myRange.toString() + (myProcessHeadingWhitespace ? "+" : "");
}
}
private final List<FormatTextRange> myRanges = new ArrayList<FormatTextRange>();
private final List<FormatTextRange> myRanges = new ArrayList<>();
public FormatTextRanges() {
}
@@ -177,7 +80,7 @@ public class FormatTextRanges {
public FormatTextRanges ensureNonEmpty() {
FormatTextRanges result = new FormatTextRanges();
for (FormatTextRange range : myRanges) {
if (range.myProcessHeadingWhitespace) {
if (range.isProcessHeadingWhitespace()) {
result.add(range.getNonEmptyTextRange(), true);
}
else {
@@ -191,4 +94,4 @@ public class FormatTextRanges {
public String toString() {
return "FormatTextRanges{" + StringUtil.join(myRanges, StringUtil.createToStringFunction(FormatTextRange.class), ",");
}
}
}
@@ -36,17 +36,8 @@ import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.concurrent.ConcurrentMap;
/**
* Formatting progressable task.
*
* @author Denis Zhdanov
* @since 2/10/11 3:00 PM
*/
public class FormattingProgressTask extends SequentialModalProgressTask implements FormattingProgressCallback {
/**
* Holds flag that indicates whether formatting was cancelled by end-user or not.
*/
public static final ThreadLocal<Boolean> FORMATTING_CANCELLED_FLAG = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
@@ -54,9 +45,6 @@ public class FormattingProgressTask extends SequentialModalProgressTask implemen
}
};
/**
* Holds max allowed progress bar value (defined at ProgressWindow.MyDialog.initDialog()).
*/
private static final double MAX_PROGRESS_VALUE = 1;
private static final double TOTAL_WEIGHT;
@@ -178,12 +166,6 @@ public class FormattingProgressTask extends SequentialModalProgressTask implemen
update(FormattingStateId.APPLYING_CHANGES, MAX_PROGRESS_VALUE * myModifiedBlocksNumber / myBlocksToModifyNumber);
}
/**
* Updates current progress state if necessary.
*
* @param state current state
* @param completionRate completion rate of the given state. Is assumed to belong to <code>[0; 1]</code> interval
*/
private void update(@NotNull FormattingStateId state, double completionRate) {
ProgressIndicator indicator = getIndicator();
if (indicator == null) {
@@ -199,7 +181,6 @@ public class FormattingProgressTask extends SequentialModalProgressTask implemen
}
newFraction += completionRate * state.getProgressWeight() / TOTAL_WEIGHT;
// We don't bother about imprecise floating point arithmetic here because that is enough for progress representation.
double currentFraction = indicator.getFraction();
if (newFraction - currentFraction < MAX_PROGRESS_VALUE / 100) {
return;
@@ -40,15 +40,24 @@ import java.util.Set;
* The main idea of block wrapping is to associate information about {@link WhiteSpace white space before block} with the block itself.
*/
public class InitialInfoBuilder {
private static final RangesAssert ASSERT = new RangesAssert();
private static final boolean INLINE_TABS_ENABLED = "true".equalsIgnoreCase(System.getProperty("inline.tabs.enabled"));
private final Map<AbstractBlockWrapper, Block> myResult = new THashMap<>();
private MultiMap<ExpandableIndent, AbstractBlockWrapper> myBlocksToForceChildrenIndent = new LinkedMultiMap<>();
private MultiMap<Alignment, Block> myBlocksToAlign = new MultiMap<>();
private Set<Alignment> myAlignmentsInsideRangeToModify = ContainerUtil.newHashSet();
private boolean myCollectAlignmentsInsideFormattingRange = false;
private final FormattingDocumentModel myModel;
private final FormatTextRanges myAffectedRanges;
private final int myPositionOfInterest;
@NotNull
private final FormattingProgressCallback myProgressCallback;
private final FormatterTagHandler myFormatterTagHandler;
private final FormattingDocumentModel myModel;
private final FormatTextRanges myAffectedRanges;
private final List<TextRange> myExtendedAffectedRanges;
private final int myPositionOfInterest;
private final FormattingProgressCallback myProgressCallback;
private final FormatterTagHandler myFormatterTagHandler;
private final CommonCodeStyleSettings.IndentOptions myOptions;
@@ -63,17 +72,6 @@ public class InitialInfoBuilder {
private ReadOnlyBlockInformationProvider myReadOnlyBlockInformationProvider;
private boolean myInsideFormatRestrictingTag;
private static final boolean INLINE_TABS_ENABLED = "true".equalsIgnoreCase(System.getProperty("inline.tabs.enabled"));
private final List<TextRange> myExtendedAffectedRanges;
private Set<Alignment> myAlignmentsInsideRangeToModify = ContainerUtil.newHashSet();
private boolean myCollectAlignmentsInsideFormattingRange = false;
private static final RangesAssert myRangesAssert = new RangesAssert();
private MultiMap<ExpandableIndent, AbstractBlockWrapper> myBlocksToForceChildrenIndent = new LinkedMultiMap<>();
private MultiMap<Alignment, Block> myBlocksToAlign = new MultiMap<>();
private InitialInfoBuilder(final Block rootBlock,
final FormattingDocumentModel model,
@Nullable final FormatTextRanges affectedRanges,
@@ -110,7 +108,7 @@ public class InitialInfoBuilder {
private int getStartOffset(@NotNull Block rootBlock) {
int minOffset = rootBlock.getTextRange().getStartOffset();
if (myAffectedRanges != null) {
for (FormatTextRanges.FormatTextRange range : myAffectedRanges.getRanges()) {
for (FormatTextRange range : myAffectedRanges.getRanges()) {
if (range.getStartOffset() < minOffset) minOffset = range.getStartOffset();
}
}
@@ -125,7 +123,7 @@ public class InitialInfoBuilder {
int maxDocOffset = myModel.getTextLength();
int maxOffset = myRootBlockWrapper != null ? myRootBlockWrapper.getEndOffset() : 0;
if (myAffectedRanges != null) {
for (FormatTextRanges.FormatTextRange range : myAffectedRanges.getRanges()) {
for (FormatTextRange range : myAffectedRanges.getRanges()) {
if (range.getTextRange().getEndOffset() > maxOffset) maxOffset = range.getTextRange().getEndOffset();
}
}
@@ -164,16 +162,8 @@ public class InitialInfoBuilder {
myCurrentWhiteSpace.append(blockStartOffset, myModel, myOptions);
if (myCollectAlignmentsInsideFormattingRange && rootBlock.getAlignment() != null
&& isAffectedByFormatting(rootBlock) && !myInsideFormatRestrictingTag)
{
myAlignmentsInsideRangeToModify.add(rootBlock.getAlignment());
}
if (rootBlock.getAlignment() != null) {
myBlocksToAlign.putValue(rootBlock.getAlignment(), rootBlock);
}
collectAlignments(rootBlock);
ReadOnlyBlockInformationProvider previousProvider = myReadOnlyBlockInformationProvider;
try {
if (rootBlock instanceof ReadOnlyBlockInformationProvider) {
@@ -185,7 +175,7 @@ public class InitialInfoBuilder {
{
final List<Block> subBlocks = rootBlock.getSubBlocks();
if (subBlocks.isEmpty() || myReadOnlyBlockInformationProvider != null && myReadOnlyBlockInformationProvider.isReadOnly(rootBlock)) {
final AbstractBlockWrapper wrapper = processSimpleBlock(rootBlock, parent, false, index, parentBlock);
final AbstractBlockWrapper wrapper = buildLeafBlock(rootBlock, parent, false, index, parentBlock);
if (!subBlocks.isEmpty()) {
wrapper.setIndent((IndentImpl)subBlocks.get(0).getIndent());
}
@@ -195,7 +185,7 @@ public class InitialInfoBuilder {
}
else {
//block building is skipped
return processSimpleBlock(rootBlock, parent, true, index, parentBlock);
return buildLeafBlock(rootBlock, parent, true, index, parentBlock);
}
}
finally {
@@ -203,9 +193,21 @@ public class InitialInfoBuilder {
}
}
private void collectAlignments(Block rootBlock) {
if (myCollectAlignmentsInsideFormattingRange && rootBlock.getAlignment() != null
&& isAffectedByFormatting(rootBlock) && !myInsideFormatRestrictingTag)
{
myAlignmentsInsideRangeToModify.add(rootBlock.getAlignment());
}
if (rootBlock.getAlignment() != null) {
myBlocksToAlign.putValue(rootBlock.getAlignment(), rootBlock);
}
}
private void checkRanges(@NotNull CompositeBlockWrapper parent, TextRange textRange) {
if (textRange.getStartOffset() < parent.getStartOffset()) {
myRangesAssert.assertInvalidRanges(
ASSERT.assertInvalidRanges(
textRange.getStartOffset(),
parent.getStartOffset(),
myModel,
@@ -214,7 +216,7 @@ public class InitialInfoBuilder {
}
if (textRange.getEndOffset() > parent.getEndOffset()) {
myRangesAssert.assertInvalidRanges(
ASSERT.assertInvalidRanges(
textRange.getEndOffset(),
parent.getEndOffset(),
myModel,
@@ -238,11 +240,11 @@ public class InitialInfoBuilder {
private static List<TextRange> getExtendedAffectedRanges(FormatTextRanges formatTextRanges) {
if (formatTextRanges == null) return null;
List<FormatTextRanges.FormatTextRange> ranges = formatTextRanges.getRanges();
List<FormatTextRange> ranges = formatTextRanges.getRanges();
List<TextRange> extended = ContainerUtil.newArrayList();
final int extendOffset = 500;
for (FormatTextRanges.FormatTextRange textRange : ranges) {
for (FormatTextRange textRange : ranges) {
TextRange range = textRange.getTextRange();
extended.add(new UnfairTextRange(range.getStartOffset() - extendOffset, range.getEndOffset() + extendOffset));
}
@@ -329,11 +331,11 @@ public class InitialInfoBuilder {
}
}
private AbstractBlockWrapper processSimpleBlock(final Block rootBlock,
@Nullable final CompositeBlockWrapper parent,
final boolean readOnly,
final int index,
@Nullable Block parentBlock)
private AbstractBlockWrapper buildLeafBlock(final Block rootBlock,
@Nullable final CompositeBlockWrapper parent,
final boolean readOnly,
final int index,
@Nullable Block parentBlock)
{
LeafBlockWrapper result = doProcessSimpleBlock(rootBlock, parent, readOnly, index, parentBlock);
myProgressCallback.afterWrappingBlock(result);
@@ -349,32 +351,16 @@ public class InitialInfoBuilder {
if (!INLINE_TABS_ENABLED && !myCurrentWhiteSpace.containsLineFeeds()) {
myCurrentWhiteSpace.setForceSkipTabulationsUsage(true);
}
final LeafBlockWrapper info =
new LeafBlockWrapper(rootBlock, parent, myCurrentWhiteSpace, myModel, myOptions, myPreviousBlock, readOnly);
LeafBlockWrapper info = new LeafBlockWrapper(rootBlock, parent, myCurrentWhiteSpace, myModel, myOptions, myPreviousBlock, readOnly);
if (index == 0) {
info.arrangeParentTextRange();
}
switch (myFormatterTagHandler.getFormatterTag(rootBlock)) {
case ON:
myInsideFormatRestrictingTag = false;
break;
case OFF:
myInsideFormatRestrictingTag = true;
break;
case NONE:
break;
}
checkInsideFormatterOffTag(rootBlock);
TextRange textRange = rootBlock.getTextRange();
if (textRange.getLength() == 0) {
myRangesAssert.assertInvalidRanges(
textRange.getStartOffset(),
textRange.getEndOffset(),
myModel,
"empty block"
);
}
checkRange(textRange);
if (myPreviousBlock != null) {
myPreviousBlock.setNextBlock(info);
}
@@ -406,6 +392,25 @@ public class InitialInfoBuilder {
return info;
}
private void checkInsideFormatterOffTag(Block rootBlock) {
switch (myFormatterTagHandler.getFormatterTag(rootBlock)) {
case ON:
myInsideFormatRestrictingTag = false;
break;
case OFF:
myInsideFormatRestrictingTag = true;
break;
case NONE:
break;
}
}
private void checkRange(TextRange textRange) {
if (textRange.getLength() == 0) {
ASSERT.assertInvalidRanges(textRange.getStartOffset(), textRange.getEndOffset(), myModel, "empty block");
}
}
private boolean currentWhiteSpaceIsReadOnly() {
if (myCurrentSpaceProperty != null && myCurrentSpaceProperty.isReadOnly()) {
return true;
@@ -419,11 +424,11 @@ public class InitialInfoBuilder {
private boolean isAffectedByFormatting(final Block block) {
if (myAffectedRanges == null) return true;
List<FormatTextRanges.FormatTextRange> allRanges = myAffectedRanges.getRanges();
List<FormatTextRange> allRanges = myAffectedRanges.getRanges();
Document document = myModel.getDocument();
int docLength = document.getTextLength();
for (FormatTextRanges.FormatTextRange range : allRanges) {
for (FormatTextRange range : allRanges) {
int startOffset = range.getStartOffset();
if (startOffset >= docLength) continue;
@@ -75,7 +75,7 @@ public class CodeFormatterFacade {
* @see CodeStyleSettings#WRAP_LONG_LINES
*/
public static final Key<Boolean> WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY
= new Key<Boolean>("WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY");
= new Key<>("WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY");
private final CodeStyleSettings mySettings;
private final FormatterTagHandler myTagHandler;
@@ -168,11 +168,11 @@ public class CodeFormatterFacade {
public void processText(PsiFile file, final FormatTextRanges ranges, boolean doPostponedFormatting) {
final Project project = file.getProject();
Document document = PsiDocumentManager.getInstance(project).getDocument(file);
final List<FormatTextRanges.FormatTextRange> textRanges = ranges.getRanges();
final List<FormatTextRange> textRanges = ranges.getRanges();
if (document instanceof DocumentWindow) {
file = InjectedLanguageManager.getInstance(file.getProject()).getTopLevelFile(file);
final DocumentWindow documentWindow = (DocumentWindow)document;
for (FormatTextRanges.FormatTextRange range : textRanges) {
for (FormatTextRange range : textRanges) {
range.setTextRange(documentWindow.injectedToHost(range.getTextRange()));
}
document = documentWindow.getDelegate();
@@ -198,14 +198,14 @@ public class CodeFormatterFacade {
if (node == null) {
node = file.getNode();
}
for (FormatTextRanges.FormatTextRange range : ranges.getRanges()) {
for (FormatTextRange range : ranges.getRanges()) {
TextRange rangeToUse = preprocess(node, range.getTextRange());
range.setTextRange(rangeToUse);
}
if (doPostponedFormatting) {
RangeMarker[] markers = new RangeMarker[textRanges.size()];
int i = 0;
for (FormatTextRanges.FormatTextRange range : textRanges) {
for (FormatTextRange range : textRanges) {
TextRange textRange = range.getTextRange();
int start = textRange.getStartOffset();
int end = textRange.getEndOffset();
@@ -220,7 +220,7 @@ public class CodeFormatterFacade {
FormattingProgressTask.FORMATTING_CANCELLED_FLAG.set(false);
component.doPostponedFormatting(file.getViewProvider());
i = 0;
for (FormatTextRanges.FormatTextRange range : textRanges) {
for (FormatTextRange range : textRanges) {
RangeMarker marker = markers[i];
if (marker != null) {
range.setTextRange(TextRange.create(marker));
@@ -247,7 +247,7 @@ public class CodeFormatterFacade {
mySettings.getIndentOptionsByFile(file, textRanges.size() == 1 ? textRanges.get(0).getTextRange() : null);
formatter.format(model, mySettings, indentOptions, ranges, myReformatContext);
for (FormatTextRanges.FormatTextRange range : textRanges) {
for (FormatTextRange range : textRanges) {
TextRange textRange = range.getTextRange();
wrapLongLinesIfNecessary(file, document, textRange.getStartOffset(), textRange.getEndOffset());
}