@@ -99,6 +99,7 @@ public class ProjectionViewer extends SourceViewer implements ITextViewerExtensi
9999 */
100100 public static final int COLLAPSE_ALL = BASE + 5 ;
101101
102+
102103 /**
103104 * Internal listener to changes of the annotation model.
104105 */
@@ -272,6 +273,34 @@ private void computeExpectedExecutionCosts() {
272273 }
273274 }
274275
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+
275304 /** The projection annotation model used by this viewer. */
276305 private ProjectionAnnotationModel fProjectionAnnotationModel ;
277306 /** The annotation model listener */
@@ -292,6 +321,11 @@ private void computeExpectedExecutionCosts() {
292321 private IDocument fReplaceVisibleDocumentExecutionTrigger ;
293322 /** <code>true</code> if projection was on the last time we switched to segmented mode. */
294323 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 ;
295329 /** The queue of projection commands used to assess the costs of projection changes. */
296330 private ProjectionCommandQueue fCommandQueue ;
297331 /**
@@ -301,6 +335,8 @@ private void computeExpectedExecutionCosts() {
301335 */
302336 private int fDeletedLines ;
303337
338+ private UpdateDocumentListener fUpdateDocumentListener = new UpdateDocumentListener ();
339+
304340
305341 /**
306342 * Creates a new projection source viewer.
@@ -510,6 +546,11 @@ public final void disableProjection() {
510546 fProjectionAnnotationModel .removeAllAnnotations ();
511547 fFindReplaceDocumentAdapter = null ;
512548 fireProjectionDisabled ();
549+ if (fVisibleRegionDuringProjection != null ) {
550+ super .setVisibleRegion (fVisibleRegionDuringProjection .getOffset (), fVisibleRegionDuringProjection .getLength ());
551+ fVisibleRegionDuringProjection = null ;
552+ }
553+ getDocument ().removeDocumentListener (fUpdateDocumentListener );
513554 }
514555 }
515556
@@ -518,9 +559,14 @@ public final void disableProjection() {
518559 */
519560 public final void enableProjection () {
520561 if (!isProjectionMode ()) {
562+ IRegion visibleRegion = getVisibleRegion ();
521563 addProjectionAnnotationModel (getVisualAnnotationModel ());
522564 fFindReplaceDocumentAdapter = null ;
523565 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 );
524570 }
525571 }
526572
@@ -529,6 +575,10 @@ private void expandAll() {
529575 IDocument doc = getDocument ();
530576 int length = doc == null ? 0 : doc .getLength ();
531577 if (isProjectionMode ()) {
578+ if (fVisibleRegionDuringProjection != null ) {
579+ offset = fVisibleRegionDuringProjection .getOffset ();
580+ length = fVisibleRegionDuringProjection .getLength ();
581+ }
532582 fProjectionAnnotationModel .expandAll (offset , length );
533583 }
534584 }
@@ -683,9 +733,48 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
683733
684734 @ Override
685735 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' ;
689778 }
690779
691780 @ Override
@@ -710,6 +799,9 @@ public void resetVisibleRegion() {
710799
711800 @ Override
712801 public IRegion getVisibleRegion () {
802+ if (fVisibleRegionDuringProjection != null ) {
803+ return fVisibleRegionDuringProjection ;
804+ }
713805 disableProjection ();
714806 IRegion visibleRegion = getModelCoverage ();
715807 if (visibleRegion == null )
0 commit comments