From b5d8d5b7a0a56e83d586f8612ed7bb7e37db1a6e Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 9 Dec 2015 18:03:10 +0300 Subject: [PATCH 01/34] Refactoring: use StateProcessor instead of State which will handle change by himself --- .../intellij/formatting/FormatProcessor.java | 99 +++++-------------- .../formatting/engine/StateProcessor.java | 58 +++++++++++ 2 files changed, 85 insertions(+), 72 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index ece6987525b1..522c74c5501e 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -17,6 +17,7 @@ package com.intellij.formatting; import com.intellij.diagnostic.LogMessageEx; +import com.intellij.formatting.engine.StateProcessor; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; @@ -161,7 +162,7 @@ public class FormatProcessor { private final int myRightMargin; @NotNull - private State myCurrentState; + private StateProcessor myStateProcessor; private MultiMap myExpandableIndents; private int myTotalBlocksWithAlignments; private int myBlockRollbacks; @@ -188,7 +189,7 @@ public class FormatProcessor { mySettings = options.mySettings; myDocument = model.getDocument(); myReformatContext = options.myReformatContext; - myCurrentState = new WrapBlocksState(block, model, options.myAffectedRanges, options.myInterestingOffset); + myStateProcessor = new StateProcessor(new WrapBlocksState(block, model, options.myAffectedRanges, options.myInterestingOffset)); myRightMargin = getRightMargin(block); } @@ -256,13 +257,9 @@ public class FormatProcessor { */ public void format(FormattingModel model, boolean sequentially) { if (sequentially) { - AdjustWhiteSpacesState adjustState = new AdjustWhiteSpacesState(); - ExpandChildrenIndent expandChildrenIndent = new ExpandChildrenIndent(); - ApplyChangesState applyChangesState = new ApplyChangesState(model); - - expandChildrenIndent.setNext(applyChangesState); - adjustState.setNext(expandChildrenIndent); - myCurrentState.setNext(adjustState); + myStateProcessor.setNextState(new AdjustWhiteSpacesState()); + myStateProcessor.setNextState(new ExpandChildrenIndent()); + myStateProcessor.setNextState(new ApplyChangesState(model)); } else { formatWithoutRealModifications(false); @@ -277,18 +274,18 @@ public class FormatProcessor { * @see #format(FormattingModel, boolean) */ public boolean iteration() { - if (myCurrentState.isDone()) { + if (myStateProcessor.isDone()) { return true; } - myCurrentState.iteration(); - return myCurrentState.isDone(); + myStateProcessor.iteration(); + return myStateProcessor.isDone(); } /** * Asks current processor to stop any active sequential processing if any. */ public void stopSequentialProcessing() { - myCurrentState.stop(); + myStateProcessor.stop(); } public void formatWithoutRealModifications() { @@ -297,15 +294,12 @@ public class FormatProcessor { @SuppressWarnings({"WhileLoopSpinsOnField"}) public void formatWithoutRealModifications(boolean sequentially) { - AdjustWhiteSpacesState adjustSpace = new AdjustWhiteSpacesState(); - adjustSpace.setNext(new ExpandChildrenIndent()); - myCurrentState.setNext(adjustSpace); - + myStateProcessor.setNextState(new AdjustWhiteSpacesState()); + myStateProcessor.setNextState(new ExpandChildrenIndent()); if (sequentially) { return; } - - doIterationsSynchronously(FormattingStateId.PROCESSING_BLOCKS); + doIterationsSynchronously(); } private void reset() { @@ -324,13 +318,13 @@ public class FormatProcessor { public void performModifications(FormattingModel model, boolean sequentially) { assert !myDisposed; - myCurrentState.setNext(new ApplyChangesState(model)); + myStateProcessor.setNextState(new ApplyChangesState(model)); if (sequentially) { return; } - doIterationsSynchronously(FormattingStateId.APPLYING_CHANGES); + doIterationsSynchronously(); } /** @@ -339,11 +333,9 @@ public class FormatProcessor { * * @param state target state to process */ - private void doIterationsSynchronously(@NotNull FormattingStateId state) { - while ((myCurrentState.getStateId() == state || state.getPreviousStates().contains(myCurrentState.getStateId())) - && !myCurrentState.isDone()) - { - myCurrentState.iteration(); + private void doIterationsSynchronously() { + while (!myStateProcessor.isDone()) { + myStateProcessor.iteration(); } } @@ -1323,22 +1315,16 @@ public class FormatProcessor { return result.toString(); } - private abstract class State { + public abstract class State { - private final FormattingStateId myStateId; - - private State myNextState; private boolean myDone; - protected State(FormattingStateId stateId) { - myStateId = stateId; - } + protected State(FormattingStateId stateId) {} public void iteration() { if (!isDone()) { doIteration(); } - shiftStateIfNecessary(); } public boolean isDone() { @@ -1349,31 +1335,12 @@ public class FormatProcessor { myDone = done; } - public void setNext(@NotNull State state) { - if (getStateId() == state.getStateId() || (myNextState != null && myNextState.getStateId() == state.getStateId())) { - return; - } - myNextState = state; - shiftStateIfNecessary(); - } - - public FormattingStateId getStateId() { - return myStateId; - } - - public void stop() { - } + public void stop() {} protected abstract void doIteration(); - protected abstract void prepare(); - - private void shiftStateIfNecessary() { - if (isDone() && myNextState != null) { - myCurrentState = myNextState; - myNextState = null; - myCurrentState.prepare(); - } - } + + public void prepare() {} + } private class WrapBlocksState extends State { @@ -1395,11 +1362,7 @@ public class FormatProcessor { myExpandableIndents = myWrapper.getExpandableIndentsBlocks(); } - - @Override - protected void prepare() { - } - + @Override public void doIteration() { if (isDone()) { @@ -1432,11 +1395,7 @@ public class FormatProcessor { AdjustWhiteSpacesState() { super(FormattingStateId.PROCESSING_BLOCKS); } - - @Override - protected void prepare() { - } - + @Override protected void doIteration() { LeafBlockWrapper blockToProcess = myCurrentBlock; @@ -1474,7 +1433,7 @@ public class FormatProcessor { } @Override - protected void prepare() { + public void prepare() { myBlocksToModify = collectBlocksToModify(); // call doModifications static method to ensure no access to state // thus we may clear formatting state @@ -1803,9 +1762,5 @@ public class FormatProcessor { current = current.getNextBlock(); } } - - @Override - protected void prepare() { - } } } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java b/platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java new file mode 100644 index 000000000000..97e663d8ace1 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.FormatProcessor; +import com.intellij.util.containers.ContainerUtil; + +import java.util.List; + +public class StateProcessor { + + private final List myStates = ContainerUtil.newArrayList(); + private FormatProcessor.State myCurrentState; + + public StateProcessor(FormatProcessor.State initial) { + myCurrentState = initial; + } + + public void setNextState(FormatProcessor.State state) { + myStates.add(state); + } + + public boolean isDone() { + return myStates.isEmpty() && myCurrentState.isDone(); + } + + public void iteration() { + if (!myCurrentState.isDone()) { + myCurrentState.iteration(); + } + shiftStateIfNecessary(); + } + + private void shiftStateIfNecessary() { + if (myCurrentState.isDone() && !myStates.isEmpty()) { + myCurrentState = myStates.get(0); + myStates.remove(0); + myCurrentState.prepare(); + } + } + + public void stop() { + myCurrentState.stop(); + } +} From ba6ea3dbc93c85833c692f958e4509dd2e7fa975 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 9 Dec 2015 18:06:51 +0300 Subject: [PATCH 02/34] Removed unnecessary stuff after refactoring --- .../intellij/formatting/FormatProcessor.java | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 522c74c5501e..149566870c9d 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -327,12 +327,6 @@ public class FormatProcessor { doIterationsSynchronously(); } - /** - * Perform iterations against the {@link #myCurrentState current state} until it's {@link FormattingStateId type} - * is {@link FormattingStateId#getPreviousStates() less} or equal to the given state. - * - * @param state target state to process - */ private void doIterationsSynchronously() { while (!myStateProcessor.isDone()) { myStateProcessor.iteration(); @@ -1315,12 +1309,10 @@ public class FormatProcessor { return result.toString(); } - public abstract class State { + public abstract static class State { private boolean myDone; - protected State(FormattingStateId stateId) {} - public void iteration() { if (!isDone()) { doIteration(); @@ -1353,7 +1345,6 @@ public class FormatProcessor { @Nullable final FormatTextRanges affectedRanges, int interestingOffset) { - super(FormattingStateId.WRAPPING_BLOCKS); myModel = model; myWrapper = InitialInfoBuilder.prepareToBuildBlocksSequentially( root, model, affectedRanges, mySettings, myDefaultIndentOption, interestingOffset, myProgressCallback @@ -1391,10 +1382,6 @@ public class FormatProcessor { } private class AdjustWhiteSpacesState extends State { - - AdjustWhiteSpacesState() { - super(FormattingStateId.PROCESSING_BLOCKS); - } @Override protected void doIteration() { @@ -1428,7 +1415,6 @@ public class FormatProcessor { private boolean myResetBulkUpdateState; private ApplyChangesState(FormattingModel model) { - super(FormattingStateId.APPLYING_CHANGES); myModel = model; } @@ -1584,10 +1570,6 @@ public class FormatProcessor { private Iterator myIterator; private MultiMap myBlocksToRealign = new MultiMap(); - public ExpandChildrenIndent() { - super(FormattingStateId.EXPANDING_CHILDREN_INDENTS); - } - @Override protected void doIteration() { if (myIterator == null) { From 133de6a712afd32dd78a534b88248ade39d581d9 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 9 Dec 2015 18:14:14 +0300 Subject: [PATCH 03/34] Extracted state from FormatProcessor into separate class --- .../intellij/formatting/FormatProcessor.java | 27 +----------- .../com/intellij/formatting/engine/State.java | 42 +++++++++++++++++++ .../formatting/engine/StateProcessor.java | 9 ++-- 3 files changed, 47 insertions(+), 31 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/State.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 149566870c9d..f4e4adb47157 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -17,6 +17,7 @@ package com.intellij.formatting; import com.intellij.diagnostic.LogMessageEx; +import com.intellij.formatting.engine.State; import com.intellij.formatting.engine.StateProcessor; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; @@ -1309,32 +1310,6 @@ public class FormatProcessor { return result.toString(); } - public abstract static class State { - - private boolean myDone; - - public void iteration() { - if (!isDone()) { - doIteration(); - } - } - - public boolean isDone() { - return myDone; - } - - protected void setDone(boolean done) { - myDone = done; - } - - public void stop() {} - - protected abstract void doIteration(); - - public void prepare() {} - - } - private class WrapBlocksState extends State { private final InitialInfoBuilder myWrapper; diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/State.java b/platform/lang-impl/src/com/intellij/formatting/engine/State.java new file mode 100644 index 000000000000..61f677fdcf6e --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/State.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2015 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.engine; + +public abstract class State { + + private boolean myDone; + + public void iteration() { + if (!isDone()) { + doIteration(); + } + } + + public boolean isDone() { + return myDone; + } + + protected void setDone(boolean done) { + myDone = done; + } + + public void stop() {} + + protected abstract void doIteration(); + + public void prepare() {} + +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java b/platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java index 97e663d8ace1..606d96e45826 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/StateProcessor.java @@ -15,21 +15,20 @@ */ package com.intellij.formatting.engine; -import com.intellij.formatting.FormatProcessor; import com.intellij.util.containers.ContainerUtil; import java.util.List; public class StateProcessor { - private final List myStates = ContainerUtil.newArrayList(); - private FormatProcessor.State myCurrentState; + private final List myStates = ContainerUtil.newArrayList(); + private State myCurrentState; - public StateProcessor(FormatProcessor.State initial) { + public StateProcessor(State initial) { myCurrentState = initial; } - public void setNextState(FormatProcessor.State state) { + public void setNextState(State state) { myStates.add(state); } From 8e06944e5f98221406d3652ff032c550b6e08665 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 9 Dec 2015 19:07:38 +0300 Subject: [PATCH 04/34] Refactoring: WrapBlocks state takes InitialInfoBuilder as a parameter --- .../intellij/formatting/FormatProcessor.java | 30 +++++++++---------- .../formatting/InitialInfoBuilder.java | 17 ++++++----- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index f4e4adb47157..a66a2fee189e 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -46,6 +46,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import static com.intellij.formatting.AbstractBlockAlignmentProcessor.Context; +import static com.intellij.formatting.InitialInfoBuilder.prepareToBuildBlocksSequentially; public class FormatProcessor { @@ -190,7 +191,10 @@ public class FormatProcessor { mySettings = options.mySettings; myDocument = model.getDocument(); myReformatContext = options.myReformatContext; - myStateProcessor = new StateProcessor(new WrapBlocksState(block, model, options.myAffectedRanges, options.myInterestingOffset)); + + InitialInfoBuilder builder = prepareToBuildBlocksSequentially(block, model, options, mySettings, myDefaultIndentOption, myProgressCallback); + + myStateProcessor = new StateProcessor(new WrapBlocksState(model, builder)); myRightMargin = getRightMargin(block); } @@ -1314,18 +1318,12 @@ public class FormatProcessor { private final InitialInfoBuilder myWrapper; private final FormattingDocumentModel myModel; - - WrapBlocksState(@NotNull Block root, - @NotNull FormattingDocumentModel model, - @Nullable final FormatTextRanges affectedRanges, - int interestingOffset) + + WrapBlocksState(@NotNull FormattingDocumentModel model, + @NotNull InitialInfoBuilder initialInfoBuilder) { myModel = model; - myWrapper = InitialInfoBuilder.prepareToBuildBlocksSequentially( - root, model, affectedRanges, mySettings, myDefaultIndentOption, interestingOffset, myProgressCallback - ); - myWrapper.setCollectAlignmentsInsideFormattingRange(myReformatContext); - + myWrapper = initialInfoBuilder; myExpandableIndents = myWrapper.getExpandableIndentsBlocks(); } @@ -1513,13 +1511,13 @@ public class FormatProcessor { public static class FormatOptions { - private CodeStyleSettings mySettings; - private CommonCodeStyleSettings.IndentOptions myIndentOptions; + public CodeStyleSettings mySettings; + public CommonCodeStyleSettings.IndentOptions myIndentOptions; - private FormatTextRanges myAffectedRanges; - private boolean myReformatContext; + public FormatTextRanges myAffectedRanges; + public boolean myReformatContext; - private int myInterestingOffset; + public int myInterestingOffset; public FormatOptions(CodeStyleSettings settings, CommonCodeStyleSettings.IndentOptions options, diff --git a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java index b9db5823b859..563b856c7b3d 100644 --- a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java +++ b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java @@ -98,15 +98,16 @@ class InitialInfoBuilder { myFormatterTagHandler = new FormatterTagHandler(settings); } - protected static InitialInfoBuilder prepareToBuildBlocksSequentially(Block root, - FormattingDocumentModel model, - @Nullable final FormatTextRanges affectedRanges, - @NotNull CodeStyleSettings settings, - final CommonCodeStyleSettings.IndentOptions options, - int interestingOffset, - @NotNull FormattingProgressCallback progressCallback) + protected static InitialInfoBuilder prepareToBuildBlocksSequentially( + Block root, + FormattingDocumentModel model, + FormatProcessor.FormatOptions formatOptions, + CodeStyleSettings settings, + CommonCodeStyleSettings.IndentOptions options, + @NotNull FormattingProgressCallback progressCallback) { - InitialInfoBuilder builder = new InitialInfoBuilder(root, model, affectedRanges, settings, options, interestingOffset, progressCallback); + InitialInfoBuilder builder = new InitialInfoBuilder(root, model, formatOptions.myAffectedRanges, settings, options, formatOptions.myInterestingOffset, progressCallback); + builder.setCollectAlignmentsInsideFormattingRange(formatOptions.myReformatContext); builder.buildFrom(root, 0, null, null, null, true); return builder; } From 47c5c572f07a595cd12f804406b541070a765d17 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 9 Dec 2015 19:33:28 +0300 Subject: [PATCH 05/34] WrapBlockState refactored --- .../intellij/formatting/FormatProcessor.java | 62 +++++++++---------- .../com/intellij/formatting/engine/State.java | 8 +++ 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index a66a2fee189e..679eeaa30523 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -168,8 +168,6 @@ public class FormatProcessor { private MultiMap myExpandableIndents; private int myTotalBlocksWithAlignments; private int myBlockRollbacks; - - private AlignmentCyclesDetector myCyclesDetector; public FormatProcessor(final FormattingDocumentModel docModel, Block rootBlock, @@ -181,7 +179,7 @@ public class FormatProcessor { this(docModel, rootBlock, new FormatOptions(settings, indentOptions, affectedRanges, false), progressCallback); } - public FormatProcessor(FormattingDocumentModel model, + public FormatProcessor(final FormattingDocumentModel model, Block block, FormatOptions options, @NotNull FormattingProgressCallback callback) @@ -191,11 +189,28 @@ public class FormatProcessor { mySettings = options.mySettings; myDocument = model.getDocument(); myReformatContext = options.myReformatContext; - - InitialInfoBuilder builder = prepareToBuildBlocksSequentially(block, model, options, mySettings, myDefaultIndentOption, myProgressCallback); - - myStateProcessor = new StateProcessor(new WrapBlocksState(model, builder)); myRightMargin = getRightMargin(block); + + final InitialInfoBuilder builder = prepareToBuildBlocksSequentially(block, model, options, mySettings, myDefaultIndentOption, myProgressCallback); + final WrapBlocksState wrapState = new WrapBlocksState(builder); + wrapState.setOnDone(new Runnable() { + @Override + public void run() { + myInfos = builder.getBlockToInfoMap(); + myRootBlockWrapper = builder.getRootBlockWrapper(); + myFirstTokenBlock = builder.getFirstTokenBlock(); + myLastTokenBlock = builder.getLastTokenBlock(); + myCurrentBlock = myFirstTokenBlock; + myTextRangeToWrapper = buildTextRangeToInfoMap(myFirstTokenBlock); + int lastBlockOffset = getLastBlock().getEndOffset(); + myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); + myLastWhiteSpace.append(Math.max(lastBlockOffset, builder.getEndOffset()), model, myDefaultIndentOption); + myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); + myTotalBlocksWithAlignments = builder.getBlocksToAlign().values().size(); + myExpandableIndents = builder.getExpandableIndentsBlocks(); + } + }); + myStateProcessor = new StateProcessor(wrapState); } private int getRightMargin(Block rootBlock) { @@ -735,9 +750,8 @@ public class FormatProcessor { BlockAlignmentProcessor.Context context = new BlockAlignmentProcessor.Context( myDocument, alignment, myCurrentBlock, myAlignmentMappings, myBackwardShiftedAlignedBlocks, getIndentOptionsToUse(myCurrentBlock, myDefaultIndentOption)); - final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock); - myCyclesDetector.registerOffsetResponsibleBlock(offsetResponsibleBlock); BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); + final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock); switch (result) { case TARGET_BLOCK_PROCESSED_NOT_ALIGNED: return true; case TARGET_BLOCK_ALIGNED: storeAlignmentMapping(); return true; @@ -751,13 +765,13 @@ public class FormatProcessor { blocksCausedRealignment.add(myCurrentBlock); storeAlignmentMapping(myCurrentBlock, offsetResponsibleBlock); - if (myCyclesDetector.isCycleDetected()) { + if (myBlockRollbacks > myTotalBlocksWithAlignments) { reportAlignmentProcessingError(context); return true; } else { - myCyclesDetector.registerBlockRollback(myCurrentBlock); myCurrentBlock = offsetResponsibleBlock.getNextBlock(); + myBlockRollbacks++; } onCurrentLineChanged(); return false; @@ -1314,17 +1328,11 @@ public class FormatProcessor { return result.toString(); } - private class WrapBlocksState extends State { - - private final InitialInfoBuilder myWrapper; - private final FormattingDocumentModel myModel; + private static class WrapBlocksState extends State { + private final InitialInfoBuilder myWrapper; - WrapBlocksState(@NotNull FormattingDocumentModel model, - @NotNull InitialInfoBuilder initialInfoBuilder) - { - myModel = model; + WrapBlocksState(@NotNull InitialInfoBuilder initialInfoBuilder) { myWrapper = initialInfoBuilder; - myExpandableIndents = myWrapper.getExpandableIndentsBlocks(); } @Override @@ -1337,20 +1345,6 @@ public class FormatProcessor { if (!isDone()) { return; } - - myInfos = myWrapper.getBlockToInfoMap(); - myRootBlockWrapper = myWrapper.getRootBlockWrapper(); - myFirstTokenBlock = myWrapper.getFirstTokenBlock(); - myLastTokenBlock = myWrapper.getLastTokenBlock(); - myCurrentBlock = myFirstTokenBlock; - myTextRangeToWrapper = buildTextRangeToInfoMap(myFirstTokenBlock); - int lastBlockOffset = getLastBlock().getEndOffset(); - myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); - myLastWhiteSpace.append(Math.max(lastBlockOffset, myWrapper.getEndOffset()), myModel, myDefaultIndentOption); - myAlignmentsInsideRangesToModify = myWrapper.getAlignmentsInsideRangeToModify(); - myTotalBlocksWithAlignments = myWrapper.getBlocksToAlign().values().size(); - - myCyclesDetector = new AlignmentCyclesDetector(myTotalBlocksWithAlignments); } } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/State.java b/platform/lang-impl/src/com/intellij/formatting/engine/State.java index 61f677fdcf6e..77f1760ae594 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/State.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/State.java @@ -18,6 +18,7 @@ package com.intellij.formatting.engine; public abstract class State { private boolean myDone; + private Runnable myOnDoneAction; public void iteration() { if (!isDone()) { @@ -30,6 +31,9 @@ public abstract class State { } protected void setDone(boolean done) { + if (!myDone && done && myOnDoneAction != null) { + myOnDoneAction.run(); + } myDone = done; } @@ -38,5 +42,9 @@ public abstract class State { protected abstract void doIteration(); public void prepare() {} + + public void setOnDone(Runnable onDoneAction) { + myOnDoneAction = onDoneAction; + } } \ No newline at end of file From cccd8c2c8591d4542a3cc0b9a5fa591b228456de Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Dec 2015 15:46:08 +0300 Subject: [PATCH 06/34] extracted WrapBlocksState --- .../intellij/formatting/FormatProcessor.java | 21 +--------- .../formatting/InitialInfoBuilder.java | 2 +- .../formatting/engine/WrapBlocksState.java | 39 +++++++++++++++++++ 3 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 679eeaa30523..b2f8dd7191ca 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -19,6 +19,7 @@ package com.intellij.formatting; import com.intellij.diagnostic.LogMessageEx; import com.intellij.formatting.engine.State; import com.intellij.formatting.engine.StateProcessor; +import com.intellij.formatting.engine.WrapBlocksState; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; @@ -1328,26 +1329,6 @@ public class FormatProcessor { return result.toString(); } - private static class WrapBlocksState extends State { - private final InitialInfoBuilder myWrapper; - - WrapBlocksState(@NotNull InitialInfoBuilder initialInfoBuilder) { - myWrapper = initialInfoBuilder; - } - - @Override - public void doIteration() { - if (isDone()) { - return; - } - - setDone(myWrapper.iteration()); - if (!isDone()) { - return; - } - } - } - private class AdjustWhiteSpacesState extends State { @Override diff --git a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java index 563b856c7b3d..404a15b90330 100644 --- a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java +++ b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java @@ -46,7 +46,7 @@ import java.util.Set; * Allows to build {@link AbstractBlockWrapper formatting block wrappers} for the target {@link Block formatting blocks}. * The main idea of block wrapping is to associate information about {@link WhiteSpace white space before block} with the block itself. */ -class InitialInfoBuilder { +public class InitialInfoBuilder { private static final Logger LOG = Logger.getInstance("#com.intellij.formatting.InitialInfoBuilder"); private final Map myResult = new THashMap(); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java b/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java new file mode 100644 index 000000000000..63e43c1dbb56 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.InitialInfoBuilder; +import org.jetbrains.annotations.NotNull; + +public class WrapBlocksState extends State { + private final InitialInfoBuilder myWrapper; + + public WrapBlocksState(@NotNull InitialInfoBuilder initialInfoBuilder) { + myWrapper = initialInfoBuilder; + } + + @Override + public void doIteration() { + if (isDone()) { + return; + } + + setDone(myWrapper.iteration()); + if (!isDone()) { + return; + } + } +} \ No newline at end of file From 71c5caa9d19d024394155c25c1ba947273c37136 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Dec 2015 16:26:54 +0300 Subject: [PATCH 07/34] Extracted BlockIndentOptions from format processor --- .../intellij/formatting/FormatProcessor.java | 55 ++++--------------- .../formatting/engine/BlockIndentOptions.java | 46 ++++++++++++++++ 2 files changed, 58 insertions(+), 43 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index b2f8dd7191ca..4889acc09b7e 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -17,6 +17,7 @@ package com.intellij.formatting; import com.intellij.diagnostic.LogMessageEx; +import com.intellij.formatting.engine.BlockIndentOptions; import com.intellij.formatting.engine.State; import com.intellij.formatting.engine.StateProcessor; import com.intellij.formatting.engine.WrapBlocksState; @@ -46,7 +47,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -import static com.intellij.formatting.AbstractBlockAlignmentProcessor.Context; +import static com.intellij.formatting.BlockAlignmentProcessor.Context; import static com.intellij.formatting.InitialInfoBuilder.prepareToBuildBlocksSequentially; public class FormatProcessor { @@ -76,6 +77,7 @@ public class FormatProcessor { private CompositeBlockWrapper myRootBlockWrapper; private TIntObjectHashMap myTextRangeToWrapper; + private final BlockIndentOptions myBlockIndentOptions; private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; private final CodeStyleSettings mySettings; private final Document myDocument; @@ -188,6 +190,7 @@ public class FormatProcessor { myProgressCallback = callback; myDefaultIndentOption = options.myIndentOptions; mySettings = options.mySettings; + myBlockIndentOptions = new BlockIndentOptions(mySettings, myDefaultIndentOption); myDocument = model.getDocument(); myReformatContext = options.myReformatContext; myRightMargin = getRightMargin(block); @@ -364,12 +367,10 @@ public class FormatProcessor { * * @param blocksToModify changes introduced by formatter * @param model current formatting model - * @param indentOption indent options to use */ @SuppressWarnings({"deprecation"}) private void applyChangesAtRewriteMode(@NotNull final List blocksToModify, - @NotNull final FormattingModel model, - @NotNull CommonCodeStyleSettings.IndentOptions indentOption) + @NotNull final FormattingModel model) { FormattingDocumentModel documentModel = model.getDocumentModel(); Document document = documentModel.getDocument(); @@ -383,7 +384,7 @@ public class FormatProcessor { for (LeafBlockWrapper block : blocksToModify) { WhiteSpace whiteSpace = block.getWhiteSpace(); CharSequence newWs = documentModel.adjustWhiteSpaceIfNecessary( - whiteSpace.generateWhiteSpace(getIndentOptionsToUse(block, indentOption)), whiteSpace.getStartOffset(), + whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)), whiteSpace.getStartOffset(), whiteSpace.getEndOffset(), block.getNode(), false ); if (changes.size() > 10000) { @@ -467,7 +468,7 @@ public class FormatProcessor { for (LeafBlockWrapper block = myFirstTokenBlock; block != null; block = block.getNextBlock()) { final WhiteSpace whiteSpace = block.getWhiteSpace(); if (!whiteSpace.isReadOnly()) { - final String newWhiteSpace = whiteSpace.generateWhiteSpace(getIndentOptionsToUse(block, myDefaultIndentOption)); + final String newWhiteSpace = whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)); if (!whiteSpace.equalsToString(newWhiteSpace)) { blocksToModify.add(block); } @@ -476,22 +477,6 @@ public 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 void processToken() { final SpacingImpl spaceProperty = myCurrentBlock.getSpaceProperty(); final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); @@ -750,7 +735,7 @@ public class FormatProcessor { BlockAlignmentProcessor.Context context = new BlockAlignmentProcessor.Context( myDocument, alignment, myCurrentBlock, myAlignmentMappings, myBackwardShiftedAlignedBlocks, - getIndentOptionsToUse(myCurrentBlock, myDefaultIndentOption)); + myBlockIndentOptions.getIndentOptions(myCurrentBlock)); BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock); switch (result) { @@ -842,7 +827,7 @@ public class FormatProcessor { } private void adjustSpacingByIndentOffset() { - IndentData offset = myCurrentBlock.calculateOffset(getIndentOptionsToUse(myCurrentBlock, myDefaultIndentOption)); + IndentData offset = myCurrentBlock.calculateOffset(myBlockIndentOptions.getIndentOptions(myCurrentBlock)); myCurrentBlock.getWhiteSpace().setSpaces(offset.getSpaces(), offset.getIndentSpaces()); } @@ -1110,7 +1095,7 @@ public 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(getIndentOptionsToUse(parent, myDefaultIndentOption), childAttributes, index).createIndentInfo(); + return parent.calculateChildOffset(myBlockIndentOptions.getIndentOptions(parent), childAttributes, index).createIndentInfo(); } else { AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(myCurrentBlock); @@ -1313,22 +1298,6 @@ public class FormatProcessor { return newIndent.getSpacesCount(options) - oldIndent.getSpacesCount(options); } - /** - * Utility method to use during debugging formatter processing. - * - * @return text that contains intermediate formatter-introduced changes (even not committed yet) - */ - @SuppressWarnings("UnusedDeclaration") - @NotNull - private String getCurrentText() { - StringBuilder result = new StringBuilder(); - for (LeafBlockWrapper block = myFirstTokenBlock; block != null; block = block.getNextBlock()) { - result.append(block.getWhiteSpace().generateWhiteSpace(getIndentOptionsToUse(block, myDefaultIndentOption))); - result.append(myDocument.getCharsSequence().subSequence(block.getStartOffset(), block.getEndOffset())); - } - return result.toString(); - } - private class AdjustWhiteSpacesState extends State { @Override @@ -1396,7 +1365,7 @@ public class FormatProcessor { final int blocksToModifyCount = myBlocksToModify.size(); if (blocksToModifyCount > BULK_REPLACE_OPTIMIZATION_CRITERIA) { - applyChangesAtRewriteMode(myBlocksToModify, myModel, myDefaultIndentOption); + applyChangesAtRewriteMode(myBlocksToModify, myModel); setDone(true); } else if (blocksToModifyCount > 50) { @@ -1415,7 +1384,7 @@ public class FormatProcessor { myModel, blockWrapper, myShift, - blockWrapper.getWhiteSpace().generateWhiteSpace(getIndentOptionsToUse(blockWrapper, myDefaultIndentOption)), + blockWrapper.getWhiteSpace().generateWhiteSpace(myBlockIndentOptions.getIndentOptions(blockWrapper)), myDefaultIndentOption ); myProgressCallback.afterApplyingChange(blockWrapper); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java b/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java new file mode 100644 index 000000000000..a1872bb15f9e --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.AbstractBlockWrapper; +import com.intellij.lang.Language; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import org.jetbrains.annotations.NotNull; + +public class BlockIndentOptions { + private final CodeStyleSettings mySettings; + private final CommonCodeStyleSettings.IndentOptions myIndentOptions; + + public BlockIndentOptions(@NotNull CodeStyleSettings settings, @NotNull CommonCodeStyleSettings.IndentOptions indentOptions) { + mySettings = settings; + myIndentOptions = indentOptions; + } + + @NotNull + public CommonCodeStyleSettings.IndentOptions getIndentOptions(@NotNull AbstractBlockWrapper block) { + final Language language = block.getLanguage(); + if (language == null) { + return myIndentOptions; + } + final CommonCodeStyleSettings commonSettings = mySettings.getCommonSettings(language); + if (commonSettings == null) { + return myIndentOptions; + } + final CommonCodeStyleSettings.IndentOptions result = commonSettings.getIndentOptions(); + return result == null ? myIndentOptions : result; + } +} \ No newline at end of file From 7ecd63a59a7d0c54351f193be54e008592808807 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Dec 2015 17:10:30 +0300 Subject: [PATCH 08/34] extracted replaceWhiteSpace from FormatProcessor --- .../intellij/formatting/FormatProcessor.java | 98 ++++--------------- .../com/intellij/formatting/IndentInside.java | 6 +- .../intellij/formatting/LeafBlockWrapper.java | 2 +- .../com/intellij/formatting/WhiteSpace.java | 2 +- .../com/intellij/formatting/engine/Utils.java | 65 ++++++++++++ 5 files changed, 91 insertions(+), 82 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/Utils.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 4889acc09b7e..e498ada92770 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -17,10 +17,7 @@ package com.intellij.formatting; import com.intellij.diagnostic.LogMessageEx; -import com.intellij.formatting.engine.BlockIndentOptions; -import com.intellij.formatting.engine.State; -import com.intellij.formatting.engine.StateProcessor; -import com.intellij.formatting.engine.WrapBlocksState; +import com.intellij.formatting.engine.*; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; @@ -430,53 +427,6 @@ public class FormatProcessor { } } - private static int replaceWhiteSpace(final FormattingModel model, - @NotNull final LeafBlockWrapper block, - int shift, - final CharSequence _newWhiteSpace, - final CommonCodeStyleSettings.IndentOptions options - ) { - final WhiteSpace whiteSpace = block.getWhiteSpace(); - final TextRange textRange = whiteSpace.getTextRange(); - final TextRange wsRange = textRange.shiftRight(shift); - final String newWhiteSpace = _newWhiteSpace.toString(); - TextRange newWhiteSpaceRange = model instanceof FormattingModelEx - ? ((FormattingModelEx) model).replaceWhiteSpace(wsRange, block.getNode(), newWhiteSpace) - : model.replaceWhiteSpace(wsRange, newWhiteSpace); - - shift += newWhiteSpaceRange.getLength() - textRange.getLength(); - - if (block.isLeaf() && whiteSpace.containsLineFeeds() && block.containsLineFeeds()) { - final TextRange currentBlockRange = block.getTextRange().shiftRight(shift); - - IndentInside oldBlockIndent = whiteSpace.getInitialLastLineIndent(); - IndentInside whiteSpaceIndent = IndentInside.createIndentOn(IndentInside.getLastLine(newWhiteSpace)); - final int shiftInside = calcShift(oldBlockIndent, whiteSpaceIndent, options); - - if (shiftInside != 0 || !oldBlockIndent.equals(whiteSpaceIndent)) { - final TextRange newBlockRange = model.shiftIndentInsideRange(block.getNode(), currentBlockRange, shiftInside); - shift += newBlockRange.getLength() - block.getLength(); - } - } - return shift; - } - - @NotNull - private List collectBlocksToModify() { - List blocksToModify = new ArrayList(); - - for (LeafBlockWrapper block = myFirstTokenBlock; block != null; block = block.getNextBlock()) { - final WhiteSpace whiteSpace = block.getWhiteSpace(); - if (!whiteSpace.isReadOnly()) { - final String newWhiteSpace = whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)); - if (!whiteSpace.equalsToString(newWhiteSpace)) { - blocksToModify.add(block); - } - } - } - return blocksToModify; - } - private void processToken() { final SpacingImpl spaceProperty = myCurrentBlock.getSpaceProperty(); final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); @@ -1282,22 +1232,6 @@ public class FormatProcessor { return myLastWhiteSpace; } - /** - * Calculates difference in visual columns between the given indents. - * - * @param oldIndent old indent - * @param newIndent new indent - * @param options indent options to use - * @return difference in visual columns between the given indents - */ - private static int calcShift(@NotNull final IndentInside oldIndent, - @NotNull final IndentInside newIndent, - @NotNull final CommonCodeStyleSettings.IndentOptions options) - { - if (oldIndent.equals(newIndent)) return 0; - return newIndent.getSpacesCount(options) - oldIndent.getSpacesCount(options); - } - private class AdjustWhiteSpacesState extends State { @Override @@ -1334,6 +1268,25 @@ public class FormatProcessor { private ApplyChangesState(FormattingModel model) { myModel = model; } + + //myFirstTokenBlock + //myBlockIndentOptions + //mySettings + + private List collectBlocksToModify() { + List blocksToModify = new ArrayList(); + + for (LeafBlockWrapper block = myFirstTokenBlock; block != null; block = block.getNextBlock()) { + final WhiteSpace whiteSpace = block.getWhiteSpace(); + if (!whiteSpace.isReadOnly()) { + final String newWhiteSpace = whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)); + if (!whiteSpace.equalsToString(newWhiteSpace)) { + blocksToModify.add(block); + } + } + } + return blocksToModify; + } @Override public void prepare() { @@ -1341,16 +1294,7 @@ public class FormatProcessor { // call doModifications static method to ensure no access to state // thus we may clear formatting state reset(); - - myInfos = null; - myRootBlockWrapper = null; - myTextRangeToWrapper = null; - myPreviousDependencies = null; - myLastWhiteSpace = null; - myFirstTokenBlock = null; - myLastTokenBlock = null; myDisposed = true; - if (myBlocksToModify.isEmpty()) { setDone(true); return; @@ -1380,7 +1324,7 @@ public class FormatProcessor { @Override protected void doIteration() { LeafBlockWrapper blockWrapper = myBlocksToModify.get(myIndex); - myShift = replaceWhiteSpace( + myShift = Utils.replaceWhiteSpace( myModel, blockWrapper, myShift, diff --git a/platform/lang-impl/src/com/intellij/formatting/IndentInside.java b/platform/lang-impl/src/com/intellij/formatting/IndentInside.java index 709a08d7d3ee..b6b126f0812f 100644 --- a/platform/lang-impl/src/com/intellij/formatting/IndentInside.java +++ b/platform/lang-impl/src/com/intellij/formatting/IndentInside.java @@ -21,7 +21,7 @@ import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -class IndentInside { +public class IndentInside { public int whiteSpaces = 0; public int tabs = 0; @@ -66,7 +66,7 @@ class IndentInside { } @NotNull - static IndentInside createIndentOn(@Nullable final CharSequence lastLine) { + public static IndentInside createIndentOn(@Nullable final CharSequence lastLine) { final IndentInside result = new IndentInside(); if (lastLine == null) { return result; @@ -79,7 +79,7 @@ class IndentInside { } @NotNull - static CharSequence getLastLine(@NotNull final CharSequence text) { + public static CharSequence getLastLine(@NotNull final CharSequence text) { int i = CharArrayUtil.shiftBackwardUntil(text, text.length() - 1, "\n"); if (i < 0) { return text; diff --git a/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java index 8ba16b4ddddd..3ebceb5c718c 100644 --- a/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java @@ -21,7 +21,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import org.jetbrains.annotations.Nullable; -class LeafBlockWrapper extends AbstractBlockWrapper { +public class LeafBlockWrapper extends AbstractBlockWrapper { private static final int CONTAIN_LINE_FEEDS = 4; private static final int READ_ONLY = 8; private static final int LEAF = 16; diff --git a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java index 63692a899925..67437c37e020 100644 --- a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java +++ b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java @@ -45,7 +45,7 @@ import java.util.ArrayList; *

* Not thread-safe. */ -class WhiteSpace { +public class WhiteSpace { private static final char LINE_FEED = '\n'; diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/Utils.java b/platform/lang-impl/src/com/intellij/formatting/engine/Utils.java new file mode 100644 index 000000000000..cc9084782ccf --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/Utils.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.*; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import org.jetbrains.annotations.NotNull; + +public class Utils { + + + private static int calcShift(@NotNull final IndentInside oldIndent, + @NotNull final IndentInside newIndent, + @NotNull final CommonCodeStyleSettings.IndentOptions options) + { + if (oldIndent.equals(newIndent)) return 0; + return newIndent.getSpacesCount(options) - oldIndent.getSpacesCount(options); + } + + public static int replaceWhiteSpace(final FormattingModel model, + @NotNull final LeafBlockWrapper block, + int shift, + final CharSequence _newWhiteSpace, + final CommonCodeStyleSettings.IndentOptions options + ) { + final WhiteSpace whiteSpace = block.getWhiteSpace(); + final TextRange textRange = whiteSpace.getTextRange(); + final TextRange wsRange = textRange.shiftRight(shift); + final String newWhiteSpace = _newWhiteSpace.toString(); + TextRange newWhiteSpaceRange = model instanceof FormattingModelEx + ? ((FormattingModelEx) model).replaceWhiteSpace(wsRange, block.getNode(), newWhiteSpace) + : model.replaceWhiteSpace(wsRange, newWhiteSpace); + + shift += newWhiteSpaceRange.getLength() - textRange.getLength(); + + if (block.isLeaf() && whiteSpace.containsLineFeeds() && block.containsLineFeeds()) { + final TextRange currentBlockRange = block.getTextRange().shiftRight(shift); + + IndentInside oldBlockIndent = whiteSpace.getInitialLastLineIndent(); + IndentInside whiteSpaceIndent = IndentInside.createIndentOn(IndentInside.getLastLine(newWhiteSpace)); + final int shiftInside = calcShift(oldBlockIndent, whiteSpaceIndent, options); + + if (shiftInside != 0 || !oldBlockIndent.equals(whiteSpaceIndent)) { + final TextRange newBlockRange = model.shiftIndentInsideRange(block.getNode(), currentBlockRange, shiftInside); + shift += newBlockRange.getLength() - block.getLength(); + } + } + return shift; + } + +} From e7128c0f8d267b4891b8642b8a47f7a9f29efa9a Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Dec 2015 17:15:29 +0300 Subject: [PATCH 09/34] moved couple methods into ApplyChangesState --- .../intellij/formatting/FormatProcessor.java | 124 +++++++++--------- .../{Utils.java => FormatProcessorUtils.java} | 2 +- 2 files changed, 63 insertions(+), 63 deletions(-) rename platform/lang-impl/src/com/intellij/formatting/engine/{Utils.java => FormatProcessorUtils.java} (98%) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index e498ada92770..388c4a1d3b62 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -358,56 +358,6 @@ public class FormatProcessor { myJavaIndentOptions = javaIndentOptions; } - /** - * Performs formatter changes in a series of blocks, for each block a new contents of document is calculated - * and whole document is replaced in one operation. - * - * @param blocksToModify changes introduced by formatter - * @param model current formatting model - */ - @SuppressWarnings({"deprecation"}) - private void applyChangesAtRewriteMode(@NotNull final List blocksToModify, - @NotNull final FormattingModel model) - { - FormattingDocumentModel documentModel = model.getDocumentModel(); - Document document = documentModel.getDocument(); - CaretOffsetUpdater caretOffsetUpdater = new CaretOffsetUpdater(document); - - if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(true); - try { - List changes = new ArrayList(); - int shift = 0; - int currentIterationShift = 0; - for (LeafBlockWrapper block : blocksToModify) { - WhiteSpace whiteSpace = block.getWhiteSpace(); - CharSequence newWs = documentModel.adjustWhiteSpaceIfNecessary( - whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)), whiteSpace.getStartOffset(), - whiteSpace.getEndOffset(), block.getNode(), false - ); - if (changes.size() > 10000) { - caretOffsetUpdater.update(changes); - CharSequence mergeResult = BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); - document.replaceString(0, document.getTextLength(), mergeResult); - shift += currentIterationShift; - currentIterationShift = 0; - changes.clear(); - } - TextChangeImpl change = new TextChangeImpl(newWs, whiteSpace.getStartOffset() + shift, whiteSpace.getEndOffset() + shift); - currentIterationShift += change.getDiff(); - changes.add(change); - } - caretOffsetUpdater.update(changes); - CharSequence mergeResult = BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); - document.replaceString(0, document.getTextLength(), mergeResult); - } - finally { - if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(false); - } - - caretOffsetUpdater.restoreCaretLocations(); - cleanupBlocks(blocksToModify); - } - private static void cleanupBlocks(List blocks) { for (LeafBlockWrapper block : blocks) { block.getParent().dispose(); @@ -416,17 +366,6 @@ public class FormatProcessor { blocks.clear(); } - @Nullable - private static DocumentEx getAffectedDocument(final FormattingModel model) { - final Document document = model.getDocumentModel().getDocument(); - if (document instanceof DocumentEx) { - return (DocumentEx)document; - } - else { - return null; - } - } - private void processToken() { final SpacingImpl spaceProperty = myCurrentBlock.getSpaceProperty(); final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); @@ -1273,6 +1212,67 @@ public class FormatProcessor { //myBlockIndentOptions //mySettings + /** + * Performs formatter changes in a series of blocks, for each block a new contents of document is calculated + * and whole document is replaced in one operation. + * + * @param blocksToModify changes introduced by formatter + * @param model current formatting model + */ + @SuppressWarnings({"deprecation"}) + private void applyChangesAtRewriteMode(@NotNull final List blocksToModify, + @NotNull final FormattingModel model) { + FormattingDocumentModel documentModel = model.getDocumentModel(); + Document document = documentModel.getDocument(); + CaretOffsetUpdater caretOffsetUpdater = new CaretOffsetUpdater(document); + + if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(true); + try { + List changes = new ArrayList(); + int shift = 0; + int currentIterationShift = 0; + for (LeafBlockWrapper block : blocksToModify) { + WhiteSpace whiteSpace = block.getWhiteSpace(); + CharSequence newWs = documentModel.adjustWhiteSpaceIfNecessary( + whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)), whiteSpace.getStartOffset(), + whiteSpace.getEndOffset(), block.getNode(), false + ); + if (changes.size() > 10000) { + caretOffsetUpdater.update(changes); + CharSequence mergeResult = + BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); + document.replaceString(0, document.getTextLength(), mergeResult); + shift += currentIterationShift; + currentIterationShift = 0; + changes.clear(); + } + TextChangeImpl change = new TextChangeImpl(newWs, whiteSpace.getStartOffset() + shift, whiteSpace.getEndOffset() + shift); + currentIterationShift += change.getDiff(); + changes.add(change); + } + caretOffsetUpdater.update(changes); + CharSequence mergeResult = BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); + document.replaceString(0, document.getTextLength(), mergeResult); + } + finally { + if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(false); + } + + caretOffsetUpdater.restoreCaretLocations(); + cleanupBlocks(blocksToModify); + } + + @Nullable + private DocumentEx getAffectedDocument(final FormattingModel model) { + final Document document = model.getDocumentModel().getDocument(); + if (document instanceof DocumentEx) { + return (DocumentEx)document; + } + else { + return null; + } + } + private List collectBlocksToModify() { List blocksToModify = new ArrayList(); @@ -1324,7 +1324,7 @@ public class FormatProcessor { @Override protected void doIteration() { LeafBlockWrapper blockWrapper = myBlocksToModify.get(myIndex); - myShift = Utils.replaceWhiteSpace( + myShift = FormatProcessorUtils.replaceWhiteSpace( myModel, blockWrapper, myShift, diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/Utils.java b/platform/lang-impl/src/com/intellij/formatting/engine/FormatProcessorUtils.java similarity index 98% rename from platform/lang-impl/src/com/intellij/formatting/engine/Utils.java rename to platform/lang-impl/src/com/intellij/formatting/engine/FormatProcessorUtils.java index cc9084782ccf..c6b8066edb26 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/Utils.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/FormatProcessorUtils.java @@ -20,7 +20,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import org.jetbrains.annotations.NotNull; -public class Utils { +public class FormatProcessorUtils { private static int calcShift(@NotNull final IndentInside oldIndent, From e2a48a0c2eabb1f051e007a0fe1512c6bb0e03e0 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Dec 2015 17:44:56 +0300 Subject: [PATCH 10/34] Removed method --- .../com/intellij/formatting/FormatProcessor.java | 15 ++------------- .../com/intellij/formatting/FormatterImpl.java | 2 -- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 388c4a1d3b62..d6d9712206f6 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -28,7 +28,6 @@ import com.intellij.openapi.editor.TextChange; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.impl.BulkChangesMerger; import com.intellij.openapi.editor.impl.TextChangeImpl; -import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; @@ -160,7 +159,6 @@ public class FormatProcessor { private WhiteSpace myLastWhiteSpace; private boolean myDisposed; - private CommonCodeStyleSettings.IndentOptions myJavaIndentOptions; private final int myRightMargin; @NotNull @@ -353,11 +351,7 @@ public class FormatProcessor { myStateProcessor.iteration(); } } - - public void setJavaIndentOptions(final CommonCodeStyleSettings.IndentOptions javaIndentOptions) { - myJavaIndentOptions = javaIndentOptions; - } - + private static void cleanupBlocks(List blocks) { for (LeafBlockWrapper block : blocks) { block.getParent().dispose(); @@ -1299,12 +1293,7 @@ public class FormatProcessor { setDone(true); return; } - - //for GeneralCodeFormatterTest - if (myJavaIndentOptions == null) { - myJavaIndentOptions = mySettings.getIndentOptions(StdFileTypes.JAVA); - } - + myProgressCallback.beforeApplyingFormatChanges(myBlocksToModify); final int blocksToModifyCount = myBlocksToModify.size(); diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index f8dbc4ed53f9..1acbc67370d7 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -181,8 +181,6 @@ public class FormatterImpl extends FormatterEx FormatProcessor processor = new FormatProcessor( model.getDocumentModel(), model.getRootBlock(), settings, indentOptions, affectedRanges, FormattingProgressCallback.EMPTY ); - processor.setJavaIndentOptions(javaIndentOptions); - processor.format(model); return processor; } From 870fc1ffe1fb6319bb2e8bf04c6c812b21c7071a Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Dec 2015 17:53:37 +0300 Subject: [PATCH 11/34] Minor --- .../intellij/formatting/FormatProcessor.java | 27 ++++++++++--------- .../formatting/engine/BlockIndentOptions.java | 4 +++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index d6d9712206f6..e5e73aa784c0 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -278,7 +278,7 @@ public class FormatProcessor { if (sequentially) { myStateProcessor.setNextState(new AdjustWhiteSpacesState()); myStateProcessor.setNextState(new ExpandChildrenIndent()); - myStateProcessor.setNextState(new ApplyChangesState(model)); + myStateProcessor.setNextState(new ApplyChangesState(model, myBlockIndentOptions)); } else { formatWithoutRealModifications(false); @@ -337,7 +337,7 @@ public class FormatProcessor { public void performModifications(FormattingModel model, boolean sequentially) { assert !myDisposed; - myStateProcessor.setNextState(new ApplyChangesState(model)); + myStateProcessor.setNextState(new ApplyChangesState(model, myBlockIndentOptions)); if (sequentially) { return; @@ -351,14 +351,6 @@ public class FormatProcessor { myStateProcessor.iteration(); } } - - private static void cleanupBlocks(List blocks) { - for (LeafBlockWrapper block : blocks) { - block.getParent().dispose(); - block.dispose(); - } - blocks.clear(); - } private void processToken() { final SpacingImpl spaceProperty = myCurrentBlock.getSpaceProperty(); @@ -1197,9 +1189,12 @@ public class FormatProcessor { private int myShift; private int myIndex; private boolean myResetBulkUpdateState; + + private BlockIndentOptions myBlockIndentOptions; - private ApplyChangesState(FormattingModel model) { + private ApplyChangesState(FormattingModel model, BlockIndentOptions indentOptions) { myModel = model; + myBlockIndentOptions = indentOptions; } //myFirstTokenBlock @@ -1256,6 +1251,14 @@ public class FormatProcessor { cleanupBlocks(blocksToModify); } + private void cleanupBlocks(List blocks) { + for (LeafBlockWrapper block : blocks) { + block.getParent().dispose(); + block.dispose(); + } + blocks.clear(); + } + @Nullable private DocumentEx getAffectedDocument(final FormattingModel model) { final Document document = model.getDocumentModel().getDocument(); @@ -1318,7 +1321,7 @@ public class FormatProcessor { blockWrapper, myShift, blockWrapper.getWhiteSpace().generateWhiteSpace(myBlockIndentOptions.getIndentOptions(blockWrapper)), - myDefaultIndentOption + myBlockIndentOptions.getIndentOptions() ); myProgressCallback.afterApplyingChange(blockWrapper); // block could be gc'd diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java b/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java index a1872bb15f9e..405663fd7a03 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java @@ -29,6 +29,10 @@ public class BlockIndentOptions { mySettings = settings; myIndentOptions = indentOptions; } + + public CommonCodeStyleSettings.IndentOptions getIndentOptions() { + return myIndentOptions; + } @NotNull public CommonCodeStyleSettings.IndentOptions getIndentOptions(@NotNull AbstractBlockWrapper block) { From 76b69d18e73fd6d7aca1bde6c3f7095d289c21cb Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Dec 2015 18:43:35 +0300 Subject: [PATCH 12/34] ApplyChangesState extracted from FormatProcessor --- .../intellij/formatting/FormatProcessor.java | 234 +----------------- .../formatting/engine/ApplyChangesState.java | 229 +++++++++++++++++ .../formatting/engine/CaretOffsetUpdater.java | 51 ++++ 3 files changed, 287 insertions(+), 227 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/CaretOffsetUpdater.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index e5e73aa784c0..e8c406d470db 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -22,13 +22,8 @@ import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.TextChange; -import com.intellij.openapi.editor.ex.DocumentEx; -import com.intellij.openapi.editor.impl.BulkChangesMerger; -import com.intellij.openapi.editor.impl.TextChangeImpl; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -36,7 +31,6 @@ import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; -import com.intellij.util.ui.UIUtil; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,15 +48,7 @@ public 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. - *

- * 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 Set myAlignmentsInsideRangesToModify = null; private boolean myReformatContext; @@ -112,6 +98,7 @@ public class FormatProcessor { private LeafBlockWrapper myFirstWrappedBlockOnLine = null; private LeafBlockWrapper myFirstTokenBlock; + private Ref myFirstTokenBlockRef = Ref.create(); private LeafBlockWrapper myLastTokenBlock; /** @@ -198,6 +185,7 @@ public class FormatProcessor { myInfos = builder.getBlockToInfoMap(); myRootBlockWrapper = builder.getRootBlockWrapper(); myFirstTokenBlock = builder.getFirstTokenBlock(); + myFirstTokenBlockRef.set(myFirstTokenBlock); myLastTokenBlock = builder.getLastTokenBlock(); myCurrentBlock = myFirstTokenBlock; myTextRangeToWrapper = buildTextRangeToInfoMap(myFirstTokenBlock); @@ -278,7 +266,7 @@ public class FormatProcessor { if (sequentially) { myStateProcessor.setNextState(new AdjustWhiteSpacesState()); myStateProcessor.setNextState(new ExpandChildrenIndent()); - myStateProcessor.setNextState(new ApplyChangesState(model, myBlockIndentOptions)); + myStateProcessor.setNextState(new ApplyChangesState(myFirstTokenBlockRef, model, myBlockIndentOptions, myProgressCallback)); } else { formatWithoutRealModifications(false); @@ -337,7 +325,7 @@ public class FormatProcessor { public void performModifications(FormattingModel model, boolean sequentially) { assert !myDisposed; - myStateProcessor.setNextState(new ApplyChangesState(model, myBlockIndentOptions)); + myStateProcessor.setNextState(new ApplyChangesState(myFirstTokenBlockRef, model, myBlockIndentOptions, myProgressCallback)); if (sequentially) { return; @@ -1181,215 +1169,7 @@ public class FormatProcessor { } } } - - private class ApplyChangesState extends State { - - private final FormattingModel myModel; - private List myBlocksToModify; - private int myShift; - private int myIndex; - private boolean myResetBulkUpdateState; - - private BlockIndentOptions myBlockIndentOptions; - - private ApplyChangesState(FormattingModel model, BlockIndentOptions indentOptions) { - myModel = model; - myBlockIndentOptions = indentOptions; - } - - //myFirstTokenBlock - //myBlockIndentOptions - //mySettings - - /** - * Performs formatter changes in a series of blocks, for each block a new contents of document is calculated - * and whole document is replaced in one operation. - * - * @param blocksToModify changes introduced by formatter - * @param model current formatting model - */ - @SuppressWarnings({"deprecation"}) - private void applyChangesAtRewriteMode(@NotNull final List blocksToModify, - @NotNull final FormattingModel model) { - FormattingDocumentModel documentModel = model.getDocumentModel(); - Document document = documentModel.getDocument(); - CaretOffsetUpdater caretOffsetUpdater = new CaretOffsetUpdater(document); - - if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(true); - try { - List changes = new ArrayList(); - int shift = 0; - int currentIterationShift = 0; - for (LeafBlockWrapper block : blocksToModify) { - WhiteSpace whiteSpace = block.getWhiteSpace(); - CharSequence newWs = documentModel.adjustWhiteSpaceIfNecessary( - whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)), whiteSpace.getStartOffset(), - whiteSpace.getEndOffset(), block.getNode(), false - ); - if (changes.size() > 10000) { - caretOffsetUpdater.update(changes); - CharSequence mergeResult = - BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); - document.replaceString(0, document.getTextLength(), mergeResult); - shift += currentIterationShift; - currentIterationShift = 0; - changes.clear(); - } - TextChangeImpl change = new TextChangeImpl(newWs, whiteSpace.getStartOffset() + shift, whiteSpace.getEndOffset() + shift); - currentIterationShift += change.getDiff(); - changes.add(change); - } - caretOffsetUpdater.update(changes); - CharSequence mergeResult = BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); - document.replaceString(0, document.getTextLength(), mergeResult); - } - finally { - if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(false); - } - - caretOffsetUpdater.restoreCaretLocations(); - cleanupBlocks(blocksToModify); - } - - private void cleanupBlocks(List blocks) { - for (LeafBlockWrapper block : blocks) { - block.getParent().dispose(); - block.dispose(); - } - blocks.clear(); - } - - @Nullable - private DocumentEx getAffectedDocument(final FormattingModel model) { - final Document document = model.getDocumentModel().getDocument(); - if (document instanceof DocumentEx) { - return (DocumentEx)document; - } - else { - return null; - } - } - - private List collectBlocksToModify() { - List blocksToModify = new ArrayList(); - - for (LeafBlockWrapper block = myFirstTokenBlock; block != null; block = block.getNextBlock()) { - final WhiteSpace whiteSpace = block.getWhiteSpace(); - if (!whiteSpace.isReadOnly()) { - final String newWhiteSpace = whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)); - if (!whiteSpace.equalsToString(newWhiteSpace)) { - blocksToModify.add(block); - } - } - } - return blocksToModify; - } - - @Override - public void prepare() { - myBlocksToModify = collectBlocksToModify(); - // call doModifications static method to ensure no access to state - // thus we may clear formatting state - reset(); - myDisposed = true; - if (myBlocksToModify.isEmpty()) { - setDone(true); - return; - } - - myProgressCallback.beforeApplyingFormatChanges(myBlocksToModify); - - final int blocksToModifyCount = myBlocksToModify.size(); - if (blocksToModifyCount > BULK_REPLACE_OPTIMIZATION_CRITERIA) { - applyChangesAtRewriteMode(myBlocksToModify, myModel); - setDone(true); - } - else if (blocksToModifyCount > 50) { - DocumentEx updatedDocument = getAffectedDocument(myModel); - if (updatedDocument != null) { - updatedDocument.setInBulkUpdate(true); - myResetBulkUpdateState = true; - } - } - } - - @Override - protected void doIteration() { - LeafBlockWrapper blockWrapper = myBlocksToModify.get(myIndex); - myShift = FormatProcessorUtils.replaceWhiteSpace( - myModel, - blockWrapper, - myShift, - blockWrapper.getWhiteSpace().generateWhiteSpace(myBlockIndentOptions.getIndentOptions(blockWrapper)), - myBlockIndentOptions.getIndentOptions() - ); - myProgressCallback.afterApplyingChange(blockWrapper); - // block could be gc'd - blockWrapper.getParent().dispose(); - blockWrapper.dispose(); - myBlocksToModify.set(myIndex, null); - myIndex++; - - if (myIndex >= myBlocksToModify.size()) { - setDone(true); - } - } - - @Override - protected void setDone(boolean done) { - super.setDone(done); - - if (myResetBulkUpdateState) { - DocumentEx document = getAffectedDocument(myModel); - if (document != null) { - document.setInBulkUpdate(false); - myResetBulkUpdateState = false; - } - } - - if (done) { - myModel.commitChanges(); - } - } - - @Override - public void stop() { - if (myIndex > 0) { - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - myModel.commitChanges(); - } - }); - } - } - } - - private static class CaretOffsetUpdater { - private final Map myCaretOffsets = new HashMap(); - - private CaretOffsetUpdater(@NotNull Document document) { - Editor[] editors = EditorFactory.getInstance().getEditors(document); - for (Editor editor : editors) { - myCaretOffsets.put(editor, editor.getCaretModel().getOffset()); - } - } - - private void update(@NotNull List changes) { - BulkChangesMerger merger = BulkChangesMerger.INSTANCE; - for (Map.Entry entry : myCaretOffsets.entrySet()) { - entry.setValue(merger.updateOffset(entry.getValue(), changes)); - } - } - - private void restoreCaretLocations() { - for (Map.Entry entry : myCaretOffsets.entrySet()) { - entry.getKey().getCaretModel().moveToOffset(entry.getValue()); - } - } - } - - + public static class FormatOptions { public CodeStyleSettings mySettings; public CommonCodeStyleSettings.IndentOptions myIndentOptions; diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java b/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java new file mode 100644 index 000000000000..1a7602a15c63 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java @@ -0,0 +1,229 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.*; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.TextChange; +import com.intellij.openapi.editor.ex.DocumentEx; +import com.intellij.openapi.editor.impl.BulkChangesMerger; +import com.intellij.openapi.editor.impl.TextChangeImpl; +import com.intellij.openapi.util.Ref; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class ApplyChangesState extends State { + + /** + * 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. + *

+ * 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 final FormattingModel myModel; + + private final Ref myFirstTokenBlock; + private final FormattingProgressCallback myProgressCallback; + + private List myBlocksToModify; + private int myShift; + private int myIndex; + private boolean myResetBulkUpdateState; + + private BlockIndentOptions myBlockIndentOptions; + + public ApplyChangesState(Ref firstBlock, + FormattingModel model, + BlockIndentOptions indentOptions, + FormattingProgressCallback progressCallback) { + myModel = model; + myBlockIndentOptions = indentOptions; + myFirstTokenBlock = firstBlock; + myProgressCallback = progressCallback; + } + + /** + * Performs formatter changes in a series of blocks, for each block a new contents of document is calculated + * and whole document is replaced in one operation. + * + * @param blocksToModify changes introduced by formatter + * @param model current formatting model + */ + @SuppressWarnings({"deprecation"}) + private void applyChangesAtRewriteMode(@NotNull final List blocksToModify, + @NotNull final FormattingModel model) { + FormattingDocumentModel documentModel = model.getDocumentModel(); + Document document = documentModel.getDocument(); + CaretOffsetUpdater caretOffsetUpdater = new CaretOffsetUpdater(document); + + if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(true); + try { + List changes = new ArrayList(); + int shift = 0; + int currentIterationShift = 0; + for (LeafBlockWrapper block : blocksToModify) { + WhiteSpace whiteSpace = block.getWhiteSpace(); + CharSequence newWs = documentModel.adjustWhiteSpaceIfNecessary( + whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)), whiteSpace.getStartOffset(), + whiteSpace.getEndOffset(), block.getNode(), false + ); + if (changes.size() > 10000) { + caretOffsetUpdater.update(changes); + CharSequence mergeResult = + BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); + document.replaceString(0, document.getTextLength(), mergeResult); + shift += currentIterationShift; + currentIterationShift = 0; + changes.clear(); + } + TextChangeImpl change = new TextChangeImpl(newWs, whiteSpace.getStartOffset() + shift, whiteSpace.getEndOffset() + shift); + currentIterationShift += change.getDiff(); + changes.add(change); + } + caretOffsetUpdater.update(changes); + CharSequence mergeResult = BulkChangesMerger.INSTANCE.mergeToCharSequence(document.getChars(), document.getTextLength(), changes); + document.replaceString(0, document.getTextLength(), mergeResult); + } + finally { + if (document instanceof DocumentEx) ((DocumentEx)document).setInBulkUpdate(false); + } + + caretOffsetUpdater.restoreCaretLocations(); + cleanupBlocks(blocksToModify); + } + + private static void cleanupBlocks(List blocks) { + for (LeafBlockWrapper block : blocks) { + block.getParent().dispose(); + block.dispose(); + } + blocks.clear(); + } + + @Nullable + private static DocumentEx getAffectedDocument(final FormattingModel model) { + final Document document = model.getDocumentModel().getDocument(); + if (document instanceof DocumentEx) { + return (DocumentEx)document; + } + else { + return null; + } + } + + private List collectBlocksToModify() { + List blocksToModify = new ArrayList(); + + for (LeafBlockWrapper block = myFirstTokenBlock.get(); block != null; block = block.getNextBlock()) { + final WhiteSpace whiteSpace = block.getWhiteSpace(); + if (!whiteSpace.isReadOnly()) { + final String newWhiteSpace = whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)); + if (!whiteSpace.equalsToString(newWhiteSpace)) { + blocksToModify.add(block); + } + } + } + return blocksToModify; + } + + @Override + public void prepare() { + myBlocksToModify = collectBlocksToModify(); + // call doModifications static method to ensure no access to state + // thus we may clear formatting state + + //reset(); + //myDisposed = true; + + if (myBlocksToModify.isEmpty()) { + setDone(true); + return; + } + + myProgressCallback.beforeApplyingFormatChanges(myBlocksToModify); + + final int blocksToModifyCount = myBlocksToModify.size(); + if (blocksToModifyCount > BULK_REPLACE_OPTIMIZATION_CRITERIA) { + applyChangesAtRewriteMode(myBlocksToModify, myModel); + setDone(true); + } + else if (blocksToModifyCount > 50) { + DocumentEx updatedDocument = getAffectedDocument(myModel); + if (updatedDocument != null) { + updatedDocument.setInBulkUpdate(true); + myResetBulkUpdateState = true; + } + } + } + + @Override + protected void doIteration() { + LeafBlockWrapper blockWrapper = myBlocksToModify.get(myIndex); + myShift = FormatProcessorUtils.replaceWhiteSpace( + myModel, + blockWrapper, + myShift, + blockWrapper.getWhiteSpace().generateWhiteSpace(myBlockIndentOptions.getIndentOptions(blockWrapper)), + myBlockIndentOptions.getIndentOptions() + ); + myProgressCallback.afterApplyingChange(blockWrapper); + // block could be gc'd + blockWrapper.getParent().dispose(); + blockWrapper.dispose(); + myBlocksToModify.set(myIndex, null); + myIndex++; + + if (myIndex >= myBlocksToModify.size()) { + setDone(true); + } + } + + @Override + protected void setDone(boolean done) { + super.setDone(done); + + if (myResetBulkUpdateState) { + DocumentEx document = getAffectedDocument(myModel); + if (document != null) { + document.setInBulkUpdate(false); + myResetBulkUpdateState = false; + } + } + + if (done) { + myModel.commitChanges(); + } + } + + @Override + public void stop() { + if (myIndex > 0) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + myModel.commitChanges(); + } + }); + } + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/CaretOffsetUpdater.java b/platform/lang-impl/src/com/intellij/formatting/engine/CaretOffsetUpdater.java new file mode 100644 index 000000000000..1423dfafb619 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/CaretOffsetUpdater.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.TextChange; +import com.intellij.openapi.editor.impl.BulkChangesMerger; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CaretOffsetUpdater { + private final Map myCaretOffsets = new HashMap(); + + public CaretOffsetUpdater(@NotNull Document document) { + Editor[] editors = EditorFactory.getInstance().getEditors(document); + for (Editor editor : editors) { + myCaretOffsets.put(editor, editor.getCaretModel().getOffset()); + } + } + + public void update(@NotNull List changes) { + BulkChangesMerger merger = BulkChangesMerger.INSTANCE; + for (Map.Entry entry : myCaretOffsets.entrySet()) { + entry.setValue(merger.updateOffset(entry.getValue(), changes)); + } + } + + public void restoreCaretLocations() { + for (Map.Entry entry : myCaretOffsets.entrySet()) { + entry.getKey().getCaretModel().moveToOffset(entry.getValue()); + } + } + } \ No newline at end of file From eaf704e0711718c36003b26ac054f68383778010 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 18 Dec 2015 12:00:46 +0300 Subject: [PATCH 13/34] extracted blocks helper, responsible for mapping offset to LeafBlock --- .../formatting/DependantSpacingImpl.java | 2 +- .../intellij/formatting/FormatProcessor.java | 79 ++--------------- .../intellij/formatting/FormatterImpl.java | 6 +- .../formatting/engine/BlocksHelper.java | 88 +++++++++++++++++++ 4 files changed, 101 insertions(+), 74 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/BlocksHelper.java diff --git a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java index 4b9d8c1023b7..9e470dec0913 100644 --- a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java @@ -95,7 +95,7 @@ public class DependantSpacingImpl extends SpacingImpl { boolean atLeastOneDependencyRangeContainsLf = false; for (TextRange dependency : myDependentRegionRanges) { - atLeastOneDependencyRangeContainsLf |= formatter.containsLineFeeds(dependency); + atLeastOneDependencyRangeContainsLf |= formatter.getBlocksHelper().containsLineFeeds(dependency); } if (atLeastOneDependencyRangeContainsLf) myFlags |= DEPENDENCE_CONTAINS_LF_MASK; diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index e8c406d470db..b7a757d5611f 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -31,7 +31,6 @@ import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; -import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,7 +56,8 @@ public class FormatProcessor { private Map myInfos; private CompositeBlockWrapper myRootBlockWrapper; - private TIntObjectHashMap myTextRangeToWrapper; + + private BlocksHelper myBlocksHelper; private final BlockIndentOptions myBlockIndentOptions; private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; @@ -188,10 +188,10 @@ public class FormatProcessor { myFirstTokenBlockRef.set(myFirstTokenBlock); myLastTokenBlock = builder.getLastTokenBlock(); myCurrentBlock = myFirstTokenBlock; - myTextRangeToWrapper = buildTextRangeToInfoMap(myFirstTokenBlock); - int lastBlockOffset = getLastBlock().getEndOffset(); + int lastBlockOffset = myLastTokenBlock.getEndOffset(); myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); myLastWhiteSpace.append(Math.max(lastBlockOffset, builder.getEndOffset()), model, myDefaultIndentOption); + myBlocksHelper = new BlocksHelper(myFirstTokenBlock, myLastTokenBlock); myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); myTotalBlocksWithAlignments = builder.getBlocksToAlign().values().size(); myExpandableIndents = builder.getExpandableIndentsBlocks(); @@ -199,6 +199,10 @@ public class FormatProcessor { }); myStateProcessor = new StateProcessor(wrapState); } + + public BlocksHelper getBlocksHelper() { + return myBlocksHelper; + } private int getRightMargin(Block rootBlock) { Language language = null; @@ -217,24 +221,6 @@ public class FormatProcessor { return mySettings.getRightMargin(language); } - private LeafBlockWrapper getLastBlock() { - LeafBlockWrapper result = myFirstTokenBlock; - while (result.getNextBlock() != null) { - result = result.getNextBlock(); - } - return result; - } - - private static TIntObjectHashMap buildTextRangeToInfoMap(final LeafBlockWrapper first) { - final TIntObjectHashMap result = new TIntObjectHashMap(); - LeafBlockWrapper current = first; - while (current != null) { - result.put(current.getStartOffset(), current); - current = current.getNextBlock(); - } - return result; - } - public void format(FormattingModel model) { format(model, false); } @@ -427,7 +413,7 @@ public class FormatProcessor { } final boolean containedLineFeeds = spacing.getMinLineFeeds() > 0; - final boolean containsLineFeeds = containsLineFeeds(textRange); + final boolean containsLineFeeds = myBlocksHelper.containsLineFeeds(textRange); if (containedLineFeeds != containsLineFeeds) { spacing.setDependentRegionLinefeedStatusChanged(); @@ -822,53 +808,6 @@ public class FormatProcessor { } } - public boolean containsLineFeeds(final TextRange dependency) { - LeafBlockWrapper child = myTextRangeToWrapper.get(dependency.getStartOffset()); - if (child == null) return false; - if (child.containsLineFeeds()) return true; - final int endOffset = dependency.getEndOffset(); - while (child.getEndOffset() < endOffset) { - child = child.getNextBlock(); - if (child == null) return false; - if (child.getWhiteSpace().containsLineFeeds()) return true; - if (child.containsLineFeeds()) return true; - } - return false; - } - - @Nullable - public LeafBlockWrapper getBlockAtOrAfter(final int startOffset) { - int current = startOffset; - LeafBlockWrapper result = null; - while (current < myLastWhiteSpace.getStartOffset()) { - final LeafBlockWrapper currentValue = myTextRangeToWrapper.get(current); - if (currentValue != null) { - result = currentValue; - break; - } - current++; - } - - LeafBlockWrapper prevBlock = getPrevBlock(result); - - if (prevBlock != null && prevBlock.contains(startOffset)) { - return prevBlock; - } - else { - return result; - } - } - - @Nullable - private LeafBlockWrapper getPrevBlock(@Nullable final LeafBlockWrapper result) { - if (result != null) { - return result.getPreviousBlock(); - } - else { - return myLastTokenBlock; - } - } - public void setAllWhiteSpacesAreReadOnly() { LeafBlockWrapper current = myFirstTokenBlock; while (current != null) { diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index 1acbc67370d7..6bda4f0849b1 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -361,7 +361,7 @@ public class FormatterImpl extends FormatterEx final FormatProcessor processor = buildProcessorAndWrapBlocks( model, block, settings, indentOptions, new FormatTextRanges(affectedRange, true) ); - final LeafBlockWrapper blockBefore = processor.getBlockAtOrAfter(affectedRange.getStartOffset()); + final LeafBlockWrapper blockBefore = processor.getBlocksHelper().getBlockAtOrAfter(affectedRange.getStartOffset()); LOG.assertTrue(blockBefore != null); WhiteSpace whiteSpace = blockBefore.getWhiteSpace(); LOG.assertTrue(whiteSpace != null); @@ -471,7 +471,7 @@ public class FormatterImpl extends FormatterEx documentModel, block, settings, indentOptions, new FormatTextRanges(affectedRange, true), offset ); - final LeafBlockWrapper blockAfterOffset = processor.getBlockAtOrAfter(offset); + final LeafBlockWrapper blockAfterOffset = processor.getBlocksHelper().getBlockAtOrAfter(offset); if (blockAfterOffset != null && blockAfterOffset.contains(offset)) { return offset; @@ -597,7 +597,7 @@ public class FormatterImpl extends FormatterEx final FormatProcessor processor = buildProcessorAndWrapBlocks( documentModel, block, settings, indentOptions, new FormatTextRanges(affectedRange, true), offset ); - final LeafBlockWrapper blockAfterOffset = processor.getBlockAtOrAfter(offset); + final LeafBlockWrapper blockAfterOffset = processor.getBlocksHelper().getBlockAtOrAfter(offset); if (blockAfterOffset != null && !blockAfterOffset.contains(offset)) { final WhiteSpace whiteSpace = blockAfterOffset.getWhiteSpace(); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/BlocksHelper.java b/platform/lang-impl/src/com/intellij/formatting/engine/BlocksHelper.java new file mode 100644 index 000000000000..59241909f17c --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/BlocksHelper.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.LeafBlockWrapper; +import com.intellij.openapi.util.TextRange; +import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.Nullable; + +public class BlocksHelper { + private final LeafBlockWrapper myLastBlock; + private TIntObjectHashMap myTextRangeToWrapper; + + public BlocksHelper(LeafBlockWrapper first, LeafBlockWrapper last) { + myLastBlock = last; + myTextRangeToWrapper = buildTextRangeToInfoMap(first); + } + + private static TIntObjectHashMap buildTextRangeToInfoMap(final LeafBlockWrapper first) { + final TIntObjectHashMap result = new TIntObjectHashMap(); + LeafBlockWrapper current = first; + while (current != null) { + result.put(current.getStartOffset(), current); + current = current.getNextBlock(); + } + return result; + } + + public boolean containsLineFeeds(final TextRange dependency) { + LeafBlockWrapper child = myTextRangeToWrapper.get(dependency.getStartOffset()); + if (child == null) return false; + if (child.containsLineFeeds()) return true; + final int endOffset = dependency.getEndOffset(); + while (child.getEndOffset() < endOffset) { + child = child.getNextBlock(); + if (child == null) return false; + if (child.getWhiteSpace().containsLineFeeds()) return true; + if (child.containsLineFeeds()) return true; + } + return false; + } + + @Nullable + public LeafBlockWrapper getBlockAtOrAfter(final int startOffset) { + int current = startOffset; + LeafBlockWrapper result = null; + while (current < myLastBlock.getEndOffset()) { + final LeafBlockWrapper currentValue = myTextRangeToWrapper.get(current); + if (currentValue != null) { + result = currentValue; + break; + } + current++; + } + + LeafBlockWrapper prevBlock = getPrevBlock(result); + + if (prevBlock != null && prevBlock.contains(startOffset)) { + return prevBlock; + } + else { + return result; + } + } + + @Nullable + private LeafBlockWrapper getPrevBlock(@Nullable final LeafBlockWrapper result) { + if (result != null) { + return result.getPreviousBlock(); + } + else { + return myLastBlock; + } + } +} \ No newline at end of file From 62764f9574cea2c8d5f01cfb4b3081e97fdff274 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 18 Dec 2015 12:01:41 +0300 Subject: [PATCH 14/34] extracted blocks mapper helper, responsible for mapping offset to LeafBlock --- .../com/intellij/formatting/DependantSpacingImpl.java | 2 +- .../src/com/intellij/formatting/FormatProcessor.java | 10 +++++----- .../src/com/intellij/formatting/FormatterImpl.java | 6 +++--- .../{BlocksHelper.java => BlockMapperHelper.java} | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) rename platform/lang-impl/src/com/intellij/formatting/engine/{BlocksHelper.java => BlockMapperHelper.java} (96%) diff --git a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java index 9e470dec0913..5ae8943d5822 100644 --- a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java @@ -95,7 +95,7 @@ public class DependantSpacingImpl extends SpacingImpl { boolean atLeastOneDependencyRangeContainsLf = false; for (TextRange dependency : myDependentRegionRanges) { - atLeastOneDependencyRangeContainsLf |= formatter.getBlocksHelper().containsLineFeeds(dependency); + atLeastOneDependencyRangeContainsLf |= formatter.getBlockMapperHelper().containsLineFeeds(dependency); } if (atLeastOneDependencyRangeContainsLf) myFlags |= DEPENDENCE_CONTAINS_LF_MASK; diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index b7a757d5611f..0d0784ad70c6 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -57,7 +57,7 @@ public class FormatProcessor { private Map myInfos; private CompositeBlockWrapper myRootBlockWrapper; - private BlocksHelper myBlocksHelper; + private BlockMapperHelper myBlockMapperHelper; private final BlockIndentOptions myBlockIndentOptions; private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; @@ -191,7 +191,7 @@ public class FormatProcessor { int lastBlockOffset = myLastTokenBlock.getEndOffset(); myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); myLastWhiteSpace.append(Math.max(lastBlockOffset, builder.getEndOffset()), model, myDefaultIndentOption); - myBlocksHelper = new BlocksHelper(myFirstTokenBlock, myLastTokenBlock); + myBlockMapperHelper = new BlockMapperHelper(myFirstTokenBlock, myLastTokenBlock); myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); myTotalBlocksWithAlignments = builder.getBlocksToAlign().values().size(); myExpandableIndents = builder.getExpandableIndentsBlocks(); @@ -200,8 +200,8 @@ public class FormatProcessor { myStateProcessor = new StateProcessor(wrapState); } - public BlocksHelper getBlocksHelper() { - return myBlocksHelper; + public BlockMapperHelper getBlockMapperHelper() { + return myBlockMapperHelper; } private int getRightMargin(Block rootBlock) { @@ -413,7 +413,7 @@ public class FormatProcessor { } final boolean containedLineFeeds = spacing.getMinLineFeeds() > 0; - final boolean containsLineFeeds = myBlocksHelper.containsLineFeeds(textRange); + final boolean containsLineFeeds = myBlockMapperHelper.containsLineFeeds(textRange); if (containedLineFeeds != containsLineFeeds) { spacing.setDependentRegionLinefeedStatusChanged(); diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index 6bda4f0849b1..2ee9c048e867 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -361,7 +361,7 @@ public class FormatterImpl extends FormatterEx final FormatProcessor processor = buildProcessorAndWrapBlocks( model, block, settings, indentOptions, new FormatTextRanges(affectedRange, true) ); - final LeafBlockWrapper blockBefore = processor.getBlocksHelper().getBlockAtOrAfter(affectedRange.getStartOffset()); + final LeafBlockWrapper blockBefore = processor.getBlockMapperHelper().getBlockAtOrAfter(affectedRange.getStartOffset()); LOG.assertTrue(blockBefore != null); WhiteSpace whiteSpace = blockBefore.getWhiteSpace(); LOG.assertTrue(whiteSpace != null); @@ -471,7 +471,7 @@ public class FormatterImpl extends FormatterEx documentModel, block, settings, indentOptions, new FormatTextRanges(affectedRange, true), offset ); - final LeafBlockWrapper blockAfterOffset = processor.getBlocksHelper().getBlockAtOrAfter(offset); + final LeafBlockWrapper blockAfterOffset = processor.getBlockMapperHelper().getBlockAtOrAfter(offset); if (blockAfterOffset != null && blockAfterOffset.contains(offset)) { return offset; @@ -597,7 +597,7 @@ public class FormatterImpl extends FormatterEx final FormatProcessor processor = buildProcessorAndWrapBlocks( documentModel, block, settings, indentOptions, new FormatTextRanges(affectedRange, true), offset ); - final LeafBlockWrapper blockAfterOffset = processor.getBlocksHelper().getBlockAtOrAfter(offset); + final LeafBlockWrapper blockAfterOffset = processor.getBlockMapperHelper().getBlockAtOrAfter(offset); if (blockAfterOffset != null && !blockAfterOffset.contains(offset)) { final WhiteSpace whiteSpace = blockAfterOffset.getWhiteSpace(); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/BlocksHelper.java b/platform/lang-impl/src/com/intellij/formatting/engine/BlockMapperHelper.java similarity index 96% rename from platform/lang-impl/src/com/intellij/formatting/engine/BlocksHelper.java rename to platform/lang-impl/src/com/intellij/formatting/engine/BlockMapperHelper.java index 59241909f17c..ffd28e568db8 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/BlocksHelper.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/BlockMapperHelper.java @@ -20,11 +20,11 @@ import com.intellij.openapi.util.TextRange; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.Nullable; -public class BlocksHelper { +public class BlockMapperHelper { private final LeafBlockWrapper myLastBlock; private TIntObjectHashMap myTextRangeToWrapper; - public BlocksHelper(LeafBlockWrapper first, LeafBlockWrapper last) { + public BlockMapperHelper(LeafBlockWrapper first, LeafBlockWrapper last) { myLastBlock = last; myTextRangeToWrapper = buildTextRangeToInfoMap(first); } From e7694da9daed3441d760248b986288c811110695 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 18 Dec 2015 12:09:56 +0300 Subject: [PATCH 15/34] extracted DependentSpacingEngine --- .../intellij/formatting/FormatProcessor.java | 83 +------------ .../com/intellij/formatting/SpacingImpl.java | 2 +- .../engine/DependentSpacingEngine.java | 109 ++++++++++++++++++ 3 files changed, 116 insertions(+), 78 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 0d0784ad70c6..d9022c1b1bfe 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -58,6 +58,7 @@ public class FormatProcessor { private CompositeBlockWrapper myRootBlockWrapper; private BlockMapperHelper myBlockMapperHelper; + private DependentSpacingEngine myDependentSpacingEngine; private final BlockIndentOptions myBlockIndentOptions; private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; @@ -101,44 +102,6 @@ public class FormatProcessor { private Ref myFirstTokenBlockRef = Ref.create(); private LeafBlockWrapper myLastTokenBlock; - /** - * Formatter provides a notion of {@link DependantSpacingImpl dependent spacing}, i.e. spacing that insist on line feed if target - * dependent region contains line feed. - *

- * Example: - *

-   *       int[] data = {1, 2, 3};
-   * 
- * We want to keep that in one line if possible but place curly braces on separate lines if the width is not enough: - *
-   *      int[] data = {    | < right margin
-   *          1, 2, 3       |
-   *      }                 |
-   * 
- * There is a possible case that particular block has dependent spacing property that targets region that lays beyond the - * current block. E.g. consider example above - '1' block has dependent spacing that targets the whole - * '{1, 2, 3}' block. So, it's not possible to answer whether line feed should be used during processing block - * '1'. - *

- * We store such 'forward dependencies' at the current collection where the key is the range of the target 'dependent forward - * region' and value is dependent spacing object. - *

- * Every time we detect that formatter changes 'has line feeds' status of such dependent region, we - * {@link DependantSpacingImpl#setDependentRegionLinefeedStatusChanged() mark} the dependent spacing as changed and schedule one more - * formatting iteration. - */ - private SortedMap myPreviousDependencies = - new TreeMap(new Comparator() { - @Override - public int compare(final TextRange o1, final TextRange o2) { - int offsetsDelta = o1.getEndOffset() - o2.getEndOffset(); - - if (offsetsDelta == 0) { - offsetsDelta = o2.getStartOffset() - o1.getStartOffset(); // starting earlier is greater - } - return offsetsDelta; - } - }); private final HashSet myAlignAgain = new HashSet(); @NotNull @@ -192,6 +155,7 @@ public class FormatProcessor { myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); myLastWhiteSpace.append(Math.max(lastBlockOffset, builder.getEndOffset()), model, myDefaultIndentOption); myBlockMapperHelper = new BlockMapperHelper(myFirstTokenBlock, myLastTokenBlock); + myDependentSpacingEngine = new DependentSpacingEngine(myBlockMapperHelper); myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); myTotalBlocksWithAlignments = builder.getBlocksToAlign().values().size(); myExpandableIndents = builder.getExpandableIndentsBlocks(); @@ -298,7 +262,7 @@ public class FormatProcessor { private void reset() { myBackwardShiftedAlignedBlocks.clear(); myAlignmentMappings.clear(); - myPreviousDependencies.clear(); + myDependentSpacingEngine.clear(); myWrapCandidate = null; if (myRootBlockWrapper != null) { myRootBlockWrapper.reset(); @@ -367,10 +331,10 @@ public class FormatProcessor { final List ranges = getDependentRegionRangesAfterCurrentWhiteSpace(spaceProperty, whiteSpace); if (!ranges.isEmpty()) { - registerUnresolvedDependentSpacingRanges(spaceProperty, ranges); + myDependentSpacingEngine.registerUnresolvedDependentSpacingRanges(spaceProperty, ranges); } - if (!whiteSpace.isIsReadOnly() && shouldReformatPreviouslyLocatedDependentSpacing(whiteSpace)) { + if (!whiteSpace.isIsReadOnly() && myDependentSpacingEngine.shouldReformatPreviouslyLocatedDependentSpacing(whiteSpace)) { myAlignAgain.add(whiteSpace); } else if (!myAlignAgain.isEmpty()) { @@ -399,41 +363,6 @@ public class FormatProcessor { return myAlignmentsInsideRangesToModify.contains(alignment); } - private boolean shouldReformatPreviouslyLocatedDependentSpacing(WhiteSpace space) { - final TextRange changed = space.getTextRange(); - final SortedMap sortedHeadMap = myPreviousDependencies.tailMap(changed); - - for (final Map.Entry entry : sortedHeadMap.entrySet()) { - final TextRange textRange = entry.getKey(); - - if (textRange.contains(changed)) { - final DependantSpacingImpl spacing = entry.getValue(); - if (spacing.isDependentRegionLinefeedStatusChanged()) { - continue; - } - - final boolean containedLineFeeds = spacing.getMinLineFeeds() > 0; - final boolean containsLineFeeds = myBlockMapperHelper.containsLineFeeds(textRange); - - if (containedLineFeeds != containsLineFeeds) { - spacing.setDependentRegionLinefeedStatusChanged(); - return true; - } - } - } - - return false; - } - - private void registerUnresolvedDependentSpacingRanges(final SpacingImpl spaceProperty, List unprocessedRanges) { - final DependantSpacingImpl dependantSpaceProperty = (DependantSpacingImpl)spaceProperty; - if (dependantSpaceProperty.isDependentRegionLinefeedStatusChanged()) return; - - for (TextRange range: unprocessedRanges) { - myPreviousDependencies.put(range, dependantSpaceProperty); - } - } - private static List getDependentRegionRangesAfterCurrentWhiteSpace(final SpacingImpl spaceProperty, final WhiteSpace whiteSpace) { @@ -1103,7 +1032,7 @@ public class FormatProcessor { } else { myAlignAgain.clear(); - myPreviousDependencies.clear(); + myDependentSpacingEngine.clear(); myCurrentBlock = myFirstTokenBlock; } } diff --git a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java index 06334cb58660..5b401576f97c 100644 --- a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NonNls; * Extends {@link Spacing} in order to keep number of additional settings like 'minSpaces', 'minLineFeeds', * 'prefLineFeeds' etc. */ -class SpacingImpl extends Spacing { +public class SpacingImpl extends Spacing { private int myMinSpaces; private int myKeepBlankLines; private int myMaxSpaces; diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java b/platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java new file mode 100644 index 000000000000..b367fd7d5cd2 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.DependantSpacingImpl; +import com.intellij.formatting.SpacingImpl; +import com.intellij.formatting.WhiteSpace; +import com.intellij.openapi.util.TextRange; + +import java.util.*; + +/** + * Formatter provides a notion of {@link DependantSpacingImpl dependent spacing}, i.e. spacing that insist on line feed if target + * dependent region contains line feed. + *

+ * Example: + *

+ *       int[] data = {1, 2, 3};
+ * 
+ * We want to keep that in one line if possible but place curly braces on separate lines if the width is not enough: + *
+ *      int[] data = {    | < right margin
+ *          1, 2, 3       |
+ *      }                 |
+ * 
+ * There is a possible case that particular block has dependent spacing property that targets region that lays beyond the + * current block. E.g. consider example above - '1' block has dependent spacing that targets the whole + * '{1, 2, 3}' block. So, it's not possible to answer whether line feed should be used during processing block + * '1'. + *

+ * We store such 'forward dependencies' at the current collection where the key is the range of the target 'dependent forward + * region' and value is dependent spacing object. + *

+ * Every time we detect that formatter changes 'has line feeds' status of such dependent region, we + * {@link DependantSpacingImpl#setDependentRegionLinefeedStatusChanged() mark} the dependent spacing as changed and schedule one more + * formatting iteration. + */ +public class DependentSpacingEngine { + private final BlockMapperHelper myBlockMapperHelper; + + private SortedMap myPreviousDependencies = + new TreeMap(new Comparator() { + @Override + public int compare(final TextRange o1, final TextRange o2) { + int offsetsDelta = o1.getEndOffset() - o2.getEndOffset(); + + if (offsetsDelta == 0) { + offsetsDelta = o2.getStartOffset() - o1.getStartOffset(); // starting earlier is greater + } + return offsetsDelta; + } + }); + + public DependentSpacingEngine(BlockMapperHelper helper) { + myBlockMapperHelper = helper; + } + + public boolean shouldReformatPreviouslyLocatedDependentSpacing(WhiteSpace space) { + final TextRange changed = space.getTextRange(); + final SortedMap sortedHeadMap = myPreviousDependencies.tailMap(changed); + + for (final Map.Entry entry : sortedHeadMap.entrySet()) { + final TextRange textRange = entry.getKey(); + + if (textRange.contains(changed)) { + final DependantSpacingImpl spacing = entry.getValue(); + if (spacing.isDependentRegionLinefeedStatusChanged()) { + continue; + } + + final boolean containedLineFeeds = spacing.getMinLineFeeds() > 0; + final boolean containsLineFeeds = myBlockMapperHelper.containsLineFeeds(textRange); + + if (containedLineFeeds != containsLineFeeds) { + spacing.setDependentRegionLinefeedStatusChanged(); + return true; + } + } + } + + return false; + } + + public void registerUnresolvedDependentSpacingRanges(final SpacingImpl spaceProperty, List unprocessedRanges) { + final DependantSpacingImpl dependantSpaceProperty = (DependantSpacingImpl)spaceProperty; + if (dependantSpaceProperty.isDependentRegionLinefeedStatusChanged()) return; + + for (TextRange range: unprocessedRanges) { + myPreviousDependencies.put(range, dependantSpaceProperty); + } + } + + public void clear() { + myPreviousDependencies.clear(); + } +} \ No newline at end of file From 69209d802c85e34b78072b7c98f69be57fd3c912 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 18 Dec 2015 12:13:46 +0300 Subject: [PATCH 16/34] minor, extracted method --- .../src/com/intellij/formatting/FormatProcessor.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index d9022c1b1bfe..2f6fa5ea7522 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -516,6 +516,12 @@ public class FormatProcessor { myBlockIndentOptions.getIndentOptions(myCurrentBlock)); BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock); + return processAlignmentResult(alignment, context, result, offsetResponsibleBlock); + } + + private boolean processAlignmentResult(AlignmentImpl alignment, Context context, + BlockAlignmentProcessor.Result result, + LeafBlockWrapper offsetResponsibleBlock) { switch (result) { case TARGET_BLOCK_PROCESSED_NOT_ALIGNED: return true; case TARGET_BLOCK_ALIGNED: storeAlignmentMapping(); return true; From 2cc931fccc4d279779578956758f6569ac06ff8e Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 18 Dec 2015 14:31:43 +0300 Subject: [PATCH 17/34] minor: removed obvious comments --- .../intellij/formatting/FormatterImpl.java | 45 +------------------ 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index 2ee9c048e867..a30f9bd789ed 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -304,21 +304,6 @@ public class FormatterImpl extends FormatterEx execute(task); } - /** - * Execute given sequential formatting task. Two approaches are possible: - *

-   * 
    - *
  • - * synchronous - the task is completely executed during the current method processing; - *
  • - *
  • - * asynchronous - the task is executed at background thread under the progress dialog; - *
  • - *
- *
- * - * @param task task to execute - */ private void execute(@NotNull SequentialTask task) { disableFormatting(); Application application = ApplicationManager.getApplication(); @@ -490,40 +475,14 @@ public class FormatterImpl extends FormatterEx return offset; } - /** - * Delegates to - * {@link #buildProcessorAndWrapBlocks(FormattingDocumentModel, Block, CodeStyleSettings, CommonCodeStyleSettings.IndentOptions, FormatTextRanges, int)} - * with '-1' as an interested offset. - * - * @param docModel - * @param rootBlock - * @param settings - * @param indentOptions - * @param affectedRanges - * @return - */ private static FormatProcessor buildProcessorAndWrapBlocks(final FormattingDocumentModel docModel, Block rootBlock, CodeStyleSettings settings, CommonCodeStyleSettings.IndentOptions indentOptions, - @Nullable FormatTextRanges affectedRanges) - { + @Nullable FormatTextRanges affectedRanges) { return buildProcessorAndWrapBlocks(docModel, rootBlock, settings, indentOptions, affectedRanges, -1); } - - /** - * Builds {@link FormatProcessor} instance and asks it to wrap all {@link Block code blocks} - * {@link FormattingModel#getRootBlock() derived from the given model}. - * - * @param docModel target model - * @param rootBlock root block to process - * @param settings code style settings to use - * @param indentOptions indent options to use - * @param affectedRanges ranges to reformat - * @param interestingOffset interesting offset; '-1' if no particular offset has a special interest - * @return format processor instance with wrapped {@link Block code blocks} - */ - @SuppressWarnings({"StatementWithEmptyBody"}) + private static FormatProcessor buildProcessorAndWrapBlocks(final FormattingDocumentModel docModel, Block rootBlock, CodeStyleSettings settings, From 56097a4a557ad08372d6ca7e28f3e2bcf1a1ca5f Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 18 Dec 2015 19:19:42 +0300 Subject: [PATCH 18/34] extracted AlignmentHelper from FormatProcessor --- .../intellij/formatting/AlignmentImpl.java | 4 +- .../intellij/formatting/FormatProcessor.java | 167 +++--------------- .../formatting/engine/AlignmentHelper.java | 163 +++++++++++++++++ 3 files changed, 187 insertions(+), 147 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java diff --git a/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java b/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java index eebf46217aac..128e4b6ea1c9 100644 --- a/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java @@ -25,7 +25,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; -class AlignmentImpl extends Alignment { +public class AlignmentImpl extends Alignment { private static final List EMPTY = Collections.emptyList(); private final boolean myAllowBackwardShift; private final Anchor myAnchor; @@ -110,7 +110,7 @@ class AlignmentImpl extends Alignment { * {@link #setParent(Alignment) its parent} using the algorithm above if any; null otherwise */ @Nullable - LeafBlockWrapper getOffsetRespBlockBefore(@Nullable final AbstractBlockWrapper block) { + public LeafBlockWrapper getOffsetRespBlockBefore(@Nullable final AbstractBlockWrapper block) { if (!continueOffsetResponsibleBlockRetrieval(block)) { return null; } diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 2f6fa5ea7522..aad0bde90483 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -16,7 +16,6 @@ package com.intellij.formatting; -import com.intellij.diagnostic.LogMessageEx; import com.intellij.formatting.engine.*; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; @@ -36,65 +35,27 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -import static com.intellij.formatting.BlockAlignmentProcessor.Context; import static com.intellij.formatting.InitialInfoBuilder.prepareToBuildBlocksSequentially; public class FormatProcessor { - - private static final Map ALIGNMENT_PROCESSORS = - new EnumMap(Alignment.Anchor.class); - static { - ALIGNMENT_PROCESSORS.put(Alignment.Anchor.LEFT, new LeftEdgeAlignmentProcessor()); - ALIGNMENT_PROCESSORS.put(Alignment.Anchor.RIGHT, new RightEdgeAlignmentProcessor()); - } - private static final Logger LOG = Logger.getInstance("#com.intellij.formatting.FormatProcessor"); private Set myAlignmentsInsideRangesToModify = null; private boolean myReformatContext; private LeafBlockWrapper myCurrentBlock; - private Map myInfos; - private CompositeBlockWrapper myRootBlockWrapper; + private Map myInfos; + private CompositeBlockWrapper myRootBlockWrapper; private BlockMapperHelper myBlockMapperHelper; private DependentSpacingEngine myDependentSpacingEngine; + private AlignmentHelper myAlignmentHelper; private final BlockIndentOptions myBlockIndentOptions; 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 - * infinite cycles that may occur when, for example following alignment is specified: - *

- *

-   *     int i1     = 1;
-   *     int i2, i3 = 2;
-   * 
- *

- * There is a possible case that 'i1', 'i2' and 'i3' blocks re-use - * the same alignment, hence, 'i1' is shifted to right during 'i3' processing but - * that causes 'i2' to be shifted right as wll because it's aligned to 'i1' that - * increases offset of 'i3' that, in turn, causes backward shift of 'i1' etc. - *

- * This map remembers such backward shifts in order to be able to break such infinite cycles. - */ - private final Map> myBackwardShiftedAlignedBlocks - = new HashMap>(); - - private final Map> myAlignmentMappings - = new HashMap>(); - - /** - * There is a possible case that we detect a 'cycled alignment' rules (see {@link #myBackwardShiftedAlignedBlocks}). We want - * just to skip processing for such alignments then. - *

- * This container holds 'bad alignment' objects that should not be processed. - */ - private final Set myAlignmentsToSkip = new HashSet(); - + private final CodeStyleSettings mySettings; + private final Document myDocument; + private LeafBlockWrapper myWrapCandidate = null; private LeafBlockWrapper myFirstWrappedBlockOnLine = null; @@ -114,8 +75,6 @@ public class FormatProcessor { @NotNull private StateProcessor myStateProcessor; private MultiMap myExpandableIndents; - private int myTotalBlocksWithAlignments; - private int myBlockRollbacks; public FormatProcessor(final FormattingDocumentModel docModel, Block rootBlock, @@ -157,7 +116,7 @@ public class FormatProcessor { myBlockMapperHelper = new BlockMapperHelper(myFirstTokenBlock, myLastTokenBlock); myDependentSpacingEngine = new DependentSpacingEngine(myBlockMapperHelper); myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); - myTotalBlocksWithAlignments = builder.getBlocksToAlign().values().size(); + myAlignmentHelper = new AlignmentHelper(myDocument, builder.getBlocksToAlign(), myBlockIndentOptions); myExpandableIndents = builder.getExpandableIndentsBlocks(); } }); @@ -260,8 +219,7 @@ public class FormatProcessor { } private void reset() { - myBackwardShiftedAlignedBlocks.clear(); - myAlignmentMappings.clear(); + myAlignmentHelper.reset(); myDependentSpacingEngine.clear(); myWrapCandidate = null; if (myRootBlockWrapper != null) { @@ -318,7 +276,7 @@ public class FormatProcessor { } } - if (!adjustIndent()) { + if (!adjustIndentAndContinue()) { return; } @@ -344,6 +302,10 @@ public class FormatProcessor { myCurrentBlock = myCurrentBlock.getNextBlock(); } + private void onCurrentLineChanged() { + myWrapCandidate = null; + } + private boolean isReformatSelectedRangesContext() { return myReformatContext && !ContainerUtil.isEmpty(myAlignmentsInsideRangesToModify); } @@ -480,10 +442,6 @@ public class FormatProcessor { !myCurrentBlock.getWhiteSpace().isReadOnly(); } - private void onCurrentLineChanged() { - myWrapCandidate = null; - } - /** * Adjusts indent of the current block. * @@ -491,11 +449,11 @@ public class FormatProcessor { * false otherwise (e.g. if previously processed block is shifted inside this method for example * because of specified alignment options) */ - private boolean adjustIndent() { + private boolean adjustIndentAndContinue() { AlignmentImpl alignment = CoreFormatterUtil.getAlignment(myCurrentBlock); WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); - if (alignment == null || myAlignmentsToSkip.contains(alignment)) { + if (alignment == null || myAlignmentHelper.shouldSkip(alignment)) { if (whiteSpace.containsLineFeeds()) { adjustSpacingByIndentOffset(); } @@ -505,94 +463,13 @@ public class FormatProcessor { return true; } - BlockAlignmentProcessor alignmentProcessor = ALIGNMENT_PROCESSORS.get(alignment.getAnchor()); - if (alignmentProcessor == null) { - LOG.error(String.format("Can't find alignment processor for alignment anchor %s", alignment.getAnchor())); - return true; + LeafBlockWrapper newCurrentBlock = myAlignmentHelper.applyAlignmentAndContinueFormatting(alignment, myCurrentBlock); + if (newCurrentBlock != myCurrentBlock) { + myCurrentBlock = newCurrentBlock; + onCurrentLineChanged(); + return false; } - - BlockAlignmentProcessor.Context context = new BlockAlignmentProcessor.Context( - myDocument, alignment, myCurrentBlock, myAlignmentMappings, myBackwardShiftedAlignedBlocks, - myBlockIndentOptions.getIndentOptions(myCurrentBlock)); - BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); - final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock); - return processAlignmentResult(alignment, context, result, offsetResponsibleBlock); - } - - private boolean processAlignmentResult(AlignmentImpl alignment, Context context, - BlockAlignmentProcessor.Result result, - LeafBlockWrapper offsetResponsibleBlock) { - switch (result) { - case TARGET_BLOCK_PROCESSED_NOT_ALIGNED: return true; - case TARGET_BLOCK_ALIGNED: storeAlignmentMapping(); return true; - case BACKWARD_BLOCK_ALIGNED: - if (offsetResponsibleBlock == null) { - return true; - } - Set blocksCausedRealignment = new HashSet(); - myBackwardShiftedAlignedBlocks.clear(); - myBackwardShiftedAlignedBlocks.put(offsetResponsibleBlock, blocksCausedRealignment); - blocksCausedRealignment.add(myCurrentBlock); - storeAlignmentMapping(myCurrentBlock, offsetResponsibleBlock); - - if (myBlockRollbacks > myTotalBlocksWithAlignments) { - reportAlignmentProcessingError(context); - return true; - } - else { - myCurrentBlock = offsetResponsibleBlock.getNextBlock(); - myBlockRollbacks++; - } - onCurrentLineChanged(); - return false; - case RECURSION_DETECTED: - myCurrentBlock = offsetResponsibleBlock; // Fall through to the 'register alignment to skip'. - case UNABLE_TO_ALIGN_BACKWARD_BLOCK: - myAlignmentsToSkip.add(alignment); - return false; - default: return true; - } - } - - private static void reportAlignmentProcessingError(Context context) { - ASTNode node = context.targetBlock.getNode(); - Language language = node != null ? node.getPsi().getLanguage() : null; - LogMessageEx.error(LOG, - (language != null ? language.getDisplayName() + ": " : "") + - "Can't align block " + context.targetBlock, context.document.getText()); - } - - /** - * We need to track blocks which white spaces are modified because of alignment rules. - *

- * This method encapsulates the logic of storing such information. - */ - private void storeAlignmentMapping() { - AlignmentImpl alignment = null; - AbstractBlockWrapper block = myCurrentBlock; - while (alignment == null && block != null) { - alignment = block.getAlignment(); - block = block.getParent(); - } - if (alignment != null) { - block = alignment.getOffsetRespBlockBefore(myCurrentBlock); - if (block != null) { - storeAlignmentMapping(myCurrentBlock, block); - } - } - } - - private void storeAlignmentMapping(AbstractBlockWrapper block1, AbstractBlockWrapper block2) { - doStoreAlignmentMapping(block1, block2); - doStoreAlignmentMapping(block2, block1); - } - - private void doStoreAlignmentMapping(AbstractBlockWrapper key, AbstractBlockWrapper value) { - Set wrappers = myAlignmentMappings.get(key); - if (wrappers == null) { - myAlignmentMappings.put(key, wrappers = new HashSet()); - } - wrappers.add(value); + return true; } /** @@ -1230,7 +1107,7 @@ public class FormatProcessor { if (space.containsLineFeeds()) { myCurrentBlock = (LeafBlockWrapper)block; - adjustIndent(); + adjustIndentAndContinue(); storeAlignmentsAfterCurrentBlock(); } } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java b/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java new file mode 100644 index 000000000000..b2ea0127a930 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java @@ -0,0 +1,163 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.diagnostic.LogMessageEx; +import com.intellij.formatting.*; +import com.intellij.lang.ASTNode; +import com.intellij.lang.Language; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; + +import java.util.EnumMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class AlignmentHelper { + private static final Logger LOG = Logger.getInstance(AlignmentHelper.class); + + private static final Map ALIGNMENT_PROCESSORS = new EnumMap(Alignment.Anchor.class); + static { + ALIGNMENT_PROCESSORS.put(Alignment.Anchor.LEFT, new LeftEdgeAlignmentProcessor()); + ALIGNMENT_PROCESSORS.put(Alignment.Anchor.RIGHT, new RightEdgeAlignmentProcessor()); + } + + private final Set myAlignmentsToSkip = ContainerUtil.newHashSet(); + private final Document myDocument; + private final BlockIndentOptions myBlockIndentOptions; + + private int myTotalBlocksWithAlignments; + private int myBlockRollbacks; + + + /** + * Remembers mappings between backward-shifted aligned block and blocks that cause that shift in order to detect + * infinite cycles that may occur when, for example following alignment is specified: + *

+ *

+   *     int i1     = 1;
+   *     int i2, i3 = 2;
+   * 
+ *

+ * There is a possible case that 'i1', 'i2' and 'i3' blocks re-use + * the same alignment, hence, 'i1' is shifted to right during 'i3' processing but + * that causes 'i2' to be shifted right as wll because it's aligned to 'i1' that + * increases offset of 'i3' that, in turn, causes backward shift of 'i1' etc. + *

+ * This map remembers such backward shifts in order to be able to break such infinite cycles. + */ + private final Map> myBackwardShiftedAlignedBlocks = ContainerUtil.newHashMap(); + private final Map> myAlignmentMappings = ContainerUtil.newHashMap(); + + public AlignmentHelper(Document document, MultiMap blocksToAlign, BlockIndentOptions options) { + myDocument = document; + myTotalBlocksWithAlignments = blocksToAlign.values().size(); + myBlockIndentOptions = options; + } + + private static void reportAlignmentProcessingError(BlockAlignmentProcessor.Context context) { + ASTNode node = context.targetBlock.getNode(); + Language language = node != null ? node.getPsi().getLanguage() : null; + LogMessageEx.error(LOG, + (language != null ? language.getDisplayName() + ": " : "") + + "Can't align block " + context.targetBlock, context.document.getText()); + } + + public LeafBlockWrapper applyAlignmentAndContinueFormatting(final AlignmentImpl alignment, final LeafBlockWrapper currentBlock) { + BlockAlignmentProcessor alignmentProcessor = ALIGNMENT_PROCESSORS.get(alignment.getAnchor()); + if (alignmentProcessor == null) { + LOG.error(String.format("Can't find alignment processor for alignment anchor %s", alignment.getAnchor())); + return currentBlock; + } + + BlockAlignmentProcessor.Context context = new BlockAlignmentProcessor.Context( + myDocument, alignment, currentBlock, myAlignmentMappings, myBackwardShiftedAlignedBlocks, + myBlockIndentOptions.getIndentOptions(currentBlock)); + BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); + final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(currentBlock); + switch (result) { + case TARGET_BLOCK_PROCESSED_NOT_ALIGNED: + return currentBlock; + case TARGET_BLOCK_ALIGNED: + storeAlignmentMapping(currentBlock); + return currentBlock; + case BACKWARD_BLOCK_ALIGNED: + if (offsetResponsibleBlock == null) { + return currentBlock; + } + Set blocksCausedRealignment = new HashSet(); + myBackwardShiftedAlignedBlocks.clear(); + myBackwardShiftedAlignedBlocks.put(offsetResponsibleBlock, blocksCausedRealignment); + blocksCausedRealignment.add(currentBlock); + storeAlignmentMapping(currentBlock, offsetResponsibleBlock); + + if (myBlockRollbacks > myTotalBlocksWithAlignments) { + reportAlignmentProcessingError(context); + return currentBlock; + } + myBlockRollbacks++; + return offsetResponsibleBlock.getNextBlock(); + case RECURSION_DETECTED: + myAlignmentsToSkip.add(alignment); + return offsetResponsibleBlock; // Fall through to the 'register alignment to skip'. + case UNABLE_TO_ALIGN_BACKWARD_BLOCK: + myAlignmentsToSkip.add(alignment); + return null; + default: + return currentBlock; + } + } + + public boolean shouldSkip(AlignmentImpl alignment) { + return myAlignmentsToSkip.contains(alignment); + } + + private void storeAlignmentMapping(AbstractBlockWrapper block1, AbstractBlockWrapper block2) { + doStoreAlignmentMapping(block1, block2); + doStoreAlignmentMapping(block2, block1); + } + + private void doStoreAlignmentMapping(AbstractBlockWrapper key, AbstractBlockWrapper value) { + Set wrappers = myAlignmentMappings.get(key); + if (wrappers == null) { + myAlignmentMappings.put(key, wrappers = new HashSet()); + } + wrappers.add(value); + } + + private void storeAlignmentMapping(LeafBlockWrapper currentBlock) { + AlignmentImpl alignment = null; + AbstractBlockWrapper block = currentBlock; + while (alignment == null && block != null) { + alignment = block.getAlignment(); + block = block.getParent(); + } + if (alignment != null) { + block = alignment.getOffsetRespBlockBefore(currentBlock); + if (block != null) { + storeAlignmentMapping(currentBlock, block); + } + } + } + + public void reset() { + myBackwardShiftedAlignedBlocks.clear(); + myAlignmentMappings.clear(); + } +} From 56d0dd8588308668447b15a2df83b614cf485223 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 22 Dec 2015 14:45:33 +0300 Subject: [PATCH 19/34] refactoring: replaced FormatProcessor with BlockMapperHelper --- .../intellij/formatting/DependantSpacingImpl.java | 5 +++-- .../com/intellij/formatting/FormatProcessor.java | 6 +++--- .../src/com/intellij/formatting/SpacingImpl.java | 5 ++--- .../src/com/intellij/formatting/WhiteSpace.java | 15 +++++---------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java index 5ae8943d5822..3f39fdb05132 100644 --- a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java @@ -16,6 +16,7 @@ package com.intellij.formatting; +import com.intellij.formatting.engine.BlockMapperHelper; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; @@ -88,14 +89,14 @@ public class DependantSpacingImpl extends SpacingImpl { } @Override - public void refresh(FormatProcessor formatter) { + public void refresh(BlockMapperHelper helper) { if (isDependentRegionLinefeedStatusChanged()) { return; } boolean atLeastOneDependencyRangeContainsLf = false; for (TextRange dependency : myDependentRegionRanges) { - atLeastOneDependencyRangeContainsLf |= formatter.getBlockMapperHelper().containsLineFeeds(dependency); + atLeastOneDependencyRangeContainsLf |= helper.containsLineFeeds(dependency); } if (atLeastOneDependencyRangeContainsLf) myFlags |= DEPENDENCE_CONTAINS_LF_MASK; diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index aad0bde90483..c6c10807fe69 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -259,7 +259,7 @@ public class FormatProcessor { } } - whiteSpace.arrangeLineFeeds(spaceProperty, this); + whiteSpace.arrangeLineFeeds(spaceProperty, myBlockMapperHelper); if (!whiteSpace.containsLineFeeds()) { whiteSpace.arrangeSpaces(spaceProperty); @@ -357,7 +357,7 @@ public class FormatProcessor { myFirstWrappedBlockOnLine = null; if (!whiteSpace.containsLineFeedsInitially()) { - whiteSpace.removeLineFeeds(spacing, this); + whiteSpace.removeLineFeeds(spacing, myBlockMapperHelper); } } @@ -530,7 +530,7 @@ public class FormatProcessor { return startColumnNow > startColumnAfterWrap; } finally { - whiteSpace.removeLineFeeds(myCurrentBlock.getSpaceProperty(), this); + whiteSpace.removeLineFeeds(myCurrentBlock.getSpaceProperty(), myBlockMapperHelper); whiteSpace.setSpaces(spaces, indentSpaces); } } diff --git a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java index 5b401576f97c..ae3a7792c25c 100644 --- a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java @@ -16,6 +16,7 @@ package com.intellij.formatting; +import com.intellij.formatting.engine.BlockMapperHelper; import org.jetbrains.annotations.NonNls; /** @@ -90,10 +91,8 @@ public class SpacingImpl extends Spacing { /** * Allows to ask to refresh current state using given formatter if necessary. - * - * @param formatter formatter to use during state refresh */ - public void refresh(FormatProcessor formatter) { + public void refresh(BlockMapperHelper helper) { } public final boolean shouldKeepLineFeeds() { diff --git a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java index 67437c37e020..edd7a09273db 100644 --- a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java +++ b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java @@ -16,6 +16,7 @@ package com.intellij.formatting; +import com.intellij.formatting.engine.BlockMapperHelper; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; @@ -392,16 +393,13 @@ public class WhiteSpace { /** * Tries to ensure that number of line feeds managed by the current {@link WhiteSpace} is consistent to the settings * defined at the given spacing property. - * - * @param spaceProperty space settings holder - * @param formatProcessor format processor to use for space settings state refreshing */ - public void arrangeLineFeeds(final SpacingImpl spaceProperty, final FormatProcessor formatProcessor) { + public void arrangeLineFeeds(final SpacingImpl spaceProperty, final BlockMapperHelper helper) { performModification(new Runnable() { @Override public void run() { if (spaceProperty != null) { - spaceProperty.refresh(formatProcessor); + spaceProperty.refresh(helper); if (spaceProperty.getMinLineFeeds() >= 0 && getLineFeeds() < spaceProperty.getMinLineFeeds()) { setLineFeeds(spaceProperty.getMinLineFeeds()); @@ -541,11 +539,8 @@ public class WhiteSpace { *

* This method may be considered a shortcut for calling {@link #arrangeLineFeeds(SpacingImpl, FormatProcessor)} and * {@link #arrangeSpaces(SpacingImpl)}. - * - * @param spacing spacing settings holder - * @param formatProcessor format processor to use to refresh state of the given 'spacing' object */ - public void removeLineFeeds(final SpacingImpl spacing, final FormatProcessor formatProcessor) { + public void removeLineFeeds(final SpacingImpl spacing, final BlockMapperHelper helper) { performModification(new Runnable() { @Override public void run() { @@ -554,7 +549,7 @@ public class WhiteSpace { myIndentSpaces = 0; } }); - arrangeLineFeeds(spacing, formatProcessor); + arrangeLineFeeds(spacing, helper); arrangeSpaces(spacing); } From aa11e5b6cb36d2dd054c5d3863e443685e2604cd Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 22 Dec 2015 18:43:01 +0300 Subject: [PATCH 20/34] extract static method --- .../src/com/intellij/formatting/FormatProcessor.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index c6c10807fe69..3bbe7cc9df2a 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -488,8 +488,13 @@ public class FormatProcessor { } private void adjustSpacingByIndentOffset() { - IndentData offset = myCurrentBlock.calculateOffset(myBlockIndentOptions.getIndentOptions(myCurrentBlock)); - myCurrentBlock.getWhiteSpace().setSpaces(offset.getSpaces(), offset.getIndentSpaces()); + adjustSpacingByIndentOffset(myBlockIndentOptions, myCurrentBlock); + } + + private static void adjustSpacingByIndentOffset(BlockIndentOptions blockIndentOptions, LeafBlockWrapper block) { + CommonCodeStyleSettings.IndentOptions options = blockIndentOptions.getIndentOptions(block); + IndentData offset = block.calculateOffset(options); + block.getWhiteSpace().setSpaces(offset.getSpaces(), offset.getIndentSpaces()); } private boolean isChopNeeded(final WrapImpl wrap) { From 8973c7a355b7cf10b01dcd9b3254f2f560fd69fb Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 22 Dec 2015 19:24:34 +0300 Subject: [PATCH 21/34] minor refactoring --- .../intellij/formatting/FormatProcessor.java | 29 +++++++++---------- .../formatting/engine/AlignmentHelper.java | 14 ++++----- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 3bbe7cc9df2a..c1d376b754ba 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -276,7 +276,10 @@ public class FormatProcessor { } } - if (!adjustIndentAndContinue()) { + LeafBlockWrapper newCurrentBlock = adjustIndent(); + if (newCurrentBlock != null) { + myCurrentBlock = newCurrentBlock; + onCurrentLineChanged(); return; } @@ -442,14 +445,14 @@ public class FormatProcessor { !myCurrentBlock.getWhiteSpace().isReadOnly(); } + /** - * 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) + * Sometimes to align myCurrentBlock we adjust whitespace of other block before. + * In such a case, we rollback to that block restarting formatting from there. + * + * @return new current block if we need to rollback, null otherwise */ - private boolean adjustIndentAndContinue() { + private LeafBlockWrapper adjustIndent() { AlignmentImpl alignment = CoreFormatterUtil.getAlignment(myCurrentBlock); WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); @@ -460,16 +463,10 @@ public class FormatProcessor { else { whiteSpace.arrangeSpaces(myCurrentBlock.getSpaceProperty()); } - return true; + return null; } - LeafBlockWrapper newCurrentBlock = myAlignmentHelper.applyAlignmentAndContinueFormatting(alignment, myCurrentBlock); - if (newCurrentBlock != myCurrentBlock) { - myCurrentBlock = newCurrentBlock; - onCurrentLineChanged(); - return false; - } - return true; + return myAlignmentHelper.applyAlignment(alignment, myCurrentBlock); } /** @@ -1112,7 +1109,7 @@ public class FormatProcessor { if (space.containsLineFeeds()) { myCurrentBlock = (LeafBlockWrapper)block; - adjustIndentAndContinue(); + adjustIndent(); //since aligned block starts new line, it should not touch any other block storeAlignmentsAfterCurrentBlock(); } } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java b/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java index b2ea0127a930..6e0042f47530 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java @@ -79,11 +79,11 @@ public class AlignmentHelper { "Can't align block " + context.targetBlock, context.document.getText()); } - public LeafBlockWrapper applyAlignmentAndContinueFormatting(final AlignmentImpl alignment, final LeafBlockWrapper currentBlock) { + public LeafBlockWrapper applyAlignment(final AlignmentImpl alignment, final LeafBlockWrapper currentBlock) { BlockAlignmentProcessor alignmentProcessor = ALIGNMENT_PROCESSORS.get(alignment.getAnchor()); if (alignmentProcessor == null) { LOG.error(String.format("Can't find alignment processor for alignment anchor %s", alignment.getAnchor())); - return currentBlock; + return null; } BlockAlignmentProcessor.Context context = new BlockAlignmentProcessor.Context( @@ -93,13 +93,13 @@ public class AlignmentHelper { final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(currentBlock); switch (result) { case TARGET_BLOCK_PROCESSED_NOT_ALIGNED: - return currentBlock; + return null; case TARGET_BLOCK_ALIGNED: storeAlignmentMapping(currentBlock); - return currentBlock; + return null; case BACKWARD_BLOCK_ALIGNED: if (offsetResponsibleBlock == null) { - return currentBlock; + return null; } Set blocksCausedRealignment = new HashSet(); myBackwardShiftedAlignedBlocks.clear(); @@ -109,7 +109,7 @@ public class AlignmentHelper { if (myBlockRollbacks > myTotalBlocksWithAlignments) { reportAlignmentProcessingError(context); - return currentBlock; + return null; } myBlockRollbacks++; return offsetResponsibleBlock.getNextBlock(); @@ -120,7 +120,7 @@ public class AlignmentHelper { myAlignmentsToSkip.add(alignment); return null; default: - return currentBlock; + return null; } } From 15fbc98024fd4abbc1d593adf29a38d8a02d6bab Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 22 Dec 2015 19:47:23 +0300 Subject: [PATCH 22/34] extracted indent adjuster --- .../intellij/formatting/FormatProcessor.java | 140 ++--------------- .../formatting/engine/IndentAdjuster.java | 143 ++++++++++++++++++ 2 files changed, 153 insertions(+), 130 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/IndentAdjuster.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index c1d376b754ba..d45a730c135e 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -50,6 +50,7 @@ public class FormatProcessor { private BlockMapperHelper myBlockMapperHelper; private DependentSpacingEngine myDependentSpacingEngine; private AlignmentHelper myAlignmentHelper; + private IndentAdjuster myIndentAdjuster; private final BlockIndentOptions myBlockIndentOptions; private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; @@ -117,6 +118,7 @@ public class FormatProcessor { myDependentSpacingEngine = new DependentSpacingEngine(myBlockMapperHelper); myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); myAlignmentHelper = new AlignmentHelper(myDocument, builder.getBlocksToAlign(), myBlockIndentOptions); + myIndentAdjuster = new IndentAdjuster(myBlockIndentOptions, myAlignmentHelper); myExpandableIndents = builder.getExpandableIndentsBlocks(); } }); @@ -276,7 +278,7 @@ public class FormatProcessor { } } - LeafBlockWrapper newCurrentBlock = adjustIndent(); + LeafBlockWrapper newCurrentBlock = myIndentAdjuster.adjustIndent(myCurrentBlock); if (newCurrentBlock != null) { myCurrentBlock = newCurrentBlock; onCurrentLineChanged(); @@ -445,55 +447,6 @@ public class FormatProcessor { !myCurrentBlock.getWhiteSpace().isReadOnly(); } - - /** - * Sometimes to align myCurrentBlock we adjust whitespace of other block before. - * In such a case, we rollback to that block restarting formatting from there. - * - * @return new current block if we need to rollback, null otherwise - */ - private LeafBlockWrapper adjustIndent() { - AlignmentImpl alignment = CoreFormatterUtil.getAlignment(myCurrentBlock); - WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); - - if (alignment == null || myAlignmentHelper.shouldSkip(alignment)) { - if (whiteSpace.containsLineFeeds()) { - adjustSpacingByIndentOffset(); - } - else { - whiteSpace.arrangeSpaces(myCurrentBlock.getSpaceProperty()); - } - return null; - } - - return myAlignmentHelper.applyAlignment(alignment, myCurrentBlock); - } - - /** - * Applies indent to the white space of {@link #myCurrentBlock currently processed wrapped block}. Both indentation - * and alignment options are took into consideration here. - */ - private void adjustLineIndent() { - IndentData alignOffset = getAlignOffset(); - - if (alignOffset == null) { - adjustSpacingByIndentOffset(); - } - else { - myCurrentBlock.getWhiteSpace().setSpaces(alignOffset.getSpaces(), alignOffset.getIndentSpaces()); - } - } - - private void adjustSpacingByIndentOffset() { - adjustSpacingByIndentOffset(myBlockIndentOptions, myCurrentBlock); - } - - private static void adjustSpacingByIndentOffset(BlockIndentOptions blockIndentOptions, LeafBlockWrapper block) { - CommonCodeStyleSettings.IndentOptions options = blockIndentOptions.getIndentOptions(block); - IndentData offset = block.calculateOffset(options); - block.getWhiteSpace().setSpaces(offset.getSpaces(), offset.getIndentSpaces()); - } - private boolean isChopNeeded(final WrapImpl wrap) { return wrap != null && wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && isSuitableInTheCurrentPosition(wrap); } @@ -527,7 +480,7 @@ public class FormatProcessor { try { final int startColumnNow = CoreFormatterUtil.getStartColumn(myCurrentBlock); whiteSpace.ensureLineFeed(); - adjustLineIndent(); + myIndentAdjuster.adjustLineIndent(myCurrentBlock); final int startColumnAfterWrap = CoreFormatterUtil.getStartColumn(myCurrentBlock); return startColumnNow > startColumnAfterWrap; } @@ -582,46 +535,6 @@ public class FormatProcessor { } } - /** - * Tries to get align-implied indent of the current block. - * - * @return indent of the current block if any; null otherwise - */ - @Nullable - private IndentData getAlignOffset() { - AbstractBlockWrapper current = myCurrentBlock; - while (true) { - final AlignmentImpl alignment = current.getAlignment(); - LeafBlockWrapper offsetResponsibleBlock; - if (alignment != null && (offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock)) != null) { - final WhiteSpace whiteSpace = offsetResponsibleBlock.getWhiteSpace(); - if (whiteSpace.containsLineFeeds()) { - return new IndentData(whiteSpace.getIndentSpaces(), whiteSpace.getSpaces()); - } - else { - final int offsetBeforeBlock = CoreFormatterUtil.getStartColumn(offsetResponsibleBlock); - final AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(myCurrentBlock); - if (indentedParentBlock == null) { - return new IndentData(0, offsetBeforeBlock); - } - else { - final int parentIndent = indentedParentBlock.getWhiteSpace().getIndentOffset(); - if (parentIndent > offsetBeforeBlock) { - return new IndentData(0, offsetBeforeBlock); - } - else { - return new IndentData(parentIndent, offsetBeforeBlock - parentIndent); - } - } - } - } - else { - current = current.getParent(); - if (current == null || current.getStartOffset() != myCurrentBlock.getStartOffset()) return null; - } - } - } - public void setAllWhiteSpacesAreReadOnly() { LeafBlockWrapper current = myFirstTokenBlock; while (current != null) { @@ -630,10 +543,10 @@ public class FormatProcessor { } } - static class ChildAttributesInfo { + public static class ChildAttributesInfo { public final AbstractBlockWrapper parent; - final ChildAttributes attributes; - final int index; + public final ChildAttributes attributes; + public final int index; public ChildAttributesInfo(final AbstractBlockWrapper parent, final ChildAttributes attributes, final int index) { this.parent = parent; @@ -662,7 +575,7 @@ public class FormatProcessor { return new IndentInfo(0, 0, 0); } - return adjustLineIndent(info.parent, info.attributes, info.index); + return myIndentAdjuster.adjustLineIndent(myCurrentBlock, info); } @Nullable @@ -707,40 +620,7 @@ public class FormatProcessor { return new ChildAttributesInfo(parent, childAttributes, index); } } - - private IndentInfo adjustLineIndent(final AbstractBlockWrapper parent, final ChildAttributes childAttributes, final int index) { - int alignOffset = getAlignOffsetBefore(childAttributes.getAlignment(), null); - if (alignOffset == -1) { - return parent.calculateChildOffset(myBlockIndentOptions.getIndentOptions(parent), childAttributes, index).createIndentInfo(); - } - else { - AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(myCurrentBlock); - if (indentedParentBlock == null) { - return new IndentInfo(0, 0, alignOffset); - } - else { - int indentOffset = indentedParentBlock.getWhiteSpace().getIndentOffset(); - if (indentOffset > alignOffset) { - return new IndentInfo(0, 0, alignOffset); - } - else { - return new IndentInfo(0, indentOffset, alignOffset - indentOffset); - } - } - } - } - - private static int getAlignOffsetBefore(@Nullable final Alignment alignment, @Nullable final LeafBlockWrapper blockAfter) { - if (alignment == null) return -1; - final LeafBlockWrapper alignRespBlock = ((AlignmentImpl)alignment).getOffsetRespBlockBefore(blockAfter); - if (alignRespBlock != null) { - return CoreFormatterUtil.getStartColumn(alignRespBlock); - } - else { - return -1; - } - } - + private static int getNewChildPosition(final AbstractBlockWrapper parent, final int offset) { AbstractBlockWrapper parentBlockToUse = getLastNestedCompositeBlockForSameRange(parent); if (!(parentBlockToUse instanceof CompositeBlockWrapper)) return 0; @@ -1109,7 +989,7 @@ public class FormatProcessor { if (space.containsLineFeeds()) { myCurrentBlock = (LeafBlockWrapper)block; - adjustIndent(); //since aligned block starts new line, it should not touch any other block + myIndentAdjuster.adjustIndent(myCurrentBlock); //since aligned block starts new line, it should not touch any other block storeAlignmentsAfterCurrentBlock(); } } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/IndentAdjuster.java b/platform/lang-impl/src/com/intellij/formatting/engine/IndentAdjuster.java new file mode 100644 index 000000000000..8d4272916928 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/IndentAdjuster.java @@ -0,0 +1,143 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.*; +import com.intellij.formatting.FormatProcessor; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import org.jetbrains.annotations.Nullable; + +public class IndentAdjuster { + private final AlignmentHelper myAlignmentHelper; + private final BlockIndentOptions myBlockIndentOptions; + + public IndentAdjuster(BlockIndentOptions blockIndentOptions, AlignmentHelper alignmentHelper) { + myAlignmentHelper = alignmentHelper; + myBlockIndentOptions = blockIndentOptions; + } + + /** + * Sometimes to align block we adjust whitespace of other block before. + * In such a case, we rollback to that block restarting formatting from there. + * + * @return new current block if we need to rollback, null otherwise + */ + public LeafBlockWrapper adjustIndent(LeafBlockWrapper block) { + AlignmentImpl alignment = CoreFormatterUtil.getAlignment(block); + WhiteSpace whiteSpace = block.getWhiteSpace(); + + if (alignment == null || myAlignmentHelper.shouldSkip(alignment)) { + if (whiteSpace.containsLineFeeds()) { + adjustSpacingByIndentOffset(block); + } + else { + whiteSpace.arrangeSpaces(block.getSpaceProperty()); + } + return null; + } + + return myAlignmentHelper.applyAlignment(alignment, block); + } + + private void adjustSpacingByIndentOffset(LeafBlockWrapper block) { + CommonCodeStyleSettings.IndentOptions options = myBlockIndentOptions.getIndentOptions(block); + IndentData offset = block.calculateOffset(options); + block.getWhiteSpace().setSpaces(offset.getSpaces(), offset.getIndentSpaces()); + } + + public void adjustLineIndent(LeafBlockWrapper myCurrentBlock) { + IndentData alignOffset = getAlignOffset(myCurrentBlock); + + if (alignOffset == null) { + adjustSpacingByIndentOffset(myCurrentBlock); + } + else { + myCurrentBlock.getWhiteSpace().setSpaces(alignOffset.getSpaces(), alignOffset.getIndentSpaces()); + } + } + + @Nullable + private static IndentData getAlignOffset(LeafBlockWrapper myCurrentBlock) { + AbstractBlockWrapper current = myCurrentBlock; + while (true) { + final AlignmentImpl alignment = current.getAlignment(); + LeafBlockWrapper offsetResponsibleBlock; + if (alignment != null && (offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock)) != null) { + final WhiteSpace whiteSpace = offsetResponsibleBlock.getWhiteSpace(); + if (whiteSpace.containsLineFeeds()) { + return new IndentData(whiteSpace.getIndentSpaces(), whiteSpace.getSpaces()); + } + else { + final int offsetBeforeBlock = CoreFormatterUtil.getStartColumn(offsetResponsibleBlock); + final AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(myCurrentBlock); + if (indentedParentBlock == null) { + return new IndentData(0, offsetBeforeBlock); + } + else { + final int parentIndent = indentedParentBlock.getWhiteSpace().getIndentOffset(); + if (parentIndent > offsetBeforeBlock) { + return new IndentData(0, offsetBeforeBlock); + } + else { + return new IndentData(parentIndent, offsetBeforeBlock - parentIndent); + } + } + } + } + else { + current = current.getParent(); + if (current == null || current.getStartOffset() != myCurrentBlock.getStartOffset()) return null; + } + } + } + + public IndentInfo adjustLineIndent(LeafBlockWrapper currentBlock, FormatProcessor.ChildAttributesInfo info) { + AbstractBlockWrapper parent = info.parent; + ChildAttributes childAttributes = info.attributes; + int index = info.index; + + int alignOffset = getAlignOffsetBefore(childAttributes.getAlignment(), null); + if (alignOffset == -1) { + return parent.calculateChildOffset(myBlockIndentOptions.getIndentOptions(parent), childAttributes, index).createIndentInfo(); + } + else { + AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(currentBlock); + if (indentedParentBlock == null) { + return new IndentInfo(0, 0, alignOffset); + } + else { + int indentOffset = indentedParentBlock.getWhiteSpace().getIndentOffset(); + if (indentOffset > alignOffset) { + return new IndentInfo(0, 0, alignOffset); + } + else { + return new IndentInfo(0, indentOffset, alignOffset - indentOffset); + } + } + } + } + + private static int getAlignOffsetBefore(@Nullable final Alignment alignment, @Nullable final LeafBlockWrapper blockAfter) { + if (alignment == null) return -1; + final LeafBlockWrapper alignRespBlock = ((AlignmentImpl)alignment).getOffsetRespBlockBefore(blockAfter); + if (alignRespBlock != null) { + return CoreFormatterUtil.getStartColumn(alignRespBlock); + } + else { + return -1; + } + } +} From ef7d5baf07c043e367fcc452c3c9e7bdb0702f49 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 22 Dec 2015 20:16:06 +0300 Subject: [PATCH 23/34] extracted ExpandChildrenIndent state to separate class --- .../formatting/AbstractBlockWrapper.java | 2 +- .../formatting/CompositeBlockWrapper.java | 2 +- .../intellij/formatting/FormatProcessor.java | 190 +-------------- .../intellij/formatting/FormatterImpl.java | 1 + .../com/intellij/formatting/IndentImpl.java | 2 +- .../formatting/InitialInfoBuilder.java | 1 + .../intellij/formatting/LeafBlockWrapper.java | 2 +- .../com/intellij/formatting/SpacingImpl.java | 2 +- .../engine/ExpandChildrenIndent.java | 225 ++++++++++++++++++ .../{ => engine}/ExpandableIndent.java | 4 +- 10 files changed, 241 insertions(+), 190 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java rename platform/lang-impl/src/com/intellij/formatting/{ => engine}/ExpandableIndent.java (92%) diff --git a/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java index 8081b7f4a47e..0043dce2f854 100644 --- a/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/AbstractBlockWrapper.java @@ -337,7 +337,7 @@ public abstract class AbstractBlockWrapper { * * @return object that encapsulates information about number of symbols before the current block */ - protected abstract IndentData getNumberOfSymbolsBeforeBlock(); + public abstract IndentData getNumberOfSymbolsBeforeBlock(); /** * @return previous block for the current block if any; null otherwise diff --git a/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java index 97508169a4de..fd5c95999d9f 100644 --- a/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/CompositeBlockWrapper.java @@ -75,7 +75,7 @@ public class CompositeBlockWrapper extends AbstractBlockWrapper{ } @Override - protected IndentData getNumberOfSymbolsBeforeBlock() { + public IndentData getNumberOfSymbolsBeforeBlock() { if (myChildren == null || myChildren.isEmpty()) { return new IndentData(0, 0); } diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index d45a730c135e..a7c5eb7b789f 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -75,7 +75,8 @@ public class FormatProcessor { @NotNull private StateProcessor myStateProcessor; - private MultiMap myExpandableIndents; + private Ref> myExpandableIndentsRef = Ref.create(); + private Ref myIndentAdjusterRef = Ref.create(); public FormatProcessor(final FormattingDocumentModel docModel, Block rootBlock, @@ -119,7 +120,8 @@ public class FormatProcessor { myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); myAlignmentHelper = new AlignmentHelper(myDocument, builder.getBlocksToAlign(), myBlockIndentOptions); myIndentAdjuster = new IndentAdjuster(myBlockIndentOptions, myAlignmentHelper); - myExpandableIndents = builder.getExpandableIndentsBlocks(); + myIndentAdjusterRef.set(myIndentAdjuster); + myExpandableIndentsRef.set(builder.getExpandableIndentsBlocks()); } }); myStateProcessor = new StateProcessor(wrapState); @@ -176,7 +178,7 @@ public class FormatProcessor { public void format(FormattingModel model, boolean sequentially) { if (sequentially) { myStateProcessor.setNextState(new AdjustWhiteSpacesState()); - myStateProcessor.setNextState(new ExpandChildrenIndent()); + myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myIndentAdjusterRef, myExpandableIndentsRef)); myStateProcessor.setNextState(new ApplyChangesState(myFirstTokenBlockRef, model, myBlockIndentOptions, myProgressCallback)); } else { @@ -213,7 +215,7 @@ public class FormatProcessor { @SuppressWarnings({"WhileLoopSpinsOnField"}) public void formatWithoutRealModifications(boolean sequentially) { myStateProcessor.setNextState(new AdjustWhiteSpacesState()); - myStateProcessor.setNextState(new ExpandChildrenIndent()); + myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myIndentAdjusterRef, myExpandableIndentsRef)); if (sequentially) { return; } @@ -831,184 +833,4 @@ public class FormatProcessor { myInterestingOffset = interestingOffset; } } - - private class ExpandChildrenIndent extends State { - private Iterator myIterator; - private MultiMap myBlocksToRealign = new MultiMap(); - - @Override - protected void doIteration() { - if (myIterator == null) { - myIterator = myExpandableIndents.keySet().iterator(); - } - if (!myIterator.hasNext()) { - setDone(true); - return; - } - - final ExpandableIndent indent = myIterator.next(); - Collection blocksToExpandIndent = myExpandableIndents.get(indent); - if (shouldExpand(blocksToExpandIndent)) { - for (AbstractBlockWrapper block : blocksToExpandIndent) { - indent.setEnforceIndent(true); - reindentNewLineChildren(block); - indent.setEnforceIndent(false); - } - } - - restoreAlignments(myBlocksToRealign); - myBlocksToRealign.clear(); - } - - private void restoreAlignments(MultiMap blocks) { - for (Alignment alignment : blocks.keySet()) { - AlignmentImpl alignmentImpl = (AlignmentImpl)alignment; - if (!alignmentImpl.isAllowBackwardShift()) continue; - - Set toRealign = alignmentImpl.getOffsetResponsibleBlocks(); - arrangeSpaces(toRealign); - - LeafBlockWrapper rightMostBlock = getRightMostBlock(toRealign); - int maxSpacesBeforeBlock = rightMostBlock.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); - int rightMostBlockLine = myDocument.getLineNumber(rightMostBlock.getStartOffset()); - - for (LeafBlockWrapper block : toRealign) { - int currentBlockLine = myDocument.getLineNumber(block.getStartOffset()); - if (currentBlockLine == rightMostBlockLine) continue; - - int blockIndent = block.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); - int delta = maxSpacesBeforeBlock - blockIndent; - if (delta > 0) { - int newSpaces = block.getWhiteSpace().getTotalSpaces() + delta; - adjustSpacingToKeepAligned(block, newSpaces); - } - } - } - } - - private void adjustSpacingToKeepAligned(LeafBlockWrapper block, int newSpaces) { - WhiteSpace space = block.getWhiteSpace(); - SpacingImpl property = block.getSpaceProperty(); - if (property == null) return; - space.arrangeSpaces(new SpacingImpl(newSpaces, newSpaces, - property.getMinLineFeeds(), - property.isReadOnly(), - property.isSafe(), - property.shouldKeepLineFeeds(), - property.getKeepBlankLines(), - property.shouldKeepFirstColumn(), - property.getPrefLineFeeds())); - } - - private LeafBlockWrapper getRightMostBlock(Collection toRealign) { - int maxSpacesBeforeBlock = -1; - LeafBlockWrapper rightMostBlock = null; - - for (LeafBlockWrapper block : toRealign) { - int spaces = block.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); - if (spaces > maxSpacesBeforeBlock) { - maxSpacesBeforeBlock = spaces; - rightMostBlock = block; - } - } - - return rightMostBlock; - } - - private void arrangeSpaces(Collection toRealign) { - for (LeafBlockWrapper block : toRealign) { - WhiteSpace whiteSpace = block.getWhiteSpace(); - SpacingImpl spacing = block.getSpaceProperty(); - whiteSpace.arrangeSpaces(spacing); - } - } - - private boolean shouldExpand(Collection blocksToExpandIndent) { - AbstractBlockWrapper last = null; - for (AbstractBlockWrapper block : blocksToExpandIndent) { - if (block.getWhiteSpace().containsLineFeeds()) { - return true; - } - last = block; - } - - if (last != null) { - AbstractBlockWrapper next = getNextBlock(last); - if (next != null && next.getWhiteSpace().containsLineFeeds()) { - int nextNewLineBlockIndent = next.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); - if (nextNewLineBlockIndent >= finMinNewLineIndent(blocksToExpandIndent)) { - return true; - } - } - } - - return false; - } - - private int finMinNewLineIndent(@NotNull Collection wrappers) { - int totalMinimum = Integer.MAX_VALUE; - for (AbstractBlockWrapper wrapper : wrappers) { - int minNewLineIndent = findMinNewLineIndent(wrapper); - if (minNewLineIndent < totalMinimum) { - totalMinimum = minNewLineIndent; - } - } - return totalMinimum; - } - - private int findMinNewLineIndent(@NotNull AbstractBlockWrapper block) { - if (block instanceof LeafBlockWrapper && block.getWhiteSpace().containsLineFeeds()) { - return block.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); - } - else if (block instanceof CompositeBlockWrapper) { - List children = ((CompositeBlockWrapper)block).getChildren(); - int currentMin = Integer.MAX_VALUE; - for (AbstractBlockWrapper child : children) { - int childIndent = findMinNewLineIndent(child); - if (childIndent < currentMin) { - currentMin = childIndent; - } - } - return currentMin; - } - return Integer.MAX_VALUE; - } - - private AbstractBlockWrapper getNextBlock(AbstractBlockWrapper block) { - List children = block.getParent().getChildren(); - int nextBlockIndex = children.indexOf(block) + 1; - if (nextBlockIndex < children.size()) { - return children.get(nextBlockIndex); - } - return null; - } - - private void reindentNewLineChildren(final @NotNull AbstractBlockWrapper block) { - if (block instanceof LeafBlockWrapper) { - WhiteSpace space = block.getWhiteSpace(); - - if (space.containsLineFeeds()) { - myCurrentBlock = (LeafBlockWrapper)block; - myIndentAdjuster.adjustIndent(myCurrentBlock); //since aligned block starts new line, it should not touch any other block - storeAlignmentsAfterCurrentBlock(); - } - } - else if (block instanceof CompositeBlockWrapper) { - List children = ((CompositeBlockWrapper)block).getChildren(); - for (AbstractBlockWrapper childBlock : children) { - reindentNewLineChildren(childBlock); - } - } - } - - private void storeAlignmentsAfterCurrentBlock() { - LeafBlockWrapper current = myCurrentBlock.getNextBlock(); - while (current != null && !current.getWhiteSpace().containsLineFeeds()) { - if (current.getAlignment() != null) { - myBlocksToRealign.putValue(current.getAlignment(), current); - } - current = current.getNextBlock(); - } - } - } } diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index a30f9bd789ed..854d33e33efa 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -16,6 +16,7 @@ package com.intellij.formatting; +import com.intellij.formatting.engine.ExpandableIndent; import com.intellij.lang.ASTNode; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; diff --git a/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java b/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java index 314531d5714b..cbed59277f78 100644 --- a/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java @@ -18,7 +18,7 @@ package com.intellij.formatting; import org.jetbrains.annotations.NonNls; -class IndentImpl extends Indent { +public class IndentImpl extends Indent { private final boolean myIsAbsolute; private final boolean myRelativeToDirectParent; diff --git a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java index 404a15b90330..6b5f377844fe 100644 --- a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java +++ b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java @@ -17,6 +17,7 @@ package com.intellij.formatting; import com.intellij.diagnostic.LogMessageEx; +import com.intellij.formatting.engine.ExpandableIndent; import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; diff --git a/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java b/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java index 3ebceb5c718c..ab3ef322a786 100644 --- a/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java +++ b/platform/lang-impl/src/com/intellij/formatting/LeafBlockWrapper.java @@ -126,7 +126,7 @@ public class LeafBlockWrapper extends AbstractBlockWrapper { } @Override - protected IndentData getNumberOfSymbolsBeforeBlock() { + public IndentData getNumberOfSymbolsBeforeBlock() { int spaces = getWhiteSpace().getSpaces(); int indentSpaces = getWhiteSpace().getIndentSpaces(); diff --git a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java index ae3a7792c25c..c46e36750ddb 100644 --- a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java @@ -77,7 +77,7 @@ public class SpacingImpl extends Spacing { return myMinLineFeeds; } - final boolean isReadOnly(){ + public final boolean isReadOnly(){ return (myFlags & READ_ONLY_MASK) != 0; } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java new file mode 100644 index 000000000000..93686fbcb953 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java @@ -0,0 +1,225 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.*; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.Ref; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class ExpandChildrenIndent extends State { + private final Ref> myExpandableIndentsRef; + private final Ref myIndentAdjusterRef; + + private final Document myDocument; + private IndentAdjuster myIndentAdjuster; + private MultiMap myExpandableIndents; + private LeafBlockWrapper myCurrentBlock; + + public ExpandChildrenIndent(Document document, + Ref indentAdjuster, + Ref> expandableIndentsRef) { + myExpandableIndentsRef = expandableIndentsRef; + myIndentAdjusterRef = indentAdjuster; + myDocument = document; + } + + private Iterator myIterator; + private MultiMap myBlocksToRealign = new MultiMap(); + + @Override + protected void doIteration() { + if (myIterator == null) { + myExpandableIndents = myExpandableIndentsRef.get(); + myIndentAdjuster = myIndentAdjusterRef.get(); + myIterator = myExpandableIndents.keySet().iterator(); + } + if (!myIterator.hasNext()) { + setDone(true); + return; + } + + final ExpandableIndent indent = myIterator.next(); + Collection blocksToExpandIndent = myExpandableIndents.get(indent); + if (shouldExpand(blocksToExpandIndent)) { + for (AbstractBlockWrapper block : blocksToExpandIndent) { + indent.setEnforceIndent(true); + reindentNewLineChildren(block); + indent.setEnforceIndent(false); + } + } + + restoreAlignments(myBlocksToRealign); + myBlocksToRealign.clear(); + } + + private void restoreAlignments(MultiMap blocks) { + for (Alignment alignment : blocks.keySet()) { + AlignmentImpl alignmentImpl = (AlignmentImpl)alignment; + if (!alignmentImpl.isAllowBackwardShift()) continue; + + Set toRealign = alignmentImpl.getOffsetResponsibleBlocks(); + arrangeSpaces(toRealign); + + LeafBlockWrapper rightMostBlock = getRightMostBlock(toRealign); + int maxSpacesBeforeBlock = rightMostBlock.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); + int rightMostBlockLine = myDocument.getLineNumber(rightMostBlock.getStartOffset()); + + for (LeafBlockWrapper block : toRealign) { + int currentBlockLine = myDocument.getLineNumber(block.getStartOffset()); + if (currentBlockLine == rightMostBlockLine) continue; + + int blockIndent = block.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); + int delta = maxSpacesBeforeBlock - blockIndent; + if (delta > 0) { + int newSpaces = block.getWhiteSpace().getTotalSpaces() + delta; + adjustSpacingToKeepAligned(block, newSpaces); + } + } + } + } + + private void adjustSpacingToKeepAligned(LeafBlockWrapper block, int newSpaces) { + WhiteSpace space = block.getWhiteSpace(); + SpacingImpl property = block.getSpaceProperty(); + if (property == null) return; + space.arrangeSpaces(new SpacingImpl(newSpaces, newSpaces, + property.getMinLineFeeds(), + property.isReadOnly(), + property.isSafe(), + property.shouldKeepLineFeeds(), + property.getKeepBlankLines(), + property.shouldKeepFirstColumn(), + property.getPrefLineFeeds())); + } + + private LeafBlockWrapper getRightMostBlock(Collection toRealign) { + int maxSpacesBeforeBlock = -1; + LeafBlockWrapper rightMostBlock = null; + + for (LeafBlockWrapper block : toRealign) { + int spaces = block.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); + if (spaces > maxSpacesBeforeBlock) { + maxSpacesBeforeBlock = spaces; + rightMostBlock = block; + } + } + + return rightMostBlock; + } + + private void arrangeSpaces(Collection toRealign) { + for (LeafBlockWrapper block : toRealign) { + WhiteSpace whiteSpace = block.getWhiteSpace(); + SpacingImpl spacing = block.getSpaceProperty(); + whiteSpace.arrangeSpaces(spacing); + } + } + + private boolean shouldExpand(Collection blocksToExpandIndent) { + AbstractBlockWrapper last = null; + for (AbstractBlockWrapper block : blocksToExpandIndent) { + if (block.getWhiteSpace().containsLineFeeds()) { + return true; + } + last = block; + } + + if (last != null) { + AbstractBlockWrapper next = getNextBlock(last); + if (next != null && next.getWhiteSpace().containsLineFeeds()) { + int nextNewLineBlockIndent = next.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); + if (nextNewLineBlockIndent >= finMinNewLineIndent(blocksToExpandIndent)) { + return true; + } + } + } + + return false; + } + + private int finMinNewLineIndent(@NotNull Collection wrappers) { + int totalMinimum = Integer.MAX_VALUE; + for (AbstractBlockWrapper wrapper : wrappers) { + int minNewLineIndent = findMinNewLineIndent(wrapper); + if (minNewLineIndent < totalMinimum) { + totalMinimum = minNewLineIndent; + } + } + return totalMinimum; + } + + private int findMinNewLineIndent(@NotNull AbstractBlockWrapper block) { + if (block instanceof LeafBlockWrapper && block.getWhiteSpace().containsLineFeeds()) { + return block.getNumberOfSymbolsBeforeBlock().getTotalSpaces(); + } + else if (block instanceof CompositeBlockWrapper) { + List children = ((CompositeBlockWrapper)block).getChildren(); + int currentMin = Integer.MAX_VALUE; + for (AbstractBlockWrapper child : children) { + int childIndent = findMinNewLineIndent(child); + if (childIndent < currentMin) { + currentMin = childIndent; + } + } + return currentMin; + } + return Integer.MAX_VALUE; + } + + private AbstractBlockWrapper getNextBlock(AbstractBlockWrapper block) { + List children = block.getParent().getChildren(); + int nextBlockIndex = children.indexOf(block) + 1; + if (nextBlockIndex < children.size()) { + return children.get(nextBlockIndex); + } + return null; + } + + private void reindentNewLineChildren(final @NotNull AbstractBlockWrapper block) { + if (block instanceof LeafBlockWrapper) { + WhiteSpace space = block.getWhiteSpace(); + + if (space.containsLineFeeds()) { + myCurrentBlock = (LeafBlockWrapper)block; + myIndentAdjuster.adjustIndent(myCurrentBlock); //since aligned block starts new line, it should not touch any other block + storeAlignmentsAfterCurrentBlock(); + } + } + else if (block instanceof CompositeBlockWrapper) { + List children = ((CompositeBlockWrapper)block).getChildren(); + for (AbstractBlockWrapper childBlock : children) { + reindentNewLineChildren(childBlock); + } + } + } + + private void storeAlignmentsAfterCurrentBlock() { + LeafBlockWrapper current = myCurrentBlock.getNextBlock(); + while (current != null && !current.getWhiteSpace().containsLineFeeds()) { + if (current.getAlignment() != null) { + myBlocksToRealign.putValue(current.getAlignment(), current); + } + current = current.getNextBlock(); + } + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/formatting/ExpandableIndent.java b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandableIndent.java similarity index 92% rename from platform/lang-impl/src/com/intellij/formatting/ExpandableIndent.java rename to platform/lang-impl/src/com/intellij/formatting/engine/ExpandableIndent.java index 8b60c5fb6ba7..fd634ca383d8 100644 --- a/platform/lang-impl/src/com/intellij/formatting/ExpandableIndent.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandableIndent.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.formatting; +package com.intellij.formatting.engine; + +import com.intellij.formatting.IndentImpl; public class ExpandableIndent extends IndentImpl { private boolean myEnforceIndent; From f5a21188e5029aafd42ca0f8c86912eb81fe9114 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 23 Dec 2015 16:03:50 +0300 Subject: [PATCH 24/34] Refactoring: processWrap now returns new current block --- .../intellij/formatting/FormatProcessor.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index a7c5eb7b789f..983a88d01ced 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -270,7 +270,9 @@ public class FormatProcessor { } try { - if (processWrap()) { + LeafBlockWrapper newBlock = processWrap(); + if (newBlock != null) { + myCurrentBlock = newBlock; return; } } @@ -354,7 +356,7 @@ public class FormatProcessor { * @return true if we have changed myCurrentBlock and need to restart its processing; false if myCurrentBlock is unchanged and we can * continue processing */ - private boolean processWrap() { + private LeafBlockWrapper processWrap() { final SpacingImpl spacing = myCurrentBlock.getSpaceProperty(); final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); @@ -379,15 +381,14 @@ public class FormatProcessor { if (wrap != null || wrapIsPresent) { if (!wrapIsPresent && !canReplaceWrapCandidate(wrap)) { - myCurrentBlock = myWrapCandidate; - return true; + return myWrapCandidate; } if (wrap != null && wrap.getChopStartBlock() != null) { // getWrapToBeUsed() returns the block only if it actually exceeds the right margin. In this case, we need to go back to the // first block that has the CHOP_IF_NEEDED wrap type and start wrapping from there. - myCurrentBlock = wrap.getChopStartBlock(); + LeafBlockWrapper newCurrentBlock = wrap.getChopStartBlock(); wrap.setActive(); - return true; + return newCurrentBlock; } if (wrap != null && isChopNeeded(wrap)) { wrap.setActive(); @@ -398,8 +399,7 @@ public class FormatProcessor { if (!wrapWasPresent) { if (myFirstWrappedBlockOnLine != null && wrap.isChildOf(myFirstWrappedBlockOnLine.getWrap(), myCurrentBlock)) { wrap.ignoreParentWrap(myFirstWrappedBlockOnLine.getWrap(), myCurrentBlock); - myCurrentBlock = myFirstWrappedBlockOnLine; - return true; + return myFirstWrappedBlockOnLine; } else { myFirstWrappedBlockOnLine = myCurrentBlock; @@ -421,11 +421,10 @@ public class FormatProcessor { } if (!whiteSpace.containsLineFeeds() && myWrapCandidate != null && !whiteSpace.isReadOnly() && lineOver()) { - myCurrentBlock = myWrapCandidate; - return true; + return myWrapCandidate; } - return false; + return null; } /** From eb8160bacf72f4e88deefad24c6273492229b47b Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 23 Dec 2015 16:13:44 +0300 Subject: [PATCH 25/34] Refactoring: processWrap takes current block as parameter --- .../intellij/formatting/FormatProcessor.java | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 983a88d01ced..b54fc7a7c5b3 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -270,7 +270,7 @@ public class FormatProcessor { } try { - LeafBlockWrapper newBlock = processWrap(); + LeafBlockWrapper newBlock = processWrap(myCurrentBlock); if (newBlock != null) { myCurrentBlock = newBlock; return; @@ -356,9 +356,9 @@ public class FormatProcessor { * @return true if we have changed myCurrentBlock and need to restart its processing; false if myCurrentBlock is unchanged and we can * continue processing */ - private LeafBlockWrapper processWrap() { - final SpacingImpl spacing = myCurrentBlock.getSpaceProperty(); - final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); + private LeafBlockWrapper processWrap(LeafBlockWrapper currentBlock) { + final SpacingImpl spacing = currentBlock.getSpaceProperty(); + final WhiteSpace whiteSpace = currentBlock.getWhiteSpace(); final boolean wrapWasPresent = whiteSpace.containsLineFeeds(); @@ -372,12 +372,12 @@ public class FormatProcessor { final boolean wrapIsPresent = whiteSpace.containsLineFeeds(); - final ArrayList wraps = myCurrentBlock.getWraps(); + final ArrayList wraps = currentBlock.getWraps(); for (WrapImpl wrap : wraps) { - wrap.setWrapOffset(myCurrentBlock.getStartOffset()); + wrap.setWrapOffset(currentBlock.getStartOffset()); } - final WrapImpl wrap = getWrapToBeUsed(wraps); + final WrapImpl wrap = getWrapToBeUsed(wraps, currentBlock); if (wrap != null || wrapIsPresent) { if (!wrapIsPresent && !canReplaceWrapCandidate(wrap)) { @@ -390,19 +390,19 @@ public class FormatProcessor { wrap.setActive(); return newCurrentBlock; } - if (wrap != null && isChopNeeded(wrap)) { + if (wrap != null && isChopNeeded(wrap, currentBlock)) { wrap.setActive(); } if (!wrapIsPresent) { whiteSpace.ensureLineFeed(); if (!wrapWasPresent) { - if (myFirstWrappedBlockOnLine != null && wrap.isChildOf(myFirstWrappedBlockOnLine.getWrap(), myCurrentBlock)) { - wrap.ignoreParentWrap(myFirstWrappedBlockOnLine.getWrap(), myCurrentBlock); + if (myFirstWrappedBlockOnLine != null && wrap.isChildOf(myFirstWrappedBlockOnLine.getWrap(), currentBlock)) { + wrap.ignoreParentWrap(myFirstWrappedBlockOnLine.getWrap(), currentBlock); return myFirstWrappedBlockOnLine; } else { - myFirstWrappedBlockOnLine = myCurrentBlock; + myFirstWrappedBlockOnLine = currentBlock; } } } @@ -411,16 +411,16 @@ public class FormatProcessor { } else { for (final WrapImpl wrap1 : wraps) { - if (isCandidateToBeWrapped(wrap1) && canReplaceWrapCandidate(wrap1)) { - myWrapCandidate = myCurrentBlock; + if (isCandidateToBeWrapped(wrap1, myCurrentBlock) && canReplaceWrapCandidate(wrap1)) { + myWrapCandidate = currentBlock; } - if (isChopNeeded(wrap1)) { - wrap1.saveChopBlock(myCurrentBlock); + if (isChopNeeded(wrap1, currentBlock)) { + wrap1.saveChopBlock(currentBlock); } } } - if (!whiteSpace.containsLineFeeds() && myWrapCandidate != null && !whiteSpace.isReadOnly() && lineOver()) { + if (!whiteSpace.containsLineFeeds() && myWrapCandidate != null && !whiteSpace.isReadOnly() && lineOver(currentBlock)) { return myWrapCandidate; } @@ -442,18 +442,18 @@ public class FormatProcessor { return wrap == currentWrap || !wrap.isChildOf(currentWrap, myCurrentBlock); } - private boolean isCandidateToBeWrapped(final WrapImpl wrap) { - return isSuitableInTheCurrentPosition(wrap) && + private boolean isCandidateToBeWrapped(final WrapImpl wrap, LeafBlockWrapper currentBlock) { + return isSuitableInTheCurrentPosition(wrap, currentBlock) && (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED || wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED) && - !myCurrentBlock.getWhiteSpace().isReadOnly(); + !currentBlock.getWhiteSpace().isReadOnly(); } - private boolean isChopNeeded(final WrapImpl wrap) { - return wrap != null && wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && isSuitableInTheCurrentPosition(wrap); + private boolean isChopNeeded(final WrapImpl wrap, LeafBlockWrapper currentBlock) { + return wrap != null && wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && isSuitableInTheCurrentPosition(wrap, currentBlock); } - private boolean isSuitableInTheCurrentPosition(final WrapImpl wrap) { - if (wrap.getWrapOffset() < myCurrentBlock.getStartOffset()) { + private boolean isSuitableInTheCurrentPosition(final WrapImpl wrap, LeafBlockWrapper currentBlock) { + if (wrap.getWrapOffset() < currentBlock.getStartOffset()) { return true; } @@ -462,10 +462,10 @@ public class FormatProcessor { } if (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED) { - return positionAfterWrappingIsSuitable(); + return positionAfterWrappingIsSuitable(currentBlock); } - return wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && lineOver() && positionAfterWrappingIsSuitable(); + return wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && lineOver(currentBlock) && positionAfterWrappingIsSuitable(currentBlock); } /** @@ -473,39 +473,39 @@ public class FormatProcessor { * * @return true if it's ok to wrap at the currently processed block; false otherwise */ - private boolean positionAfterWrappingIsSuitable() { - final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); + private boolean positionAfterWrappingIsSuitable(LeafBlockWrapper currentBlock) { + final WhiteSpace whiteSpace = currentBlock.getWhiteSpace(); if (whiteSpace.containsLineFeeds()) return true; final int spaces = whiteSpace.getSpaces(); int indentSpaces = whiteSpace.getIndentSpaces(); try { - final int startColumnNow = CoreFormatterUtil.getStartColumn(myCurrentBlock); + final int startColumnNow = CoreFormatterUtil.getStartColumn(currentBlock); whiteSpace.ensureLineFeed(); - myIndentAdjuster.adjustLineIndent(myCurrentBlock); - final int startColumnAfterWrap = CoreFormatterUtil.getStartColumn(myCurrentBlock); + myIndentAdjuster.adjustLineIndent(currentBlock); + final int startColumnAfterWrap = CoreFormatterUtil.getStartColumn(currentBlock); return startColumnNow > startColumnAfterWrap; } finally { - whiteSpace.removeLineFeeds(myCurrentBlock.getSpaceProperty(), myBlockMapperHelper); + whiteSpace.removeLineFeeds(currentBlock.getSpaceProperty(), myBlockMapperHelper); whiteSpace.setSpaces(spaces, indentSpaces); } } @Nullable - private WrapImpl getWrapToBeUsed(final ArrayList wraps) { + private WrapImpl getWrapToBeUsed(final ArrayList wraps, LeafBlockWrapper currentBlock) { if (wraps.isEmpty()) { return null; } - if (myWrapCandidate == myCurrentBlock) return wraps.get(0); + if (myWrapCandidate == currentBlock) return wraps.get(0); for (final WrapImpl wrap : wraps) { - if (!isSuitableInTheCurrentPosition(wrap)) continue; + if (!isSuitableInTheCurrentPosition(wrap, currentBlock)) continue; if (wrap.isActive()) return wrap; final WrapImpl.Type type = wrap.getType(); if (type == WrapImpl.Type.WRAP_ALWAYS) return wrap; if (type == WrapImpl.Type.WRAP_AS_NEEDED || type == WrapImpl.Type.CHOP_IF_NEEDED) { - if (lineOver()) { + if (lineOver(currentBlock)) { return wrap; } } @@ -517,9 +517,9 @@ public class FormatProcessor { * @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() && - CoreFormatterUtil.getStartColumn(myCurrentBlock) + myCurrentBlock.getLength() > myRightMargin; + private boolean lineOver(LeafBlockWrapper currentBlock) { + return !currentBlock.containsLineFeeds() && + CoreFormatterUtil.getStartColumn(currentBlock) + currentBlock.getLength() > myRightMargin; } private void defineAlignOffset(final LeafBlockWrapper block) { From 377a8555a8ed8024bdf0d930e40cbfc153eb5342 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 23 Dec 2015 16:53:21 +0300 Subject: [PATCH 26/34] extracted WrapProcessor --- .../intellij/formatting/FormatProcessor.java | 189 +--------------- .../src/com/intellij/formatting/WrapImpl.java | 20 +- .../formatting/engine/WrapProcessor.java | 205 ++++++++++++++++++ 3 files changed, 227 insertions(+), 187 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index b54fc7a7c5b3..5e2122dd747d 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -33,7 +33,10 @@ import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import static com.intellij.formatting.InitialInfoBuilder.prepareToBuildBlocksSequentially; @@ -47,7 +50,7 @@ public class FormatProcessor { private Map myInfos; private CompositeBlockWrapper myRootBlockWrapper; - private BlockMapperHelper myBlockMapperHelper; + private DependentSpacingEngine myDependentSpacingEngine; private AlignmentHelper myAlignmentHelper; private IndentAdjuster myIndentAdjuster; @@ -57,8 +60,8 @@ public class FormatProcessor { private final CodeStyleSettings mySettings; private final Document myDocument; - private LeafBlockWrapper myWrapCandidate = null; - private LeafBlockWrapper myFirstWrappedBlockOnLine = null; + private BlockMapperHelper myBlockMapperHelper; + private LeafBlockWrapper myFirstTokenBlock; private Ref myFirstTokenBlockRef = Ref.create(); @@ -77,6 +80,8 @@ public class FormatProcessor { private StateProcessor myStateProcessor; private Ref> myExpandableIndentsRef = Ref.create(); private Ref myIndentAdjusterRef = Ref.create(); + private WrapProcessor myWrapProcessor; + private LeafBlockWrapper myWrapCandidate; public FormatProcessor(final FormattingDocumentModel docModel, Block rootBlock, @@ -122,6 +127,7 @@ public class FormatProcessor { myIndentAdjuster = new IndentAdjuster(myBlockIndentOptions, myAlignmentHelper); myIndentAdjusterRef.set(myIndentAdjuster); myExpandableIndentsRef.set(builder.getExpandableIndentsBlocks()); + myWrapProcessor = new WrapProcessor(myBlockMapperHelper, myIndentAdjuster, myRightMargin); } }); myStateProcessor = new StateProcessor(wrapState); @@ -270,7 +276,7 @@ public class FormatProcessor { } try { - LeafBlockWrapper newBlock = processWrap(myCurrentBlock); + LeafBlockWrapper newBlock = myWrapProcessor.processWrap(myCurrentBlock); if (newBlock != null) { myCurrentBlock = newBlock; return; @@ -350,178 +356,7 @@ public class FormatProcessor { }); } - /** - * Processes the wrap of the current block. - * - * @return true if we have changed myCurrentBlock and need to restart its processing; false if myCurrentBlock is unchanged and we can - * continue processing - */ - private LeafBlockWrapper processWrap(LeafBlockWrapper currentBlock) { - final SpacingImpl spacing = currentBlock.getSpaceProperty(); - final WhiteSpace whiteSpace = currentBlock.getWhiteSpace(); - - final boolean wrapWasPresent = whiteSpace.containsLineFeeds(); - - if (wrapWasPresent) { - myFirstWrappedBlockOnLine = null; - - if (!whiteSpace.containsLineFeedsInitially()) { - whiteSpace.removeLineFeeds(spacing, myBlockMapperHelper); - } - } - - final boolean wrapIsPresent = whiteSpace.containsLineFeeds(); - - final ArrayList wraps = currentBlock.getWraps(); - for (WrapImpl wrap : wraps) { - wrap.setWrapOffset(currentBlock.getStartOffset()); - } - - final WrapImpl wrap = getWrapToBeUsed(wraps, currentBlock); - - if (wrap != null || wrapIsPresent) { - if (!wrapIsPresent && !canReplaceWrapCandidate(wrap)) { - return myWrapCandidate; - } - if (wrap != null && wrap.getChopStartBlock() != null) { - // getWrapToBeUsed() returns the block only if it actually exceeds the right margin. In this case, we need to go back to the - // first block that has the CHOP_IF_NEEDED wrap type and start wrapping from there. - LeafBlockWrapper newCurrentBlock = wrap.getChopStartBlock(); - wrap.setActive(); - return newCurrentBlock; - } - if (wrap != null && isChopNeeded(wrap, currentBlock)) { - wrap.setActive(); - } - - if (!wrapIsPresent) { - whiteSpace.ensureLineFeed(); - if (!wrapWasPresent) { - if (myFirstWrappedBlockOnLine != null && wrap.isChildOf(myFirstWrappedBlockOnLine.getWrap(), currentBlock)) { - wrap.ignoreParentWrap(myFirstWrappedBlockOnLine.getWrap(), currentBlock); - return myFirstWrappedBlockOnLine; - } - else { - myFirstWrappedBlockOnLine = currentBlock; - } - } - } - - myWrapCandidate = null; - } - else { - for (final WrapImpl wrap1 : wraps) { - if (isCandidateToBeWrapped(wrap1, myCurrentBlock) && canReplaceWrapCandidate(wrap1)) { - myWrapCandidate = currentBlock; - } - if (isChopNeeded(wrap1, currentBlock)) { - wrap1.saveChopBlock(currentBlock); - } - } - } - - if (!whiteSpace.containsLineFeeds() && myWrapCandidate != null && !whiteSpace.isReadOnly() && lineOver(currentBlock)) { - return myWrapCandidate; - } - - return null; - } - - /** - * 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 - */ - private boolean canReplaceWrapCandidate(WrapImpl wrap) { - if (myWrapCandidate == null) return true; - WrapImpl.Type type = wrap.getType(); - if (wrap.isActive() && (type == WrapImpl.Type.CHOP_IF_NEEDED || type == WrapImpl.Type.WRAP_ALWAYS)) return true; - final WrapImpl currentWrap = myWrapCandidate.getWrap(); - return wrap == currentWrap || !wrap.isChildOf(currentWrap, myCurrentBlock); - } - - private boolean isCandidateToBeWrapped(final WrapImpl wrap, LeafBlockWrapper currentBlock) { - return isSuitableInTheCurrentPosition(wrap, currentBlock) && - (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED || wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED) && - !currentBlock.getWhiteSpace().isReadOnly(); - } - - private boolean isChopNeeded(final WrapImpl wrap, LeafBlockWrapper currentBlock) { - return wrap != null && wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && isSuitableInTheCurrentPosition(wrap, currentBlock); - } - - private boolean isSuitableInTheCurrentPosition(final WrapImpl wrap, LeafBlockWrapper currentBlock) { - if (wrap.getWrapOffset() < currentBlock.getStartOffset()) { - return true; - } - - if (wrap.isWrapFirstElement()) { - return true; - } - - if (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED) { - return positionAfterWrappingIsSuitable(currentBlock); - } - - return wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && lineOver(currentBlock) && positionAfterWrappingIsSuitable(currentBlock); - } - - /** - * 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 - */ - private boolean positionAfterWrappingIsSuitable(LeafBlockWrapper currentBlock) { - final WhiteSpace whiteSpace = currentBlock.getWhiteSpace(); - if (whiteSpace.containsLineFeeds()) return true; - final int spaces = whiteSpace.getSpaces(); - int indentSpaces = whiteSpace.getIndentSpaces(); - try { - final int startColumnNow = CoreFormatterUtil.getStartColumn(currentBlock); - whiteSpace.ensureLineFeed(); - myIndentAdjuster.adjustLineIndent(currentBlock); - final int startColumnAfterWrap = CoreFormatterUtil.getStartColumn(currentBlock); - return startColumnNow > startColumnAfterWrap; - } - finally { - whiteSpace.removeLineFeeds(currentBlock.getSpaceProperty(), myBlockMapperHelper); - whiteSpace.setSpaces(spaces, indentSpaces); - } - } - - @Nullable - private WrapImpl getWrapToBeUsed(final ArrayList wraps, LeafBlockWrapper currentBlock) { - if (wraps.isEmpty()) { - return null; - } - if (myWrapCandidate == currentBlock) return wraps.get(0); - - for (final WrapImpl wrap : wraps) { - if (!isSuitableInTheCurrentPosition(wrap, currentBlock)) continue; - if (wrap.isActive()) return wrap; - - final WrapImpl.Type type = wrap.getType(); - if (type == WrapImpl.Type.WRAP_ALWAYS) return wrap; - if (type == WrapImpl.Type.WRAP_AS_NEEDED || type == WrapImpl.Type.CHOP_IF_NEEDED) { - if (lineOver(currentBlock)) { - return wrap; - } - } - } - return null; - } - - /** - * @return true if {@link #myCurrentBlock currently processed wrapped block} doesn't contain line feeds and - * exceeds right margin; false otherwise - */ - private boolean lineOver(LeafBlockWrapper currentBlock) { - return !currentBlock.containsLineFeeds() && - CoreFormatterUtil.getStartColumn(currentBlock) + currentBlock.getLength() > myRightMargin; - } - + private void defineAlignOffset(final LeafBlockWrapper block) { AbstractBlockWrapper current = myCurrentBlock; while (true) { diff --git a/platform/lang-impl/src/com/intellij/formatting/WrapImpl.java b/platform/lang-impl/src/com/intellij/formatting/WrapImpl.java index 7be44a8e6816..1eb07bf66265 100644 --- a/platform/lang-impl/src/com/intellij/formatting/WrapImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/WrapImpl.java @@ -20,7 +20,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -class WrapImpl extends Wrap { +public class WrapImpl extends Wrap { /** * The block where the wrap needs to happen if the CHOP wrap mode is used and the chain of blocks exceeds the right margin. */ @@ -124,11 +124,11 @@ class WrapImpl extends Wrap { myIgnoredWraps.get(wrap).add(currentBlock); } - enum Type{ + public enum Type{ DO_NOT_WRAP, WRAP_AS_NEEDED, CHOP_IF_NEEDED, WRAP_ALWAYS } - LeafBlockWrapper getChopStartBlock() { + public LeafBlockWrapper getChopStartBlock() { return myChopStartBlock; } @@ -139,7 +139,7 @@ class WrapImpl extends Wrap { *

  • '{@link #isActive() isActive}' property value is set (to true)
  • * */ - void setActive() { + public void setActive() { myChopStartBlock = null; myFlags |= ACTIVE_MASK; } @@ -150,7 +150,7 @@ class WrapImpl extends Wrap { * * @param startOffset new '{@link #getWrapOffset() firstPosition}' property value to use if current value is undefined (negative) */ - void setWrapOffset(final int startOffset) { + public void setWrapOffset(final int startOffset) { if (myWrapOffset < 0) { myWrapOffset = startOffset; } @@ -160,7 +160,7 @@ class WrapImpl extends Wrap { * @return '{@link #getWrapOffset() firstPosition}' property value defined previously via {@link #setWrapOffset(int)} if any; * '-1' otherwise */ - int getWrapOffset() { + public int getWrapOffset() { return myWrapOffset; } @@ -180,7 +180,7 @@ class WrapImpl extends Wrap { myFlags |= (wrapFirstElement ? WRAP_FIRST_ELEMENT_MASK:0) | (myType.ordinal() << TYPE_SHIFT) | (myId << ID_SHIFT); } - final Type getType() { + public final Type getType() { return myTypes[(myFlags & TYPE_MASK) >>> TYPE_SHIFT]; } @@ -190,17 +190,17 @@ class WrapImpl extends Wrap { * * @return 'wrapFirstElement' property value */ - final boolean isWrapFirstElement() { + public final boolean isWrapFirstElement() { return (myFlags & WRAP_FIRST_ELEMENT_MASK) != 0; } - void saveChopBlock(LeafBlockWrapper current) { + public void saveChopBlock(LeafBlockWrapper current) { if (myChopStartBlock == null) { myChopStartBlock = current; } } - final boolean isActive() { + public final boolean isActive() { return (myFlags & ACTIVE_MASK) != 0; } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java b/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java new file mode 100644 index 000000000000..68d510176f36 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java @@ -0,0 +1,205 @@ +/* + * Copyright 2000-2015 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.engine; + +import com.intellij.formatting.*; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; + +public class WrapProcessor { + private LeafBlockWrapper myFirstWrappedBlockOnLine = null; + private BlockMapperHelper myBlockMapperHelper; + private LeafBlockWrapper myWrapCandidate = null; + private IndentAdjuster myIndentAdjuster; + private int myRightMargin; + + public WrapProcessor(BlockMapperHelper blockHelper, IndentAdjuster indentAdjuster, int rightMargin) { + myIndentAdjuster = indentAdjuster; + myBlockMapperHelper = blockHelper; + myRightMargin = rightMargin; + } + + private boolean isSuitableInTheCurrentPosition(final WrapImpl wrap, LeafBlockWrapper currentBlock) { + if (wrap.getWrapOffset() < currentBlock.getStartOffset()) { + return true; + } + + if (wrap.isWrapFirstElement()) { + return true; + } + + if (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED) { + return positionAfterWrappingIsSuitable(currentBlock); + } + + return wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && lineOver(currentBlock) && positionAfterWrappingIsSuitable(currentBlock); + } + + /** + * @return true if {@link #myCurrentBlock currently processed wrapped block} doesn't contain line feeds and + * exceeds right margin; false otherwise + */ + private boolean lineOver(LeafBlockWrapper currentBlock) { + return !currentBlock.containsLineFeeds() && + CoreFormatterUtil.getStartColumn(currentBlock) + currentBlock.getLength() > myRightMargin; + } + + + /** + * 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 + */ + private boolean positionAfterWrappingIsSuitable(LeafBlockWrapper currentBlock) { + final WhiteSpace whiteSpace = currentBlock.getWhiteSpace(); + if (whiteSpace.containsLineFeeds()) return true; + final int spaces = whiteSpace.getSpaces(); + int indentSpaces = whiteSpace.getIndentSpaces(); + try { + final int startColumnNow = CoreFormatterUtil.getStartColumn(currentBlock); + whiteSpace.ensureLineFeed(); + myIndentAdjuster.adjustLineIndent(currentBlock); + final int startColumnAfterWrap = CoreFormatterUtil.getStartColumn(currentBlock); + return startColumnNow > startColumnAfterWrap; + } + finally { + whiteSpace.removeLineFeeds(currentBlock.getSpaceProperty(), myBlockMapperHelper); + whiteSpace.setSpaces(spaces, indentSpaces); + } + } + + + @Nullable + private WrapImpl getWrapToBeUsed(final ArrayList wraps, LeafBlockWrapper currentBlock) { + if (wraps.isEmpty()) { + return null; + } + if (myWrapCandidate == currentBlock) return wraps.get(0); + + for (final WrapImpl wrap : wraps) { + if (!isSuitableInTheCurrentPosition(wrap, currentBlock)) continue; + if (wrap.isActive()) return wrap; + + final WrapImpl.Type type = wrap.getType(); + if (type == WrapImpl.Type.WRAP_ALWAYS) return wrap; + if (type == WrapImpl.Type.WRAP_AS_NEEDED || type == WrapImpl.Type.CHOP_IF_NEEDED) { + if (lineOver(currentBlock)) { + return wrap; + } + } + } + return null; + } + + private boolean isCandidateToBeWrapped(final WrapImpl wrap, LeafBlockWrapper currentBlock) { + return isSuitableInTheCurrentPosition(wrap, currentBlock) && + (wrap.getType() == WrapImpl.Type.WRAP_AS_NEEDED || wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED) && + !currentBlock.getWhiteSpace().isReadOnly(); + } + + /** + * 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 + */ + private boolean canReplaceWrapCandidate(WrapImpl wrap, LeafBlockWrapper currentBlock) { + if (myWrapCandidate == null) return true; + WrapImpl.Type type = wrap.getType(); + if (wrap.isActive() && (type == WrapImpl.Type.CHOP_IF_NEEDED || type == WrapImpl.Type.WRAP_ALWAYS)) return true; + final WrapImpl currentWrap = myWrapCandidate.getWrap(); + return wrap == currentWrap || !wrap.isChildOf(currentWrap, currentBlock); + } + + public LeafBlockWrapper processWrap(LeafBlockWrapper currentBlock) { + final SpacingImpl spacing = currentBlock.getSpaceProperty(); + final WhiteSpace whiteSpace = currentBlock.getWhiteSpace(); + + final boolean wrapWasPresent = whiteSpace.containsLineFeeds(); + + if (wrapWasPresent) { + myFirstWrappedBlockOnLine = null; + + if (!whiteSpace.containsLineFeedsInitially()) { + whiteSpace.removeLineFeeds(spacing, myBlockMapperHelper); + } + } + + final boolean wrapIsPresent = whiteSpace.containsLineFeeds(); + + final ArrayList wraps = currentBlock.getWraps(); + for (WrapImpl wrap : wraps) { + wrap.setWrapOffset(currentBlock.getStartOffset()); + } + + final WrapImpl wrap = getWrapToBeUsed(wraps, currentBlock); + + if (wrap != null || wrapIsPresent) { + if (!wrapIsPresent && !canReplaceWrapCandidate(wrap, currentBlock)) { + return myWrapCandidate; + } + if (wrap != null && wrap.getChopStartBlock() != null) { + // getWrapToBeUsed() returns the block only if it actually exceeds the right margin. In this case, we need to go back to the + // first block that has the CHOP_IF_NEEDED wrap type and start wrapping from there. + LeafBlockWrapper newCurrentBlock = wrap.getChopStartBlock(); + wrap.setActive(); + return newCurrentBlock; + } + if (wrap != null && isChopNeeded(wrap, currentBlock)) { + wrap.setActive(); + } + + if (!wrapIsPresent) { + whiteSpace.ensureLineFeed(); + if (!wrapWasPresent) { + if (myFirstWrappedBlockOnLine != null && wrap.isChildOf(myFirstWrappedBlockOnLine.getWrap(), currentBlock)) { + wrap.ignoreParentWrap(myFirstWrappedBlockOnLine.getWrap(), currentBlock); + return myFirstWrappedBlockOnLine; + } + else { + myFirstWrappedBlockOnLine = currentBlock; + } + } + } + + myWrapCandidate = null; + } + else { + for (final WrapImpl wrap1 : wraps) { + if (isCandidateToBeWrapped(wrap1, currentBlock) && canReplaceWrapCandidate(wrap1, currentBlock)) { + myWrapCandidate = currentBlock; + } + if (isChopNeeded(wrap1, currentBlock)) { + wrap1.saveChopBlock(currentBlock); + } + } + } + + if (!whiteSpace.containsLineFeeds() && myWrapCandidate != null && !whiteSpace.isReadOnly() && lineOver(currentBlock)) { + return myWrapCandidate; + } + + return null; + } + + private boolean isChopNeeded(final WrapImpl wrap, LeafBlockWrapper currentBlock) { + return wrap != null && wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && isSuitableInTheCurrentPosition(wrap, currentBlock); + } + + +} From a64ce0d0c2d21a1da1f703f7c0c3d9863a137303 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 23 Dec 2015 16:56:44 +0300 Subject: [PATCH 27/34] minor changes --- .../src/com/intellij/formatting/FormatProcessor.java | 5 ++--- .../src/com/intellij/formatting/engine/WrapProcessor.java | 8 ++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 5e2122dd747d..34809c034f05 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -81,7 +81,6 @@ public class FormatProcessor { private Ref> myExpandableIndentsRef = Ref.create(); private Ref myIndentAdjusterRef = Ref.create(); private WrapProcessor myWrapProcessor; - private LeafBlockWrapper myWrapCandidate; public FormatProcessor(final FormattingDocumentModel docModel, Block rootBlock, @@ -231,7 +230,7 @@ public class FormatProcessor { private void reset() { myAlignmentHelper.reset(); myDependentSpacingEngine.clear(); - myWrapCandidate = null; + myWrapProcessor.reset(); if (myRootBlockWrapper != null) { myRootBlockWrapper.reset(); } @@ -318,7 +317,7 @@ public class FormatProcessor { } private void onCurrentLineChanged() { - myWrapCandidate = null; + myWrapProcessor.onCurrentLineChanged(); } private boolean isReformatSelectedRangesContext() { diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java b/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java index 68d510176f36..bb2b2e5761f8 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java @@ -200,6 +200,14 @@ public class WrapProcessor { private boolean isChopNeeded(final WrapImpl wrap, LeafBlockWrapper currentBlock) { return wrap != null && wrap.getType() == WrapImpl.Type.CHOP_IF_NEEDED && isSuitableInTheCurrentPosition(wrap, currentBlock); } + + + public void reset() { + myWrapCandidate = null; + } + public void onCurrentLineChanged() { + myWrapCandidate = null; + } } From 0d6b4765b2a86af02f183896fe8bc53d4a2f84ad Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Sat, 20 Feb 2016 15:30:11 +0300 Subject: [PATCH 28/34] [FormatProcessor refactoring] processBlocksBefore - use AdjustWhiteSpace instead of rewriting it --- .../intellij/formatting/FormatProcessor.java | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 34809c034f05..7f3741c238c2 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -585,23 +585,19 @@ public class FormatProcessor { } return result; } - + private void processBlocksBefore(final int offset) { - while (true) { - myAlignAgain.clear(); - myCurrentBlock = myFirstTokenBlock; - while (myCurrentBlock != null && myCurrentBlock.getStartOffset() < offset) { - processToken(); - if (myCurrentBlock == null) { - myCurrentBlock = myLastTokenBlock; - if (myCurrentBlock != null) { - myProgressCallback.afterProcessingBlock(myCurrentBlock); - } - break; - } - } - if (myAlignAgain.isEmpty()) return; - reset(); + AdjustWhiteSpacesState state = new AdjustWhiteSpacesState(); + myCurrentBlock = myFirstTokenBlock; + + LeafBlockWrapper last = null; + while (!state.isDone() && myCurrentBlock.getStartOffset() < offset) { + last = myCurrentBlock; + state.doIteration(); + } + + if (myCurrentBlock == null) { + myCurrentBlock = last; } } From 29955ea5e4d375cd8c7dde8b4a35b6d96462fb60 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Sat, 20 Feb 2016 17:48:41 +0300 Subject: [PATCH 29/34] [FormatProcessor] AdjustWhiteSpaces state extracted --- .../intellij/formatting/AlignmentImpl.java | 2 +- .../intellij/formatting/FormatProcessor.java | 210 ++++----------- .../engine/AdjustWhiteSpacesState.java | 240 ++++++++++++++++++ 3 files changed, 287 insertions(+), 165 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java diff --git a/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java b/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java index 128e4b6ea1c9..76ccf6819107 100644 --- a/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/AlignmentImpl.java @@ -135,7 +135,7 @@ public class AlignmentImpl extends Alignment { * * @param block wrapped block to register within the current alignment object */ - void setOffsetRespBlock(final LeafBlockWrapper block) { + public void setOffsetRespBlock(final LeafBlockWrapper block) { if (block == null) { return; } diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 7f3741c238c2..08d663545c6c 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -21,19 +21,15 @@ import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -45,15 +41,16 @@ public class FormatProcessor { private Set myAlignmentsInsideRangesToModify = null; private boolean myReformatContext; - private LeafBlockWrapper myCurrentBlock; - private Map myInfos; private CompositeBlockWrapper myRootBlockWrapper; private DependentSpacingEngine myDependentSpacingEngine; + private Ref myDependentSpacingRef = Ref.create(); private AlignmentHelper myAlignmentHelper; + private IndentAdjuster myIndentAdjuster; + private final BlockIndentOptions myBlockIndentOptions; private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; @@ -62,13 +59,11 @@ public class FormatProcessor { private BlockMapperHelper myBlockMapperHelper; - private LeafBlockWrapper myFirstTokenBlock; private Ref myFirstTokenBlockRef = Ref.create(); private LeafBlockWrapper myLastTokenBlock; - - private final HashSet myAlignAgain = new HashSet(); + @NotNull private final FormattingProgressCallback myProgressCallback; @@ -79,8 +74,13 @@ public class FormatProcessor { @NotNull private StateProcessor myStateProcessor; private Ref> myExpandableIndentsRef = Ref.create(); - private Ref myIndentAdjusterRef = Ref.create(); + private WrapProcessor myWrapProcessor; + + private Ref myIndentAdjusterRef = Ref.create(); + private Ref myBlockMapperHelperRef = Ref.create(); + private Ref myWrapProcessorRef = Ref.create(); + private Ref> myAlignmentsInsideRangesToModifyRef = Ref.create(); public FormatProcessor(final FormattingDocumentModel docModel, Block rootBlock, @@ -115,18 +115,29 @@ public class FormatProcessor { myFirstTokenBlock = builder.getFirstTokenBlock(); myFirstTokenBlockRef.set(myFirstTokenBlock); myLastTokenBlock = builder.getLastTokenBlock(); - myCurrentBlock = myFirstTokenBlock; + int lastBlockOffset = myLastTokenBlock.getEndOffset(); myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); myLastWhiteSpace.append(Math.max(lastBlockOffset, builder.getEndOffset()), model, myDefaultIndentOption); + myBlockMapperHelper = new BlockMapperHelper(myFirstTokenBlock, myLastTokenBlock); + myBlockMapperHelperRef.set(myBlockMapperHelper); + myDependentSpacingEngine = new DependentSpacingEngine(myBlockMapperHelper); + myDependentSpacingRef.set(myDependentSpacingEngine); + myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); + myAlignmentsInsideRangesToModifyRef.set(myAlignmentsInsideRangesToModify); + myAlignmentHelper = new AlignmentHelper(myDocument, builder.getBlocksToAlign(), myBlockIndentOptions); + myIndentAdjuster = new IndentAdjuster(myBlockIndentOptions, myAlignmentHelper); myIndentAdjusterRef.set(myIndentAdjuster); + myExpandableIndentsRef.set(builder.getExpandableIndentsBlocks()); + myWrapProcessor = new WrapProcessor(myBlockMapperHelper, myIndentAdjuster, myRightMargin); + myWrapProcessorRef.set(myWrapProcessor); } }); myStateProcessor = new StateProcessor(wrapState); @@ -182,7 +193,7 @@ public class FormatProcessor { */ public void format(FormattingModel model, boolean sequentially) { if (sequentially) { - myStateProcessor.setNextState(new AdjustWhiteSpacesState()); + myStateProcessor.setNextState(createAdjustWhiteSpaceState()); myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myIndentAdjusterRef, myExpandableIndentsRef)); myStateProcessor.setNextState(new ApplyChangesState(myFirstTokenBlockRef, model, myBlockIndentOptions, myProgressCallback)); } @@ -192,6 +203,18 @@ public class FormatProcessor { } } + @NotNull + private AdjustWhiteSpacesState createAdjustWhiteSpaceState() { + return new AdjustWhiteSpacesState(myFirstTokenBlockRef, + myDependentSpacingRef, + myWrapProcessorRef, + myIndentAdjusterRef, + myBlockMapperHelperRef, + myAlignmentsInsideRangesToModifyRef, + myReformatContext, + myProgressCallback); + } + /** * Asks current processor to perform processing iteration * @@ -219,7 +242,7 @@ public class FormatProcessor { @SuppressWarnings({"WhileLoopSpinsOnField"}) public void formatWithoutRealModifications(boolean sequentially) { - myStateProcessor.setNextState(new AdjustWhiteSpacesState()); + myStateProcessor.setNextState(createAdjustWhiteSpaceState()); myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myIndentAdjusterRef, myExpandableIndentsRef)); if (sequentially) { return; @@ -257,119 +280,6 @@ public class FormatProcessor { } } - private void processToken() { - final SpacingImpl spaceProperty = myCurrentBlock.getSpaceProperty(); - final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); - - if (isReformatSelectedRangesContext()) { - if (isCurrentBlockAlignmentUsedInRangesToModify() && whiteSpace.isReadOnly() && spaceProperty != null && !spaceProperty.isReadOnly()) { - whiteSpace.setReadOnly(false); - whiteSpace.setLineFeedsAreReadOnly(true); - } - } - - whiteSpace.arrangeLineFeeds(spaceProperty, myBlockMapperHelper); - - if (!whiteSpace.containsLineFeeds()) { - whiteSpace.arrangeSpaces(spaceProperty); - } - - try { - LeafBlockWrapper newBlock = myWrapProcessor.processWrap(myCurrentBlock); - if (newBlock != null) { - myCurrentBlock = newBlock; - return; - } - } - finally { - if (whiteSpace.containsLineFeeds()) { - onCurrentLineChanged(); - } - } - - LeafBlockWrapper newCurrentBlock = myIndentAdjuster.adjustIndent(myCurrentBlock); - if (newCurrentBlock != null) { - myCurrentBlock = newCurrentBlock; - onCurrentLineChanged(); - return; - } - - defineAlignOffset(myCurrentBlock); - - if (myCurrentBlock.containsLineFeeds()) { - onCurrentLineChanged(); - } - - - final List ranges = getDependentRegionRangesAfterCurrentWhiteSpace(spaceProperty, whiteSpace); - if (!ranges.isEmpty()) { - myDependentSpacingEngine.registerUnresolvedDependentSpacingRanges(spaceProperty, ranges); - } - - if (!whiteSpace.isIsReadOnly() && myDependentSpacingEngine.shouldReformatPreviouslyLocatedDependentSpacing(whiteSpace)) { - myAlignAgain.add(whiteSpace); - } - else if (!myAlignAgain.isEmpty()) { - myAlignAgain.remove(whiteSpace); - } - - myCurrentBlock = myCurrentBlock.getNextBlock(); - } - - private void onCurrentLineChanged() { - myWrapProcessor.onCurrentLineChanged(); - } - - private boolean isReformatSelectedRangesContext() { - return myReformatContext && !ContainerUtil.isEmpty(myAlignmentsInsideRangesToModify); - } - - private boolean isCurrentBlockAlignmentUsedInRangesToModify() { - AbstractBlockWrapper block = myCurrentBlock; - AlignmentImpl alignment = myCurrentBlock.getAlignment(); - - while (alignment == null) { - block = block.getParent(); - if (block == null || block.getStartOffset() != myCurrentBlock.getStartOffset()) { - return false; - } - alignment = block.getAlignment(); - } - - return myAlignmentsInsideRangesToModify.contains(alignment); - } - - private static List getDependentRegionRangesAfterCurrentWhiteSpace(final SpacingImpl spaceProperty, - final WhiteSpace whiteSpace) - { - if (!(spaceProperty instanceof DependantSpacingImpl)) return ContainerUtil.emptyList(); - - if (whiteSpace.isReadOnly() || whiteSpace.isLineFeedsAreReadOnly()) return ContainerUtil.emptyList(); - - DependantSpacingImpl spacing = (DependantSpacingImpl)spaceProperty; - return ContainerUtil.filter(spacing.getDependentRegionRanges(), new Condition() { - @Override - public boolean value(TextRange dependencyRange) { - return whiteSpace.getStartOffset() < dependencyRange.getEndOffset(); - } - }); - } - - - private void defineAlignOffset(final LeafBlockWrapper block) { - AbstractBlockWrapper current = myCurrentBlock; - while (true) { - final AlignmentImpl alignment = current.getAlignment(); - if (alignment != null) { - alignment.setOffsetRespBlock(block); - } - current = current.getParent(); - if (current == null) return; - if (current.getStartOffset() != myCurrentBlock.getStartOffset()) return; - - } - } - public void setAllWhiteSpacesAreReadOnly() { LeafBlockWrapper current = myFirstTokenBlock; while (current != null) { @@ -391,10 +301,10 @@ public class FormatProcessor { } public IndentInfo getIndentAt(final int offset) { - processBlocksBefore(offset); - AbstractBlockWrapper parent = getParentFor(offset, myCurrentBlock); + LeafBlockWrapper current = processBlocksBefore(offset); + AbstractBlockWrapper parent = getParentFor(offset, current); if (parent == null) { - final LeafBlockWrapper previousBlock = myCurrentBlock.getPreviousBlock(); + final LeafBlockWrapper previousBlock = current.getPreviousBlock(); if (previousBlock != null) parent = getParentFor(offset, previousBlock); if (parent == null) return new IndentInfo(0, 0, 0); } @@ -410,7 +320,7 @@ public class FormatProcessor { return new IndentInfo(0, 0, 0); } - return myIndentAdjuster.adjustLineIndent(myCurrentBlock, info); + return myIndentAdjuster.adjustLineIndent(current, info); } @Nullable @@ -586,19 +496,16 @@ public class FormatProcessor { return result; } - private void processBlocksBefore(final int offset) { - AdjustWhiteSpacesState state = new AdjustWhiteSpacesState(); - myCurrentBlock = myFirstTokenBlock; - + private LeafBlockWrapper processBlocksBefore(final int offset) { + AdjustWhiteSpacesState state = new AdjustWhiteSpacesState(myFirstTokenBlock, myDependentSpacingEngine, myWrapProcessor, myIndentAdjuster, myBlockMapperHelper, myAlignmentsInsideRangesToModify, myReformatContext, myProgressCallback); + LeafBlockWrapper last = null; - while (!state.isDone() && myCurrentBlock.getStartOffset() < offset) { - last = myCurrentBlock; + while (!state.isDone() && state.getCurrentBlock().getStartOffset() < offset) { + last = state.getCurrentBlock(); state.doIteration(); } - if (myCurrentBlock == null) { - myCurrentBlock = last; - } + return state.getCurrentBlock() != null ? state.getCurrentBlock() : last; } public LeafBlockWrapper getFirstTokenBlock() { @@ -608,31 +515,6 @@ public class FormatProcessor { public WhiteSpace getLastWhiteSpace() { return myLastWhiteSpace; } - - private class AdjustWhiteSpacesState extends State { - - @Override - protected void doIteration() { - LeafBlockWrapper blockToProcess = myCurrentBlock; - processToken(); - if (blockToProcess != null) { - myProgressCallback.afterProcessingBlock(blockToProcess); - } - - if (myCurrentBlock != null) { - return; - } - - if (myAlignAgain.isEmpty()) { - setDone(true); - } - else { - myAlignAgain.clear(); - myDependentSpacingEngine.clear(); - myCurrentBlock = myFirstTokenBlock; - } - } - } public static class FormatOptions { public CodeStyleSettings mySettings; diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java b/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java new file mode 100644 index 000000000000..ed8614994fe4 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java @@ -0,0 +1,240 @@ +/* + * 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.engine; + +import com.intellij.formatting.*; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.TextRange; +import com.intellij.util.containers.ContainerUtil; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class AdjustWhiteSpacesState extends State { + + private final FormattingProgressCallback myProgressCallback; + + private Ref myFirstBlockRef; + private Ref myWrapProcessorRef; + private Ref myIndentAdjusterRef; + private Ref myBlockMapperHelperRef; + private Ref> myAlignmentsInsideRangesToModifyRef; + + private LeafBlockWrapper myCurrentBlock; + + private Ref myDependentSpacingEngineRef; + + private DependentSpacingEngine myDependentSpacingEngine; + private WrapProcessor myWrapProcessor; + private BlockMapperHelper myBlockMapperHelper; + private IndentAdjuster myIndentAdjuster; + + + private boolean myReformatContext; + private Set myAlignmentsInsideRangesToModify = null; + + private final HashSet myAlignAgain = new HashSet(); + + + public AdjustWhiteSpacesState(Ref firstBlockRef, + Ref dependentSpacingEngineRef, + Ref wrapProcessorRef, + Ref indentAdjusterRef, + Ref blockMapperHelperRef, + Ref> alignmentsInsideRanges, + boolean isReformatContext, + FormattingProgressCallback progressCallback) { + myFirstBlockRef = firstBlockRef; + myDependentSpacingEngineRef = dependentSpacingEngineRef; + myWrapProcessorRef = wrapProcessorRef; + myProgressCallback = progressCallback; + myIndentAdjusterRef = indentAdjusterRef; + myBlockMapperHelperRef = blockMapperHelperRef; + myReformatContext = isReformatContext; + myAlignmentsInsideRangesToModifyRef = alignmentsInsideRanges; + } + + public AdjustWhiteSpacesState(LeafBlockWrapper firstBlock, + DependentSpacingEngine dependentSpacingEngine, + WrapProcessor wrapProcessor, + IndentAdjuster indentAdjuster, + BlockMapperHelper blockMapperHelper, + Set alignmentsInsideRangesToModify, + boolean isReformatContext, + FormattingProgressCallback progressCallback) + { + myCurrentBlock = firstBlock; + myDependentSpacingEngine = dependentSpacingEngine; + myWrapProcessor = wrapProcessor; + myIndentAdjuster = indentAdjuster; + myBlockMapperHelper = blockMapperHelper; + myAlignmentsInsideRangesToModify = alignmentsInsideRangesToModify; + myReformatContext = isReformatContext; + myProgressCallback = progressCallback; + } + + public LeafBlockWrapper getCurrentBlock() { + return myCurrentBlock; + } + + @Override + public void doIteration() { + if (myDependentSpacingEngine == null) { + myDependentSpacingEngine = myDependentSpacingEngineRef.get(); + myCurrentBlock = myFirstBlockRef.get(); + myWrapProcessor = myWrapProcessorRef.get(); + myIndentAdjuster = myIndentAdjusterRef.get(); + myWrapProcessor = myWrapProcessorRef.get(); + myBlockMapperHelper = myBlockMapperHelperRef.get(); + myAlignmentsInsideRangesToModify = myAlignmentsInsideRangesToModifyRef.get(); + } + + LeafBlockWrapper blockToProcess = myCurrentBlock; + processToken(); + if (blockToProcess != null) { + myProgressCallback.afterProcessingBlock(blockToProcess); + } + + if (myCurrentBlock != null) { + return; + } + + if (myAlignAgain.isEmpty()) { + setDone(true); + } + else { + myAlignAgain.clear(); + myDependentSpacingEngine.clear(); + myCurrentBlock = myFirstBlockRef.get(); + } + } + + private boolean isReformatSelectedRangesContext() { + return myReformatContext && !ContainerUtil.isEmpty(myAlignmentsInsideRangesToModify); + } + + private void defineAlignOffset(final LeafBlockWrapper block) { + AbstractBlockWrapper current = myCurrentBlock; + while (true) { + final AlignmentImpl alignment = current.getAlignment(); + if (alignment != null) { + alignment.setOffsetRespBlock(block); + } + current = current.getParent(); + if (current == null) return; + if (current.getStartOffset() != myCurrentBlock.getStartOffset()) return; + } + } + + private void onCurrentLineChanged() { + myWrapProcessor.onCurrentLineChanged(); + } + + private boolean isCurrentBlockAlignmentUsedInRangesToModify() { + AbstractBlockWrapper block = myCurrentBlock; + AlignmentImpl alignment = myCurrentBlock.getAlignment(); + + while (alignment == null) { + block = block.getParent(); + if (block == null || block.getStartOffset() != myCurrentBlock.getStartOffset()) { + return false; + } + alignment = block.getAlignment(); + } + + return myAlignmentsInsideRangesToModify.contains(alignment); + } + + private static List getDependentRegionRangesAfterCurrentWhiteSpace(final SpacingImpl spaceProperty, + final WhiteSpace whiteSpace) { + if (!(spaceProperty instanceof DependantSpacingImpl)) return ContainerUtil.emptyList(); + + if (whiteSpace.isReadOnly() || whiteSpace.isLineFeedsAreReadOnly()) return ContainerUtil.emptyList(); + + DependantSpacingImpl spacing = (DependantSpacingImpl)spaceProperty; + return ContainerUtil.filter(spacing.getDependentRegionRanges(), new Condition() { + @Override + public boolean value(TextRange dependencyRange) { + return whiteSpace.getStartOffset() < dependencyRange.getEndOffset(); + } + }); + } + + + private void processToken() { + final SpacingImpl spaceProperty = myCurrentBlock.getSpaceProperty(); + final WhiteSpace whiteSpace = myCurrentBlock.getWhiteSpace(); + + if (isReformatSelectedRangesContext()) { + if (isCurrentBlockAlignmentUsedInRangesToModify() && + whiteSpace.isReadOnly() && + spaceProperty != null && + !spaceProperty.isReadOnly()) { + whiteSpace.setReadOnly(false); + whiteSpace.setLineFeedsAreReadOnly(true); + } + } + + whiteSpace.arrangeLineFeeds(spaceProperty, myBlockMapperHelper); + + if (!whiteSpace.containsLineFeeds()) { + whiteSpace.arrangeSpaces(spaceProperty); + } + + try { + LeafBlockWrapper newBlock = myWrapProcessor.processWrap(myCurrentBlock); + if (newBlock != null) { + myCurrentBlock = newBlock; + return; + } + } + finally { + if (whiteSpace.containsLineFeeds()) { + onCurrentLineChanged(); + } + } + + LeafBlockWrapper newCurrentBlock = myIndentAdjuster.adjustIndent(myCurrentBlock); + if (newCurrentBlock != null) { + myCurrentBlock = newCurrentBlock; + onCurrentLineChanged(); + return; + } + + defineAlignOffset(myCurrentBlock); + + if (myCurrentBlock.containsLineFeeds()) { + onCurrentLineChanged(); + } + + + final List ranges = getDependentRegionRangesAfterCurrentWhiteSpace(spaceProperty, whiteSpace); + if (!ranges.isEmpty()) { + myDependentSpacingEngine.registerUnresolvedDependentSpacingRanges(spaceProperty, ranges); + } + + if (!whiteSpace.isIsReadOnly() && myDependentSpacingEngine.shouldReformatPreviouslyLocatedDependentSpacing(whiteSpace)) { + myAlignAgain.add(whiteSpace); + } + else if (!myAlignAgain.isEmpty()) { + myAlignAgain.remove(whiteSpace); + } + + myCurrentBlock = myCurrentBlock.getNextBlock(); + } +} \ No newline at end of file From c766970404f4b60ac82bd7f3a5383d429224fade Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Sat, 20 Feb 2016 18:56:00 +0300 Subject: [PATCH 30/34] [FormatProcessor] Use WrapBlocksState to transfer data between formatting iterations --- .../intellij/formatting/FormatProcessor.java | 181 +++--------------- .../formatting/InitialInfoBuilder.java | 6 +- .../engine/AdjustWhiteSpacesState.java | 58 +++--- .../formatting/engine/ApplyChangesState.java | 22 +-- .../formatting/engine/BlockIndentOptions.java | 30 ++- .../engine/ExpandChildrenIndent.java | 30 ++- .../formatting/engine/WrapBlocksState.java | 105 +++++++++- 7 files changed, 203 insertions(+), 229 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 08d663545c6c..4a0ac47b285d 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -17,70 +17,37 @@ package com.intellij.formatting; import com.intellij.formatting.engine.*; -import com.intellij.lang.ASTNode; -import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.Ref; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Map; -import java.util.Set; import static com.intellij.formatting.InitialInfoBuilder.prepareToBuildBlocksSequentially; public class FormatProcessor { private static final Logger LOG = Logger.getInstance("#com.intellij.formatting.FormatProcessor"); - private Set myAlignmentsInsideRangesToModify = null; + + private final WrapBlocksState myWrapState; + private boolean myReformatContext; - - private Map myInfos; - private CompositeBlockWrapper myRootBlockWrapper; - - private DependentSpacingEngine myDependentSpacingEngine; - private Ref myDependentSpacingRef = Ref.create(); - private AlignmentHelper myAlignmentHelper; - - private IndentAdjuster myIndentAdjuster; - - private final BlockIndentOptions myBlockIndentOptions; private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; private final CodeStyleSettings mySettings; private final Document myDocument; - private BlockMapperHelper myBlockMapperHelper; - - private LeafBlockWrapper myFirstTokenBlock; - private Ref myFirstTokenBlockRef = Ref.create(); - private LeafBlockWrapper myLastTokenBlock; - @NotNull private final FormattingProgressCallback myProgressCallback; - private WhiteSpace myLastWhiteSpace; private boolean myDisposed; - private final int myRightMargin; @NotNull private StateProcessor myStateProcessor; - private Ref> myExpandableIndentsRef = Ref.create(); - - private WrapProcessor myWrapProcessor; - - private Ref myIndentAdjusterRef = Ref.create(); - private Ref myBlockMapperHelperRef = Ref.create(); - private Ref myWrapProcessorRef = Ref.create(); - private Ref> myAlignmentsInsideRangesToModifyRef = Ref.create(); public FormatProcessor(final FormattingDocumentModel docModel, Block rootBlock, @@ -100,102 +67,29 @@ public class FormatProcessor { myProgressCallback = callback; myDefaultIndentOption = options.myIndentOptions; mySettings = options.mySettings; - myBlockIndentOptions = new BlockIndentOptions(mySettings, myDefaultIndentOption); + myBlockIndentOptions = new BlockIndentOptions(mySettings, myDefaultIndentOption, block); myDocument = model.getDocument(); myReformatContext = options.myReformatContext; - myRightMargin = getRightMargin(block); final InitialInfoBuilder builder = prepareToBuildBlocksSequentially(block, model, options, mySettings, myDefaultIndentOption, myProgressCallback); - final WrapBlocksState wrapState = new WrapBlocksState(builder); - wrapState.setOnDone(new Runnable() { - @Override - public void run() { - myInfos = builder.getBlockToInfoMap(); - myRootBlockWrapper = builder.getRootBlockWrapper(); - myFirstTokenBlock = builder.getFirstTokenBlock(); - myFirstTokenBlockRef.set(myFirstTokenBlock); - myLastTokenBlock = builder.getLastTokenBlock(); - - int lastBlockOffset = myLastTokenBlock.getEndOffset(); - myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); - myLastWhiteSpace.append(Math.max(lastBlockOffset, builder.getEndOffset()), model, myDefaultIndentOption); - - myBlockMapperHelper = new BlockMapperHelper(myFirstTokenBlock, myLastTokenBlock); - myBlockMapperHelperRef.set(myBlockMapperHelper); - - myDependentSpacingEngine = new DependentSpacingEngine(myBlockMapperHelper); - myDependentSpacingRef.set(myDependentSpacingEngine); - - myAlignmentsInsideRangesToModify = builder.getAlignmentsInsideRangeToModify(); - myAlignmentsInsideRangesToModifyRef.set(myAlignmentsInsideRangesToModify); - - myAlignmentHelper = new AlignmentHelper(myDocument, builder.getBlocksToAlign(), myBlockIndentOptions); - - myIndentAdjuster = new IndentAdjuster(myBlockIndentOptions, myAlignmentHelper); - myIndentAdjusterRef.set(myIndentAdjuster); - - myExpandableIndentsRef.set(builder.getExpandableIndentsBlocks()); - - myWrapProcessor = new WrapProcessor(myBlockMapperHelper, myIndentAdjuster, myRightMargin); - myWrapProcessorRef.set(myWrapProcessor); - } - }); - myStateProcessor = new StateProcessor(wrapState); + myWrapState = new WrapBlocksState(builder, myBlockIndentOptions); + + myStateProcessor = new StateProcessor(myWrapState); } public BlockMapperHelper getBlockMapperHelper() { - return myBlockMapperHelper; - } - - private int getRightMargin(Block rootBlock) { - Language language = null; - if (rootBlock instanceof ASTBlock) { - ASTNode node = ((ASTBlock)rootBlock).getNode(); - if (node != null) { - PsiElement psiElement = node.getPsi(); - if (psiElement.isValid()) { - PsiFile psiFile = psiElement.getContainingFile(); - if (psiFile != null) { - language = psiFile.getViewProvider().getBaseLanguage(); - } - } - } - } - return mySettings.getRightMargin(language); + return myWrapState.getBlockMapperHelper(); } public void format(FormattingModel model) { format(model, false); } - /** - * Asks current processor to perform formatting. - *

    - * There are two processing approaches at the moment: - *

    -   * 
      - *
    • perform formatting during the current method call;
    • - *
    • - * split the whole formatting process to the set of fine-grained tasks and execute them sequentially during - * subsequent {@link #iteration()} calls; - *
    • - *
    - *
    - *

    - * Here is rationale for the second approach - formatting may introduce changes to the underlying document and IntelliJ IDEA - * is designed in a way that write access is allowed from EDT only. That means that every time we execute particular action - * from EDT we have no chance of performing any other actions from EDT simultaneously (e.g. we may want to show progress bar - * that reflects current formatting state but the progress bar can' bet updated if formatting is performed during a single long - * method call). So, we can interleave formatting iterations with GUI state updates. - * - * @param model target formatting model - * @param sequentially flag that indicates what kind of processing should be used - */ public void format(FormattingModel model, boolean sequentially) { if (sequentially) { - myStateProcessor.setNextState(createAdjustWhiteSpaceState()); - myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myIndentAdjusterRef, myExpandableIndentsRef)); - myStateProcessor.setNextState(new ApplyChangesState(myFirstTokenBlockRef, model, myBlockIndentOptions, myProgressCallback)); + myStateProcessor.setNextState(new AdjustWhiteSpacesState(myWrapState, myProgressCallback, myReformatContext)); + myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myWrapState)); + myStateProcessor.setNextState(new ApplyChangesState(model, myWrapState, myProgressCallback, myBlockIndentOptions)); } else { formatWithoutRealModifications(false); @@ -203,24 +97,6 @@ public class FormatProcessor { } } - @NotNull - private AdjustWhiteSpacesState createAdjustWhiteSpaceState() { - return new AdjustWhiteSpacesState(myFirstTokenBlockRef, - myDependentSpacingRef, - myWrapProcessorRef, - myIndentAdjusterRef, - myBlockMapperHelperRef, - myAlignmentsInsideRangesToModifyRef, - myReformatContext, - myProgressCallback); - } - - /** - * Asks current processor to perform processing iteration - * - * @return true if the processing is finished; false otherwise - * @see #format(FormattingModel, boolean) - */ public boolean iteration() { if (myStateProcessor.isDone()) { return true; @@ -242,30 +118,20 @@ public class FormatProcessor { @SuppressWarnings({"WhileLoopSpinsOnField"}) public void formatWithoutRealModifications(boolean sequentially) { - myStateProcessor.setNextState(createAdjustWhiteSpaceState()); - myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myIndentAdjusterRef, myExpandableIndentsRef)); + myStateProcessor.setNextState(new AdjustWhiteSpacesState(myWrapState, myProgressCallback, myReformatContext)); + myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myWrapState)); if (sequentially) { return; } doIterationsSynchronously(); } - private void reset() { - myAlignmentHelper.reset(); - myDependentSpacingEngine.clear(); - myWrapProcessor.reset(); - if (myRootBlockWrapper != null) { - myRootBlockWrapper.reset(); - } - } - public void performModifications(FormattingModel model) { performModifications(model, false); } public void performModifications(FormattingModel model, boolean sequentially) { - assert !myDisposed; - myStateProcessor.setNextState(new ApplyChangesState(myFirstTokenBlockRef, model, myBlockIndentOptions, myProgressCallback)); + myStateProcessor.setNextState(new ApplyChangesState(model, myWrapState, myProgressCallback, myBlockIndentOptions)); if (sequentially) { return; @@ -281,7 +147,7 @@ public class FormatProcessor { } public void setAllWhiteSpacesAreReadOnly() { - LeafBlockWrapper current = myFirstTokenBlock; + LeafBlockWrapper current = myWrapState.getFirstBlock(); while (current != null) { current.getWhiteSpace().setReadOnly(true); current = current.getNextBlock(); @@ -309,7 +175,7 @@ public class FormatProcessor { if (parent == null) return new IndentInfo(0, 0, 0); } int index = getNewChildPosition(parent, offset); - final Block block = myInfos.get(parent); + final Block block = myWrapState.getBlockToInfoMap().get(parent); if (block == null) { return new IndentInfo(0, 0, 0); @@ -320,7 +186,8 @@ public class FormatProcessor { return new IndentInfo(0, 0, 0); } - return myIndentAdjuster.adjustLineIndent(current, info); + IndentAdjuster adjuster = myWrapState.getIndentAdjuster(); + return adjuster.adjustLineIndent(current, info); } @Nullable @@ -409,8 +276,9 @@ public class FormatProcessor { @Nullable private AbstractBlockWrapper getPreviousIncompleteBlock(final LeafBlockWrapper block, final int offset) { if (block == null) { - if (myLastTokenBlock.isIncomplete()) { - return myLastTokenBlock; + LeafBlockWrapper lastTokenBlock = myWrapState.getLastBlock(); + if (lastTokenBlock.isIncomplete()) { + return lastTokenBlock; } else { return null; @@ -497,7 +365,8 @@ public class FormatProcessor { } private LeafBlockWrapper processBlocksBefore(final int offset) { - AdjustWhiteSpacesState state = new AdjustWhiteSpacesState(myFirstTokenBlock, myDependentSpacingEngine, myWrapProcessor, myIndentAdjuster, myBlockMapperHelper, myAlignmentsInsideRangesToModify, myReformatContext, myProgressCallback); + AdjustWhiteSpacesState state = new AdjustWhiteSpacesState(myWrapState, myProgressCallback, myReformatContext); + state.prepare(); LeafBlockWrapper last = null; while (!state.isDone() && state.getCurrentBlock().getStartOffset() < offset) { @@ -509,11 +378,11 @@ public class FormatProcessor { } public LeafBlockWrapper getFirstTokenBlock() { - return myFirstTokenBlock; + return myWrapState.getFirstBlock(); } public WhiteSpace getLastWhiteSpace() { - return myLastWhiteSpace; + return myWrapState.getLastWhiteSpace(); } public static class FormatOptions { diff --git a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java index 6b5f377844fe..db44a57a373f 100644 --- a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java +++ b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java @@ -122,8 +122,12 @@ public class InitialInfoBuilder { } return minOffset; } + + public FormattingDocumentModel getFormattingDocumentModel() { + return myModel; + } - int getEndOffset() { + public int getEndOffset() { int maxDocOffset = myModel.getTextLength(); int maxOffset = myRootBlockWrapper != null ? myRootBlockWrapper.getEndOffset() : 0; if (myAffectedRanges != null) { diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java b/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java index ed8614994fe4..3b433ccf82a3 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java @@ -17,7 +17,6 @@ package com.intellij.formatting.engine; import com.intellij.formatting.*; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.util.containers.ContainerUtil; @@ -28,17 +27,12 @@ import java.util.Set; public class AdjustWhiteSpacesState extends State { private final FormattingProgressCallback myProgressCallback; + + private WrapBlocksState myWrapBlocksState; - private Ref myFirstBlockRef; - private Ref myWrapProcessorRef; - private Ref myIndentAdjusterRef; - private Ref myBlockMapperHelperRef; - private Ref> myAlignmentsInsideRangesToModifyRef; private LeafBlockWrapper myCurrentBlock; - - private Ref myDependentSpacingEngineRef; - + private DependentSpacingEngine myDependentSpacingEngine; private WrapProcessor myWrapProcessor; private BlockMapperHelper myBlockMapperHelper; @@ -49,24 +43,27 @@ public class AdjustWhiteSpacesState extends State { private Set myAlignmentsInsideRangesToModify = null; private final HashSet myAlignAgain = new HashSet(); + private LeafBlockWrapper myFirstBlock; - - public AdjustWhiteSpacesState(Ref firstBlockRef, - Ref dependentSpacingEngineRef, - Ref wrapProcessorRef, - Ref indentAdjusterRef, - Ref blockMapperHelperRef, - Ref> alignmentsInsideRanges, - boolean isReformatContext, - FormattingProgressCallback progressCallback) { - myFirstBlockRef = firstBlockRef; - myDependentSpacingEngineRef = dependentSpacingEngineRef; - myWrapProcessorRef = wrapProcessorRef; + public AdjustWhiteSpacesState(WrapBlocksState state, + FormattingProgressCallback progressCallback, + boolean isReformatContext) { + myWrapBlocksState = state; myProgressCallback = progressCallback; - myIndentAdjusterRef = indentAdjusterRef; - myBlockMapperHelperRef = blockMapperHelperRef; myReformatContext = isReformatContext; - myAlignmentsInsideRangesToModifyRef = alignmentsInsideRanges; + } + + @Override + public void prepare() { + if (myWrapBlocksState != null) { + myFirstBlock = myWrapBlocksState.getFirstBlock(); + myCurrentBlock = myFirstBlock; + myDependentSpacingEngine = myWrapBlocksState.getDependentSpacingEngine(); + myWrapProcessor = myWrapBlocksState.getWrapProcessor(); + myIndentAdjuster = myWrapBlocksState.getIndentAdjuster(); + myBlockMapperHelper = myWrapBlocksState.getBlockMapperHelper(); + myAlignmentsInsideRangesToModify = myWrapBlocksState.getAlignmentsInsideRangesToModify(); + } } public AdjustWhiteSpacesState(LeafBlockWrapper firstBlock, @@ -78,6 +75,7 @@ public class AdjustWhiteSpacesState extends State { boolean isReformatContext, FormattingProgressCallback progressCallback) { + myWrapBlocksState = null; myCurrentBlock = firstBlock; myDependentSpacingEngine = dependentSpacingEngine; myWrapProcessor = wrapProcessor; @@ -94,16 +92,6 @@ public class AdjustWhiteSpacesState extends State { @Override public void doIteration() { - if (myDependentSpacingEngine == null) { - myDependentSpacingEngine = myDependentSpacingEngineRef.get(); - myCurrentBlock = myFirstBlockRef.get(); - myWrapProcessor = myWrapProcessorRef.get(); - myIndentAdjuster = myIndentAdjusterRef.get(); - myWrapProcessor = myWrapProcessorRef.get(); - myBlockMapperHelper = myBlockMapperHelperRef.get(); - myAlignmentsInsideRangesToModify = myAlignmentsInsideRangesToModifyRef.get(); - } - LeafBlockWrapper blockToProcess = myCurrentBlock; processToken(); if (blockToProcess != null) { @@ -120,7 +108,7 @@ public class AdjustWhiteSpacesState extends State { else { myAlignAgain.clear(); myDependentSpacingEngine.clear(); - myCurrentBlock = myFirstBlockRef.get(); + myCurrentBlock = myFirstBlock; } } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java b/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java index 1a7602a15c63..c6a19b1df540 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java @@ -21,7 +21,6 @@ import com.intellij.openapi.editor.TextChange; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.impl.BulkChangesMerger; import com.intellij.openapi.editor.impl.TextChangeImpl; -import com.intellij.openapi.util.Ref; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,9 +40,9 @@ public class ApplyChangesState extends State { private final FormattingModel myModel; - - private final Ref myFirstTokenBlock; + private final FormattingProgressCallback myProgressCallback; + private final WrapBlocksState myWrapState; private List myBlocksToModify; private int myShift; @@ -52,16 +51,13 @@ public class ApplyChangesState extends State { private BlockIndentOptions myBlockIndentOptions; - public ApplyChangesState(Ref firstBlock, - FormattingModel model, - BlockIndentOptions indentOptions, - FormattingProgressCallback progressCallback) { + public ApplyChangesState(FormattingModel model, WrapBlocksState state, FormattingProgressCallback callback, BlockIndentOptions options) { myModel = model; - myBlockIndentOptions = indentOptions; - myFirstTokenBlock = firstBlock; - myProgressCallback = progressCallback; + myWrapState = state; + myProgressCallback = callback; + myBlockIndentOptions = options; } - + /** * Performs formatter changes in a series of blocks, for each block a new contents of document is calculated * and whole document is replaced in one operation. @@ -133,8 +129,8 @@ public class ApplyChangesState extends State { private List collectBlocksToModify() { List blocksToModify = new ArrayList(); - - for (LeafBlockWrapper block = myFirstTokenBlock.get(); block != null; block = block.getNextBlock()) { + LeafBlockWrapper firstBlock = myWrapState.getFirstBlock(); + for (LeafBlockWrapper block = firstBlock; block != null; block = block.getNextBlock()) { final WhiteSpace whiteSpace = block.getWhiteSpace(); if (!whiteSpace.isReadOnly()) { final String newWhiteSpace = whiteSpace.generateWhiteSpace(myBlockIndentOptions.getIndentOptions(block)); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java b/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java index 405663fd7a03..9cdb278fb594 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/BlockIndentOptions.java @@ -15,8 +15,13 @@ */ package com.intellij.formatting.engine; +import com.intellij.formatting.ASTBlock; import com.intellij.formatting.AbstractBlockWrapper; +import com.intellij.formatting.Block; +import com.intellij.lang.ASTNode; import com.intellij.lang.Language; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import org.jetbrains.annotations.NotNull; @@ -24,10 +29,12 @@ import org.jetbrains.annotations.NotNull; public class BlockIndentOptions { private final CodeStyleSettings mySettings; private final CommonCodeStyleSettings.IndentOptions myIndentOptions; + private final int myRightMargin; - public BlockIndentOptions(@NotNull CodeStyleSettings settings, @NotNull CommonCodeStyleSettings.IndentOptions indentOptions) { + public BlockIndentOptions(@NotNull CodeStyleSettings settings, @NotNull CommonCodeStyleSettings.IndentOptions indentOptions, Block block) { mySettings = settings; myIndentOptions = indentOptions; + myRightMargin = calcRightMargin(block); } public CommonCodeStyleSettings.IndentOptions getIndentOptions() { @@ -47,4 +54,25 @@ public class BlockIndentOptions { final CommonCodeStyleSettings.IndentOptions result = commonSettings.getIndentOptions(); return result == null ? myIndentOptions : result; } + + public int getRightMargin() { + return myRightMargin; + } + + private int calcRightMargin(Block rootBlock) { + Language language = null; + if (rootBlock instanceof ASTBlock) { + ASTNode node = ((ASTBlock)rootBlock).getNode(); + if (node != null) { + PsiElement psiElement = node.getPsi(); + if (psiElement.isValid()) { + PsiFile psiFile = psiElement.getContainingFile(); + if (psiFile != null) { + language = psiFile.getViewProvider().getBaseLanguage(); + } + } + } + } + return mySettings.getRightMargin(language); + } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java index 93686fbcb953..b200c4ef5396 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java @@ -17,7 +17,6 @@ package com.intellij.formatting.engine; import com.intellij.formatting.*; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.Ref; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; @@ -27,32 +26,29 @@ import java.util.List; import java.util.Set; public class ExpandChildrenIndent extends State { - private final Ref> myExpandableIndentsRef; - private final Ref myIndentAdjusterRef; - private final Document myDocument; + private final WrapBlocksState myWrapState; private IndentAdjuster myIndentAdjuster; private MultiMap myExpandableIndents; private LeafBlockWrapper myCurrentBlock; - public ExpandChildrenIndent(Document document, - Ref indentAdjuster, - Ref> expandableIndentsRef) { - myExpandableIndentsRef = expandableIndentsRef; - myIndentAdjusterRef = indentAdjuster; - myDocument = document; - } - private Iterator myIterator; private MultiMap myBlocksToRealign = new MultiMap(); + public ExpandChildrenIndent(Document document, WrapBlocksState state) { + myDocument = document; + myWrapState = state; + } + + @Override + public void prepare() { + myExpandableIndents = myWrapState.getExpandableIndent(); + myIndentAdjuster = myWrapState.getIndentAdjuster(); + myIterator = myExpandableIndents.keySet().iterator(); + } + @Override protected void doIteration() { - if (myIterator == null) { - myExpandableIndents = myExpandableIndentsRef.get(); - myIndentAdjuster = myIndentAdjusterRef.get(); - myIterator = myExpandableIndents.keySet().iterator(); - } if (!myIterator.hasNext()) { setDone(true); return; diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java b/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java index 63e43c1dbb56..12c783b4bce1 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java @@ -15,14 +15,28 @@ */ package com.intellij.formatting.engine; -import com.intellij.formatting.InitialInfoBuilder; +import com.intellij.formatting.*; +import com.intellij.openapi.editor.Document; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; +import java.util.Map; +import java.util.Set; + public class WrapBlocksState extends State { private final InitialInfoBuilder myWrapper; + private final BlockIndentOptions myBlockIndentOptions; + private WhiteSpace myLastWhiteSpace; + private BlockMapperHelper myBlockMapperHelper; + private DependentSpacingEngine myDependentSpacingEngine; + private AlignmentHelper myAlignmentHelper; + private IndentAdjuster myIndentAdjuster; + private WrapProcessor myWrapProcessor; - public WrapBlocksState(@NotNull InitialInfoBuilder initialInfoBuilder) { + public WrapBlocksState(@NotNull InitialInfoBuilder initialInfoBuilder, BlockIndentOptions blockIndentOptions) { myWrapper = initialInfoBuilder; + myBlockIndentOptions = blockIndentOptions; } @Override @@ -30,10 +44,89 @@ public class WrapBlocksState extends State { if (isDone()) { return; } - setDone(myWrapper.iteration()); - if (!isDone()) { - return; - } } + + public Map getBlockToInfoMap() { + return myWrapper.getBlockToInfoMap(); + } + + public LeafBlockWrapper getFirstBlock() { + assertDone(); + return myWrapper.getFirstTokenBlock(); + } + + public LeafBlockWrapper getLastBlock() { + assertDone(); + return myWrapper.getLastTokenBlock(); + } + + public WhiteSpace getLastWhiteSpace() { + assertDone(); + if (myLastWhiteSpace == null) { + int lastBlockOffset = getLastBlock().getEndOffset(); + myLastWhiteSpace = new WhiteSpace(lastBlockOffset, false); + FormattingDocumentModel model = myWrapper.getFormattingDocumentModel(); + CommonCodeStyleSettings.IndentOptions options = myBlockIndentOptions.getIndentOptions(); + myLastWhiteSpace.append(Math.max(lastBlockOffset, myWrapper.getEndOffset()), model, options); + } + return myLastWhiteSpace; + } + + public BlockMapperHelper getBlockMapperHelper() { + assertDone(); + if (myBlockMapperHelper == null) { + myBlockMapperHelper = new BlockMapperHelper(getFirstBlock(), getLastBlock()); + } + return myBlockMapperHelper; + } + + public DependentSpacingEngine getDependentSpacingEngine() { + assertDone(); + if (myDependentSpacingEngine == null) { + myDependentSpacingEngine = new DependentSpacingEngine(getBlockMapperHelper()); + } + return myDependentSpacingEngine; + } + + public Set getAlignmentsInsideRangesToModify() { + assertDone(); + return myWrapper.getAlignmentsInsideRangeToModify(); + } + + public AlignmentHelper getAlignmentHelper() { + assertDone(); + if (myAlignmentHelper == null) { + Document document = myWrapper.getFormattingDocumentModel().getDocument(); + myAlignmentHelper = new AlignmentHelper(document, myWrapper.getBlocksToAlign(), myBlockIndentOptions); + } + return myAlignmentHelper; + } + + public IndentAdjuster getIndentAdjuster() { + assertDone(); + if (myIndentAdjuster == null) { + myIndentAdjuster = new IndentAdjuster(myBlockIndentOptions, getAlignmentHelper()); + } + return myIndentAdjuster; + } + + public MultiMap getExpandableIndent() { + assertDone(); + return myWrapper.getExpandableIndentsBlocks(); + } + + public WrapProcessor getWrapProcessor() { + assertDone(); + if (myWrapProcessor == null) { + int rightMargin = myBlockIndentOptions.getRightMargin(); + myWrapProcessor = new WrapProcessor(myBlockMapperHelper, getIndentAdjuster(), rightMargin); + } + return myWrapProcessor; + } + + private void assertDone() { + if (!isDone()) throw new IllegalStateException(); + } + } \ No newline at end of file From 8438853a512c7e76475f14052b8e85262387a235 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 19 Apr 2016 12:09:08 +0300 Subject: [PATCH 31/34] Fix for IDEA-151630 restored after rebase --- .../formatting/engine/AlignmentHelper.java | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java b/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java index 6e0042f47530..a5a48b202b15 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/AlignmentHelper.java @@ -42,33 +42,16 @@ public class AlignmentHelper { private final Document myDocument; private final BlockIndentOptions myBlockIndentOptions; - private int myTotalBlocksWithAlignments; - private int myBlockRollbacks; + private final AlignmentCyclesDetector myCyclesDetector; - - /** - * Remembers mappings between backward-shifted aligned block and blocks that cause that shift in order to detect - * infinite cycles that may occur when, for example following alignment is specified: - *

    - *

    -   *     int i1     = 1;
    -   *     int i2, i3 = 2;
    -   * 
    - *

    - * There is a possible case that 'i1', 'i2' and 'i3' blocks re-use - * the same alignment, hence, 'i1' is shifted to right during 'i3' processing but - * that causes 'i2' to be shifted right as wll because it's aligned to 'i1' that - * increases offset of 'i3' that, in turn, causes backward shift of 'i1' etc. - *

    - * This map remembers such backward shifts in order to be able to break such infinite cycles. - */ private final Map> myBackwardShiftedAlignedBlocks = ContainerUtil.newHashMap(); private final Map> myAlignmentMappings = ContainerUtil.newHashMap(); public AlignmentHelper(Document document, MultiMap blocksToAlign, BlockIndentOptions options) { myDocument = document; - myTotalBlocksWithAlignments = blocksToAlign.values().size(); myBlockIndentOptions = options; + int totalBlocks = blocksToAlign.values().size(); + myCyclesDetector = new AlignmentCyclesDetector(totalBlocks); } private static void reportAlignmentProcessingError(BlockAlignmentProcessor.Context context) { @@ -89,8 +72,9 @@ public class AlignmentHelper { BlockAlignmentProcessor.Context context = new BlockAlignmentProcessor.Context( myDocument, alignment, currentBlock, myAlignmentMappings, myBackwardShiftedAlignedBlocks, myBlockIndentOptions.getIndentOptions(currentBlock)); - BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); final LeafBlockWrapper offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(currentBlock); + myCyclesDetector.registerOffsetResponsibleBlock(offsetResponsibleBlock); + BlockAlignmentProcessor.Result result = alignmentProcessor.applyAlignment(context); switch (result) { case TARGET_BLOCK_PROCESSED_NOT_ALIGNED: return null; @@ -106,12 +90,11 @@ public class AlignmentHelper { myBackwardShiftedAlignedBlocks.put(offsetResponsibleBlock, blocksCausedRealignment); blocksCausedRealignment.add(currentBlock); storeAlignmentMapping(currentBlock, offsetResponsibleBlock); - - if (myBlockRollbacks > myTotalBlocksWithAlignments) { + if (myCyclesDetector.isCycleDetected()) { reportAlignmentProcessingError(context); return null; } - myBlockRollbacks++; + myCyclesDetector.registerBlockRollback(currentBlock); return offsetResponsibleBlock.getNextBlock(); case RECURSION_DETECTED: myAlignmentsToSkip.add(alignment); From a900c118e4bb78db613c50796b71e604837a7451 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 19 Apr 2016 12:38:27 +0300 Subject: [PATCH 32/34] Minor refactorings --- .../formatting/DependantSpacingImpl.java | 4 ++-- .../intellij/formatting/FormatProcessor.java | 12 ++++++------ .../com/intellij/formatting/FormatterImpl.java | 6 +++--- .../com/intellij/formatting/SpacingImpl.java | 4 ++-- .../com/intellij/formatting/WhiteSpace.java | 6 +++--- .../engine/AdjustWhiteSpacesState.java | 10 +++++----- .../formatting/engine/ApplyChangesState.java | 4 ++-- ...ckMapperHelper.java => BlockRangesMap.java} | 4 ++-- .../engine/DependentSpacingEngine.java | 8 ++++---- ...ent.java => ExpandChildrenIndentState.java} | 4 ++-- .../formatting/engine/WrapBlocksState.java | 18 +++++++++++------- .../formatting/engine/WrapProcessor.java | 10 +++++----- 12 files changed, 47 insertions(+), 43 deletions(-) rename platform/lang-impl/src/com/intellij/formatting/engine/{BlockMapperHelper.java => BlockRangesMap.java} (96%) rename platform/lang-impl/src/com/intellij/formatting/engine/{ExpandChildrenIndent.java => ExpandChildrenIndentState.java} (98%) diff --git a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java index 3f39fdb05132..c56ce5f376f0 100644 --- a/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/DependantSpacingImpl.java @@ -16,7 +16,7 @@ package com.intellij.formatting; -import com.intellij.formatting.engine.BlockMapperHelper; +import com.intellij.formatting.engine.BlockRangesMap; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; @@ -89,7 +89,7 @@ public class DependantSpacingImpl extends SpacingImpl { } @Override - public void refresh(BlockMapperHelper helper) { + public void refresh(BlockRangesMap helper) { if (isDependentRegionLinefeedStatusChanged()) { return; } diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 4a0ac47b285d..2271157b6951 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -77,8 +77,8 @@ public class FormatProcessor { myStateProcessor = new StateProcessor(myWrapState); } - public BlockMapperHelper getBlockMapperHelper() { - return myWrapState.getBlockMapperHelper(); + public BlockRangesMap getBlockRangesMap() { + return myWrapState.getBlockRangesMap(); } public void format(FormattingModel model) { @@ -88,8 +88,8 @@ public class FormatProcessor { public void format(FormattingModel model, boolean sequentially) { if (sequentially) { myStateProcessor.setNextState(new AdjustWhiteSpacesState(myWrapState, myProgressCallback, myReformatContext)); - myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myWrapState)); - myStateProcessor.setNextState(new ApplyChangesState(model, myWrapState, myProgressCallback, myBlockIndentOptions)); + myStateProcessor.setNextState(new ExpandChildrenIndentState(myDocument, myWrapState)); + myStateProcessor.setNextState(new ApplyChangesState(model, myWrapState, myProgressCallback)); } else { formatWithoutRealModifications(false); @@ -119,7 +119,7 @@ public class FormatProcessor { @SuppressWarnings({"WhileLoopSpinsOnField"}) public void formatWithoutRealModifications(boolean sequentially) { myStateProcessor.setNextState(new AdjustWhiteSpacesState(myWrapState, myProgressCallback, myReformatContext)); - myStateProcessor.setNextState(new ExpandChildrenIndent(myDocument, myWrapState)); + myStateProcessor.setNextState(new ExpandChildrenIndentState(myDocument, myWrapState)); if (sequentially) { return; } @@ -131,7 +131,7 @@ public class FormatProcessor { } public void performModifications(FormattingModel model, boolean sequentially) { - myStateProcessor.setNextState(new ApplyChangesState(model, myWrapState, myProgressCallback, myBlockIndentOptions)); + myStateProcessor.setNextState(new ApplyChangesState(model, myWrapState, myProgressCallback)); if (sequentially) { return; diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index 854d33e33efa..2fd4d92f865c 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -347,7 +347,7 @@ public class FormatterImpl extends FormatterEx final FormatProcessor processor = buildProcessorAndWrapBlocks( model, block, settings, indentOptions, new FormatTextRanges(affectedRange, true) ); - final LeafBlockWrapper blockBefore = processor.getBlockMapperHelper().getBlockAtOrAfter(affectedRange.getStartOffset()); + final LeafBlockWrapper blockBefore = processor.getBlockRangesMap().getBlockAtOrAfter(affectedRange.getStartOffset()); LOG.assertTrue(blockBefore != null); WhiteSpace whiteSpace = blockBefore.getWhiteSpace(); LOG.assertTrue(whiteSpace != null); @@ -457,7 +457,7 @@ public class FormatterImpl extends FormatterEx documentModel, block, settings, indentOptions, new FormatTextRanges(affectedRange, true), offset ); - final LeafBlockWrapper blockAfterOffset = processor.getBlockMapperHelper().getBlockAtOrAfter(offset); + final LeafBlockWrapper blockAfterOffset = processor.getBlockRangesMap().getBlockAtOrAfter(offset); if (blockAfterOffset != null && blockAfterOffset.contains(offset)) { return offset; @@ -557,7 +557,7 @@ public class FormatterImpl extends FormatterEx final FormatProcessor processor = buildProcessorAndWrapBlocks( documentModel, block, settings, indentOptions, new FormatTextRanges(affectedRange, true), offset ); - final LeafBlockWrapper blockAfterOffset = processor.getBlockMapperHelper().getBlockAtOrAfter(offset); + final LeafBlockWrapper blockAfterOffset = processor.getBlockRangesMap().getBlockAtOrAfter(offset); if (blockAfterOffset != null && !blockAfterOffset.contains(offset)) { final WhiteSpace whiteSpace = blockAfterOffset.getWhiteSpace(); diff --git a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java index c46e36750ddb..d0009f7aeb5e 100644 --- a/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/SpacingImpl.java @@ -16,7 +16,7 @@ package com.intellij.formatting; -import com.intellij.formatting.engine.BlockMapperHelper; +import com.intellij.formatting.engine.BlockRangesMap; import org.jetbrains.annotations.NonNls; /** @@ -92,7 +92,7 @@ public class SpacingImpl extends Spacing { /** * Allows to ask to refresh current state using given formatter if necessary. */ - public void refresh(BlockMapperHelper helper) { + public void refresh(BlockRangesMap helper) { } public final boolean shouldKeepLineFeeds() { diff --git a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java index edd7a09273db..7bee50ca0ea3 100644 --- a/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java +++ b/platform/lang-impl/src/com/intellij/formatting/WhiteSpace.java @@ -16,7 +16,7 @@ package com.intellij.formatting; -import com.intellij.formatting.engine.BlockMapperHelper; +import com.intellij.formatting.engine.BlockRangesMap; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; @@ -394,7 +394,7 @@ public class WhiteSpace { * Tries to ensure that number of line feeds managed by the current {@link WhiteSpace} is consistent to the settings * defined at the given spacing property. */ - public void arrangeLineFeeds(final SpacingImpl spaceProperty, final BlockMapperHelper helper) { + public void arrangeLineFeeds(final SpacingImpl spaceProperty, final BlockRangesMap helper) { performModification(new Runnable() { @Override public void run() { @@ -540,7 +540,7 @@ public class WhiteSpace { * This method may be considered a shortcut for calling {@link #arrangeLineFeeds(SpacingImpl, FormatProcessor)} and * {@link #arrangeSpaces(SpacingImpl)}. */ - public void removeLineFeeds(final SpacingImpl spacing, final BlockMapperHelper helper) { + public void removeLineFeeds(final SpacingImpl spacing, final BlockRangesMap helper) { performModification(new Runnable() { @Override public void run() { diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java b/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java index 3b433ccf82a3..1088d66132f8 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/AdjustWhiteSpacesState.java @@ -35,7 +35,7 @@ public class AdjustWhiteSpacesState extends State { private DependentSpacingEngine myDependentSpacingEngine; private WrapProcessor myWrapProcessor; - private BlockMapperHelper myBlockMapperHelper; + private BlockRangesMap myBlockRangesMap; private IndentAdjuster myIndentAdjuster; @@ -61,7 +61,7 @@ public class AdjustWhiteSpacesState extends State { myDependentSpacingEngine = myWrapBlocksState.getDependentSpacingEngine(); myWrapProcessor = myWrapBlocksState.getWrapProcessor(); myIndentAdjuster = myWrapBlocksState.getIndentAdjuster(); - myBlockMapperHelper = myWrapBlocksState.getBlockMapperHelper(); + myBlockRangesMap = myWrapBlocksState.getBlockRangesMap(); myAlignmentsInsideRangesToModify = myWrapBlocksState.getAlignmentsInsideRangesToModify(); } } @@ -70,7 +70,7 @@ public class AdjustWhiteSpacesState extends State { DependentSpacingEngine dependentSpacingEngine, WrapProcessor wrapProcessor, IndentAdjuster indentAdjuster, - BlockMapperHelper blockMapperHelper, + BlockRangesMap blockRangesMap, Set alignmentsInsideRangesToModify, boolean isReformatContext, FormattingProgressCallback progressCallback) @@ -80,7 +80,7 @@ public class AdjustWhiteSpacesState extends State { myDependentSpacingEngine = dependentSpacingEngine; myWrapProcessor = wrapProcessor; myIndentAdjuster = indentAdjuster; - myBlockMapperHelper = blockMapperHelper; + myBlockRangesMap = blockRangesMap; myAlignmentsInsideRangesToModify = alignmentsInsideRangesToModify; myReformatContext = isReformatContext; myProgressCallback = progressCallback; @@ -178,7 +178,7 @@ public class AdjustWhiteSpacesState extends State { } } - whiteSpace.arrangeLineFeeds(spaceProperty, myBlockMapperHelper); + whiteSpace.arrangeLineFeeds(spaceProperty, myBlockRangesMap); if (!whiteSpace.containsLineFeeds()) { whiteSpace.arrangeSpaces(spaceProperty); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java b/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java index c6a19b1df540..3f6eb0968ed6 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/ApplyChangesState.java @@ -51,11 +51,11 @@ public class ApplyChangesState extends State { private BlockIndentOptions myBlockIndentOptions; - public ApplyChangesState(FormattingModel model, WrapBlocksState state, FormattingProgressCallback callback, BlockIndentOptions options) { + public ApplyChangesState(FormattingModel model, WrapBlocksState state, FormattingProgressCallback callback) { myModel = model; myWrapState = state; myProgressCallback = callback; - myBlockIndentOptions = options; + myBlockIndentOptions = state.getBlockIndentOptions(); } /** diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/BlockMapperHelper.java b/platform/lang-impl/src/com/intellij/formatting/engine/BlockRangesMap.java similarity index 96% rename from platform/lang-impl/src/com/intellij/formatting/engine/BlockMapperHelper.java rename to platform/lang-impl/src/com/intellij/formatting/engine/BlockRangesMap.java index ffd28e568db8..422cf75a364f 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/BlockMapperHelper.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/BlockRangesMap.java @@ -20,11 +20,11 @@ import com.intellij.openapi.util.TextRange; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.Nullable; -public class BlockMapperHelper { +public class BlockRangesMap { private final LeafBlockWrapper myLastBlock; private TIntObjectHashMap myTextRangeToWrapper; - public BlockMapperHelper(LeafBlockWrapper first, LeafBlockWrapper last) { + public BlockRangesMap(LeafBlockWrapper first, LeafBlockWrapper last) { myLastBlock = last; myTextRangeToWrapper = buildTextRangeToInfoMap(first); } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java b/platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java index b367fd7d5cd2..c3e1d1ac39cc 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/DependentSpacingEngine.java @@ -49,7 +49,7 @@ import java.util.*; * formatting iteration. */ public class DependentSpacingEngine { - private final BlockMapperHelper myBlockMapperHelper; + private final BlockRangesMap myBlockRangesMap; private SortedMap myPreviousDependencies = new TreeMap(new Comparator() { @@ -64,8 +64,8 @@ public class DependentSpacingEngine { } }); - public DependentSpacingEngine(BlockMapperHelper helper) { - myBlockMapperHelper = helper; + public DependentSpacingEngine(BlockRangesMap helper) { + myBlockRangesMap = helper; } public boolean shouldReformatPreviouslyLocatedDependentSpacing(WhiteSpace space) { @@ -82,7 +82,7 @@ public class DependentSpacingEngine { } final boolean containedLineFeeds = spacing.getMinLineFeeds() > 0; - final boolean containsLineFeeds = myBlockMapperHelper.containsLineFeeds(textRange); + final boolean containsLineFeeds = myBlockRangesMap.containsLineFeeds(textRange); if (containedLineFeeds != containsLineFeeds) { spacing.setDependentRegionLinefeedStatusChanged(); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndentState.java similarity index 98% rename from platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java rename to platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndentState.java index b200c4ef5396..296a436760b3 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndent.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/ExpandChildrenIndentState.java @@ -25,7 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -public class ExpandChildrenIndent extends State { +public class ExpandChildrenIndentState extends State { private final Document myDocument; private final WrapBlocksState myWrapState; private IndentAdjuster myIndentAdjuster; @@ -35,7 +35,7 @@ public class ExpandChildrenIndent extends State { private Iterator myIterator; private MultiMap myBlocksToRealign = new MultiMap(); - public ExpandChildrenIndent(Document document, WrapBlocksState state) { + public ExpandChildrenIndentState(Document document, WrapBlocksState state) { myDocument = document; myWrapState = state; } diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java b/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java index 12c783b4bce1..aeb8097c1967 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/WrapBlocksState.java @@ -28,7 +28,7 @@ public class WrapBlocksState extends State { private final InitialInfoBuilder myWrapper; private final BlockIndentOptions myBlockIndentOptions; private WhiteSpace myLastWhiteSpace; - private BlockMapperHelper myBlockMapperHelper; + private BlockRangesMap myBlockRangesMap; private DependentSpacingEngine myDependentSpacingEngine; private AlignmentHelper myAlignmentHelper; private IndentAdjuster myIndentAdjuster; @@ -73,18 +73,18 @@ public class WrapBlocksState extends State { return myLastWhiteSpace; } - public BlockMapperHelper getBlockMapperHelper() { + public BlockRangesMap getBlockRangesMap() { assertDone(); - if (myBlockMapperHelper == null) { - myBlockMapperHelper = new BlockMapperHelper(getFirstBlock(), getLastBlock()); + if (myBlockRangesMap == null) { + myBlockRangesMap = new BlockRangesMap(getFirstBlock(), getLastBlock()); } - return myBlockMapperHelper; + return myBlockRangesMap; } public DependentSpacingEngine getDependentSpacingEngine() { assertDone(); if (myDependentSpacingEngine == null) { - myDependentSpacingEngine = new DependentSpacingEngine(getBlockMapperHelper()); + myDependentSpacingEngine = new DependentSpacingEngine(getBlockRangesMap()); } return myDependentSpacingEngine; } @@ -120,10 +120,14 @@ public class WrapBlocksState extends State { assertDone(); if (myWrapProcessor == null) { int rightMargin = myBlockIndentOptions.getRightMargin(); - myWrapProcessor = new WrapProcessor(myBlockMapperHelper, getIndentAdjuster(), rightMargin); + myWrapProcessor = new WrapProcessor(myBlockRangesMap, getIndentAdjuster(), rightMargin); } return myWrapProcessor; } + + public BlockIndentOptions getBlockIndentOptions() { + return myBlockIndentOptions; + } private void assertDone() { if (!isDone()) throw new IllegalStateException(); diff --git a/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java b/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java index bb2b2e5761f8..acce258962dd 100644 --- a/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/engine/WrapProcessor.java @@ -22,14 +22,14 @@ import java.util.ArrayList; public class WrapProcessor { private LeafBlockWrapper myFirstWrappedBlockOnLine = null; - private BlockMapperHelper myBlockMapperHelper; + private BlockRangesMap myBlockRangesMap; private LeafBlockWrapper myWrapCandidate = null; private IndentAdjuster myIndentAdjuster; private int myRightMargin; - public WrapProcessor(BlockMapperHelper blockHelper, IndentAdjuster indentAdjuster, int rightMargin) { + public WrapProcessor(BlockRangesMap blockHelper, IndentAdjuster indentAdjuster, int rightMargin) { myIndentAdjuster = indentAdjuster; - myBlockMapperHelper = blockHelper; + myBlockRangesMap = blockHelper; myRightMargin = rightMargin; } @@ -77,7 +77,7 @@ public class WrapProcessor { return startColumnNow > startColumnAfterWrap; } finally { - whiteSpace.removeLineFeeds(currentBlock.getSpaceProperty(), myBlockMapperHelper); + whiteSpace.removeLineFeeds(currentBlock.getSpaceProperty(), myBlockRangesMap); whiteSpace.setSpaces(spaces, indentSpaces); } } @@ -136,7 +136,7 @@ public class WrapProcessor { myFirstWrappedBlockOnLine = null; if (!whiteSpace.containsLineFeedsInitially()) { - whiteSpace.removeLineFeeds(spacing, myBlockMapperHelper); + whiteSpace.removeLineFeeds(spacing, myBlockRangesMap); } } From c666cc44eaa96234c000a3cb62749a2fe39c09d5 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 19 Apr 2016 12:51:03 +0300 Subject: [PATCH 33/34] Fixed yellow code --- .../intellij/formatting/FormatProcessor.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 2271157b6951..8ba0ce9305db 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -32,15 +32,9 @@ public class FormatProcessor { private static final Logger LOG = Logger.getInstance("#com.intellij.formatting.FormatProcessor"); private final WrapBlocksState myWrapState; - private boolean myReformatContext; - - private final BlockIndentOptions myBlockIndentOptions; - private final CommonCodeStyleSettings.IndentOptions myDefaultIndentOption; - private final CodeStyleSettings mySettings; private final Document myDocument; - @NotNull private final FormattingProgressCallback myProgressCallback; @@ -65,14 +59,16 @@ public class FormatProcessor { @NotNull FormattingProgressCallback callback) { myProgressCallback = callback; - myDefaultIndentOption = options.myIndentOptions; - mySettings = options.mySettings; - myBlockIndentOptions = new BlockIndentOptions(mySettings, myDefaultIndentOption, block); + + CommonCodeStyleSettings.IndentOptions defaultIndentOption = options.myIndentOptions; + CodeStyleSettings settings = options.mySettings; + BlockIndentOptions blockIndentOptions = new BlockIndentOptions(settings, defaultIndentOption, block); + myDocument = model.getDocument(); myReformatContext = options.myReformatContext; - final InitialInfoBuilder builder = prepareToBuildBlocksSequentially(block, model, options, mySettings, myDefaultIndentOption, myProgressCallback); - myWrapState = new WrapBlocksState(builder, myBlockIndentOptions); + final InitialInfoBuilder builder = prepareToBuildBlocksSequentially(block, model, options, settings, defaultIndentOption, myProgressCallback); + myWrapState = new WrapBlocksState(builder, blockIndentOptions); myStateProcessor = new StateProcessor(myWrapState); } From fb6455604b6f599a1aeeb0a701a7113e227fcad7 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Tue, 19 Apr 2016 13:46:58 +0300 Subject: [PATCH 34/34] Cleanup --- .../lang-impl/src/com/intellij/formatting/FormatProcessor.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java index 8ba0ce9305db..caf00cbf450e 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatProcessor.java @@ -38,8 +38,6 @@ public class FormatProcessor { @NotNull private final FormattingProgressCallback myProgressCallback; - private boolean myDisposed; - @NotNull private StateProcessor myStateProcessor; @@ -112,7 +110,6 @@ public class FormatProcessor { formatWithoutRealModifications(false); } - @SuppressWarnings({"WhileLoopSpinsOnField"}) public void formatWithoutRealModifications(boolean sequentially) { myStateProcessor.setNextState(new AdjustWhiteSpacesState(myWrapState, myProgressCallback, myReformatContext)); myStateProcessor.setNextState(new ExpandChildrenIndentState(myDocument, myWrapState));