Skip to content

Commit dde2668

Browse files
danthe1stiloveeclipse
authored andcommitted
Prevent IllegalStateException setting visible region with collapsed
projection region
1 parent 68371cb commit dde2668

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-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: 68 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,72 @@ public void testSetVisibleRegionExpandsBorderingProjectionRegions() {
433435
}
434436
}
435437

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

0 commit comments

Comments
 (0)