diff --git a/bundles/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF b/bundles/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF index 9f0f659919b..0933dc953b7 100644 --- a/bundles/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.ltk.core.refactoring Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.ltk.core.refactoring; singleton:=true -Bundle-Version: 3.15.200.qualifier +Bundle-Version: 3.15.300.qualifier Bundle-Activator: org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/DeleteResourcesProcessor.java b/bundles/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/DeleteResourcesProcessor.java index c77f5e1e9d8..598e49af19b 100644 --- a/bundles/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/DeleteResourcesProcessor.java +++ b/bundles/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/DeleteResourcesProcessor.java @@ -16,11 +16,14 @@ import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -28,6 +31,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory; import org.eclipse.core.filebuffers.FileBuffers; @@ -120,12 +124,30 @@ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws Core @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { - pm.beginTask("", 1); //$NON-NLS-1$ + SubMonitor subMonitor= SubMonitor.convert(pm, fResources.length); try { RefactoringStatus result= new RefactoringStatus(); + boolean lightweightAutoRefresh= Platform.getPreferencesService().getBoolean(ResourcesPlugin.PI_RESOURCES, + ResourcesPlugin.PREF_LIGHTWEIGHT_AUTO_REFRESH, false, null); + + List remainingResources= new ArrayList<>(fResources.length); for (IResource resource : fResources) { - if (!isSynchronizedExcludingLinkedResources(resource)) { + boolean inSync= isSynchronizedExcludingLinkedResources(resource); + if (!inSync && lightweightAutoRefresh && resource.isAccessible()) { + try { + resource.refreshLocal(IResource.DEPTH_INFINITE, subMonitor.split(1)); + } catch (CoreException e) { + // refresh failed; the out-of-sync warning below covers it + } + if (!resource.exists()) { + // deleted externally; the refresh already removed it from the workspace + continue; + } + inSync= isSynchronizedExcludingLinkedResources(resource); + } + remainingResources.add(resource); + if (!inSync) { String pathLabel= BasicElementLabels.getPathLabel(resource.getFullPath(), false); String locationLabel= null; @@ -156,6 +178,9 @@ public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditio result.addWarning(warning); } } + if (remainingResources.size() != fResources.length) { + fResources= remainingResources.toArray(IResource[]::new); + } checkDirtyResources(result); diff --git a/tests/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java b/tests/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java index 8b075f415a7..dbe418d1554 100644 --- a/tests/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java +++ b/tests/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java @@ -19,7 +19,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -29,6 +31,8 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -66,6 +70,7 @@ public void setUp() throws Exception { @AfterEach public void tearDown() throws Exception { + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).remove(ResourcesPlugin.PREF_LIGHTWEIGHT_AUTO_REFRESH); fProject.delete(); } @@ -392,6 +397,65 @@ public void testDeleteRefactoring3_bug343584() throws Exception { } } + @Test + public void testDeleteRefactoringOutOfSync_noAutoRefresh() throws Exception { + setLightweightAutoRefresh(false); + IFolder testFolder= fProject.createFolder("test"); + IFile file= fProject.createFile(testFolder, "myFile.txt", "hello"); + modifyExternally(file); + + RefactoringContext context= createDeleteRefactoringContext(file); + try { + RefactoringStatus status= context.getRefactoring().checkAllConditions(new NullProgressMonitor()); + assertTrue(status.hasWarning(), "expected an out-of-sync warning"); + } finally { + context.dispose(); + } + } + + @Test + public void testDeleteRefactoringOutOfSync_autoRefresh() throws Exception { + setLightweightAutoRefresh(true); + IFolder testFolder= fProject.createFolder("test"); + IFile file= fProject.createFile(testFolder, "myFile.txt", "hello"); + File localFile= modifyExternally(file); + + RefactoringContext context= createDeleteRefactoringContext(file); + try { + Refactoring refactoring= context.getRefactoring(); + RefactoringStatus status= refactoring.checkAllConditions(new NullProgressMonitor()); + assertTrue(status.isOK(), () -> "expected no warning but was: " + status); + + perform(refactoring.createChange(new NullProgressMonitor())); + + assertFalse(file.exists()); + assertFalse(localFile.exists()); + } finally { + context.dispose(); + } + } + + @Test + public void testDeleteRefactoringDeletedExternally_autoRefresh() throws Exception { + setLightweightAutoRefresh(true); + IFolder testFolder= fProject.createFolder("test"); + IFile file= fProject.createFile(testFolder, "myFile.txt", "hello"); + Files.delete(file.getLocation().toFile().toPath()); + + RefactoringContext context= createDeleteRefactoringContext(file); + try { + Refactoring refactoring= context.getRefactoring(); + RefactoringStatus status= refactoring.checkAllConditions(new NullProgressMonitor()); + assertTrue(status.isOK(), () -> "expected no warning but was: " + status); + + perform(refactoring.createChange(new NullProgressMonitor())); + + assertFalse(file.exists()); + } finally { + context.dispose(); + } + } + @Test public void testCopyProjectRefactoring() throws Exception { String content1= "hello"; @@ -419,6 +483,32 @@ public void testCopyProjectRefactoring() throws Exception { assertFalse(targetProject.exists()); } + private void setLightweightAutoRefresh(boolean enabled) { + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).putBoolean(ResourcesPlugin.PREF_LIGHTWEIGHT_AUTO_REFRESH, enabled); + } + + /** + * Modifies the file in the local file system without notifying the workspace, so the file is + * out of sync afterwards. + */ + private File modifyExternally(IFile file) throws IOException { + File localFile= file.getLocation().toFile(); + Files.writeString(localFile.toPath(), "external change"); + assertTrue(localFile.setLastModified(localFile.lastModified() + 5000)); + assertFalse(file.isSynchronized(IResource.DEPTH_ZERO)); + return localFile; + } + + private RefactoringContext createDeleteRefactoringContext(IResource... resources) throws CoreException { + RefactoringContribution contribution= RefactoringCore.getRefactoringContribution(DeleteResourcesDescriptor.ID); + DeleteResourcesDescriptor descriptor= (DeleteResourcesDescriptor) contribution.createDescriptor(); + descriptor.setResources(resources); + RefactoringStatus status= new RefactoringStatus(); + RefactoringContext context= descriptor.createRefactoringContext(status); + assertTrue(status.isOK()); + return context; + } + private Change perform(Change change) throws CoreException { PerformChangeOperation op= new PerformChangeOperation(change); op.run(null);