Skip to content

Conversation

@danthe1st
Copy link
Contributor

@danthe1st danthe1st commented Oct 26, 2025

When collapsing projection regions and then setting the visible region in a certain way and then changing it to the surrounding visible region, it is possible that consecutive ProjectionMapping#toImageLine calls throw an IllegalStateException. This is because ProjectionDocument#internalRemoveMasterDocumentRange adds a Position to the document when a Position with the same offset but a length of 0 can be present already. In this case, ISynchronizableDocument#addPosition (which is supposed to insert it into the sorted array) might insert it before the Position resulting in the fragments being sorted incorrectly (the 0-length fragment after the positive-length fragment with both having the same offset). This can then result in IllegalStateExceptions when calling ProjectionMapping#toImageLine (which happens while drawing line numbers for example).

A similar issue also happens in ProjectionDocument#internalAddMasterDocumentRange. There, it is possible that a region is expanded to the right in a way that it fully surrounds the region on the right. While expansion is normally handled by Segments (ranges in the slave document), the Fragments are not correctly reordered in this case causing computeProjectedMasterRegions(int offsetInMaster, int lengthInMaster) not finding the region (at least that's what happened with my reproducer) and hence not being able to remove it from the editor. In the situation where one Fragment includes (is a "superset" of) another, it should be safe to delete the smaller one fixing the invalid state.

This change fixes these problems.

Standalone reproducer/test

To reproduce the issue, run the test added with this PR without the changes to ProjectionDocument.
Without the fix, the tests throw exceptions similar to the following:

java.lang.IllegalStateException: startLine (6) does not match endLine (12)
	at org.eclipse.jface.text.projection.ProjectionMapping.toImageLine(ProjectionMapping.java:482)
	at org.eclipse.jface.text.tests.ProjectionViewerTest.testImageLineStateAfterSettingVisibleRegionsWithProjectionsSetMethodAndClass(ProjectionViewerTest.java:483)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

and

org.opentest4j.AssertionFailedError: expected: <-1> but was: <8>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:145)
	at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:531)
	at org.eclipse.jface.text.tests.ProjectionViewerTest.testImageLineStateAfterSettingVisibleRegionsWithProjectionsSetDifferentMethods(ProjectionViewerTest.java:543)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Reproducing with JDT (patch)

This can be reproduced with JDT using the code from this PR I wrote following these steps:

  • Enable Window > Preferences > Java > Editor > Only show the selected Java element and folding in Window > Preferences > Java > Editor > Folding
  • Create the following class:
package test;

public class TM {
	void a() {
		
	}
	
	void b() {
		
	}
	
	void c() {
		
	}
}
  • Collapse the b() method
  • Select a() in the outline (does not happen with the other methods) to only show that method
  • Select the TM class in the outline
  • The following exception occurs and line numbers break:
!ENTRY org.eclipse.ui 4 0 2025-10-26 11:10:59.431
!MESSAGE Unhandled event loop exception
!STACK 0
java.lang.IllegalStateException: startLine (6) does not match endLine (12)
	at org.eclipse.jface.text.projection.ProjectionMapping.toImageLine(ProjectionMapping.java:482)
	at org.eclipse.jface.text.TextViewer.modelLine2WidgetLine(TextViewer.java:5397)
	at org.eclipse.jface.text.JFaceTextUtil.modelLineToWidgetLine(JFaceTextUtil.java:254)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.doPaint(LineNumberRulerColumn.java:728)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.doubleBufferPaint(LineNumberRulerColumn.java:703)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.lambda$2(LineNumberRulerColumn.java:616)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:268)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5889)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1665)
	at org.eclipse.swt.widgets.Control.gtk_draw(Control.java:3902)
	at org.eclipse.swt.widgets.Scrollable.gtk_draw(Scrollable.java:394)
	at org.eclipse.swt.widgets.Composite.gtk_draw(Composite.java:507)
	at org.eclipse.swt.widgets.Canvas.gtk_draw(Canvas.java:173)
	at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:2614)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:6868)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:6196)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_do_event(Native Method)
	at org.eclipse.swt.widgets.Display.eventProc(Display.java:1624)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_iteration_do(Native Method)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4542)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1147)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1038)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:677)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1387)

!ENTRY org.eclipse.ui 4 0 2025-10-26 11:11:02.394
!MESSAGE Unhandled event loop exception
!STACK 0
java.lang.IllegalStateException: startLine (6) does not match endLine (12)
	at org.eclipse.jface.text.projection.ProjectionMapping.toImageLine(ProjectionMapping.java:482)
	at org.eclipse.jface.text.TextViewer.modelLine2WidgetLine(TextViewer.java:5397)
	at org.eclipse.jface.text.JFaceTextUtil.modelLineToWidgetLine(JFaceTextUtil.java:254)
	at org.eclipse.jface.internal.text.source.DiffPainter.paintLine(DiffPainter.java:220)
	at org.eclipse.jface.internal.text.source.DiffPainter.paint(DiffPainter.java:160)
	at org.eclipse.jface.text.source.LineNumberChangeRulerColumn.doPaint(LineNumberChangeRulerColumn.java:181)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.lambda$6(LineNumberRulerColumn.java:712)
	at org.eclipse.swt.graphics.Image.drawWithImageGcDrawer(Image.java:1179)
	at org.eclipse.swt.graphics.Image.<init>(Image.java:705)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.newFullBufferImage(LineNumberRulerColumn.java:714)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.doubleBufferPaint(LineNumberRulerColumn.java:701)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.lambda$2(LineNumberRulerColumn.java:616)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:268)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5889)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1665)
	at org.eclipse.swt.widgets.Control.gtk_draw(Control.java:3902)
	at org.eclipse.swt.widgets.Scrollable.gtk_draw(Scrollable.java:394)
	at org.eclipse.swt.widgets.Composite.gtk_draw(Composite.java:507)
	at org.eclipse.swt.widgets.Canvas.gtk_draw(Canvas.java:173)
	at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:2614)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:6868)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:6196)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_do_event(Native Method)
	at org.eclipse.swt.widgets.Display.eventProc(Display.java:1624)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_iteration_do(Native Method)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4542)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1147)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1038)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:677)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1387)

!ENTRY org.eclipse.ui 4 0 2025-10-26 11:11:04.136
!MESSAGE Unhandled event loop exception
!STACK 0
java.lang.IllegalStateException: startLine (6) does not match endLine (12)
	at org.eclipse.jface.text.projection.ProjectionMapping.toImageLine(ProjectionMapping.java:482)
	at org.eclipse.jface.text.TextViewer.modelLine2WidgetLine(TextViewer.java:5397)
	at org.eclipse.jface.text.JFaceTextUtil.modelLineToWidgetLine(JFaceTextUtil.java:254)
	at org.eclipse.jface.internal.text.source.DiffPainter.paintLine(DiffPainter.java:220)
	at org.eclipse.jface.internal.text.source.DiffPainter.paint(DiffPainter.java:160)
	at org.eclipse.jface.text.source.LineNumberChangeRulerColumn.doPaint(LineNumberChangeRulerColumn.java:181)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.lambda$6(LineNumberRulerColumn.java:712)
	at org.eclipse.swt.graphics.Image.drawWithImageGcDrawer(Image.java:1179)
	at org.eclipse.swt.graphics.Image.<init>(Image.java:705)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.newFullBufferImage(LineNumberRulerColumn.java:714)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.doubleBufferPaint(LineNumberRulerColumn.java:701)
	at org.eclipse.jface.text.source.LineNumberRulerColumn.lambda$2(LineNumberRulerColumn.java:616)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:268)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5889)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1665)
	at org.eclipse.swt.widgets.Control.gtk_draw(Control.java:3902)
	at org.eclipse.swt.widgets.Scrollable.gtk_draw(Scrollable.java:394)
	at org.eclipse.swt.widgets.Composite.gtk_draw(Composite.java:507)
	at org.eclipse.swt.widgets.Canvas.gtk_draw(Canvas.java:173)
	at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:2614)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:6868)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:6196)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_do_event(Native Method)
	at org.eclipse.swt.widgets.Display.eventProc(Display.java:1624)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_iteration_do(Native Method)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4542)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1147)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1038)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:677)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
  • If you don't select the TM class in the outline but instead the b() method, the c() method will be shown incorrectly.

@danthe1st danthe1st force-pushed the projection-visible-region-imageLine-exception branch from 8067f5c to 883f03e Compare October 26, 2025 10:25
@github-actions
Copy link
Contributor

github-actions bot commented Oct 26, 2025

Test Results

 3 018 files  ±0   3 018 suites  ±0   2h 23m 29s ⏱️ +25s
 8 236 tests +2   7 987 ✅ +2  249 💤 ±0  0 ❌ ±0 
23 628 runs  +6  22 834 ✅ +6  794 💤 ±0  0 ❌ ±0 

Results for commit c8b2d47. ± Comparison against base commit 3a441d9.

♻️ This comment has been updated with latest results.

@danthe1st
Copy link
Contributor Author

danthe1st commented Oct 26, 2025

Test failures might be #2708 (comment) and #1517

@danthe1st
Copy link
Contributor Author

danthe1st commented Oct 27, 2025

Even with the fix, it seems there is still an issue with the JDT syntax highlighting breaking. As I don't know what causes this issue/#3455, I am marking this PR as a draft for now.

This is actually (at least partially) fixed by #3457 (not sure why and it wasn't intentional but if it works, it works). However, it is still a bit weird in my tests so I'm keeping it as a draft.

@danthe1st danthe1st marked this pull request as ready for review October 27, 2025 17:51
@danthe1st danthe1st marked this pull request as draft October 27, 2025 18:00
@iloveeclipse iloveeclipse force-pushed the projection-visible-region-imageLine-exception branch from 883f03e to dde2668 Compare October 30, 2025 21:13
@danthe1st danthe1st force-pushed the projection-visible-region-imageLine-exception branch 2 times, most recently from 5041a5d to 17cd4dd Compare November 1, 2025 11:27
@danthe1st danthe1st marked this pull request as ready for review November 1, 2025 12:19
@danthe1st danthe1st force-pushed the projection-visible-region-imageLine-exception branch from 17cd4dd to 1b4b381 Compare November 1, 2025 12:20
@danthe1st danthe1st force-pushed the projection-visible-region-imageLine-exception branch from 1b4b381 to c8b2d47 Compare November 1, 2025 12:23
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.

1 participant