Skip to content

Conversation

@tobiasmelcher
Copy link
Contributor

  • Updated productive code to prevent IllegalArgumentException when replacing text in a StyledText control with variable line height and no focus, by adding boundary checks.
  • Added a regression test to validate the fix: ensures no exception is thrown when replacing the full text range under these conditions.

Fixes: #2302

@tobiasmelcher
Copy link
Contributor Author

@HeikoKlare could you please take a look and review? Do you think that the boundary checks do make sense?

@github-actions
Copy link
Contributor

github-actions bot commented Jul 16, 2025

Test Results

   539 files   -  7     539 suites   - 7   35m 19s ⏱️ + 6m 3s
 4 359 tests  - 53   4 344 ✅  - 51   14 💤  - 3  1 ❌ +1 
16 668 runs   - 50  16 543 ✅  - 48  124 💤  - 3  1 ❌ +1 

For more details on these failures, see this check.

Results for commit 49fa012. ± Comparison against base commit bef3ad7.

This pull request removes 54 and adds 1 tests. Note that renamed tests count towards both.
AllWin32Tests ImageWin32Tests ‑ testDisposeDrawnImageBeforeRequestingTargetForOtherZoom
AllWin32Tests ImageWin32Tests ‑ testDrawImageAtDifferentZooms(boolean)[1] true
AllWin32Tests ImageWin32Tests ‑ testDrawImageAtDifferentZooms(boolean)[2] false
AllWin32Tests ImageWin32Tests ‑ testImageDataForDifferentFractionalZoomsShouldBeDifferent
AllWin32Tests ImageWin32Tests ‑ testImageShouldHaveDimesionAsPerZoomLevel
AllWin32Tests ImageWin32Tests ‑ testRetrieveImageDataAtDifferentZooms(boolean)[1] true
AllWin32Tests ImageWin32Tests ‑ testRetrieveImageDataAtDifferentZooms(boolean)[2] false
AllWin32Tests TestTreeColumn ‑ test_ColumnOrder
AllWin32Tests Test_org_eclipse_swt_dnd_DND ‑ testByteArrayTransfer
AllWin32Tests Test_org_eclipse_swt_dnd_DND ‑ testFileTransfer
…
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_custom_StyledText ‑ test_replaceTextRangeWithVariableHeight

♻️ This comment has been updated with latest results.

@tobiasmelcher tobiasmelcher force-pushed the styled_text_index_out_of_bounds branch from 747348b to 4b1f261 Compare July 16, 2025 15:26
Copy link
Member

@BeckerWdf BeckerWdf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A don't know that code good enough to really be able to judge on this.

@BeckerWdf
Copy link
Member

@HeikoKlare: Do you feel able to review this?

Copy link
Contributor

@mickaelistria mickaelistria left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general idea of adding checks to prevent error is fine.
But I find the repetition of a similar check everywhere is making the code less clear. I've put a few suggestions inline, which I believe can make algorithms slightly clearer to understand, refining the loops instead of adding conditions here and there.

while (lineIndex < lineCount) {
if (delta <= 0) break;
delta -= renderer.getCachedLineHeight(lineIndex++);
if (lineIndex >= 0 && lineIndex < lineCount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lineIndex < lineCount doesn't have to be repeated.
Also the condition lineIndex >= 0 can be skipped if we initialize lineIndex to Math.max(0, topIndex)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done with 9ff6acd

Comment on lines 1100 to 1104
int topIndexHeight = 0;
if (topIndex >= 0 && topIndex < lineCount) {
topIndexHeight = renderer.getCachedLineHeight(topIndex);
}
topIndexY = -topIndexHeight - delta;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be clearer to write it like

if (topIndex >= 0 && topIndex < lineCount) {
   topIndexY = -renderer.getCachedLineHeight(topIndex);
}
topIndexY -= delta;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done with 9ff6acd

return topIndexY + topMargin;
int height = topIndexY;
if (lineIndex > topIndex) {
for (int i = topIndex; i < lineIndex; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use

for (int i = Math.max(topIndex, 0); i < Math.min(lineIndex, lineCount); i++) {

here and get rid of next conditions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done with 9ff6acd

@HeikoKlare
Copy link
Contributor

Sorry, I think I am not the best to review here as I am not that deep into the StyledText. Thanks to @mickaelistria for jumping in and reviewing! In case you need a second look from my side, please ping me again.

@tobiasmelcher
Copy link
Contributor Author

thanks a lot @mickaelistria for reviewing the code. Do you think that we can merge this change?

@tobiasmelcher tobiasmelcher force-pushed the styled_text_index_out_of_bounds branch from 9ff6acd to 8fc5cf8 Compare July 28, 2025 08:45
Copy link
Contributor

@mickaelistria mickaelistria left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(sorry for nitpicking, but I have sad memories of fighting with this code and can't resist the discussion)
I have put some comments inline about possible ways to make code clearer.

}
} else {
for (int i = topIndex - 1; i >= lineIndex; i--) {
if (i < 0 || i >= lineCount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition could also be merged in the loop, just like above (with the trick that te loop is reversed here)

for (int i - Math.min(topIndex - 1, lineCount); i >= Math.max(0, lineIndex); i--) {

or, if you prefer expressive names

int lastLineToConsider = Math.min(topIndex - 1, lineCount);
int firstLineToConsider = Math.max(0, lineIndex);
for (int i = lastLineToConsider; i >= firstLineToConsider; i--) {
...

which actually isn't so interesting to get in reverse order, it could easily be

for (int i = firstLineToConsider, i <= lastLineToConsider; i++)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done with 4d9d7e6; looks like that you were testing me with int lastLineToConsider = Math.min(topIndex - 1, lineCount); if I understood the algorithm :-) But luckily, the test detected the off-by-one error.

@mickaelistria
Copy link
Contributor

Great, thanks!
I am also wondering whether it would make things clearer to create a simple boolean lineExists(int lineNumber) { return lineNumber >= 0 && lineNumber < lineCount); } method to reuse here and there. What do you think?

@tobiasmelcher
Copy link
Contributor Author

Great, thanks! I am also wondering whether it would make things clearer to create a simple boolean lineExists(int lineNumber) { return lineNumber >= 0 && lineNumber < lineCount); } method to reuse here and there. What do you think?

done with [95618eb] - could you please check? Did I catch all locations? I hope I did not introduce any error with this change.

}
if (lineIndex == 0 || -delta + renderer.getCachedLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
int lineHeight = 0;
if (lineIndex >= 0 && lineIndex < lineCount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be lineExists(lineIndex)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot. Sorry my fault, I was searching for all occurrences of "lineCount" but somehow missed this location. Code updated with [99ae962].

int lineHeight = renderer.getCachedLineHeight(lineIndex - 1);
int previousLineIndex = lineIndex - 1;
int lineHeight = 0;
if (previousLineIndex >= 0 && previousLineIndex < lineCount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be lineExists(lineIndex)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot. Sorry my fault, I was searching for all occurrences of "lineCount" but somehow missed this location. Code updated with [99ae962].

} else {
topIndex = lineIndex - 1;
topIndexY = -renderer.getCachedLineHeight(topIndex) - delta;
if (topIndex >= 0 && topIndex < lineCount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be lineExists(lineIndex)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot. Sorry my fault, I was searching for all occurrences of "lineCount" but somehow missed this location. Code updated with [99ae962].

lineIndex++;
}
int lineHeight = 0;
if (lineIndex >= 0 && lineIndex < lineCount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be lineExists(lineIndex)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot. Sorry my fault, I was searching for all occurrences of "lineCount" but somehow missed this location. Code updated with [99ae962].

@mickaelistria
Copy link
Contributor

Thanks, let's merge it now!

@mickaelistria
Copy link
Contributor

@tobiasmelcher Can you please squash those commits as a single one and push --force on your branch to update the PR?

height

- Updated productive code to prevent IllegalArgumentException when
replacing text in a StyledText control with variable line height and no
focus, by adding boundary checks.
- Added a regression test to validate the fix: ensures no exception is
thrown when replacing the full text range under these conditions.

Fixes: eclipse-platform#2302
@tobiasmelcher tobiasmelcher force-pushed the styled_text_index_out_of_bounds branch from 99ae962 to 49fa012 Compare July 30, 2025 11:01
@tobiasmelcher
Copy link
Contributor Author

test error seems not to be related to this change

Error: Tests run: 388, Failures: 1, Errors: 0, Skipped: 6, Time elapsed: 105.7 s <<< FAILURE! -- in
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser
Error: org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser.test_setUrl_remote[browser flags: 0] -- Time elapsed: >> 37.11 s <<< FAILURE!

@mickaelistria mickaelistria merged commit c0dbf00 into eclipse-platform:master Jul 30, 2025
15 of 17 checks passed
@mickaelistria
Copy link
Contributor

Thank you!

@BeckerWdf
Copy link
Member

Thanks @tobiasmelcher for fixing and thanks @mickaelistria for reviewing and merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression for programmatic deletion of text from non-focused StyledText with non-fixed line-height

5 participants