Skip to content

Commit 2f3a4b7

Browse files
committed
Allow using visible regions with projections eclipse-platform#3073
While ProjectionViewer supports both using visible regions and projections, these features cannot be used in conjunction. This change allows the use of projections when visible regions are used. Fixes eclipse-platform#3074
1 parent 789b373 commit 2f3a4b7

File tree

2 files changed

+373
-5
lines changed

2 files changed

+373
-5
lines changed

bundles/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2018 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -272,6 +272,34 @@ private void computeExpectedExecutionCosts() {
272272
}
273273
}
274274

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+
275303
/** The projection annotation model used by this viewer. */
276304
private ProjectionAnnotationModel fProjectionAnnotationModel;
277305
/** The annotation model listener */
@@ -292,6 +320,11 @@ private void computeExpectedExecutionCosts() {
292320
private IDocument fReplaceVisibleDocumentExecutionTrigger;
293321
/** <code>true</code> if projection was on the last time we switched to segmented mode. */
294322
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;
295328
/** The queue of projection commands used to assess the costs of projection changes. */
296329
private ProjectionCommandQueue fCommandQueue;
297330
/**
@@ -301,6 +334,8 @@ private void computeExpectedExecutionCosts() {
301334
*/
302335
private int fDeletedLines;
303336

337+
private UpdateDocumentListener fUpdateDocumentListener;
338+
304339

305340
/**
306341
* Creates a new projection source viewer.
@@ -313,6 +348,7 @@ private void computeExpectedExecutionCosts() {
313348
*/
314349
public ProjectionViewer(Composite parent, IVerticalRuler ruler, IOverviewRuler overviewRuler, boolean showsAnnotationOverview, int styles) {
315350
super(parent, ruler, overviewRuler, showsAnnotationOverview, styles);
351+
fUpdateDocumentListener= new UpdateDocumentListener();
316352
}
317353

318354
/**
@@ -510,6 +546,14 @@ 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+
IDocument document= getDocument();
554+
if (document != null) {
555+
document.removeDocumentListener(fUpdateDocumentListener);
556+
}
513557
}
514558
}
515559

@@ -518,9 +562,18 @@ public final void disableProjection() {
518562
*/
519563
public final void enableProjection() {
520564
if (!isProjectionMode()) {
565+
IRegion visibleRegion= getVisibleRegion();
521566
addProjectionAnnotationModel(getVisualAnnotationModel());
522567
fFindReplaceDocumentAdapter= null;
523568
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);
524577
}
525578
}
526579

@@ -529,6 +582,10 @@ private void expandAll() {
529582
IDocument doc= getDocument();
530583
int length= doc == null ? 0 : doc.getLength();
531584
if (isProjectionMode()) {
585+
if (fVisibleRegionDuringProjection != null) {
586+
offset= fVisibleRegionDuringProjection.getOffset();
587+
length= fVisibleRegionDuringProjection.getLength();
588+
}
532589
fProjectionAnnotationModel.expandAll(offset, length);
533590
}
534591
}
@@ -683,9 +740,50 @@ private int toLineStart(IDocument document, int offset, boolean testLastLine) th
683740

684741
@Override
685742
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+
while (end < documentLength && isWhitespaceButNotNewline(document.getChar(end))) {
764+
end++;
765+
}
766+
if (end < documentLength && isLineBreak(document.getChar(end))) {
767+
end++;
768+
}
769+
770+
int endInvisibleRegionLength= documentLength - end;
771+
if (endInvisibleRegionLength > 0) {
772+
collapse(end, endInvisibleRegionLength, true);
773+
}
774+
fVisibleRegionDuringProjection= new Region(start, end - start);
775+
} catch (BadLocationException e) {
776+
e.printStackTrace();
777+
}
778+
fVisibleRegionDuringProjection= new Region(start, length);
779+
}
780+
781+
private boolean isWhitespaceButNotNewline(char c) {
782+
return Character.isWhitespace(c) && !isLineBreak(c);
783+
}
784+
785+
private boolean isLineBreak(char c) {
786+
return c == '\n' || c == '\r';
689787
}
690788

691789
@Override
@@ -710,6 +808,9 @@ public void resetVisibleRegion() {
710808

711809
@Override
712810
public IRegion getVisibleRegion() {
811+
if (fVisibleRegionDuringProjection != null) {
812+
return fVisibleRegionDuringProjection;
813+
}
713814
disableProjection();
714815
IRegion visibleRegion= getModelCoverage();
715816
if (visibleRegion == null)

0 commit comments

Comments
 (0)