From 45d8078b823dc6fc92c6a413af7d688b9442a55f Mon Sep 17 00:00:00 2001 From: Mehmet Emin Karaman Date: Mon, 22 Jul 2024 12:44:22 +0200 Subject: [PATCH] Fix editor sync bug, when model file was edited outside of eclipse. This bugfix contains the fix for unsync workspace files wrt. the file system (see the DirtyStateEditorSupportIntegrationTest.testModifyFileInExternEditor) and a one liner for beeing able to save the file when the changes were ignored. (see the ignored test case DirtyStateEditorSupportIntegrationTest.testModifyDirtyFileInExternEditor). The issue for this fix can be found here: https://github.com/eclipse/xtext/issues/2385 --- ...irtyStateEditorSupportIntegrationTest.java | 73 +++++++++++++++++++ .../eclipse/xtext/ui/editor/XtextEditor.java | 16 ++++ .../editor/model/XtextDocumentProvider.java | 14 ---- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/editor/DirtyStateEditorSupportIntegrationTest.java b/org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/editor/DirtyStateEditorSupportIntegrationTest.java index 7aff6f28075..c6984cb1f1c 100644 --- a/org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/editor/DirtyStateEditorSupportIntegrationTest.java +++ b/org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/editor/DirtyStateEditorSupportIntegrationTest.java @@ -8,28 +8,36 @@ */ package org.eclipse.xtext.ui.tests.editor; +import java.io.File; +import java.io.FileWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.util.URI; import org.eclipse.jface.text.IUndoManager; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.editors.text.FileDocumentProvider; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.IResourceDescription; import org.eclipse.xtext.resource.IResourceServiceProvider; import org.eclipse.xtext.ui.editor.XtextEditor; import org.eclipse.xtext.ui.editor.XtextSourceViewer; +import org.eclipse.xtext.ui.editor.model.IXtextDocument; import org.eclipse.xtext.ui.refactoring.ui.SyncUtil; import org.eclipse.xtext.ui.testing.AbstractEditorTest; import org.eclipse.xtext.ui.testing.util.IResourcesSetupUtil; import org.junit.Assert; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import com.google.common.collect.Iterables; @@ -116,7 +124,72 @@ public void testUndoRedo() throws Exception { .getQualifiedName().getLastSegment()); Iterables.getLast(events); } + + /* + * @see https://github.com/eclipse/xtext/issues/2385 + */ + @Test public void testModifyFileInExternEditor() throws Exception { + IXtextDocument document = editor.getDocument(); + + Display.getDefault().readAndDispatch(); + + assertNotEquals(document.get(), ""); + assertTrue(editor.getDocumentProvider() instanceof FileDocumentProvider); + + FileDocumentProvider fileDocumentProvider = (FileDocumentProvider)editor.getDocumentProvider(); + + getWorkbench().getActiveWorkbenchWindow().getActivePage().getViewReferences()[0].getView(false).setFocus(); + + File externalEditFile = ((IFileEditorInput)editor.getEditorInput()).getFile().getLocation().toFile(); + try (FileWriter fw = new FileWriter(externalEditFile)) { + fw.write(""); + } + + assertFalse(fileDocumentProvider.isSynchronized(editor.getEditorInput())); + + editor.setFocus(); + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, null); + syncUtil.yieldToQueuedDisplayJobs(new NullProgressMonitor()); + syncUtil.waitForReconciler(editor); + syncUtil.waitForBuild(new NullProgressMonitor()); + + assertTrue(fileDocumentProvider.isSynchronized(editor.getEditorInput())); + + assertEquals(document.get(), ""); + } + /** + * This test needs a manual button click. + */ + @Ignore + @Test public void testModifyDirtyFileInExternEditor() throws Exception { + IXtextDocument document = editor.getDocument(); + + Display.getDefault().readAndDispatch(); + + assertNotEquals(document.get(), ""); + document.set("/* Hello World! */"); + assertEquals("/* Hello World! */", document.get()); + assertTrue(editor.isDirty()); + + getWorkbench().getActiveWorkbenchWindow().getActivePage().getViewReferences()[0].getView(false).setFocus(); + + File externalEditFile = ((IFileEditorInput)editor.getEditorInput()).getFile().getLocation().toFile(); + try (FileWriter fw = new FileWriter(externalEditFile)) { + fw.write(""); + } + editor.setFocus(); + // Dialog opens and the "Ignore file change" button has to be clicked. + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, null); + syncUtil.yieldToQueuedDisplayJobs(new NullProgressMonitor()); + syncUtil.waitForReconciler(editor); + assertEquals(document.get(), "/* Hello World! */"); + + assertTrue(editor.isDirty()); + editor.doSave(new NullProgressMonitor()); + assertFalse(editor.isDirty()); + } + private IEObjectDescription getFirstExportedObjectInLastEventDelta() { return Iterables.getFirst(Iterables.getLast(events).getDeltas().get(0).getNew().getExportedObjects(), null); } diff --git a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/XtextEditor.java b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/XtextEditor.java index 11159105a22..c1fd4b599e8 100755 --- a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/XtextEditor.java +++ b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/XtextEditor.java @@ -76,6 +76,8 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.IDocumentProviderExtension3; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; @@ -327,6 +329,20 @@ public void doSave(IProgressMonitor progressMonitor) { callback.afterSave(this); } + @Override + protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) { + super.performSave(overwrite || isSynchronized(), progressMonitor); + } + + + private boolean isSynchronized() { + IResource resource = getResource(); + if (resource != null) { + return resource.isSynchronized(IResource.DEPTH_ZERO); + } + return false; + } + @Override protected void updateState(final IEditorInput input) { new DisplayRunnable() { diff --git a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/model/XtextDocumentProvider.java b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/model/XtextDocumentProvider.java index c8a804bf3ca..9fc94d3e1fc 100644 --- a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/model/XtextDocumentProvider.java +++ b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/model/XtextDocumentProvider.java @@ -408,20 +408,6 @@ public long getModificationStamp(Object element) { } } - @Override - public boolean isSynchronized(Object element) { - ElementInfo info = getElementInfo(element); - long synchronizationStamp; - if (info instanceof FileInfo) { - synchronizationStamp = ((FileInfo) info).fModificationStamp; - } else if (info instanceof URIInfo) { - synchronizationStamp = ((URIInfo) info).synchronizationStamp; - } else { - return super.isSynchronized(element); - } - return synchronizationStamp == getModificationStamp(element); - } - @Override public boolean isModifiable(Object element) { if (isWorkspaceExternalEditorInput(element)) {