@@ -99,6 +99,7 @@ public class ProjectionViewer extends SourceViewer implements ITextViewerExtensi
99
99
*/
100
100
public static final int COLLAPSE_ALL = BASE + 5 ;
101
101
102
+
102
103
/**
103
104
* Internal listener to changes of the annotation model.
104
105
*/
@@ -272,6 +273,34 @@ private void computeExpectedExecutionCosts() {
272
273
}
273
274
}
274
275
276
+ /**
277
+ * An {@link IDocumentListener} that makes sure that {@link #fVisibleRegionDuringProjection} is
278
+ * updated when the document changes and ensures that the collapsed region after the visible
279
+ * region is recreated appropriately.
280
+ */
281
+ private final class UpdateDocumentListener implements IDocumentListener {
282
+ @ Override
283
+ public void documentChanged (DocumentEvent event ) {
284
+ if (fVisibleRegionDuringProjection != null ) {
285
+ int oldLength = event .getLength ();
286
+ int newLength = event .getText ().length ();
287
+ int oldVisibleRegionEnd = fVisibleRegionDuringProjection .getOffset () + fVisibleRegionDuringProjection .getLength ();
288
+
289
+ if (event .getOffset () < fVisibleRegionDuringProjection .getOffset ()) {
290
+ fVisibleRegionDuringProjection = new Region (fVisibleRegionDuringProjection .getOffset () + newLength - oldLength , fVisibleRegionDuringProjection .getLength ());
291
+ } else {
292
+ if (event .getOffset () + oldLength < oldVisibleRegionEnd ) {
293
+ fVisibleRegionDuringProjection = new Region (fVisibleRegionDuringProjection .getOffset (), fVisibleRegionDuringProjection .getLength () + newLength - oldLength );
294
+ }
295
+ }
296
+ }
297
+ }
298
+
299
+ @ Override
300
+ public void documentAboutToBeChanged (DocumentEvent event ) {
301
+ }
302
+ }
303
+
275
304
/** The projection annotation model used by this viewer. */
276
305
private ProjectionAnnotationModel fProjectionAnnotationModel ;
277
306
/** The annotation model listener */
@@ -292,6 +321,11 @@ private void computeExpectedExecutionCosts() {
292
321
private IDocument fReplaceVisibleDocumentExecutionTrigger ;
293
322
/** <code>true</code> if projection was on the last time we switched to segmented mode. */
294
323
private boolean fWasProjectionEnabled ;
324
+ /**
325
+ * The region set by {@link #setVisibleRegion(int, int)} during projection or <code>null</code>
326
+ * if not in a projection
327
+ */
328
+ private IRegion fVisibleRegionDuringProjection ;
295
329
/** The queue of projection commands used to assess the costs of projection changes. */
296
330
private ProjectionCommandQueue fCommandQueue ;
297
331
/**
@@ -301,6 +335,8 @@ private void computeExpectedExecutionCosts() {
301
335
*/
302
336
private int fDeletedLines ;
303
337
338
+ private UpdateDocumentListener fUpdateDocumentListener = new UpdateDocumentListener ();
339
+
304
340
305
341
/**
306
342
* Creates a new projection source viewer.
@@ -510,6 +546,11 @@ public final void disableProjection() {
510
546
fProjectionAnnotationModel .removeAllAnnotations ();
511
547
fFindReplaceDocumentAdapter = null ;
512
548
fireProjectionDisabled ();
549
+ if (fVisibleRegionDuringProjection != null ) {
550
+ super .setVisibleRegion (fVisibleRegionDuringProjection .getOffset (), fVisibleRegionDuringProjection .getLength ());
551
+ fVisibleRegionDuringProjection = null ;
552
+ }
553
+ getDocument ().removeDocumentListener (fUpdateDocumentListener );
513
554
}
514
555
}
515
556
@@ -518,9 +559,14 @@ public final void disableProjection() {
518
559
*/
519
560
public final void enableProjection () {
520
561
if (!isProjectionMode ()) {
562
+ IRegion visibleRegion = getVisibleRegion ();
521
563
addProjectionAnnotationModel (getVisualAnnotationModel ());
522
564
fFindReplaceDocumentAdapter = null ;
523
565
fireProjectionEnabled ();
566
+ if (visibleRegion != null && (visibleRegion .getOffset () != 0 || visibleRegion .getLength () != 0 ) && visibleRegion .getLength () < getDocument ().getLength ()) {
567
+ setVisibleRegion (visibleRegion .getOffset (), visibleRegion .getLength ());
568
+ }
569
+ getDocument ().addDocumentListener (fUpdateDocumentListener );
524
570
}
525
571
}
526
572
@@ -529,6 +575,10 @@ private void expandAll() {
529
575
IDocument doc = getDocument ();
530
576
int length = doc == null ? 0 : doc .getLength ();
531
577
if (isProjectionMode ()) {
578
+ if (fVisibleRegionDuringProjection != null ) {
579
+ offset = fVisibleRegionDuringProjection .getOffset ();
580
+ length = fVisibleRegionDuringProjection .getLength ();
581
+ }
532
582
fProjectionAnnotationModel .expandAll (offset , length );
533
583
}
534
584
}
@@ -683,9 +733,48 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
683
733
684
734
@ Override
685
735
public void setVisibleRegion (int start , int length ) {
686
- fWasProjectionEnabled = isProjectionMode ();
687
- disableProjection ();
688
- super .setVisibleRegion (start , length );
736
+ if (isProjectionMode ()) {
737
+ try {
738
+ int documentLength = getDocument ().getLength ();
739
+ if (fVisibleRegionDuringProjection != null ) {
740
+ expand (0 , fVisibleRegionDuringProjection .getOffset (), false );
741
+ int oldEnd = fVisibleRegionDuringProjection .getOffset () + fVisibleRegionDuringProjection .getLength ();
742
+ expand (oldEnd , documentLength - oldEnd , false );
743
+ }
744
+ collapse (0 , start , true );
745
+
746
+ int end = start + length + 1 ;
747
+ // ensure that trailing whitespace is included
748
+ // In this case, the line break needs to be included as well
749
+ boolean movedDueToTrailingWhitespace = false ;
750
+ while (end < documentLength && isWhitespaceButNotNewline (getDocument ().getChar (end ))) {
751
+ end ++;
752
+ movedDueToTrailingWhitespace = true ;
753
+ }
754
+ if (movedDueToTrailingWhitespace && end < documentLength && isLineBreak (getDocument ().getChar (end ))) {
755
+ end ++;
756
+ }
757
+
758
+ int endInvisibleRegionLength = documentLength - end ;
759
+ if (endInvisibleRegionLength > 0 ) {
760
+ collapse (end , endInvisibleRegionLength , true );
761
+ }
762
+ fVisibleRegionDuringProjection = new Region (start , end - start );
763
+ } catch (BadLocationException e ) {
764
+ e .printStackTrace ();
765
+ }
766
+ fVisibleRegionDuringProjection = new Region (start , length );
767
+ } else {
768
+ super .setVisibleRegion (start , length );
769
+ }
770
+ }
771
+
772
+ private boolean isWhitespaceButNotNewline (char c ) {
773
+ return Character .isWhitespace (c ) && !isLineBreak (c );
774
+ }
775
+
776
+ private boolean isLineBreak (char c ) {
777
+ return c == '\n' || c == '\r' ;
689
778
}
690
779
691
780
@ Override
@@ -710,6 +799,9 @@ public void resetVisibleRegion() {
710
799
711
800
@ Override
712
801
public IRegion getVisibleRegion () {
802
+ if (fVisibleRegionDuringProjection != null ) {
803
+ return fVisibleRegionDuringProjection ;
804
+ }
713
805
disableProjection ();
714
806
IRegion visibleRegion = getModelCoverage ();
715
807
if (visibleRegion == null )
0 commit comments