Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public void run() {
/** Indicates whether this annotation should be painted as range */
private boolean fIsRangeIndication= false;

private boolean hidden= false;

/**
* Creates a new expanded projection annotation.
*/
Expand Down Expand Up @@ -115,6 +117,9 @@ private void drawRangeIndication(GC gc, Canvas canvas, Rectangle r) {

@Override
public void paint(GC gc, Canvas canvas, Rectangle rectangle) {
if (hidden) {
return;
}
Image image= getImage(canvas.getDisplay());
if (image != null) {
ImageUtilities.drawImage(image, gc, canvas, rectangle, SWT.CENTER, SWT.TOP);
Expand All @@ -128,6 +133,10 @@ public void paint(GC gc, Canvas canvas, Rectangle rectangle) {
}
}

void setHidden(boolean hidden) {
this.hidden= hidden;
}

@Override
public int getLayer() {
return IAnnotationPresentation.DEFAULT_LAYER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ private void processModelChanged(IAnnotationModel model, AnnotationModelEvent ev
fProjectionSummary.updateSummaries();
}
processCatchupRequest(event);
correctChangedAnnotationVisibility(event);

} else if (model == getAnnotationModel() && fProjectionSummary != null) {
fProjectionSummary.updateSummaries();
Expand Down Expand Up @@ -770,15 +771,46 @@ public void setVisibleRegion(int start, int length) {
// If the visible region changes, make sure collapsed regions outside of the old visible regions are expanded
// and collapse everything outside the new visible region
int end= computeEndOfVisibleRegion(start, length, document);
Region newVisibleRegion= new Region(start, end - start - 1);
expandProjectionAnnotationsBorderingRegion(newVisibleRegion);
expandOutsideCurrentVisibleRegion(document);
collapseOutsideOfNewVisibleRegion(start, end, document);
fConfiguredVisibleRegion= new Region(start, end - start - 1);
fConfiguredVisibleRegion= newVisibleRegion;
hideProjectionAnnotationsOutsideOfVisibleRegion();
} catch (BadLocationException e) {
ILog log= ILog.of(getClass());
log.log(new Status(IStatus.WARNING, getClass(), IStatus.OK, null, e));
}
}

private void expandProjectionAnnotationsBorderingRegion(Region region) throws BadLocationException {
for (Iterator<Annotation> it= fProjectionAnnotationModel.getAnnotationIterator(); it.hasNext();) {
Annotation annotation= it.next();
Position position= fProjectionAnnotationModel.getPosition(annotation);
if (bordersOrSurroundsRegion(position, region)) {
fProjectionAnnotationModel.expand(annotation);
}
}
}

private void hideProjectionAnnotationsOutsideOfVisibleRegion() throws BadLocationException {
for (Iterator<Annotation> it= fProjectionAnnotationModel.getAnnotationIterator(); it.hasNext();) {
Annotation annotation= it.next();
hideProjectionAnnotationIfPartsAreOutsideOfVisibleRegion(annotation);
}
}

private void hideProjectionAnnotationIfPartsAreOutsideOfVisibleRegion(Annotation annotation) throws BadLocationException {
Position position= fProjectionAnnotationModel.getPosition(annotation);
if (annotation instanceof ProjectionAnnotation a) {
if (overlapsWithNonVisibleRegions(position.getOffset(), position.getLength())) {
a.setHidden(true);
} else {
a.setHidden(false);
}
}
}

private void expandOutsideCurrentVisibleRegion(IDocument document) throws BadLocationException {
if (fConfiguredVisibleRegion != null) {
expand(0, fConfiguredVisibleRegion.getOffset(), false, true);
Expand Down Expand Up @@ -855,6 +887,12 @@ public void resetVisibleRegion() {
super.resetVisibleRegion();
}
fConfiguredVisibleRegion= null;
for (Iterator<Annotation> it= fProjectionAnnotationModel.getAnnotationIterator(); it.hasNext();) {
Annotation annotation= it.next();
if (annotation instanceof ProjectionAnnotation a) {
a.setHidden(false);
}
}
}

@Override
Expand Down Expand Up @@ -1014,11 +1052,25 @@ private boolean overlapsWithNonVisibleRegions(int offset, int length) throws Bad
return false;
}
// ignore overlaps within the same line
int visibleRegionStartLineOffset= getDocument().getLineInformationOfOffset(fConfiguredVisibleRegion.getOffset()).getOffset();
int regionToCheckEndLineOffset= getDocument().getLineInformationOfOffset(offset + length).getOffset();
int visibleRegionStartLineOffset= atStartOfLine(fConfiguredVisibleRegion.getOffset());
int regionToCheckEndLineOffset= atStartOfLine(offset + length);
return offset < visibleRegionStartLineOffset || regionToCheckEndLineOffset > fConfiguredVisibleRegion.getOffset() + fConfiguredVisibleRegion.getLength();
}


private boolean bordersOrSurroundsRegion(Position position, Region region) throws BadLocationException {
if (atStartOfLine(position.getOffset()) <= region.getOffset() + region.getLength()
&& atStartOfLine(position.getOffset() + position.length) >= region.getOffset() + region.getLength()) {
return true;
}
return atStartOfLine(position.getOffset()) <= region.getOffset()
&& position.getOffset() + position.getLength() > atStartOfLine(region.getOffset());
}

private int atStartOfLine(int off) throws BadLocationException {
return getDocument().getLineInformationOfOffset(off).getOffset();
}

/**
* Processes the request for catch up with the annotation model in the UI thread. If the current
* thread is not the UI thread or there are pending catch up requests, a new request is posted.
Expand Down Expand Up @@ -1090,6 +1142,20 @@ protected final void postCatchupRequest(final AnnotationModelEvent event) {
}
}

private void correctChangedAnnotationVisibility(AnnotationModelEvent event) {
try {
for (Annotation annotation : event.getAddedAnnotations()) {
hideProjectionAnnotationIfPartsAreOutsideOfVisibleRegion(annotation);
}
for (Annotation annotation : event.getChangedAnnotations()) {
hideProjectionAnnotationIfPartsAreOutsideOfVisibleRegion(annotation);
}
} catch (BadLocationException e) {
ILog log= ILog.of(getClass());
log.log(new Status(IStatus.WARNING, getClass(), IStatus.OK, null, e));
}
}

/**
* Tests whether the visible document's master document
* is identical to this viewer's document.
Expand Down
1 change: 1 addition & 0 deletions tests/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Import-Package: org.mockito,
org.mockito.invocation,
org.mockito.stubbing,
org.junit.jupiter.api;version="[5.14.0,6.0.0)",
org.junit.jupiter.api.function;version="[5.14.0,6.0.0)",
org.junit.jupiter.params;version="[5.14.0,6.0.0)",
org.junit.jupiter.params.provider;version="[5.14.0,6.0.0)",
org.junit.platform.suite.api;version="[1.14.0,2.0.0)"
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
*******************************************************************************/
package org.eclipse.jface.text.tests;

import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

Expand All @@ -19,6 +22,7 @@
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import org.eclipse.jface.text.BadLocationException;
Expand Down Expand Up @@ -368,4 +372,148 @@ public void testRemoveEntireVisibleRegion() throws BadLocationException {
shell.dispose();
}
}

@Test
public void testSetVisibleRegionDoesNotExpandOutsideProjectionRegions() {
Shell shell= new Shell();
shell.setLayout(new FillLayout());
TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, false, SWT.NONE);
String documentContent= """
Hello
World
abc
123
456
789
""";
Document document= new Document(documentContent);
viewer.setDocument(document, new AnnotationModel());
viewer.enableProjection();
ProjectionAnnotation firstAnnotation= new ProjectionAnnotation(true);
ProjectionAnnotation secondAnnotation= new ProjectionAnnotation(true);
viewer.getProjectionAnnotationModel().addAnnotation(firstAnnotation, new Position(0, documentContent.indexOf("World")));
viewer.getProjectionAnnotationModel().addAnnotation(secondAnnotation, new Position(documentContent.indexOf("456"), documentContent.length() - documentContent.indexOf("456")));

viewer.setVisibleRegion(documentContent.indexOf("abc"), documentContent.indexOf("123") - documentContent.indexOf("abc"));
shell.setVisible(true);
try {
assertTrue(firstAnnotation.isCollapsed());
assertTrue(secondAnnotation.isCollapsed());
} finally {
shell.dispose();
}
}

@Test
public void testSetVisibleRegionExpandsBorderingProjectionRegions() {
Shell shell= new Shell();
shell.setLayout(new FillLayout());
TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, false, SWT.NONE);
String documentContent= """
Hello
World
123
456
""";
Document document= new Document(documentContent);
viewer.setDocument(document, new AnnotationModel());
viewer.enableProjection();
ProjectionAnnotation firstAnnotation= new ProjectionAnnotation(true);
ProjectionAnnotation secondAnnotation= new ProjectionAnnotation(true);
viewer.getProjectionAnnotationModel().addAnnotation(firstAnnotation, new Position(0, documentContent.indexOf("123")));
viewer.getProjectionAnnotationModel().addAnnotation(secondAnnotation, new Position(documentContent.indexOf("123"), documentContent.length() - documentContent.indexOf("123")));

viewer.setVisibleRegion(documentContent.indexOf("World"), documentContent.indexOf("456") - documentContent.indexOf("World"));
shell.setVisible(true);
try {
assertFalse(firstAnnotation.isCollapsed());
assertFalse(secondAnnotation.isCollapsed());
} finally {
shell.dispose();
}
}

@Test
public void testProjectionRegionsShownOnlyInVisibleRegion() {
Shell shell= new Shell(Display.getCurrent());
shell.setLayout(new FillLayout());
TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, true, SWT.ALL);
String documentContent= """

visible_region_start

projection_start

visible_region_end

projection_end

""";
Document document= new Document(documentContent);
viewer.setDocument(document, new AnnotationModel());
ProjectionAnnotation annotation= addVisibleRegionAndProjection(viewer, documentContent);
try {
assertEquals("""
visible_region_start

projection_start

visible_region_end
""", viewer.getVisibleDocument().get());

annotation.paint(null, null, null); //should exit early and not throw NPE
} finally {
shell.dispose();
}
}

@Test
public void testProjectionRegionsShownWithinVisibleRegion() {
Shell shell= new Shell(Display.getCurrent());
shell.setLayout(new FillLayout());
TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, true, SWT.ALL);
String documentContent= """

visible_region_start

projection_start

projection_end

visible_region_end

""";
Document document= new Document(documentContent);
viewer.setDocument(document, new AnnotationModel());
ProjectionAnnotation annotation= addVisibleRegionAndProjection(viewer, documentContent);
try {
assertEquals("""
visible_region_start

projection_start

projection_end

visible_region_end
""", viewer.getVisibleDocument().get());

assertThrows(NullPointerException.class, () -> annotation.paint(null, null, null), "expected to run painting logic");
} finally {
shell.dispose();
}
}

private ProjectionAnnotation addVisibleRegionAndProjection(TestProjectionViewer viewer, String documentContent) {
int visibleRegionStart= documentContent.indexOf("visible_region_start");
int visibleRegionEnd= documentContent.indexOf("\n", documentContent.indexOf("visible_region_end")) + 1;

int projectionStart= documentContent.indexOf("projection_start");
int projectionEnd= documentContent.indexOf("\n", documentContent.indexOf("projection_end")) + 1;

viewer.setVisibleRegion(visibleRegionStart, visibleRegionEnd - visibleRegionStart);
viewer.enableProjection();
ProjectionAnnotation annotation= new ProjectionAnnotation();
viewer.getProjectionAnnotationModel().addAnnotation(annotation, new Position(projectionStart, projectionEnd - projectionStart));
return annotation;
}
}
Loading