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
20
20
import java .util .Iterator ;
21
21
import java .util .List ;
22
22
23
+ import org .osgi .framework .Bundle ;
24
+
23
25
import org .eclipse .swt .SWTError ;
24
26
import org .eclipse .swt .custom .ST ;
25
27
import org .eclipse .swt .custom .StyledText ;
33
35
import org .eclipse .swt .widgets .Display ;
34
36
35
37
import org .eclipse .core .runtime .Assert ;
38
+ import org .eclipse .core .runtime .ILog ;
39
+ import org .eclipse .core .runtime .IStatus ;
40
+ import org .eclipse .core .runtime .Platform ;
41
+ import org .eclipse .core .runtime .Status ;
36
42
37
43
import org .eclipse .jface .internal .text .SelectionProcessor ;
38
44
@@ -272,6 +278,32 @@ private void computeExpectedExecutionCosts() {
272
278
}
273
279
}
274
280
281
+ /**
282
+ * An {@link IDocumentListener} that makes sure that {@link #fVisibleRegionDuringProjection} is
283
+ * updated when the document changes and ensures that the collapsed region after the visible
284
+ * region is recreated appropriately.
285
+ */
286
+ private final class UpdateDocumentListener implements IDocumentListener {
287
+ @ Override
288
+ public void documentChanged (DocumentEvent event ) {
289
+ if (fVisibleRegionDuringProjection == null ) {
290
+ return ;
291
+ }
292
+ int oldLength = event .getLength ();
293
+ int newLength = event .getText ().length ();
294
+ int oldVisibleRegionEnd = fVisibleRegionDuringProjection .getOffset () + fVisibleRegionDuringProjection .getLength ();
295
+ if (event .getOffset () < fVisibleRegionDuringProjection .getOffset ()) {
296
+ fVisibleRegionDuringProjection = new Region (fVisibleRegionDuringProjection .getOffset () + newLength - oldLength , fVisibleRegionDuringProjection .getLength ());
297
+ } else if (event .getOffset () + oldLength < oldVisibleRegionEnd ) {
298
+ fVisibleRegionDuringProjection = new Region (fVisibleRegionDuringProjection .getOffset (), fVisibleRegionDuringProjection .getLength () + newLength - oldLength );
299
+ }
300
+ }
301
+
302
+ @ Override
303
+ public void documentAboutToBeChanged (DocumentEvent event ) {
304
+ }
305
+ }
306
+
275
307
/** The projection annotation model used by this viewer. */
276
308
private ProjectionAnnotationModel fProjectionAnnotationModel ;
277
309
/** The annotation model listener */
@@ -292,6 +324,11 @@ private void computeExpectedExecutionCosts() {
292
324
private IDocument fReplaceVisibleDocumentExecutionTrigger ;
293
325
/** <code>true</code> if projection was on the last time we switched to segmented mode. */
294
326
private boolean fWasProjectionEnabled ;
327
+ /**
328
+ * The region set by {@link #setVisibleRegion(int, int)} during projection or <code>null</code>
329
+ * if not in a projection
330
+ */
331
+ private IRegion fVisibleRegionDuringProjection ;
295
332
/** The queue of projection commands used to assess the costs of projection changes. */
296
333
private ProjectionCommandQueue fCommandQueue ;
297
334
/**
@@ -301,6 +338,8 @@ private void computeExpectedExecutionCosts() {
301
338
*/
302
339
private int fDeletedLines ;
303
340
341
+ private UpdateDocumentListener fUpdateDocumentListener ;
342
+
304
343
305
344
/**
306
345
* Creates a new projection source viewer.
@@ -313,6 +352,7 @@ private void computeExpectedExecutionCosts() {
313
352
*/
314
353
public ProjectionViewer (Composite parent , IVerticalRuler ruler , IOverviewRuler overviewRuler , boolean showsAnnotationOverview , int styles ) {
315
354
super (parent , ruler , overviewRuler , showsAnnotationOverview , styles );
355
+ fUpdateDocumentListener = new UpdateDocumentListener ();
316
356
}
317
357
318
358
/**
@@ -510,6 +550,14 @@ public final void disableProjection() {
510
550
fProjectionAnnotationModel .removeAllAnnotations ();
511
551
fFindReplaceDocumentAdapter = null ;
512
552
fireProjectionDisabled ();
553
+ if (fVisibleRegionDuringProjection != null ) {
554
+ super .setVisibleRegion (fVisibleRegionDuringProjection .getOffset (), fVisibleRegionDuringProjection .getLength ());
555
+ fVisibleRegionDuringProjection = null ;
556
+ }
557
+ IDocument document = getDocument ();
558
+ if (document != null ) {
559
+ document .removeDocumentListener (fUpdateDocumentListener );
560
+ }
513
561
}
514
562
}
515
563
@@ -518,9 +566,18 @@ public final void disableProjection() {
518
566
*/
519
567
public final void enableProjection () {
520
568
if (!isProjectionMode ()) {
569
+ IRegion visibleRegion = getVisibleRegion ();
521
570
addProjectionAnnotationModel (getVisualAnnotationModel ());
522
571
fFindReplaceDocumentAdapter = null ;
523
572
fireProjectionEnabled ();
573
+ IDocument document = getDocument ();
574
+ if (document == null ) {
575
+ return ;
576
+ }
577
+ if (visibleRegion != null && (visibleRegion .getOffset () != 0 || visibleRegion .getLength () != 0 ) && visibleRegion .getLength () < document .getLength ()) {
578
+ setVisibleRegion (visibleRegion .getOffset (), visibleRegion .getLength ());
579
+ }
580
+ document .addDocumentListener (fUpdateDocumentListener );
524
581
}
525
582
}
526
583
@@ -529,6 +586,10 @@ private void expandAll() {
529
586
IDocument doc = getDocument ();
530
587
int length = doc == null ? 0 : doc .getLength ();
531
588
if (isProjectionMode ()) {
589
+ if (fVisibleRegionDuringProjection != null ) {
590
+ offset = fVisibleRegionDuringProjection .getOffset ();
591
+ length = fVisibleRegionDuringProjection .getLength ();
592
+ }
532
593
fProjectionAnnotationModel .expandAll (offset , length );
533
594
}
534
595
}
@@ -683,9 +744,73 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
683
744
684
745
@ Override
685
746
public void setVisibleRegion (int start , int length ) {
686
- fWasProjectionEnabled = isProjectionMode ();
687
- disableProjection ();
688
- super .setVisibleRegion (start , length );
747
+ if (!isProjectionMode ()) {
748
+ super .setVisibleRegion (start , length );
749
+ return ;
750
+ }
751
+ IDocument document = getDocument ();
752
+ if (document == null ) {
753
+ return ;
754
+ }
755
+ try {
756
+ // If the visible region changes, make sure collapsed regions outside of the old visible regions are expanded
757
+ // and collapse everything outside the new visible region
758
+ int end = computeEndOfVisibleRegion (start , length , document );
759
+ expandOutsideCurrentVisibleRegion (document );
760
+ collapseOutsideOfNewVisibleRegion (start , end , document );
761
+ fVisibleRegionDuringProjection = new Region (start , end - start );
762
+ } catch (BadLocationException e ) {
763
+ String PLUGIN_ID = "org.eclipse.jface.text" ; //$NON-NLS-1$
764
+ Bundle bundle = Platform .getBundle (PLUGIN_ID );
765
+ ILog log = ILog .of (bundle );
766
+ log .log (new Status (IStatus .WARNING , getClass (), IStatus .OK , e .getMessage (), e ));
767
+ }
768
+ fVisibleRegionDuringProjection = new Region (start , length );
769
+ }
770
+
771
+ private void expandOutsideCurrentVisibleRegion (IDocument document ) throws BadLocationException {
772
+ if (fVisibleRegionDuringProjection != null ) {
773
+ expand (0 , fVisibleRegionDuringProjection .getOffset (), false );
774
+ int oldEnd = fVisibleRegionDuringProjection .getOffset () + fVisibleRegionDuringProjection .getLength ();
775
+ int length = document .getLength () - oldEnd ;
776
+ if (length > 0 ) {
777
+ expand (oldEnd , length , false );
778
+ }
779
+ }
780
+ }
781
+
782
+ private void collapseOutsideOfNewVisibleRegion (int start , int end , IDocument document ) throws BadLocationException {
783
+ int documentLength = document .getLength ();
784
+ collapse (0 , start , true );
785
+
786
+ int endInvisibleRegionLength = documentLength - end ;
787
+ if (endInvisibleRegionLength > 0 ) {
788
+ collapse (end , endInvisibleRegionLength , true );
789
+ }
790
+ }
791
+
792
+ private static int computeEndOfVisibleRegion (int start , int length , IDocument document ) throws BadLocationException {
793
+ int documentLength = document .getLength ();
794
+ int end = start + length + 1 ;
795
+ // ensure that trailing whitespace is included
796
+ // In this case, the line break needs to be included as well
797
+ boolean visibleRegionEndsWithTrailingWhitespace = isWhitespaceButNotNewline (document .getChar (end - 1 ));
798
+ while (end < documentLength && isWhitespaceButNotNewline (document .getChar (end ))) {
799
+ end ++;
800
+ visibleRegionEndsWithTrailingWhitespace = true ;
801
+ }
802
+ if (visibleRegionEndsWithTrailingWhitespace && end < documentLength && isLineBreak (document .getChar (end ))) {
803
+ end ++;
804
+ }
805
+ return end ;
806
+ }
807
+
808
+ private static boolean isWhitespaceButNotNewline (char c ) {
809
+ return Character .isWhitespace (c ) && !isLineBreak (c );
810
+ }
811
+
812
+ private static boolean isLineBreak (char c ) {
813
+ return c == '\n' || c == '\r' ;
689
814
}
690
815
691
816
@ Override
@@ -710,6 +835,9 @@ public void resetVisibleRegion() {
710
835
711
836
@ Override
712
837
public IRegion getVisibleRegion () {
838
+ if (fVisibleRegionDuringProjection != null ) {
839
+ return fVisibleRegionDuringProjection ;
840
+ }
713
841
disableProjection ();
714
842
IRegion visibleRegion = getModelCoverage ();
715
843
if (visibleRegion == null )
0 commit comments