diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java index add65b485710..fd4c45aad5dc 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.editorActions.moveUpDown; +import com.intellij.codeInsight.folding.CodeFoldingManager; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; @@ -44,7 +45,7 @@ class MoverWrapper { return myInfo; } - public final void move(Editor editor, final PsiFile file) { + public final void move(final Editor editor, final PsiFile file) { assert myInfo.toMove2 != null; myMover.beforeMove(editor, myInfo, myIsDown); final Document document = editor.getDocument(); @@ -91,19 +92,8 @@ class MoverWrapper { // Pointer for the fold region from method1 points to 'method2()' now and vice versa (check range markers processing on // document change for further information). I.e. information about fold regions statuses holds the data swapped for // 'method1' and 'method2'. Hence, we want to apply correct 'collapsed' status. - FoldRegion topRegion = null; - FoldRegion bottomRegion = null; - for (FoldRegion foldRegion : editor.getFoldingModel().getAllFoldRegions()) { - if (!foldRegion.isValid() || (!contains(myInfo.range1, foldRegion) && !contains(myInfo.range2, foldRegion))) { - continue; - } - if (contains(myInfo.range1, foldRegion) && !contains(topRegion, foldRegion)) { - topRegion = foldRegion; - } - else if (contains(myInfo.range2, foldRegion) && !contains(bottomRegion, foldRegion)) { - bottomRegion = foldRegion; - } - } + final FoldRegion topRegion = findTopLevelRegionInRange(editor, myInfo.range1); + final FoldRegion bottomRegion = findTopLevelRegionInRange(editor, myInfo.range2); document.insertString(myInfo.range1.getStartOffset(), textToInsert2); document.deleteString(myInfo.range1.getStartOffset()+textToInsert2.length(), myInfo.range1.getEndOffset()); @@ -120,14 +110,19 @@ class MoverWrapper { // Swap fold regions status if necessary. if (topRegion != null && bottomRegion != null) { - final FoldRegion finalTopRegion = topRegion; - final FoldRegion finalBottomRegion = bottomRegion; + CodeFoldingManager.getInstance(project).updateFoldRegions(editor); editor.getFoldingModel().runBatchFoldingOperation(new Runnable() { @Override public void run() { - boolean topExpanded = finalTopRegion.isExpanded(); - finalTopRegion.setExpanded(finalBottomRegion.isExpanded()); - finalBottomRegion.setExpanded(topExpanded); + FoldRegion newTopRegion = findTopLevelRegionInRange(editor, myInfo.range1); + if (newTopRegion != null) { + newTopRegion.setExpanded(bottomRegion.isExpanded()); + } + + FoldRegion newBottomRegion = findTopLevelRegionInRange(editor, myInfo.range2); + if (newBottomRegion != null) { + newBottomRegion.setExpanded(topRegion.isExpanded()); + } } }); } @@ -148,6 +143,16 @@ class MoverWrapper { editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } + private static FoldRegion findTopLevelRegionInRange(Editor editor, RangeMarker range) { + FoldRegion result = null; + for (FoldRegion foldRegion : editor.getFoldingModel().getAllFoldRegions()) { + if (foldRegion.isValid() && contains(range, foldRegion) && !contains(result, foldRegion)) { + result = foldRegion; + } + } + return result; + } + /** * Allows to check if text range defined by the given range marker completely contains text range of the given fold region. * diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java index 0acfbbcc33f8..e9987064169f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java @@ -80,12 +80,15 @@ abstract class FoldRegionsTree { void rebuild() { ArrayList topLevels = new ArrayList(myRegions.size() / 2); ArrayList visible = new ArrayList(myRegions.size()); + ArrayList allValid = new ArrayList(myRegions.size()); FoldRegion[] regions = toFoldArray(myRegions); FoldRegion currentCollapsed = null; for (FoldRegion region : regions) { if (!region.isValid()) { continue; } + + allValid.add(region); if (currentCollapsed == null || !contains(currentCollapsed, region)) { visible.add(region); @@ -99,6 +102,10 @@ abstract class FoldRegionsTree { } } + if (allValid.size() < myRegions.size()) { + myRegions = allValid; + } + myCachedTopLevelRegions = toFoldArray(topLevels); myCachedVisible = toFoldArray(visible); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/FoldingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/FoldingTest.java index 6d370d85ce31..d9fb2c5cb29c 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/FoldingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/FoldingTest.java @@ -1,15 +1,22 @@ package com.intellij.openapi.editor; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.util.Ref; import com.intellij.testFramework.LightPlatformTestCase; +import com.intellij.testFramework.PlatformTestCase; import org.jetbrains.annotations.NonNls; /** * @author max */ public class FoldingTest extends LightPlatformTestCase { + + static { + PlatformTestCase.autodetectPlatformPrefix(); + } + public void testStressFoldingFromZeroOffset() throws Exception { for (int len = 2; len < 25; len++) { stress(len); @@ -68,6 +75,32 @@ public class FoldingTest extends LightPlatformTestCase { } } + public void testCleanupInvalidRegions() { + final DocumentImpl doc = new DocumentImpl("foo1\nfoo2\nfoo3\nfoo4"); + Editor editor = EditorFactory.getInstance().createEditor(doc); + final FoldingModel model = editor.getFoldingModel(); + try { + model.runBatchFoldingOperation(new Runnable() { + @Override + public void run() { + model.addFoldRegion(0, 4, "/*...*/"); + model.addFoldRegion(5, 9, "/*...*/"); + } + }); + assertSize(2, model.getAllFoldRegions()); + WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() { + @Override + public void run() { + doc.deleteString(0, 5); + } + }); + assertSize(1, model.getAllFoldRegions()); + } + finally { + EditorFactory.getInstance().releaseEditor(editor); + } + } + public void testIntersects () throws Exception { @NonNls DocumentImpl doc = new DocumentImpl("I don't know what you mean by `glory,'\" Alice said" + "Humpty Dumpty smiled contemptuously. \"Of course you don't -- till I tell you. I meant `there's a nice knock-down argument for you!'" +