Skip to content

Commit 0ce1430

Browse files
danthe1stiloveeclipse
authored andcommitted
Ensure enableProjection() sets the correct visible region and move empty
document range at EOF to start of removed range Fixes eclipse-platform#3380 and eclipse-xtext/xtext#3531
1 parent 493d107 commit 0ce1430

File tree

3 files changed

+90
-37
lines changed

3 files changed

+90
-37
lines changed

bundles/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -281,23 +281,23 @@ private void computeExpectedExecutionCosts() {
281281
}
282282

283283
/**
284-
* An {@link IDocumentListener} that makes sure that {@link #fVisibleRegionDuringProjection} is
285-
* updated when the document changes and ensures that the collapsed region after the visible
286-
* region is recreated appropriately.
284+
* An {@link IDocumentListener} that makes sure that {@link #fConfiguredVisibleRegion} is updated when the
285+
* document changes and ensures that the collapsed region after the visible region is recreated
286+
* appropriately.
287287
*/
288288
private final class UpdateDocumentListener implements IDocumentListener {
289289
@Override
290290
public void documentChanged(DocumentEvent event) {
291-
if (fVisibleRegionDuringProjection == null) {
291+
if (fConfiguredVisibleRegion == null) {
292292
return;
293293
}
294294
int oldLength= event.getLength();
295295
int newLength= event.getText().length();
296-
int oldVisibleRegionEnd= fVisibleRegionDuringProjection.getOffset() + fVisibleRegionDuringProjection.getLength();
297-
if (event.getOffset() < fVisibleRegionDuringProjection.getOffset()) {
298-
fVisibleRegionDuringProjection= new Region(fVisibleRegionDuringProjection.getOffset() + newLength - oldLength, fVisibleRegionDuringProjection.getLength());
296+
int oldVisibleRegionEnd= fConfiguredVisibleRegion.getOffset() + fConfiguredVisibleRegion.getLength();
297+
if (event.getOffset() < fConfiguredVisibleRegion.getOffset()) {
298+
fConfiguredVisibleRegion= new Region(fConfiguredVisibleRegion.getOffset() + newLength - oldLength, fConfiguredVisibleRegion.getLength());
299299
} else if (event.getOffset() + oldLength <= oldVisibleRegionEnd) {
300-
fVisibleRegionDuringProjection= new Region(fVisibleRegionDuringProjection.getOffset(), fVisibleRegionDuringProjection.getLength() + newLength - oldLength);
300+
fConfiguredVisibleRegion= new Region(fConfiguredVisibleRegion.getOffset(), fConfiguredVisibleRegion.getLength() + newLength - oldLength);
301301
}
302302
}
303303

@@ -324,13 +324,11 @@ public void documentAboutToBeChanged(DocumentEvent event) {
324324
private List<AnnotationModelEvent> fPendingRequests= new ArrayList<>();
325325
/** The replace-visible-document execution trigger */
326326
private IDocument fReplaceVisibleDocumentExecutionTrigger;
327-
/** <code>true</code> if projection was on the last time we switched to segmented mode. */
328-
private boolean fWasProjectionEnabled;
329327
/**
330-
* The region set by {@link #setVisibleRegion(int, int)} during projection or <code>null</code>
331-
* if not in a projection
328+
* The region set by {@link #setVisibleRegion(int, int)} or <code>null</code>
329+
* if no visible region has been set
332330
*/
333-
private IRegion fVisibleRegionDuringProjection;
331+
private IRegion fConfiguredVisibleRegion;
334332
/** The queue of projection commands used to assess the costs of projection changes. */
335333
private ProjectionCommandQueue fCommandQueue;
336334
/**
@@ -340,7 +338,7 @@ public void documentAboutToBeChanged(DocumentEvent event) {
340338
*/
341339
private int fDeletedLines;
342340

343-
private UpdateDocumentListener fUpdateDocumentListener;
341+
private final UpdateDocumentListener fUpdateDocumentListener;
344342

345343
/**
346344
* Creates a new projection source viewer.
@@ -400,12 +398,15 @@ public void setDocument(IDocument document, IAnnotationModel annotationModel, in
400398
}
401399

402400
if (fProjectionAnnotationModel != null) {
403-
removeDocumentUpdateListener();
404401
wasProjectionEnabled= removeProjectionAnnotationModel(getVisualAnnotationModel()) != null;
405402
fProjectionAnnotationModel= null;
406403
}
407404

405+
removeDocumentUpdateListener();
408406
super.setDocument(document, annotationModel, modelRangeOffset, modelRangeLength);
407+
if (document != null) {
408+
document.addDocumentListener(fUpdateDocumentListener);
409+
}
409410

410411
if (wasProjectionEnabled && document != null) {
411412
enableProjection();
@@ -560,9 +561,9 @@ public final void disableProjection() {
560561
fProjectionAnnotationModel.removeAllAnnotations();
561562
fFindReplaceDocumentAdapter= null;
562563
fireProjectionDisabled();
563-
if (fVisibleRegionDuringProjection != null) {
564-
super.setVisibleRegion(fVisibleRegionDuringProjection.getOffset(), fVisibleRegionDuringProjection.getLength());
565-
fVisibleRegionDuringProjection= null;
564+
if (fConfiguredVisibleRegion != null) {
565+
super.setVisibleRegion(fConfiguredVisibleRegion.getOffset(), fConfiguredVisibleRegion.getLength());
566+
fConfiguredVisibleRegion= null;
566567
}
567568
removeDocumentUpdateListener();
568569
}
@@ -580,8 +581,9 @@ public final void enableProjection() {
580581
if (document == null) {
581582
return;
582583
}
583-
IRegion visibleRegion= getVisibleRegion();
584+
IRegion visibleRegion= fConfiguredVisibleRegion;
584585
if (visibleRegion != null && (visibleRegion.getOffset() != 0 || visibleRegion.getLength() != 0) && visibleRegion.getLength() < document.getLength()) {
586+
fConfiguredVisibleRegion= null;
585587
setVisibleRegion(visibleRegion.getOffset(), visibleRegion.getLength());
586588
}
587589
document.addDocumentListener(fUpdateDocumentListener);
@@ -593,9 +595,9 @@ private void expandAll() {
593595
IDocument doc= getDocument();
594596
int length= doc == null ? 0 : doc.getLength();
595597
if (isProjectionMode()) {
596-
if (fVisibleRegionDuringProjection != null) {
597-
offset= fVisibleRegionDuringProjection.getOffset();
598-
length= fVisibleRegionDuringProjection.getLength();
598+
if (fConfiguredVisibleRegion != null) {
599+
offset= fConfiguredVisibleRegion.getOffset();
600+
length= fConfiguredVisibleRegion.getLength();
599601
}
600602
fProjectionAnnotationModel.expandAll(offset, length);
601603
}
@@ -757,6 +759,7 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
757759
public void setVisibleRegion(int start, int length) {
758760
if (!isProjectionMode()) {
759761
super.setVisibleRegion(start, length);
762+
fConfiguredVisibleRegion= new Region(start, length);
760763
return;
761764
}
762765
IDocument document= getDocument();
@@ -769,17 +772,17 @@ public void setVisibleRegion(int start, int length) {
769772
int end= computeEndOfVisibleRegion(start, length, document);
770773
expandOutsideCurrentVisibleRegion(document);
771774
collapseOutsideOfNewVisibleRegion(start, end, document);
772-
fVisibleRegionDuringProjection= new Region(start, end - start - 1);
775+
fConfiguredVisibleRegion= new Region(start, end - start - 1);
773776
} catch (BadLocationException e) {
774777
ILog log= ILog.of(getClass());
775778
log.log(new Status(IStatus.WARNING, getClass(), IStatus.OK, null, e));
776779
}
777780
}
778781

779782
private void expandOutsideCurrentVisibleRegion(IDocument document) throws BadLocationException {
780-
if (fVisibleRegionDuringProjection != null) {
781-
expand(0, fVisibleRegionDuringProjection.getOffset(), false, true);
782-
int oldEnd= fVisibleRegionDuringProjection.getOffset() + fVisibleRegionDuringProjection.getLength();
783+
if (fConfiguredVisibleRegion != null) {
784+
expand(0, fConfiguredVisibleRegion.getOffset(), false, true);
785+
int oldEnd= fConfiguredVisibleRegion.getOffset() + fConfiguredVisibleRegion.getLength();
783786
int length= document.getLength() - oldEnd;
784787
if (length > 0) {
785788
expand(oldEnd, length, false, true);
@@ -841,16 +844,23 @@ protected void setVisibleDocument(IDocument document) {
841844

842845
@Override
843846
public void resetVisibleRegion() {
844-
super.resetVisibleRegion();
845-
if (fWasProjectionEnabled) {
846-
enableProjection();
847+
if (isProjectionMode()) {
848+
try {
849+
expandOutsideCurrentVisibleRegion(getDocument());
850+
} catch (BadLocationException e) {
851+
ILog log= ILog.of(getClass());
852+
log.log(new Status(IStatus.WARNING, getClass(), IStatus.OK, null, e));
853+
}
854+
} else {
855+
super.resetVisibleRegion();
847856
}
857+
fConfiguredVisibleRegion= null;
848858
}
849859

850860
@Override
851861
public IRegion getVisibleRegion() {
852-
if (fVisibleRegionDuringProjection != null) {
853-
return fVisibleRegionDuringProjection;
862+
if (isProjectionMode() && fConfiguredVisibleRegion != null) {
863+
return fConfiguredVisibleRegion;
854864
}
855865
IRegion visibleRegion= getModelCoverage();
856866
if (visibleRegion == null) {
@@ -862,8 +872,8 @@ public IRegion getVisibleRegion() {
862872

863873
@Override
864874
public boolean overlapsWithVisibleRegion(int offset, int length) {
865-
if (fVisibleRegionDuringProjection != null) {
866-
return TextUtilities.overlaps(fVisibleRegionDuringProjection, new Region(offset, length));
875+
if (isProjectionMode() && fConfiguredVisibleRegion != null) {
876+
return TextUtilities.overlaps(fConfiguredVisibleRegion, new Region(offset, length));
867877
}
868878
IRegion coverage= getModelCoverage();
869879
if (coverage == null) {
@@ -999,9 +1009,14 @@ private void expand(int offset, int length, boolean fireRedraw, boolean performO
9991009
}
10001010
}
10011011

1002-
private boolean overlapsWithNonVisibleRegions(int offset, int length) {
1003-
return fVisibleRegionDuringProjection != null
1004-
&& (offset < fVisibleRegionDuringProjection.getOffset() || offset + length > fVisibleRegionDuringProjection.getOffset() + fVisibleRegionDuringProjection.getLength());
1012+
private boolean overlapsWithNonVisibleRegions(int offset, int length) throws BadLocationException {
1013+
if (fConfiguredVisibleRegion == null) {
1014+
return false;
1015+
}
1016+
// ignore overlaps within the same line
1017+
int visibleRegionStartLineOffset= getDocument().getLineInformationOfOffset(fConfiguredVisibleRegion.getOffset()).getOffset();
1018+
int regionToCheckEndLineOffset= getDocument().getLineInformationOfOffset(offset + length).getOffset();
1019+
return offset < visibleRegionStartLineOffset || regionToCheckEndLineOffset > fConfiguredVisibleRegion.getOffset() + fConfiguredVisibleRegion.getLength();
10051020
}
10061021

10071022
/**
@@ -1486,7 +1501,6 @@ private boolean willAutoExpand(Position position, int offset, int length) {
14861501

14871502
@Override
14881503
protected void handleDispose() {
1489-
fWasProjectionEnabled= false;
14901504
removeDocumentUpdateListener();
14911505
super.handleDispose();
14921506
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ private void internalRemoveMasterDocumentRange(int offsetInMaster, int lengthInM
417417
if (fragment.getOffset() == offsetInMaster) {
418418
fragment.setOffset(offsetInMaster + lengthInMaster);
419419
fragment.setLength(fragment.getLength() - lengthInMaster);
420+
if (fragment.getLength() == 0 && offsetInMaster != 0 && offsetInMaster + lengthInMaster == getMasterDocument().getLength()) {
421+
fragment.setOffset(offsetInMaster);
422+
}
420423
} else {
421424
// split fragment into three fragments, let position updater remove it
422425

tests/org.eclipse.text.tests/projection/org/eclipse/text/tests/ProjectionDocumentTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.junit.Assert;
2828
import org.junit.Before;
2929
import org.junit.Test;
30+
import org.junit.jupiter.api.Assertions;
3031

3132
import org.eclipse.jface.text.BadLocationException;
3233
import org.eclipse.jface.text.DefaultLineTracker;
@@ -2652,4 +2653,39 @@ public void test29_38() {
26522653
assertTrue(false);
26532654
}
26542655
}
2656+
2657+
@Test
2658+
public void testFullDocumentRangeRemovedAtOnce() throws BadLocationException {
2659+
fSlaveDocument.addMasterDocumentRange(0, fMasterDocument.getLength());
2660+
fSlaveDocument.removeMasterDocumentRange(0, fMasterDocument.getLength());
2661+
Assertions.assertEquals("", fSlaveDocument.get());
2662+
Position[] fragments = fSlaveDocument.getFragments2();
2663+
Assertions.assertEquals(1, fragments.length);
2664+
Assertions.assertEquals(new Position(fMasterDocument.getLength(), 0), fragments[0]);
2665+
}
2666+
2667+
@Test
2668+
public void testFullDocumentRangeRemovedInTwoParts() throws BadLocationException {
2669+
fSlaveDocument.addMasterDocumentRange(0, fMasterDocument.getLength());
2670+
fSlaveDocument.removeMasterDocumentRange(0, 10);
2671+
Assertions.assertEquals(fMasterDocument.getLength() - 10, fSlaveDocument.getLength());
2672+
fSlaveDocument.removeMasterDocumentRange(10, fMasterDocument.getLength() - 10);
2673+
Assertions.assertEquals("", fSlaveDocument.get());
2674+
Position[] fragments = fSlaveDocument.getFragments2();
2675+
Assertions.assertEquals(1, fragments.length);
2676+
Assertions.assertEquals(new Position(10, 0), fragments[0]);
2677+
}
2678+
2679+
@Test
2680+
public void testRemovePartsOfDocument() throws BadLocationException {
2681+
fSlaveDocument.addMasterDocumentRange(0, fMasterDocument.getLength());
2682+
fSlaveDocument.removeMasterDocumentRange(0, 10);
2683+
Assertions.assertEquals(fMasterDocument.getLength() - 10, fSlaveDocument.getLength());
2684+
fSlaveDocument.removeMasterDocumentRange(10, fMasterDocument.getLength() - 20);
2685+
Assertions.assertEquals(fMasterDocument.get(fMasterDocument.getLength() - 10, 10),
2686+
fSlaveDocument.get());
2687+
Position[] fragments = fSlaveDocument.getFragments2();
2688+
Assertions.assertEquals(1, fragments.length);
2689+
Assertions.assertEquals(new Position(fMasterDocument.getLength() - 10, 10), fragments[0]);
2690+
}
26552691
}

0 commit comments

Comments
 (0)