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 #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)