diff --git a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffCodeMiningProvider.java b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffCodeMiningProvider.java index 840781bce82..143a2a68eec 100644 --- a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffCodeMiningProvider.java +++ b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffCodeMiningProvider.java @@ -229,15 +229,7 @@ private void createLineHeaderCodeMinings(List diffs, List diffs, List diffs, List diffs, List= end && !startsLine(doc, end)) { + return new UnifiedDiffFooterCodeMining(doc, this, null, diff, tabWidth, this.deletionBackgroundColor); + } + // a position must not reach beyond the document, otherwise the annotation model + // silently drops it + int start = Math.min(offset, end); + return new UnifiedDiffLineHeaderCodeMining(new Position(start, start < end ? 1 : 0), this, diff, tabWidth, + this.detailedDiffColor, this.deletionBackgroundColor, tv); + } + + private static boolean startsLine(IDocument doc, int offset) throws BadLocationException { + return doc.getLineOffset(doc.getLineOfOffset(offset)) == offset; + } + static class UnifiedDiffFooterCodeMining extends DocumentFooterCodeMining { private final String unifiedDiffLabel; private final Color deletionBackgroundColor; diff --git a/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java b/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java index 9c5ff3e96f5..5947cf5a5be 100644 --- a/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java +++ b/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java @@ -322,6 +322,25 @@ public void testWhitespaceOnlyChangeIsReportedWhenNotIgnored() { "with ignoreWhiteSpace(false) the changed indentation is a diff"); } + /** + * A diff at the very end of the document is shown as a code mining. Only a line + * header mining reserves its height in the text widget, so an emptied file must + * not fall back to a footer mining: the removed content would be unreachable + * because the viewer would have nothing to scroll over. + */ + @Test + public void testRemovingTheWholeContentStaysScrollable() { + setEditorContent(""); + String removed = "a removed line\n".repeat(200); + + assertTrue(UnifiedDiff.create(editor, removed, UnifiedDiffMode.OVERLAY_READ_ONLY_MODE).open().isOK()); + + StyledText widget = viewer().getTextWidget(); + int reserved = waitForVerticalIndent(widget); + assertTrue(reserved >= 200 * widget.getLineHeight(), + "the 200 removed lines must reserve scrollable space, but only " + reserved + " pixels were reserved"); + } + /** * Opening a second diff on the same editor must replace the first one instead * of stacking annotations and diffs on top of each other. @@ -410,6 +429,20 @@ private static int countAnnotations(IAnnotationModel model, String type) { return annotations(model, type).size(); } + /** + * The vertical indent of a line is only applied while the code mining is + * painted, and the minings themselves are resolved asynchronously. + */ + private static int waitForVerticalIndent(StyledText widget) { + long deadline = System.currentTimeMillis() + 10_000; + int indent = 0; + while (indent == 0 && System.currentTimeMillis() < deadline) { + forcePaintCycle(widget); + indent = widget.getLineVerticalIndent(0); + } + return indent; + } + private static void forcePaintCycle(StyledText tw) { if (tw == null || tw.isDisposed()) { return;