From 3d29b58bff119b1fdc8a0b9b32b438d2d82fcc4d Mon Sep 17 00:00:00 2001 From: Martin Jobst Date: Mon, 26 Jan 2026 16:18:55 +0100 Subject: [PATCH 1/2] Use destination URI when creating resource during copy refactoring When performing a COPY refactoring, the ResourceRelocationContext created the resource with the original URI and then changed the URI of the resource after loading. This left any proxy URIs in the loaded resource still pointing to the original file. When later resolving the proxies, a second resource for the original file was created on demand and any local references were resolved into that resource and not the copy. This avoids the problem by creating the resource with the destination URI instead, so that any proxies will be created with the correct URI. --- .../xtext/ide/refactoring/ResourceRelocationContext.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/refactoring/ResourceRelocationContext.java b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/refactoring/ResourceRelocationContext.java index 2ae227f8f38..a88184c7ea7 100644 --- a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/refactoring/ResourceRelocationContext.java +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/refactoring/ResourceRelocationContext.java @@ -65,13 +65,12 @@ protected Resource loadAndWatchResource(ResourceRelocationChange change) { changeSerializer.addModification(original, (Resource it) -> original.setURI(change.getToURI())); return original; case COPY: - Resource copy = resourceSet.createResource(change.getFromURI()); + Resource copy = resourceSet.createResource(change.getToURI()); try { copy.load(resourceSet.getURIConverter().createInputStream(change.getFromURI()), null); } catch (IOException e) { Exceptions.sneakyThrow(e); } - copy.setURI(change.getToURI()); return copy; default: return null; From 730caaebd076e28e5498a47638f19e146708845f Mon Sep 17 00:00:00 2001 From: Martin Jobst Date: Tue, 27 Jan 2026 14:42:29 +0100 Subject: [PATCH 2/2] Add test for copy resource refactoring This adds tests for the copy resource refactoring based on the Java copy processor. --- .../tests/refactoring/ResourceCopyTest.java | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/refactoring/ResourceCopyTest.java diff --git a/org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/refactoring/ResourceCopyTest.java b/org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/refactoring/ResourceCopyTest.java new file mode 100644 index 00000000000..5caf4fd1efe --- /dev/null +++ b/org.eclipse.xtext.ui.tests/src-longrunning/org/eclipse/xtext/ui/tests/refactoring/ResourceCopyTest.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2026 TypeFox GmbH (http://www.typefox.io) and others. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.xtext.ui.tests.refactoring; + +import java.util.stream.Stream; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.ltk.core.refactoring.resource.CopyResourcesDescriptor; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.XtextRunner; +import org.eclipse.xtext.testlanguages.fileAware.ui.tests.FileAwareTestLanguageUiInjectorProvider; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * @author koehnlein - Initial contribution and API + * @author mx990 - adapted from ResourceMoveTest + */ +@InjectWith(FileAwareTestLanguageUiInjectorProvider.class) +@RunWith(XtextRunner.class) +public class ResourceCopyTest extends AbstractResourceRelocationTest { + @Test + public void testCopyFile() throws Exception { + String model1 = + "package foo.bar\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + IFile x = file("foo/bar/X.fileawaretestlanguage", model1); + String model2 = + "package foo\n" + + "element Y {\n" + + " ref bar.X\n" + + "}\n"; + file("foo/Y.fileawaretestlanguage", model2); + performCopy(folder("foo/baz"), x); + Assert.assertTrue(x.exists()); + assertFileContents("foo/bar/X.fileawaretestlanguage", model1); + assertFileContents("foo/Y.fileawaretestlanguage", model2); + String expectation1 = + "package foo.baz\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + assertFileContents("foo/baz/X.fileawaretestlanguage", expectation1); + } + + @Test + public void testCopyFile_2() throws Exception { + String model1 = + "package foo.bar\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + file("foo/bar/X.fileawaretestlanguage", model1); + String model2 = + "package foo\n" + + "element Y {\n" + + " ref bar.X\n" + + "}\n"; + IFile y = file("foo/Y.fileawaretestlanguage", model2); + performCopy(folder("foo/baz"), y); + Assert.assertTrue(y.exists()); + assertFileContents("foo/bar/X.fileawaretestlanguage", model1); + assertFileContents("foo/Y.fileawaretestlanguage", model2); + String expectation1 = + "package foo.baz\n" + + "element Y {\n" + + " ref foo.bar.X\n" + + "}\n"; + assertFileContents("foo/baz/Y.fileawaretestlanguage", expectation1); + } + + @Test + public void testCopyFiles() throws Exception { + String model1 = + "package foo.bar\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + IFile x = file("foo/X.fileawaretestlanguage", model1); + String model2 = + "package foo\n" + + "element Y {\n" + + " ref bar.X\n" + + "}\n"; + IFile y = file("foo/Y.fileawaretestlanguage", model2); + performCopy(folder("foo/baz"), x, y); + Assert.assertTrue(y.exists()); + assertFileContents("foo/X.fileawaretestlanguage", model1); + assertFileContents("foo/Y.fileawaretestlanguage", model2); + String expectation1 = + "package foo.baz\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + assertFileContents("foo/baz/X.fileawaretestlanguage", expectation1); + String expectation2 = + "package foo.baz\n" + + "element Y {\n" + + " ref X\n" + + "}\n"; + assertFileContents("foo/baz/Y.fileawaretestlanguage", expectation2); + } + + @Test + public void testCopyDirectory() throws Exception { + String model1 = + "package foo.bar\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + IFile x = file("foo/bar/X.fileawaretestlanguage", model1); + String model2 = + "package foo\n" + + "element Y {\n" + + " ref bar.X\n" + + "}\n"; + file("foo/Y.fileawaretestlanguage", model2); + performCopy(folder("foo/baz"), x.getParent()); + Assert.assertTrue(x.exists()); + assertFileContents("foo/bar/X.fileawaretestlanguage", model1); + assertFileContents("foo/Y.fileawaretestlanguage", model2); + String expectation1 = + "package foo.baz.bar\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + assertFileContents("foo/baz/bar/X.fileawaretestlanguage", expectation1); + } + + @Test + public void testCopyDirectoryToRoot() throws Exception { + String model1 = + "package foo.bar\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + IFile x = file("foo/bar/X.fileawaretestlanguage", model1); + String model2 = + "package foo\n" + + "element Y {\n" + + " ref bar.X\n" + + "}\n"; + file("foo/Y.fileawaretestlanguage", model2); + performCopy(project, x.getParent()); + Assert.assertTrue(x.exists()); + assertFileContents("foo/bar/X.fileawaretestlanguage", model1); + assertFileContents("foo/Y.fileawaretestlanguage", model2); + String expectation1 = + "package bar\n" + + "element X {\n" + + " ref X\n" + + "}\n"; + assertFileContents("bar/X.fileawaretestlanguage", expectation1); + } + + protected void performCopy(IContainer theDestination, IResource... theResources) throws Exception { + CopyResourcesDescriptor copyResourcesDescriptor = new CopyResourcesDescriptor(); + copyResourcesDescriptor.setResourcePaths(Stream.of(theResources).map(IResource::getFullPath).toArray(IPath[]::new)); + copyResourcesDescriptor.setDestinationPaths(Stream.of(theResources).map(IResource::getFullPath).map(IPath::lastSegment) + .map(theDestination.getFullPath()::append).toArray(IPath[]::new)); + performRefactoring(copyResourcesDescriptor); + } +}