2828import org .eclipse .swt .dnd .TextTransfer ;
2929import org .eclipse .swt .dnd .Transfer ;
3030import org .eclipse .swt .events .VerifyEvent ;
31+ import org .eclipse .swt .graphics .GC ;
3132import org .eclipse .swt .graphics .Point ;
33+ import org .eclipse .swt .graphics .Rectangle ;
34+ import org .eclipse .swt .widgets .Canvas ;
3235import org .eclipse .swt .widgets .Composite ;
3336import 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