Skip to content

Commit 7a9cf09

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 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)