From e3ed53200b6ef2b3164517a4a69acf131091bd74 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Mon, 15 Dec 2025 12:59:50 +0100 Subject: [PATCH] Ensure the last line is included in projection-based visible regions This change ensures that setting the visible region with projections enabled does not hide the last line in case there is additional text at the end of the line or the file is using Windows line endings. --- .../source/projection/ProjectionViewer.java | 18 ++--- .../text/tests/ProjectionViewerTest.java | 67 +++++++++++++++++++ 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/bundles/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java b/bundles/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java index 34893d66997..5d58ab9ec40 100644 --- a/bundles/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java +++ b/bundles/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java @@ -841,24 +841,20 @@ private void collapseOutsideOfNewVisibleRegion(int start, int end, IDocument doc private static int computeEndOfVisibleRegion(int start, int length, IDocument document) throws BadLocationException { int documentLength= document.getLength(); - int end= start + length + 1; - // ensure that trailing whitespace is included - // In this case, the line break needs to be included as well - boolean visibleRegionEndsWithTrailingWhitespace= end < documentLength && isWhitespaceButNotNewline(document.getChar(end - 1)); - while (end < documentLength && isWhitespaceButNotNewline(document.getChar(end))) { + int end= start + length; + // ensure that the last line is fully included because projections cannot include partial lines + while (end < documentLength && !isLineBreak(document.getChar(end))) { end++; - visibleRegionEndsWithTrailingWhitespace= true; } - if (visibleRegionEndsWithTrailingWhitespace && end < documentLength && isLineBreak(document.getChar(end))) { + if (end < documentLength) { + end++; + } + if (end < documentLength && document.getChar(end) == '\n' && document.getChar(end - 1) == '\r') { end++; } return end; } - private static boolean isWhitespaceButNotNewline(char c) { - return Character.isWhitespace(c) && !isLineBreak(c); - } - private static boolean isLineBreak(char c) { return c == '\n' || c == '\r'; } diff --git a/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ProjectionViewerTest.java b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ProjectionViewerTest.java index e32c54520a7..77832053cc8 100644 --- a/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ProjectionViewerTest.java +++ b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ProjectionViewerTest.java @@ -16,6 +16,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; @@ -516,4 +518,69 @@ private ProjectionAnnotation addVisibleRegionAndProjection(TestProjectionViewer viewer.getProjectionAnnotationModel().addAnnotation(annotation, new Position(projectionStart, projectionEnd - projectionStart)); return annotation; } + + @ParameterizedTest + @CsvSource({ "true", "false" }) + void testDifferentLineEndings(boolean crlf) { + Shell shell= new Shell(Display.getCurrent()); + shell.setLayout(new FillLayout()); + TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, true, SWT.ALL); + String documentContent= """ + // before + { + // within + } + // after + """; + if (crlf) { + documentContent= documentContent.replace("\n", "\r\n"); + } + Document document= new Document(documentContent); + viewer.setDocument(document, new AnnotationModel()); + int start= documentContent.indexOf('{'); + int end= documentContent.indexOf('}') + 1; + viewer.enableProjection(); + viewer.setVisibleRegion(start, end - start); + assertEquals(documentContent.substring(start, documentContent.indexOf("// after")), viewer.getVisibleDocument().get()); + } + + @Test + void testIncludesLastLineIfAdditionalTextPresent() { + Shell shell= new Shell(Display.getCurrent()); + shell.setLayout(new FillLayout()); + TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, true, SWT.ALL); + String documentContent= """ + // before + { + // within + }// ... + // should be hidden + """; + Document document= new Document(documentContent); + viewer.setDocument(document, new AnnotationModel()); + int start= documentContent.indexOf('{'); + int end= documentContent.indexOf('}') + 1; + viewer.enableProjection(); + viewer.setVisibleRegion(start, end - start); + assertEquals(documentContent.substring(start, documentContent.indexOf("// should be hidden")), viewer.getVisibleDocument().get()); + } + + @Test + void testSetVisibleRegionUntilEOF() { + Shell shell= new Shell(Display.getCurrent()); + shell.setLayout(new FillLayout()); + TestProjectionViewer viewer= new TestProjectionViewer(shell, null, null, true, SWT.ALL); + String documentContent= """ + // before + { + // within + }"""; + Document document= new Document(documentContent); + viewer.setDocument(document, new AnnotationModel()); + int start= documentContent.indexOf('{'); + int end= documentContent.indexOf('}') + 1; + viewer.enableProjection(); + viewer.setVisibleRegion(start, end - start); + assertEquals(documentContent.substring(start), viewer.getVisibleDocument().get()); + } }