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 ;
338
+
304
339
305
340
/**
306
341
* Creates a new projection source viewer.
@@ -313,6 +348,7 @@ private void computeExpectedExecutionCosts() {
313
348
*/
314
349
public ProjectionViewer (Composite parent , IVerticalRuler ruler , IOverviewRuler overviewRuler , boolean showsAnnotationOverview , int styles ) {
315
350
super (parent , ruler , overviewRuler , showsAnnotationOverview , styles );
351
+ fUpdateDocumentListener = new UpdateDocumentListener ();
316
352
}
317
353
318
354
/**
@@ -510,6 +546,14 @@ 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
+ IDocument document = getDocument ();
554
+ if (document != null ) {
555
+ document .removeDocumentListener (fUpdateDocumentListener );
556
+ }
513
557
}
514
558
}
515
559
@@ -518,9 +562,18 @@ public final void disableProjection() {
518
562
*/
519
563
public final void enableProjection () {
520
564
if (!isProjectionMode ()) {
565
+ IRegion visibleRegion = getVisibleRegion ();
521
566
addProjectionAnnotationModel (getVisualAnnotationModel ());
522
567
fFindReplaceDocumentAdapter = null ;
523
568
fireProjectionEnabled ();
569
+ IDocument document = getDocument ();
570
+ if (document == null ) {
571
+ return ;
572
+ }
573
+ if (visibleRegion != null && (visibleRegion .getOffset () != 0 || visibleRegion .getLength () != 0 ) && visibleRegion .getLength () < document .getLength ()) {
574
+ setVisibleRegion (visibleRegion .getOffset (), visibleRegion .getLength ());
575
+ }
576
+ document .addDocumentListener (fUpdateDocumentListener );
524
577
}
525
578
}
526
579
@@ -529,6 +582,10 @@ private void expandAll() {
529
582
IDocument doc = getDocument ();
530
583
int length = doc == null ? 0 : doc .getLength ();
531
584
if (isProjectionMode ()) {
585
+ if (fVisibleRegionDuringProjection != null ) {
586
+ offset = fVisibleRegionDuringProjection .getOffset ();
587
+ length = fVisibleRegionDuringProjection .getLength ();
588
+ }
532
589
fProjectionAnnotationModel .expandAll (offset , length );
533
590
}
534
591
}
@@ -683,9 +740,52 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
683
740
684
741
@ Override
685
742
public void setVisibleRegion (int start , int length ) {
686
- fWasProjectionEnabled = isProjectionMode ();
687
- disableProjection ();
688
- super .setVisibleRegion (start , length );
743
+ if (!isProjectionMode ()) {
744
+ super .setVisibleRegion (start , length );
745
+ return ;
746
+ }
747
+ IDocument document = getDocument ();
748
+ if (document == null ) {
749
+ return ;
750
+ }
751
+ try {
752
+ int documentLength = document .getLength ();
753
+ if (fVisibleRegionDuringProjection != null ) {
754
+ expand (0 , fVisibleRegionDuringProjection .getOffset (), false );
755
+ int oldEnd = fVisibleRegionDuringProjection .getOffset () + fVisibleRegionDuringProjection .getLength ();
756
+ expand (oldEnd , documentLength - oldEnd , false );
757
+ }
758
+ collapse (0 , start , true );
759
+
760
+ int end = start + length + 1 ;
761
+ // ensure that trailing whitespace is included
762
+ // In this case, the line break needs to be included as well
763
+ boolean visibleRegionEndsWithTrailingWhitespace = isWhitespaceButNotNewline (document .getChar (end - 1 ));
764
+ while (end < documentLength && isWhitespaceButNotNewline (document .getChar (end ))) {
765
+ end ++;
766
+ visibleRegionEndsWithTrailingWhitespace = true ;
767
+ }
768
+ if (visibleRegionEndsWithTrailingWhitespace && end < documentLength && isLineBreak (document .getChar (end ))) {
769
+ end ++;
770
+ }
771
+
772
+ int endInvisibleRegionLength = documentLength - end ;
773
+ if (endInvisibleRegionLength > 0 ) {
774
+ collapse (end , endInvisibleRegionLength , true );
775
+ }
776
+ fVisibleRegionDuringProjection = new Region (start , end - start );
777
+ } catch (BadLocationException e ) {
778
+ e .printStackTrace ();
779
+ }
780
+ fVisibleRegionDuringProjection = new Region (start , length );
781
+ }
782
+
783
+ private boolean isWhitespaceButNotNewline (char c ) {
784
+ return Character .isWhitespace (c ) && !isLineBreak (c );
785
+ }
786
+
787
+ private boolean isLineBreak (char c ) {
788
+ return c == '\n' || c == '\r' ;
689
789
}
690
790
691
791
@ Override
@@ -710,6 +810,9 @@ public void resetVisibleRegion() {
710
810
711
811
@ Override
712
812
public IRegion getVisibleRegion () {
813
+ if (fVisibleRegionDuringProjection != null ) {
814
+ return fVisibleRegionDuringProjection ;
815
+ }
713
816
disableProjection ();
714
817
IRegion visibleRegion = getModelCoverage ();
715
818
if (visibleRegion == null )
0 commit comments