Skip to content

Commit ae9e870

Browse files
tobias-melcherBeckerWdf
authored andcommitted
WhitespaceCharacterPainter: fix off-by-one error at CR and LF offsets
1 parent 929564c commit ae9e870

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/WhitespaceCharacterPainter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
407407
if (fShowCarriageReturn) {
408408
if (visibleChar.length() > 0 && cache.contains(fTextWidget, lineOffset + textOffset)) {
409409
textOffset--;
410+
delta--;
410411
break;
411412
}
412413
visibleChar.append(CARRIAGE_RETURN_SIGN);
@@ -420,6 +421,7 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
420421
if (fShowLineFeed) {
421422
if (visibleChar.length() > 0 && cache.contains(fTextWidget, lineOffset + textOffset)) {
422423
textOffset--;
424+
delta--;
423425
break;
424426
}
425427
visibleChar.append(LINE_FEED_SIGN);

tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/TestWhitespaceCharacterPainter.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111
*******************************************************************************/
1212
package org.eclipse.jface.text.tests;
1313

14+
import static org.junit.Assert.assertEquals;
15+
import static org.junit.Assert.assertNotEquals;
1416
import static org.mockito.ArgumentMatchers.anyBoolean;
1517
import static org.mockito.ArgumentMatchers.anyInt;
1618
import static org.mockito.ArgumentMatchers.anyString;
19+
import static org.mockito.Mockito.doAnswer;
1720
import static org.mockito.Mockito.mock;
1821
import static org.mockito.Mockito.times;
1922
import static org.mockito.Mockito.verify;
2023
import static org.mockito.Mockito.when;
2124

25+
import java.util.ArrayList;
2226
import java.util.Arrays;
2327
import java.util.List;
2428

@@ -63,6 +67,28 @@ public void after() {
6367
shell.dispose();
6468
}
6569

70+
@Test
71+
public void multipleSpacesAfterNewLine() throws Exception {
72+
List<DrawStringParams> params= collectDrawStringParamsInPaintControl("\n \n ", Arrays.asList(6));
73+
assertEquals(4, params.size());
74+
DrawStringParams first= params.get(0);
75+
assertEquals("\u00b6", first.str);
76+
DrawStringParams second= params.get(1);
77+
assertEquals("\u00b7\u00b7\u00b7\u00b7\u00b7", second.str);
78+
assertNotEquals(first.y, second.y); // y pos of first and second line should be different
79+
}
80+
81+
@Test
82+
public void multipleSpacesAfterCarriageReturn() throws Exception {
83+
List<DrawStringParams> params= collectDrawStringParamsInPaintControl("\r\n \r\n ", Arrays.asList(7));
84+
assertEquals(4, params.size());
85+
DrawStringParams first= params.get(0);
86+
assertEquals("\u00a4\u00b6", first.str);
87+
DrawStringParams second= params.get(1);
88+
assertEquals("\u00b7\u00b7\u00b7\u00b7\u00b7", second.str);
89+
assertNotEquals(first.y, second.y); // y pos of first and second line should be different
90+
}
91+
6692
@Test
6793
public void glyphMetricsTakenIntoAccount() throws Exception {
6894
verifyDrawStringCalledNTimes("first \nsecond \nthird \n", Arrays.asList(6, 15), 5);
@@ -133,6 +159,75 @@ public FontMetrics answer(InvocationOnMock invocation) throws Throwable {
133159
verify(ev.gc, times(times)).drawString(anyString(), anyInt(), anyInt(), anyBoolean());
134160
}
135161

162+
private static final record DrawStringParams(String str, int x, int y) {
163+
}
164+
165+
private List<DrawStringParams> collectDrawStringParamsInPaintControl(String source, List<Integer> styleRangeOffsets) {
166+
SourceViewer sourceViewer= new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER);
167+
sourceViewer.setDocument(new Document(source));
168+
StyledText textWidget= sourceViewer.getTextWidget();
169+
textWidget.setFont(JFaceResources.getTextFont());
170+
WhitespaceCharacterPainter whitespaceCharPainter= new WhitespaceCharacterPainter(sourceViewer, true, true, true, true, true, true, true,
171+
true, true, true, true, 100);
172+
sourceViewer.addPainter(whitespaceCharPainter);
173+
for (Integer offset : styleRangeOffsets) {
174+
textWidget.setStyleRange(createStyleRangeWithMetrics(offset));
175+
}
176+
Event e= new Event();
177+
e.widget= textWidget;
178+
PaintEvent ev= new PaintEvent(e);
179+
180+
ev.gc= mock(GC.class);
181+
when(ev.gc.getClipping()).thenReturn(new Rectangle(0, 0, 100, 100));
182+
when(ev.gc.stringExtent(anyString())).thenAnswer(new Answer<Point>() {
183+
@Override
184+
public Point answer(InvocationOnMock invocation) throws Throwable {
185+
GC gc= new GC(shell);
186+
gc.setFont(JFaceResources.getTextFont());
187+
Point result= gc.stringExtent(invocation.getArgument(0));
188+
gc.dispose();
189+
return result;
190+
}
191+
});
192+
when(ev.gc.textExtent(anyString())).thenAnswer(new Answer<Point>() {
193+
@Override
194+
public Point answer(InvocationOnMock invocation) throws Throwable {
195+
GC gc= new GC(shell);
196+
gc.setFont(JFaceResources.getTextFont());
197+
Point result= gc.textExtent(invocation.getArgument(0));
198+
gc.dispose();
199+
return result;
200+
}
201+
});
202+
when(ev.gc.getFontMetrics()).thenAnswer(new Answer<FontMetrics>() {
203+
@Override
204+
public FontMetrics answer(InvocationOnMock invocation) throws Throwable {
205+
GC gc= new GC(shell);
206+
gc.setFont(JFaceResources.getTextFont());
207+
FontMetrics metrics= gc.getFontMetrics();
208+
gc.dispose();
209+
return metrics;
210+
}
211+
});
212+
List<DrawStringParams> params= new ArrayList<>();
213+
doAnswer(new Answer<Void>() {
214+
@Override
215+
public Void answer(InvocationOnMock invocation) throws Throwable {
216+
String str= invocation.getArgument(0, String.class);
217+
Integer x= invocation.getArgument(1, Integer.class);
218+
Integer y= invocation.getArgument(2, Integer.class);
219+
params.add(new DrawStringParams(str, x, y));
220+
return null;
221+
}
222+
}).when(ev.gc).drawString(anyString(), anyInt(), anyInt(), anyBoolean());
223+
ev.x= 0;
224+
ev.y= 0;
225+
ev.width= 100;
226+
ev.height= 100;
227+
whitespaceCharPainter.paintControl(ev);
228+
return params;
229+
}
230+
136231
private StyleRange createStyleRangeWithMetrics(int start) {
137232
StyleRange sr= new StyleRange();
138233
sr.start= start;

0 commit comments

Comments
 (0)