Skip to content

Commit ea68ca2

Browse files
tobias-melcherBeckerWdf
authored andcommitted
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)
1 parent 4d989e4 commit ea68ca2

File tree

5 files changed

+126
-5
lines changed

5 files changed

+126
-5
lines changed

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,9 @@ public void documentAboutToBeChanged(DocumentEvent event) {
163163

164164
@Override
165165
public void documentChanged(DocumentEvent event) {
166-
// Do nothing
166+
if (endOffset != null && event != null && event.fDocument != null && event.fDocument.getLength() > endOffset) {
167+
endOffset= null;
168+
}
167169
}
168170

169171
@Override

tests/org.eclipse.ui.editors.tests/plugin.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,14 @@
2222
id="org.eclipse.jface.text.tests.codemining.CodeMiningTestProvider">
2323
</codeMiningProvider>
2424
</extension>
25-
</plugin>
25+
<extension
26+
point="org.eclipse.ui.editors">
27+
<editor
28+
class="org.eclipse.jface.text.tests.codemining.TextProjectionTextEditor"
29+
default="false"
30+
extensions="testprojectionviewer"
31+
id="org.eclipse.jface.text.tests.codemining.TestProjectionTextEditor"
32+
name="TestProjectionTextEditor">
33+
</editor>
34+
</extension>
35+
</plugin>

tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.io.ByteArrayInputStream;
1414
import java.text.SimpleDateFormat;
1515
import java.util.Date;
16+
import java.util.HashMap;
1617
import java.util.concurrent.Callable;
1718

1819
import org.junit.After;
@@ -41,8 +42,12 @@
4142
import org.eclipse.jface.text.BadLocationException;
4243
import org.eclipse.jface.text.IDocument;
4344
import org.eclipse.jface.text.ITextViewer;
45+
import org.eclipse.jface.text.Position;
46+
import org.eclipse.jface.text.source.Annotation;
4447
import org.eclipse.jface.text.source.ISourceViewer;
4548
import org.eclipse.jface.text.source.ISourceViewerExtension5;
49+
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
50+
import org.eclipse.jface.text.source.projection.ProjectionViewer;
4651

4752
import org.eclipse.ui.IEditorPart;
4853
import org.eclipse.ui.IWorkbenchPage;
@@ -100,6 +105,60 @@ public void run(IProgressMonitor monitor) throws CoreException {
100105
}, new NullProgressMonitor());
101106
}
102107

108+
@Test
109+
public void testInlinedAnnotationSupportIsInLinesReturnsValidResultAfterDocumentChange() throws Exception {
110+
IFile file = project.getFile("test.testprojectionviewer");
111+
if (file.exists()) {
112+
file.delete(true, new NullProgressMonitor());
113+
}
114+
String source = "first\nsecond\nthird\n";
115+
file.create(new ByteArrayInputStream(source.getBytes("UTF-8")), true, new NullProgressMonitor());
116+
CodeMiningTestProvider.provideHeaderMiningAtLine = 2;
117+
CodeMiningTestProvider.lineHeaderMiningText = " first line header\n secone line header\n third line header";
118+
int offset = source.indexOf("second") + "second".length();
119+
IEditorPart editor = IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
120+
drainEventQueue();
121+
ISourceViewer viewer = (ISourceViewer) editor.getAdapter(ITextViewer.class);
122+
StyledText widget = viewer.getTextWidget();
123+
124+
var annotationModel = ((ProjectionViewer) viewer).getProjectionAnnotationModel();
125+
var deletionsArray = new Annotation[] {};
126+
var additions = new HashMap<Annotation, Position>();
127+
ProjectionAnnotation annot = new ProjectionAnnotation();
128+
additions.put(annot, new Position(0, source.length()));
129+
annotationModel.modifyAnnotations(deletionsArray, additions, null);
130+
131+
Assert.assertTrue("Line header code mining above 3rd line not drawn",
132+
waitForCondition(widget.getDisplay(), 2000, new Callable<Boolean>() {
133+
@Override
134+
public Boolean call() throws Exception {
135+
try {
136+
return existsPixelWithNonBackgroundColorAtLine(viewer, 2);
137+
} catch (BadLocationException e) {
138+
e.printStackTrace();
139+
return false;
140+
}
141+
}
142+
}));
143+
144+
IDocument doc = viewer.getDocument();
145+
widget.setCaretOffset(offset);
146+
doc.replace(offset, 0, "\n insert text");
147+
drainEventQueue();
148+
Assert.assertTrue("Line header code mining above 4th line after inserting text not drawn",
149+
waitForCondition(widget.getDisplay(), 2000, new Callable<Boolean>() {
150+
@Override
151+
public Boolean call() throws Exception {
152+
try {
153+
return existsPixelWithNonBackgroundColorAtLine(viewer, 3);
154+
} catch (BadLocationException e) {
155+
e.printStackTrace();
156+
return false;
157+
}
158+
}
159+
}));
160+
}
161+
103162
@Test
104163
public void testCodeMiningOnEmptyLine() throws Exception {
105164
IFile file = project.getFile("test.txt");
@@ -208,12 +267,17 @@ private static boolean existsPixelWithNonBackgroundColorAtLine(ITextViewer viewe
208267
lineLength = 0;
209268
}
210269
int verticalScroolBarWidth = viewer.getTextWidget().getVerticalBar().getThumbBounds().width;
211-
Rectangle lineBounds = widget.getTextBounds(document.getLineOffset(line),
212-
document.getLineOffset(line) + lineLength);
270+
int lineOffset = document.getLineOffset(line);
271+
Rectangle lineBounds = widget.getTextBounds(lineOffset, lineOffset + lineLength);
272+
String lineStr = document.get(lineOffset, lineLength);
213273
Image image = new Image(widget.getDisplay(), widget.getSize().x, widget.getSize().y);
214274
try {
215275
GC gc = new GC(widget);
216276
gc.copyArea(image, 0, 0);
277+
Point textExtent = gc.textExtent(lineStr);
278+
if (lineBounds.height - textExtent.y > textExtent.y) {
279+
lineBounds.height -= textExtent.y;
280+
}
217281
gc.dispose();
218282
ImageData imageData = image.getImageData();
219283
for (int x = lineBounds.x + 1; x < image.getBounds().width - verticalScroolBarWidth

tests/org.eclipse.ui.editors.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTestProvider.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
public class CodeMiningTestProvider extends AbstractCodeMiningProvider {
3030
public static int provideHeaderMiningAtLine = -1;
3131
public static int provideContentMiningAtOffset = -1;
32-
32+
public static String lineHeaderMiningText;
3333
@Override
3434
public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer,
3535
IProgressMonitor monitor) {
@@ -42,6 +42,9 @@ public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextVi
4242
minings.add(new LineHeaderCodeMining(provideHeaderMiningAtLine, viewer.getDocument(), this) {
4343
@Override
4444
public String getLabel() {
45+
if (lineHeaderMiningText != null) {
46+
return lineHeaderMiningText;
47+
}
4548
return "line header mining";
4649
}
4750
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 SAP
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.jface.text.tests.codemining;
12+
13+
import org.eclipse.swt.widgets.Composite;
14+
15+
import org.eclipse.jface.text.source.ISourceViewer;
16+
import org.eclipse.jface.text.source.IVerticalRuler;
17+
import org.eclipse.jface.text.source.projection.ProjectionSupport;
18+
import org.eclipse.jface.text.source.projection.ProjectionViewer;
19+
20+
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
21+
22+
public class TextProjectionTextEditor extends AbstractDecoratedTextEditor {
23+
24+
@Override
25+
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
26+
fAnnotationAccess = getAnnotationAccess();
27+
fOverviewRuler = createOverviewRuler(getSharedColors());
28+
ISourceViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(),
29+
styles);
30+
getSourceViewerDecorationSupport(viewer);
31+
return viewer;
32+
}
33+
34+
@Override
35+
public void createPartControl(Composite parent) {
36+
super.createPartControl(parent);
37+
var projectionViewer = (ProjectionViewer) getSourceViewer();
38+
var projectionSupport = new ProjectionSupport(projectionViewer, getAnnotationAccess(), getSharedColors());
39+
projectionSupport.install();
40+
projectionViewer.enableProjection();
41+
}
42+
}

0 commit comments

Comments
 (0)