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