eclipse.platform.ui icon indicating copy to clipboard operation
eclipse.platform.ui copied to clipboard

editors opened with IDE.openEditors don't persist restart if never activated

Open jonahgraham opened this issue 10 months ago • 6 comments

Let's make sure issue is not already fixed in latest builds first.

Steps to reproduce

From a fresh installation and clean workspace:

  • Create a project called 'example' with two files, 'file1.txt' and 'file2.txt'
  • Run the code below (only file1 editor will be active + created)
  • Restart the IDE
  • Click on file2's editor tab (an error editor will be opened)

Place this code in a command or similar to run it:

IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject("example");
var files = new IFile[] { project.getFile("file1.txt"), project.getFile("file2.txt") };
try {
	IDE.openEditors(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), files);
} catch (MultiPartInitException e) {
	e.printStackTrace();
}

Screenshots and stack trace

After the restart, when you click on the file2.txt tab you get this:

Image

full stack trace from above screenshot's "Details..."
java.lang.Exception
	at org.eclipse.ui.internal.EditorReference.createErrorPart(EditorReference.java:332)
	at org.eclipse.ui.internal.EditorReference.createPart(EditorReference.java:313)
	at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.createPart(CompatibilityPart.java:304)
	at org.eclipse.ui.internal.e4.compatibility.CompatibilityEditor.createPart(CompatibilityEditor.java:61)
	at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.create(CompatibilityPart.java:342)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:56)
	at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:977)
	at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:939)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalInject(InjectorImpl.java:139)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:386)
	at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:312)
	at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:203)
	at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.createFromBundle(ReflectionContributionFactory.java:90)
	at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.doCreate(ReflectionContributionFactory.java:59)
	at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.create(ReflectionContributionFactory.java:42)
	at org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer.createWidget(ContributedPartRenderer.java:134)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createWidget(PartRenderingEngine.java:991)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:658)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:762)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:727)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:711)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.lambda$0(PartServiceImpl.java:105)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.lambda$0(UIEventHandler.java:38)
	at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
	at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:133)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:5962)
	at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:38)
	at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
	at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:131)
	at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
	at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:44)
	at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
	at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:60)
	at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElementGen(ElementContainerImpl.java:168)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:187)
	at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer.lambda$28(StackRenderer.java:1160)
	at org.eclipse.swt.events.SelectionListener$1.widgetSelected(SelectionListener.java:84)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:265)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5857)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1617)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1643)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1626)
	at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:1373)
	at org.eclipse.swt.custom.CTabFolder.setSelection(CTabFolder.java:3258)
	at org.eclipse.swt.custom.CTabFolder.onMouse(CTabFolder.java:1897)
	at org.eclipse.swt.custom.CTabFolder.lambda$0(CTabFolder.java:331)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5857)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1617)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:5067)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4519)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1151)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1042)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:663)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:570)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:178)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:208)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:143)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:109)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:439)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:271)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:668)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:605)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1481)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1454)
  • [x] I understand reporting an issue to this OSS project does not mandate anyone to fix it. Other contributors may consider the issue, or not, at their own convenience. The most efficient way to get it fixed is that I fix it myself and contribute it back as a good quality patch to the project.

jonahgraham avatar Feb 18 '25 16:02 jonahgraham

@jonahgraham : is this a regression in 4.35?

iloveeclipse avatar Feb 18 '25 16:02 iloveeclipse

@jonahgraham : is this a regression in 4.35?

No.

(More details coming soon, I'm writing some of my findings)

jonahgraham avatar Feb 18 '25 16:02 jonahgraham

IDE.openEditors uses IWorkbenchPage.openEditors(IEditorInput[], String[], int), which is where the bug "really" may be.

I think what is happening is that without the editor being activated, there is no memento being created for it, so nothing is being persisted on restart.

My guess is that IWorkbenchPage.openEditors(IEditorInput[], String[], IMemento[], int, int) works if you supply the IMemento array because then the persisted state is updated (i.e. editor.getPersistedState().put(WorkbenchPartReference.MEMENTO_KEY, writer.toString());)

On restart, without a Memento for the editor, EditorReference doesn't get its descriptor assigned.

jonahgraham avatar Feb 18 '25 16:02 jonahgraham

I thought I could work around the problem by passing in an empty memento because that is what this code implies:

https://github.com/eclipse-platform/eclipse.platform.ui/blob/745385a64d99bfcf8c2a72139c72ee1829599ef2/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/WorkbenchPage.java#L4706-L4716

But it turns out that if block contents is no-op, because outerMem is never used - and even if it was, the memento created is incomplete.

Creating the memento like this does work:


XMLMemento editorMem = XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_EDITOR);
editorMem.putString(IWorkbenchConstants.TAG_ID, editorDescriptions[i]);

IPersistableElement persistable = editorInputs[i].getPersistable();
IMemento inputMem = editorMem.createChild(IWorkbenchConstants.TAG_INPUT);
inputMem.putString(IWorkbenchConstants.TAG_FACTORY_ID, persistable.getFactoryId());
persistable.saveState(inputMem);

But I suspect the code is still incorrect/insufficient as it probably should create the editor reference and let it save the persistable state

jonahgraham avatar Feb 18 '25 17:02 jonahgraham

I think I have tracked down the real problem - EditorRefernces are only being persisted if they have been activated in Workbench.persist:

https://github.com/eclipse-platform/eclipse.platform.ui/blob/5fa25b1127d9a90a2cc7604507845515a69b0d47/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/Workbench.java#L1213-L1214

But you can't just call reference.persist() because if the getEditor(false) is null, then org.eclipse.ui.internal.EditorReference.getEditorState() uses the previously persisted state as the current state - but the problem we have here is there is no previously persisted state. Somewhat of a Catch-22.

jonahgraham avatar Feb 18 '25 17:02 jonahgraham

I don't think I can solve this issue in the near term - but perhaps an api note should be added to IDE.openEditors and IWorkbenchPage.openEditors that references this issue so extenders know that inactivated editors have a problem.

For my immediate issue (part of https://github.com/eclipse-cdt/cdt-lsp/pull/421) I am just going to activate all the editors immediately.

jonahgraham avatar Feb 18 '25 17:02 jonahgraham