Skip to content

Commit 88d2623

Browse files
committed
Allow using visible regions with projections
1 parent a4840da commit 88d2623

File tree

2 files changed

+171
-3
lines changed

2 files changed

+171
-3
lines changed

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

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
import org.eclipse.swt.dnd.TextTransfer;
2929
import org.eclipse.swt.dnd.Transfer;
3030
import org.eclipse.swt.events.VerifyEvent;
31+
import org.eclipse.swt.graphics.GC;
3132
import org.eclipse.swt.graphics.Point;
33+
import org.eclipse.swt.graphics.Rectangle;
34+
import org.eclipse.swt.widgets.Canvas;
3235
import org.eclipse.swt.widgets.Composite;
3336
import org.eclipse.swt.widgets.Display;
3437

@@ -272,6 +275,43 @@ private void computeExpectedExecutionCosts() {
272275
}
273276
}
274277

278+
/**
279+
* A {@link ProjectionAnnotation} that is always collapsed and invisible.
280+
*/
281+
private static class InvisibleCollapsedProjectionAnnotation extends ProjectionAnnotation {
282+
public InvisibleCollapsedProjectionAnnotation() {
283+
super(true);
284+
}
285+
286+
@Override
287+
public void paint(GC gc, Canvas canvas, Rectangle rectangle) {
288+
}
289+
}
290+
291+
/**
292+
* An {@link IProjectionPosition} that includes hiding the offset and length.
293+
*/
294+
private static class ExactRegionProjectionPosition extends Position implements IProjectionPosition {
295+
296+
public ExactRegionProjectionPosition(int offset, int length) {
297+
super(offset, length);
298+
}
299+
300+
@Override
301+
public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException {
302+
return new IRegion[] {
303+
new Region(getOffset(), getLength())
304+
};
305+
}
306+
307+
@Override
308+
public int computeCaptionOffset(IDocument document) throws BadLocationException {
309+
return 0;
310+
}
311+
312+
}
313+
314+
275315
/** The projection annotation model used by this viewer. */
276316
private ProjectionAnnotationModel fProjectionAnnotationModel;
277317
/** The annotation model listener */
@@ -292,6 +332,11 @@ private void computeExpectedExecutionCosts() {
292332
private IDocument fReplaceVisibleDocumentExecutionTrigger;
293333
/** <code>true</code> if projection was on the last time we switched to segmented mode. */
294334
private boolean fWasProjectionEnabled;
335+
/**
336+
* The region set by {@link #setVisibleRegion(int, int)} during projection or <code>null</code>
337+
* if not in a projection
338+
*/
339+
private IRegion fVisibleRegionDuringProjection;
295340
/** The queue of projection commands used to assess the costs of projection changes. */
296341
private ProjectionCommandQueue fCommandQueue;
297342
/**
@@ -510,6 +555,10 @@ public final void disableProjection() {
510555
fProjectionAnnotationModel.removeAllAnnotations();
511556
fFindReplaceDocumentAdapter= null;
512557
fireProjectionDisabled();
558+
if (fVisibleRegionDuringProjection != null) {
559+
super.setVisibleRegion(fVisibleRegionDuringProjection.getOffset(), fVisibleRegionDuringProjection.getLength());
560+
fVisibleRegionDuringProjection= null;
561+
}
513562
}
514563
}
515564

@@ -518,9 +567,13 @@ public final void disableProjection() {
518567
*/
519568
public final void enableProjection() {
520569
if (!isProjectionMode()) {
570+
IRegion visibleRegion= getVisibleRegion();
521571
addProjectionAnnotationModel(getVisualAnnotationModel());
522572
fFindReplaceDocumentAdapter= null;
523573
fireProjectionEnabled();
574+
if (visibleRegion != null) {
575+
setVisibleRegion(visibleRegion.getOffset(), visibleRegion.getLength());
576+
}
524577
}
525578
}
526579

@@ -683,9 +736,24 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
683736

684737
@Override
685738
public void setVisibleRegion(int start, int length) {
686-
fWasProjectionEnabled= isProjectionMode();
687-
disableProjection();
688-
super.setVisibleRegion(start, length);
739+
if (isProjectionMode()) {
740+
for (Iterator<Annotation> annotationIterator= fProjectionAnnotationModel.getAnnotationIterator(); annotationIterator.hasNext();) {
741+
Annotation ann= annotationIterator.next();
742+
if (ann instanceof InvisibleCollapsedProjectionAnnotation) {
743+
fProjectionAnnotationModel.removeAnnotation(ann);
744+
}
745+
}
746+
if (start > 0) {
747+
fProjectionAnnotationModel.addAnnotation(new InvisibleCollapsedProjectionAnnotation(), new ExactRegionProjectionPosition(0, start));
748+
}
749+
int regionEnd= start + length;
750+
if (regionEnd < getDocument().getLength()) {
751+
fProjectionAnnotationModel.addAnnotation(new InvisibleCollapsedProjectionAnnotation(), new Position(regionEnd, getDocument().getLength() - regionEnd));
752+
}
753+
fVisibleRegionDuringProjection= new Region(start, length);
754+
} else {
755+
super.setVisibleRegion(start, length);
756+
}
689757
}
690758

691759
@Override
@@ -710,6 +778,9 @@ public void resetVisibleRegion() {
710778

711779
@Override
712780
public IRegion getVisibleRegion() {
781+
if (fVisibleRegionDuringProjection != null) {
782+
return fVisibleRegionDuringProjection;
783+
}
713784
disableProjection();
714785
IRegion visibleRegion= getModelCoverage();
715786
if (visibleRegion == null)

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

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
package org.eclipse.jface.text.tests;
1212

1313
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertTrue;
15+
16+
import java.util.Iterator;
1417

1518
import org.junit.Test;
1619

@@ -28,6 +31,7 @@
2831
import org.eclipse.jface.text.ITextSelection;
2932
import org.eclipse.jface.text.Position;
3033
import org.eclipse.jface.text.Region;
34+
import org.eclipse.jface.text.source.Annotation;
3135
import org.eclipse.jface.text.source.AnnotationModel;
3236
import org.eclipse.jface.text.source.projection.IProjectionPosition;
3337
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
@@ -75,4 +79,97 @@ public void testCopyPaste() {
7579
shell.dispose();
7680
}
7781
}
82+
83+
@Test
84+
public void testVisibleRegionDoesNotChangeWithProjections() {
85+
Shell shell= new Shell();
86+
shell.setLayout(new FillLayout());
87+
ProjectionViewer viewer= new ProjectionViewer(shell, null, null, false, SWT.NONE);
88+
String documentContent= """
89+
Hello
90+
World
91+
123
92+
456
93+
""";
94+
Document document= new Document(documentContent);
95+
viewer.setDocument(document, new AnnotationModel());
96+
int regionLength= documentContent.indexOf('\n');
97+
viewer.setVisibleRegion(0, regionLength);
98+
viewer.enableProjection();
99+
viewer.getProjectionAnnotationModel().addAnnotation(new ProjectionAnnotation(false), new ProjectionPosition(document));
100+
shell.setVisible(true);
101+
try {
102+
assertEquals(0, viewer.getVisibleRegion().getOffset());
103+
assertEquals(regionLength, viewer.getVisibleRegion().getLength());
104+
105+
viewer.getTextOperationTarget().doOperation(ProjectionViewer.COLLAPSE_ALL);
106+
assertEquals(0, viewer.getVisibleRegion().getOffset());
107+
assertEquals(regionLength, viewer.getVisibleRegion().getLength());
108+
} finally {
109+
shell.dispose();
110+
}
111+
}
112+
113+
@Test
114+
public void testVisibleRegionAddsProjectionAnnotationsIfProjectionsEnabled() {
115+
testProjectionAnnotationsFromVisibleRegion(true);
116+
}
117+
118+
@Test
119+
public void testEnableProjectionAddsProjectionAnnotationsIfVisibleRegionEnabled() {
120+
testProjectionAnnotationsFromVisibleRegion(false);
121+
}
122+
123+
private void testProjectionAnnotationsFromVisibleRegion(boolean enableProjectionFirst) {
124+
Shell shell= new Shell();
125+
shell.setLayout(new FillLayout());
126+
ProjectionViewer viewer= new ProjectionViewer(shell, null, null, false, SWT.NONE);
127+
String documentContent= """
128+
Hello
129+
World
130+
123
131+
456
132+
""";
133+
Document document= new Document(documentContent);
134+
viewer.setDocument(document, new AnnotationModel());
135+
int secondLineStart= documentContent.indexOf("World");
136+
int secondLineEnd= documentContent.indexOf('\n', secondLineStart);
137+
138+
shell.setVisible(true);
139+
if (enableProjectionFirst) {
140+
viewer.enableProjection();
141+
viewer.setVisibleRegion(secondLineStart, secondLineEnd - secondLineStart);
142+
} else {
143+
viewer.setVisibleRegion(secondLineStart, secondLineEnd - secondLineStart);
144+
viewer.enableProjection();
145+
}
146+
147+
boolean startAnnotationFound= false;
148+
boolean endAnnotationFound= false;
149+
int annotationCount= 0;
150+
151+
try {
152+
for (Iterator<Annotation> it= viewer.getProjectionAnnotationModel().getAnnotationIterator(); it.hasNext();) {
153+
Annotation annotation= it.next();
154+
assertEquals("org.eclipse.jface.text.source.projection.ProjectionViewer.InvisibleCollapsedProjectionAnnotation", annotation.getClass().getCanonicalName());
155+
Position position= viewer.getProjectionAnnotationModel().getPosition(annotation);
156+
157+
if (position.getOffset() == 0) {
158+
assertEquals("org.eclipse.jface.text.source.projection.ProjectionViewer.ExactRegionProjectionPosition", position.getClass().getCanonicalName());
159+
assertEquals("Hello\n".length(), position.getLength());
160+
startAnnotationFound= true;
161+
} else {
162+
assertEquals(secondLineEnd, position.getOffset());
163+
assertEquals(9, position.getLength());
164+
endAnnotationFound= true;
165+
}
166+
annotationCount++;
167+
}
168+
assertEquals(2, annotationCount);
169+
assertTrue(startAnnotationFound);
170+
assertTrue(endAnnotationFound);
171+
} finally {
172+
shell.dispose();
173+
}
174+
}
78175
}

0 commit comments

Comments
 (0)