Skip to content

Commit 7ff50f2

Browse files
committed
Allow using visible regions with projections #3073
While ProjectionViewer supports both using visible regions and projections, these features cannot be used in conjunction. This change allows the use of projections when visible regions are used. Fixes #3074
1 parent d2177cf commit 7ff50f2

File tree

2 files changed

+293
-3
lines changed

2 files changed

+293
-3
lines changed

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

Lines changed: 117 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

@@ -99,6 +102,7 @@ public class ProjectionViewer extends SourceViewer implements ITextViewerExtensi
99102
*/
100103
public static final int COLLAPSE_ALL= BASE + 5;
101104

105+
102106
/**
103107
* Internal listener to changes of the annotation model.
104108
*/
@@ -272,6 +276,77 @@ private void computeExpectedExecutionCosts() {
272276
}
273277
}
274278

279+
/**
280+
* A {@link ProjectionAnnotation} that is always collapsed and invisible.
281+
*/
282+
private static class InvisibleCollapsedProjectionAnnotation extends ProjectionAnnotation {
283+
public InvisibleCollapsedProjectionAnnotation() {
284+
super(true);
285+
}
286+
287+
@Override
288+
public void paint(GC gc, Canvas canvas, Rectangle rectangle) {
289+
}
290+
}
291+
292+
/**
293+
* An {@link IProjectionPosition} that includes hiding the offset and length.
294+
*/
295+
private static class ExactRegionProjectionPosition extends Position implements IProjectionPosition {
296+
297+
public ExactRegionProjectionPosition(int offset, int length) {
298+
super(offset, length);
299+
}
300+
301+
@Override
302+
public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException {
303+
return new IRegion[] {
304+
new Region(getOffset(), getLength())
305+
};
306+
}
307+
308+
@Override
309+
public int computeCaptionOffset(IDocument document) throws BadLocationException {
310+
return 0;
311+
}
312+
313+
}
314+
315+
/**
316+
* An {@link IDocumentListener} that makes sure that {@link #fVisibleRegionDuringProjection} is
317+
* updated when the document changes and ensures that the collapsed region after the visible
318+
* region is recreated appropriately.
319+
*/
320+
private final class UpdateDocumentListener implements IDocumentListener {
321+
@Override
322+
public void documentChanged(DocumentEvent event) {
323+
if (fVisibleRegionDuringProjection != null) {
324+
int oldLength= event.getLength();
325+
int newLength= event.getText().length();
326+
int oldVisibleRegionEnd= fVisibleRegionDuringProjection.getOffset() + fVisibleRegionDuringProjection.getLength();
327+
328+
if (oldVisibleRegionEnd >= event.getOffset() && oldVisibleRegionEnd <= event.getOffset() + event.getLength()) {
329+
// If the end of the visible region is modified, the projection annotation needs to be recreated
330+
int newVisibleRegionEnd= event.getOffset() + newLength;
331+
fProjectionAnnotationModel.addAnnotation(new InvisibleCollapsedProjectionAnnotation(), new Position(newVisibleRegionEnd, getDocument().getLength() - newVisibleRegionEnd));
332+
fVisibleRegionDuringProjection= new Region(fVisibleRegionDuringProjection.getOffset(), newVisibleRegionEnd - fVisibleRegionDuringProjection.getOffset());
333+
}
334+
335+
if (event.getOffset() < fVisibleRegionDuringProjection.getOffset()) {
336+
fVisibleRegionDuringProjection= new Region(fVisibleRegionDuringProjection.getOffset() + newLength - oldLength, fVisibleRegionDuringProjection.getLength());
337+
} else {
338+
if (event.getOffset() + oldLength < oldVisibleRegionEnd) {
339+
fVisibleRegionDuringProjection= new Region(fVisibleRegionDuringProjection.getOffset(), fVisibleRegionDuringProjection.getLength() + newLength - oldLength);
340+
}
341+
}
342+
}
343+
}
344+
345+
@Override
346+
public void documentAboutToBeChanged(DocumentEvent event) {
347+
}
348+
}
349+
275350
/** The projection annotation model used by this viewer. */
276351
private ProjectionAnnotationModel fProjectionAnnotationModel;
277352
/** The annotation model listener */
@@ -292,6 +367,11 @@ private void computeExpectedExecutionCosts() {
292367
private IDocument fReplaceVisibleDocumentExecutionTrigger;
293368
/** <code>true</code> if projection was on the last time we switched to segmented mode. */
294369
private boolean fWasProjectionEnabled;
370+
/**
371+
* The region set by {@link #setVisibleRegion(int, int)} during projection or <code>null</code>
372+
* if not in a projection
373+
*/
374+
private IRegion fVisibleRegionDuringProjection;
295375
/** The queue of projection commands used to assess the costs of projection changes. */
296376
private ProjectionCommandQueue fCommandQueue;
297377
/**
@@ -301,6 +381,8 @@ private void computeExpectedExecutionCosts() {
301381
*/
302382
private int fDeletedLines;
303383

384+
private UpdateDocumentListener fUpdateDocumentListener= new UpdateDocumentListener();
385+
304386

305387
/**
306388
* Creates a new projection source viewer.
@@ -510,6 +592,11 @@ public final void disableProjection() {
510592
fProjectionAnnotationModel.removeAllAnnotations();
511593
fFindReplaceDocumentAdapter= null;
512594
fireProjectionDisabled();
595+
if (fVisibleRegionDuringProjection != null) {
596+
super.setVisibleRegion(fVisibleRegionDuringProjection.getOffset(), fVisibleRegionDuringProjection.getLength());
597+
fVisibleRegionDuringProjection= null;
598+
}
599+
getDocument().removeDocumentListener(fUpdateDocumentListener);
513600
}
514601
}
515602

@@ -518,9 +605,14 @@ public final void disableProjection() {
518605
*/
519606
public final void enableProjection() {
520607
if (!isProjectionMode()) {
608+
IRegion visibleRegion= getVisibleRegion();
521609
addProjectionAnnotationModel(getVisualAnnotationModel());
522610
fFindReplaceDocumentAdapter= null;
523611
fireProjectionEnabled();
612+
if (visibleRegion != null && (visibleRegion.getOffset() != 0 || visibleRegion.getLength() != 0)) {
613+
setVisibleRegion(visibleRegion.getOffset(), visibleRegion.getLength());
614+
}
615+
getDocument().addDocumentListener(fUpdateDocumentListener);
524616
}
525617
}
526618

@@ -529,6 +621,10 @@ private void expandAll() {
529621
IDocument doc= getDocument();
530622
int length= doc == null ? 0 : doc.getLength();
531623
if (isProjectionMode()) {
624+
if (fVisibleRegionDuringProjection != null) {
625+
offset= fVisibleRegionDuringProjection.getOffset();
626+
length= fVisibleRegionDuringProjection.getLength();
627+
}
532628
fProjectionAnnotationModel.expandAll(offset, length);
533629
}
534630
}
@@ -683,9 +779,24 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
683779

684780
@Override
685781
public void setVisibleRegion(int start, int length) {
686-
fWasProjectionEnabled= isProjectionMode();
687-
disableProjection();
688-
super.setVisibleRegion(start, length);
782+
if (isProjectionMode()) {
783+
for (Iterator<Annotation> annotationIterator= fProjectionAnnotationModel.getAnnotationIterator(); annotationIterator.hasNext();) {
784+
Annotation ann= annotationIterator.next();
785+
if (ann instanceof InvisibleCollapsedProjectionAnnotation) {
786+
fProjectionAnnotationModel.removeAnnotation(ann);
787+
}
788+
}
789+
if (start > 0) {
790+
fProjectionAnnotationModel.addAnnotation(new InvisibleCollapsedProjectionAnnotation(), new ExactRegionProjectionPosition(0, start));
791+
}
792+
int regionEnd= start + length + 2;
793+
if (regionEnd < getDocument().getLength()) {
794+
fProjectionAnnotationModel.addAnnotation(new InvisibleCollapsedProjectionAnnotation(), new ExactRegionProjectionPosition(regionEnd, getDocument().getLength() - regionEnd));
795+
}
796+
fVisibleRegionDuringProjection= new Region(start, length);
797+
} else {
798+
super.setVisibleRegion(start, length);
799+
}
689800
}
690801

691802
@Override
@@ -710,6 +821,9 @@ public void resetVisibleRegion() {
710821

711822
@Override
712823
public IRegion getVisibleRegion() {
824+
if (fVisibleRegionDuringProjection != null) {
825+
return fVisibleRegionDuringProjection;
826+
}
713827
disableProjection();
714828
IRegion visibleRegion= getModelCoverage();
715829
if (visibleRegion == null)

0 commit comments

Comments
 (0)