From fd89ca402c8ad64822762eaf7b663e50e655282e Mon Sep 17 00:00:00 2001 From: Tobias Melcher Date: Tue, 3 Dec 2024 18:51:49 +0100 Subject: [PATCH 1/2] fix InlinedAnnotationSupport#isInVisibleLines isInVisibleLines uses a cached endOffset variable which is not correctly invalidated when ProjectionSupport is enabled. cached endOffset variable is only reset in callback documentAboutToBeChanged but not in documentChanged which is not sufficient. Scenario: 1. document.replace(...) is called 2. documentAboutToBeChanged is called and endOffset is set to null 3. source viewer repaint event is triggered (between documentAboutToBeChanged and documentChanged) and code minings are drawn, isInVisibleLines is called with the old not yet updated document and endOffset is set. The repaint event is only triggered in this timeslot when ProjectionSupport is installed on the SourceViewer. 4. documentChanged called; now document is up to date but endOffset cache contains outdated value of the old document 5. code minings at end of document are no longer rendered because isInVisibleLines returns false (which is not correct) --- .../inlined/InlinedAnnotationSupport.java | 4 +- tests/org.eclipse.ui.editors.tests/plugin.xml | 12 +++- .../text/tests/codemining/CodeMiningTest.java | 68 ++++++++++++++++++- .../codemining/CodeMiningTestProvider.java | 5 +- .../codemining/TextProjectionTextEditor.java | 42 ++++++++++++ 5 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/TextProjectionTextEditor.java diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java index 2674b836fe8..b59ed29c4ba 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java @@ -163,7 +163,9 @@ public void documentAboutToBeChanged(DocumentEvent event) { @Override public void documentChanged(DocumentEvent event) { - // Do nothing + if (endOffset != null && event != null && event.fDocument != null && event.fDocument.getLength() > endOffset) { + endOffset= null; + } } @Override diff --git a/tests/org.eclipse.ui.editors.tests/plugin.xml b/tests/org.eclipse.ui.editors.tests/plugin.xml index c43805fae01..4d8159a5c94 100644 --- a/tests/org.eclipse.ui.editors.tests/plugin.xml +++ b/tests/org.eclipse.ui.editors.tests/plugin.xml @@ -22,4 +22,14 @@ id="org.eclipse.jface.text.tests.codemining.CodeMiningTestProvider"> - \ No newline at end of file + + + + + diff --git a/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java b/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java index d91521f50f0..558572147f0 100644 --- a/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java +++ b/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java @@ -13,6 +13,7 @@ import java.io.ByteArrayInputStream; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashMap; import java.util.concurrent.Callable; import org.junit.After; @@ -41,8 +42,12 @@ import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.ISourceViewerExtension5; +import org.eclipse.jface.text.source.projection.ProjectionAnnotation; +import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; @@ -100,6 +105,60 @@ public void run(IProgressMonitor monitor) throws CoreException { }, new NullProgressMonitor()); } + @Test + public void testInlinedAnnotationSupportIsInLinesReturnsValidResultAfterDocumentChange() throws Exception { + IFile file = project.getFile("test.testprojectionviewer"); + if (file.exists()) { + file.delete(true, new NullProgressMonitor()); + } + String source = "first\nsecond\nthird\n"; + file.create(new ByteArrayInputStream(source.getBytes("UTF-8")), true, new NullProgressMonitor()); + CodeMiningTestProvider.provideHeaderMiningAtLine = 2; + CodeMiningTestProvider.lineHeaderMiningText = " first line header\n secone line header\n third line header"; + int offset = source.indexOf("second") + "second".length(); + IEditorPart editor = IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); + drainEventQueue(); + ISourceViewer viewer = (ISourceViewer) editor.getAdapter(ITextViewer.class); + StyledText widget = viewer.getTextWidget(); + + var annotationModel = ((ProjectionViewer) viewer).getProjectionAnnotationModel(); + var deletionsArray = new Annotation[] {}; + var additions = new HashMap(); + ProjectionAnnotation annot = new ProjectionAnnotation(); + additions.put(annot, new Position(0, source.length())); + annotationModel.modifyAnnotations(deletionsArray, additions, null); + + Assert.assertTrue("Line header code mining above 3rd line not drawn", + waitForCondition(widget.getDisplay(), 2000, new Callable() { + @Override + public Boolean call() throws Exception { + try { + return existsPixelWithNonBackgroundColorAtLine(viewer, 2); + } catch (BadLocationException e) { + e.printStackTrace(); + return false; + } + } + })); + + IDocument doc = viewer.getDocument(); + widget.setCaretOffset(offset); + doc.replace(offset, 0, "\n insert text"); + drainEventQueue(); + Assert.assertTrue("Line header code mining above 4th line after inserting text not drawn", + waitForCondition(widget.getDisplay(), 2000, new Callable() { + @Override + public Boolean call() throws Exception { + try { + return existsPixelWithNonBackgroundColorAtLine(viewer, 3); + } catch (BadLocationException e) { + e.printStackTrace(); + return false; + } + } + })); + } + @Test public void testCodeMiningOnEmptyLine() throws Exception { IFile file = project.getFile("test.txt"); @@ -208,12 +267,17 @@ private static boolean existsPixelWithNonBackgroundColorAtLine(ITextViewer viewe lineLength = 0; } int verticalScroolBarWidth = viewer.getTextWidget().getVerticalBar().getThumbBounds().width; - Rectangle lineBounds = widget.getTextBounds(document.getLineOffset(line), - document.getLineOffset(line) + lineLength); + int lineOffset = document.getLineOffset(line); + Rectangle lineBounds = widget.getTextBounds(lineOffset, lineOffset + lineLength); + String lineStr = document.get(lineOffset, lineLength); Image image = new Image(widget.getDisplay(), widget.getSize().x, widget.getSize().y); try { GC gc = new GC(widget); gc.copyArea(image, 0, 0); + Point textExtent = gc.textExtent(lineStr); + if (lineBounds.height - textExtent.y > textExtent.y) { + lineBounds.height -= textExtent.y; + } gc.dispose(); ImageData imageData = image.getImageData(); for (int x = lineBounds.x + 1; x < image.getBounds().width - verticalScroolBarWidth diff --git a/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTestProvider.java b/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTestProvider.java index 60f5ff4ffee..c3f6072bd10 100644 --- a/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTestProvider.java +++ b/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTestProvider.java @@ -29,7 +29,7 @@ public class CodeMiningTestProvider extends AbstractCodeMiningProvider { public static int provideHeaderMiningAtLine = -1; public static int provideContentMiningAtOffset = -1; - + public static String lineHeaderMiningText; @Override public CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { @@ -42,6 +42,9 @@ public CompletableFuture> provideCodeMinings(ITextVi minings.add(new LineHeaderCodeMining(provideHeaderMiningAtLine, viewer.getDocument(), this) { @Override public String getLabel() { + if (lineHeaderMiningText != null) { + return lineHeaderMiningText; + } return "line header mining"; } }); diff --git a/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/TextProjectionTextEditor.java b/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/TextProjectionTextEditor.java new file mode 100644 index 00000000000..ef5440709e7 --- /dev/null +++ b/tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/TextProjectionTextEditor.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2024 SAP + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jface.text.tests.codemining; + +import org.eclipse.swt.widgets.Composite; + +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.projection.ProjectionSupport; +import org.eclipse.jface.text.source.projection.ProjectionViewer; + +import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor; + +public class TextProjectionTextEditor extends AbstractDecoratedTextEditor { + + @Override + protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { + fAnnotationAccess = getAnnotationAccess(); + fOverviewRuler = createOverviewRuler(getSharedColors()); + ISourceViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), + styles); + getSourceViewerDecorationSupport(viewer); + return viewer; + } + + @Override + public void createPartControl(Composite parent) { + super.createPartControl(parent); + var projectionViewer = (ProjectionViewer) getSourceViewer(); + var projectionSupport = new ProjectionSupport(projectionViewer, getAnnotationAccess(), getSharedColors()); + projectionSupport.install(); + projectionViewer.enableProjection(); + } +} \ No newline at end of file From c983d936cb240fc4d1aca2a0aba3e29b58f91d3c Mon Sep 17 00:00:00 2001 From: Eclipse Platform Bot Date: Tue, 3 Dec 2024 17:57:45 +0000 Subject: [PATCH 2/2] Version bump(s) for 4.35 stream --- tests/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF index 74a27a2c7f8..506d79157e6 100644 --- a/tests/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.ui.editors.tests;singleton:=true -Bundle-Version: 3.13.600.qualifier +Bundle-Version: 3.13.700.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Export-Package: org.eclipse.jface.text.tests.codemining,