cleanup invalid fold regions (fix console leaks, IDEA-124626)

This commit is contained in:
peter
2014-05-06 18:36:01 +02:00
parent d2ead5248c
commit 17e28ba9a7
3 changed files with 64 additions and 19 deletions
@@ -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.
*
@@ -80,12 +80,15 @@ abstract class FoldRegionsTree {
void rebuild() {
ArrayList<FoldRegion> topLevels = new ArrayList<FoldRegion>(myRegions.size() / 2);
ArrayList<FoldRegion> visible = new ArrayList<FoldRegion>(myRegions.size());
ArrayList<FoldRegion> allValid = new ArrayList<FoldRegion>(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);
@@ -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!'" +