Skip to content

Commit 7a9cf09

Browse files
committed
Allow using visible regions with projections #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 #3074
1 parent d2177cf commit 7a9cf09

File tree

2 files changed

+327
-3
lines changed

2 files changed

+327
-3
lines changed

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

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)