Skip to content

Commit 8067f5c

Browse files
committed
Prevent IllegalStateException setting visible region with collapsed
projection region
1 parent ce17c9e commit 8067f5c

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

bundles/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocument.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
package org.eclipse.jface.text.projection;
1616

1717
import java.util.ArrayList;
18+
import java.util.Arrays;
19+
import java.util.Comparator;
1820
import java.util.List;
1921

2022
import org.eclipse.jface.text.AbstractDocument;
@@ -438,6 +440,11 @@ private void internalRemoveMasterDocumentRange(int offsetInMaster, int lengthInM
438440
segment= new Segment(offset, fragment.segment.getOffset() + fragment.segment.getLength() - offset);
439441
newFragment.segment= segment;
440442
segment.fragment= newFragment;
443+
if (newFragment.getLength() != 0 && fMasterDocument.containsPosition(fFragmentsCategory, newFragment.getOffset(), 0)) {
444+
// prevent inserting position with non-zero length after position with 0-length by removing zero-length position
445+
removePositionAt(fMasterDocument, fFragmentsCategory, new Position(newFragment.getOffset(), 0));
446+
removePositionAt(this, fSegmentsCategory, new Position(fMapping.toImageOffset(newFragment.getOffset()), 0));
447+
}
441448
fMasterDocument.addPosition(fFragmentsCategory, newFragment);
442449
addPosition(fSegmentsCategory, segment);
443450

@@ -454,6 +461,14 @@ private void internalRemoveMasterDocumentRange(int offsetInMaster, int lengthInM
454461
}
455462
}
456463

464+
private void removePositionAt(IDocument document, String category, Position toRemove) throws BadPositionCategoryException {
465+
Position[] positions= document.getPositions(category);
466+
int index= Arrays.binarySearch(positions, toRemove, Comparator.comparingInt(Position::getOffset));
467+
if (index != -1) {
468+
document.removePosition(category, positions[index]);
469+
}
470+
}
471+
457472
/**
458473
* Returns the sequence of all master document regions which are contained
459474
* in the given master document range and which are not yet part of this

tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ProjectionViewerTest.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
import org.eclipse.jface.text.BadLocationException;
2929
import org.eclipse.jface.text.Document;
3030
import org.eclipse.jface.text.IDocument;
31+
import org.eclipse.jface.text.IDocumentInformationMapping;
3132
import org.eclipse.jface.text.IRegion;
3233
import org.eclipse.jface.text.ITextOperationTarget;
3334
import org.eclipse.jface.text.ITextSelection;
3435
import org.eclipse.jface.text.Position;
3536
import org.eclipse.jface.text.Region;
37+
import org.eclipse.jface.text.projection.ProjectionDocument;
3638
import org.eclipse.jface.text.source.AnnotationModel;
3739
import org.eclipse.jface.text.source.IOverviewRuler;
3840
import org.eclipse.jface.text.source.IVerticalRuler;
@@ -433,6 +435,71 @@ public void testSetVisibleRegionExpandsBorderingProjectionRegions() {
433435
}
434436
}
435437

438+
@Test
439+
public void testImageLineStateAfterSettingVisibleRegionsWithProjections() throws BadLocationException {
440+
Shell shell= new Shell();
441+
shell.setLayout(new FillLayout());
442+
TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, false, SWT.NONE);
443+
String documentContent= """
444+
public class TM {
445+
void a() {
446+
// ...
447+
}
448+
449+
void b() {
450+
// ...
451+
}
452+
453+
void c() {
454+
// ...
455+
}
456+
}
457+
""";
458+
Document document= new Document(documentContent);
459+
viewer.setDocument(document, new AnnotationModel());
460+
viewer.enableProjection();
461+
addAnnotationBetween(viewer, new ProjectionAnnotation(false), "\tvoid a()", "\t}");
462+
ProjectionAnnotation annotationToCollapse= new ProjectionAnnotation(false);
463+
addAnnotationBetween(viewer, annotationToCollapse, "\tvoid b()", "\t}");
464+
addAnnotationBetween(viewer, new ProjectionAnnotation(false), "\tvoid c()", "\t}");
465+
466+
shell.setVisible(true);
467+
try {
468+
viewer.getProjectionAnnotationModel().collapse(annotationToCollapse);
469+
470+
Position firstMethod= findPositionFromStartAndEndText(viewer, "\tvoid a()", "}");
471+
viewer.setVisibleRegion(firstMethod.getOffset(), firstMethod.getLength() + 1);
472+
viewer.setVisibleRegion(documentContent.indexOf("class"), documentContent.length() - documentContent.indexOf("class"));
473+
474+
IDocumentInformationMapping mapping= ((ProjectionDocument) viewer.getVisibleDocument()).getDocumentInformationMapping();
475+
// toImageLine should not throw exceptions
476+
for (int i= 0; i < 5; i++) {
477+
int imageLine= mapping.toImageLine(i);// should not throw exception
478+
assertEquals(i, imageLine);
479+
}
480+
assertEquals(-1, mapping.toImageLine(6), "should still be collapsed");
481+
for (int i= 7; i < documentContent.split("\n").length; i++) {
482+
int imageLine= mapping.toImageLine(i);// should not throw exception
483+
assertEquals(i - 1, imageLine);
484+
}
485+
} finally {
486+
shell.dispose();
487+
}
488+
}
489+
490+
private void addAnnotationBetween(TestProjectionViewer viewer, ProjectionAnnotation annotationToCollapse, String startText, String endText) {
491+
Position position= findPositionFromStartAndEndText(viewer, startText, endText);
492+
viewer.getProjectionAnnotationModel().addAnnotation(annotationToCollapse, position);
493+
}
494+
495+
private Position findPositionFromStartAndEndText(TestProjectionViewer viewer, String startText, String endText) {
496+
String documentContent= viewer.getDocument().get();
497+
int startIndex= documentContent.indexOf(startText);
498+
int endIndex= documentContent.indexOf(endText, startIndex + 1);
499+
Position position= new Position(startIndex, endIndex - startIndex);
500+
return position;
501+
}
502+
436503
@Test
437504
public void testProjectionRegionsShownOnlyInVisibleRegion() {
438505
Shell shell= new Shell(Display.getCurrent());

0 commit comments

Comments
 (0)