mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-15 08:18:55 +00:00
[ChangeSerializer] update references with FQNs and semi-FQNs properly
Signed-off-by: Moritz Eysholdt <moritz.eysholdt@typefox.io>
This commit is contained in:
parent
85f07a23c0
commit
6c671f9aaf
16 changed files with 449 additions and 157 deletions
|
@ -235,6 +235,72 @@ class ChangeSerializerTest {
|
|||
'''
|
||||
}
|
||||
|
||||
@Test
|
||||
def void testRenameFqn1() {
|
||||
val fs = new InMemoryURIHandler()
|
||||
fs += "inmemory:/file1.pstl" -> '''
|
||||
#1 r {
|
||||
X refs a1.a2 X.a1.a2 r.X.a1.a2 { a1 { a2 refs a2 { a3 { ref a3 } } } }
|
||||
Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }
|
||||
}
|
||||
'''
|
||||
|
||||
val rs = fs.createResourceSet
|
||||
val model = rs.contents("inmemory:/file1.pstl", Node)
|
||||
|
||||
val serializer = newChangeSerializer()
|
||||
serializer.addModification(model.eResource) [
|
||||
model.children.head.children.head.children.head.name = "b"
|
||||
]
|
||||
Assert.assertEquals(1, model.eResource.resourceSet.resources.size)
|
||||
serializer.endRecordChangesToTextDocuments === '''
|
||||
----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------
|
||||
#1 r {
|
||||
X refs <15:5|a1.b> <21:7|a1.b> <29:9|a1.b> { a1 { <46:2|b> refs <54:2|b> { a3 { ref a3 } } } }
|
||||
Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }
|
||||
}
|
||||
--------------------------------------------------------------------------------
|
||||
15 5 "a1.a2" -> "a1.b"
|
||||
21 7 "X.a1.a2" -> "a1.b"
|
||||
29 9 "r.X.a1.a2" -> "a1.b"
|
||||
46 2 "a2" -> "b"
|
||||
54 2 "a2" -> "b"
|
||||
'''
|
||||
}
|
||||
|
||||
@Test
|
||||
def void testRenameFqn1ValueConversion() {
|
||||
val fs = new InMemoryURIHandler()
|
||||
fs += "inmemory:/file1.pstl" -> '''
|
||||
#1 r {
|
||||
X refs ^a1.^a2 ^X.^a1.^a2 ^r.^X.^a1.^a2 { a1 { a2 refs ^a2 { a3 { ref ^a3 } } } }
|
||||
Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }
|
||||
}
|
||||
'''
|
||||
|
||||
val rs = fs.createResourceSet
|
||||
val model = rs.contents("inmemory:/file1.pstl", Node)
|
||||
|
||||
val serializer = newChangeSerializer()
|
||||
serializer.addModification(model.eResource) [
|
||||
model.children.head.children.head.children.head.name = "b"
|
||||
]
|
||||
Assert.assertEquals(1, model.eResource.resourceSet.resources.size)
|
||||
serializer.endRecordChangesToTextDocuments === '''
|
||||
----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------
|
||||
#1 r {
|
||||
X refs <15:7|a1.b> <23:10|a1.b> <34:13|a1.b> { a1 { <55:2|b> refs <63:3|b> { a3 { ref ^a3 } } } }
|
||||
Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }
|
||||
}
|
||||
--------------------------------------------------------------------------------
|
||||
15 7 "^a1.^a2" -> "a1.b"
|
||||
23 10 "^X.^a1.^a2" -> "a1.b"
|
||||
34 13 "^r.^X.^a1.^a2" -> "a1.b"
|
||||
55 2 "a2" -> "b"
|
||||
63 3 "^a2" -> "b"
|
||||
'''
|
||||
}
|
||||
|
||||
@Test
|
||||
def void testResourceURIChange() {
|
||||
val fs = new InMemoryURIHandler()
|
||||
|
|
|
@ -12,7 +12,6 @@ import org.eclipse.lsp4j.RenameParams
|
|||
import org.eclipse.lsp4j.TextDocumentIdentifier
|
||||
import org.eclipse.xtext.testing.AbstractLanguageServerTest
|
||||
import org.junit.Test
|
||||
import org.junit.Ignore
|
||||
|
||||
/**
|
||||
* @author koehnlein - Initial contribution and API
|
||||
|
@ -45,7 +44,7 @@ class RenameTest2 extends AbstractLanguageServerTest {
|
|||
'''.toString, toExpectation(workspaceEdit))
|
||||
}
|
||||
|
||||
@Test@Ignore
|
||||
@Test
|
||||
def void testRenameContainer() {
|
||||
val model = '''
|
||||
package foo
|
||||
|
@ -66,8 +65,8 @@ class RenameTest2 extends AbstractLanguageServerTest {
|
|||
assertEquals('''
|
||||
changes :
|
||||
Foo.fileawaretestlanguage : Baz [[2, 8] .. [2, 11]]
|
||||
Baz [[3, 9] .. [3, 12]]
|
||||
Baz [[3, 6] .. [3, 9]]
|
||||
Bar [[5, 5] .. [5, 16]]
|
||||
Bar [[6, 5] .. [6, 12]]
|
||||
documentChanges :
|
||||
'''.toString, toExpectation(workspaceEdit))
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@ package org.eclipse.xtext.ide.tests.testlanguage
|
|||
|
||||
import com.google.inject.Binder
|
||||
import com.google.inject.name.Names
|
||||
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdater
|
||||
import org.eclipse.xtext.ide.tests.testlanguage.ide.serializer.PartialSerializationTestLanguageReferenceUpdater
|
||||
import org.eclipse.xtext.resource.IResourceDescriptions
|
||||
import org.eclipse.xtext.resource.impl.LiveShadowedResourceDescriptions
|
||||
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider
|
||||
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdater
|
||||
import org.eclipse.xtext.ide.tests.testlanguage.ide.serializer.PartialSerializationTestLanguageReferenceUpdater
|
||||
import org.eclipse.xtext.ide.tests.testlanguage.scoping.PartialSerializationTestLanguageValueConverter
|
||||
|
||||
/**
|
||||
* Use this class to register components to be used at runtime / without the Equinox extension registry.
|
||||
|
@ -28,4 +29,9 @@ class PartialSerializationTestLanguageRuntimeModule extends AbstractPartialSeria
|
|||
def Class<? extends IReferenceUpdater> bindCleanupStrategy() {
|
||||
return PartialSerializationTestLanguageReferenceUpdater
|
||||
}
|
||||
|
||||
override bindIValueConverterService() {
|
||||
PartialSerializationTestLanguageValueConverter
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.ide.tests.testlanguage.scoping
|
||||
|
||||
import com.google.inject.Inject
|
||||
import org.eclipse.xtext.common.services.DefaultTerminalConverters
|
||||
import org.eclipse.xtext.conversion.IValueConverter
|
||||
import org.eclipse.xtext.conversion.ValueConverter
|
||||
import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
class PartialSerializationTestLanguageValueConverter extends DefaultTerminalConverters {
|
||||
|
||||
@Inject QualifiedNameValueConverter fqnc
|
||||
|
||||
@ValueConverter(rule="QualifiedName")
|
||||
def IValueConverter<String> QualifiedName() {
|
||||
return fqnc
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ import org.eclipse.xtext.testing.InjectWith;
|
|||
import org.eclipse.xtext.testing.XtextRunner;
|
||||
import org.eclipse.xtext.testing.util.InMemoryURIHandler;
|
||||
import org.eclipse.xtext.xbase.lib.Extension;
|
||||
import org.eclipse.xtext.xbase.lib.IterableExtensions;
|
||||
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
|
||||
import org.eclipse.xtext.xbase.lib.Pair;
|
||||
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
|
||||
|
@ -355,6 +356,114 @@ public class ChangeSerializerTest {
|
|||
this._changeSerializerTestHelper.operator_tripleEquals(_endRecordChangesToTextDocuments, _builder_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameFqn1() {
|
||||
final InMemoryURIHandler fs = new InMemoryURIHandler();
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("#1 r {");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
_builder.append("X refs a1.a2 X.a1.a2 r.X.a1.a2 { a1 { a2 refs a2 { a3 { ref a3 } } } }");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
_builder.append("Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }");
|
||||
_builder.newLine();
|
||||
_builder.append("}");
|
||||
_builder.newLine();
|
||||
Pair<String, String> _mappedTo = Pair.<String, String>of("inmemory:/file1.pstl", _builder.toString());
|
||||
this._changeSerializerTestHelper.operator_add(fs, _mappedTo);
|
||||
final ResourceSet rs = this._changeSerializerTestHelper.createResourceSet(fs);
|
||||
final Node model = this._changeSerializerTestHelper.<Node>contents(rs, "inmemory:/file1.pstl", Node.class);
|
||||
final IChangeSerializer serializer = this._changeSerializerTestHelper.newChangeSerializer();
|
||||
final IChangeSerializer.IModification<Resource> _function = (Resource it) -> {
|
||||
Node _head = IterableExtensions.<Node>head(IterableExtensions.<Node>head(IterableExtensions.<Node>head(model.getChildren()).getChildren()).getChildren());
|
||||
_head.setName("b");
|
||||
};
|
||||
serializer.<Resource>addModification(model.eResource(), _function);
|
||||
Assert.assertEquals(1, model.eResource().getResourceSet().getResources().size());
|
||||
Collection<IEmfResourceChange> _endRecordChangesToTextDocuments = this._changeSerializerTestHelper.endRecordChangesToTextDocuments(serializer);
|
||||
StringConcatenation _builder_1 = new StringConcatenation();
|
||||
_builder_1.append("----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("#1 r {");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("\t");
|
||||
_builder_1.append("X refs <15:5|a1.b> <21:7|a1.b> <29:9|a1.b> { a1 { <46:2|b> refs <54:2|b> { a3 { ref a3 } } } }");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("\t");
|
||||
_builder_1.append("Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("}");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("--------------------------------------------------------------------------------");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("15 5 \"a1.a2\" -> \"a1.b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("21 7 \"X.a1.a2\" -> \"a1.b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("29 9 \"r.X.a1.a2\" -> \"a1.b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("46 2 \"a2\" -> \"b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("54 2 \"a2\" -> \"b\"");
|
||||
_builder_1.newLine();
|
||||
this._changeSerializerTestHelper.operator_tripleEquals(_endRecordChangesToTextDocuments, _builder_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameFqn1ValueConversion() {
|
||||
final InMemoryURIHandler fs = new InMemoryURIHandler();
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("#1 r {");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
_builder.append("X refs ^a1.^a2 ^X.^a1.^a2 ^r.^X.^a1.^a2 { a1 { a2 refs ^a2 { a3 { ref ^a3 } } } }");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
_builder.append("Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }");
|
||||
_builder.newLine();
|
||||
_builder.append("}");
|
||||
_builder.newLine();
|
||||
Pair<String, String> _mappedTo = Pair.<String, String>of("inmemory:/file1.pstl", _builder.toString());
|
||||
this._changeSerializerTestHelper.operator_add(fs, _mappedTo);
|
||||
final ResourceSet rs = this._changeSerializerTestHelper.createResourceSet(fs);
|
||||
final Node model = this._changeSerializerTestHelper.<Node>contents(rs, "inmemory:/file1.pstl", Node.class);
|
||||
final IChangeSerializer serializer = this._changeSerializerTestHelper.newChangeSerializer();
|
||||
final IChangeSerializer.IModification<Resource> _function = (Resource it) -> {
|
||||
Node _head = IterableExtensions.<Node>head(IterableExtensions.<Node>head(IterableExtensions.<Node>head(model.getChildren()).getChildren()).getChildren());
|
||||
_head.setName("b");
|
||||
};
|
||||
serializer.<Resource>addModification(model.eResource(), _function);
|
||||
Assert.assertEquals(1, model.eResource().getResourceSet().getResources().size());
|
||||
Collection<IEmfResourceChange> _endRecordChangesToTextDocuments = this._changeSerializerTestHelper.endRecordChangesToTextDocuments(serializer);
|
||||
StringConcatenation _builder_1 = new StringConcatenation();
|
||||
_builder_1.append("----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("#1 r {");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("\t");
|
||||
_builder_1.append("X refs <15:7|a1.b> <23:10|a1.b> <34:13|a1.b> { a1 { <55:2|b> refs <63:3|b> { a3 { ref ^a3 } } } }");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("\t");
|
||||
_builder_1.append("Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("}");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("--------------------------------------------------------------------------------");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("15 7 \"^a1.^a2\" -> \"a1.b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("23 10 \"^X.^a1.^a2\" -> \"a1.b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("34 13 \"^r.^X.^a1.^a2\" -> \"a1.b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("55 2 \"a2\" -> \"b\"");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("63 3 \"^a2\" -> \"b\"");
|
||||
_builder_1.newLine();
|
||||
this._changeSerializerTestHelper.operator_tripleEquals(_endRecordChangesToTextDocuments, _builder_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceURIChange() {
|
||||
final InMemoryURIHandler fs = new InMemoryURIHandler();
|
||||
|
|
|
@ -14,7 +14,6 @@ import org.eclipse.lsp4j.WorkspaceEdit;
|
|||
import org.eclipse.xtend2.lib.StringConcatenation;
|
||||
import org.eclipse.xtext.testing.AbstractLanguageServerTest;
|
||||
import org.eclipse.xtext.xbase.lib.Exceptions;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -65,7 +64,6 @@ public class RenameTest2 extends AbstractLanguageServerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testRenameContainer() {
|
||||
try {
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
|
@ -105,10 +103,10 @@ public class RenameTest2 extends AbstractLanguageServerTest {
|
|||
_builder_1.append("Foo.fileawaretestlanguage : Baz [[2, 8] .. [2, 11]]");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append(" ");
|
||||
_builder_1.append("Baz [[3, 9] .. [3, 12]]");
|
||||
_builder_1.append("Bar [[5, 5] .. [5, 16]]");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append(" ");
|
||||
_builder_1.append("Baz [[3, 6] .. [3, 9]]");
|
||||
_builder_1.append("Bar [[6, 5] .. [6, 12]]");
|
||||
_builder_1.newLine();
|
||||
_builder_1.append("documentChanges : ");
|
||||
_builder_1.newLine();
|
||||
|
|
|
@ -9,9 +9,11 @@ package org.eclipse.xtext.ide.tests.testlanguage;
|
|||
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.name.Names;
|
||||
import org.eclipse.xtext.conversion.IValueConverterService;
|
||||
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdater;
|
||||
import org.eclipse.xtext.ide.tests.testlanguage.AbstractPartialSerializationTestLanguageRuntimeModule;
|
||||
import org.eclipse.xtext.ide.tests.testlanguage.ide.serializer.PartialSerializationTestLanguageReferenceUpdater;
|
||||
import org.eclipse.xtext.ide.tests.testlanguage.scoping.PartialSerializationTestLanguageValueConverter;
|
||||
import org.eclipse.xtext.resource.IResourceDescriptions;
|
||||
import org.eclipse.xtext.resource.impl.LiveShadowedResourceDescriptions;
|
||||
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider;
|
||||
|
@ -30,4 +32,9 @@ public class PartialSerializationTestLanguageRuntimeModule extends AbstractParti
|
|||
public Class<? extends IReferenceUpdater> bindCleanupStrategy() {
|
||||
return PartialSerializationTestLanguageReferenceUpdater.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IValueConverterService> bindIValueConverterService() {
|
||||
return PartialSerializationTestLanguageValueConverter.class;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ public class PartialSerializationTestLanguageReferenceUpdater extends ReferenceU
|
|||
List<IUpdatableReference> _updatableReferences = context.getUpdatableReferences();
|
||||
for (final IUpdatableReference target : _updatableReferences) {
|
||||
{
|
||||
final EObjectDescriptionDeltaProvider.Delta delta = context.getEObjectDescriptionDeltas().findContainingDelta(target.getTargetEObject());
|
||||
final EObjectDescriptionDeltaProvider.Delta delta = this.findContainingDelta(context.getEObjectDescriptionDeltas(), target.getTargetEObject());
|
||||
if ((delta != null)) {
|
||||
final QualifiedName original = IterableExtensions.<IEObjectDescription>head(delta.getSnapshot().getDescriptions()).getQualifiedName();
|
||||
final QualifiedName modified = IterableExtensions.<IEObjectDescription>head(delta.getDescriptions()).getQualifiedName();
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.xtext.ide.tests.testlanguage.scoping;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import org.eclipse.xtext.common.services.DefaultTerminalConverters;
|
||||
import org.eclipse.xtext.conversion.IValueConverter;
|
||||
import org.eclipse.xtext.conversion.ValueConverter;
|
||||
import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public class PartialSerializationTestLanguageValueConverter extends DefaultTerminalConverters {
|
||||
@Inject
|
||||
private QualifiedNameValueConverter fqnc;
|
||||
|
||||
@ValueConverter(rule = "QualifiedName")
|
||||
public IValueConverter<String> QualifiedName() {
|
||||
return this.fqnc;
|
||||
}
|
||||
}
|
|
@ -9,8 +9,6 @@ package org.eclipse.xtext.ide.serializer.hooks;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.EReference;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionDiffBuilder;
|
||||
import org.eclipse.xtext.ide.serializer.impl.EObjectDescriptionDeltaProvider.Deltas;
|
||||
import org.eclipse.xtext.resource.XtextResource;
|
||||
|
@ -33,7 +31,5 @@ public interface IReferenceUpdaterContext {
|
|||
|
||||
void modifyModel(Runnable runnable);
|
||||
|
||||
void updateReference(EObject owner, EReference reference);
|
||||
|
||||
void updateReference(EObject owner, EReference reference, int index);
|
||||
void updateReference(IUpdatableReference updatableReference);
|
||||
}
|
|
@ -12,6 +12,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.util.EcoreUtil;
|
||||
|
@ -34,45 +35,118 @@ public class EObjectDescriptionDeltaProvider {
|
|||
public static class Delta {
|
||||
private List<IEObjectDescription> descriptions = Lists.newArrayList();
|
||||
private final EObject object;
|
||||
private boolean qualifiedNameChanged;
|
||||
private boolean simpleNameAndUserDataChanged;
|
||||
private IEObjectSnapshot snapshot;
|
||||
|
||||
public Delta(EObject object) {
|
||||
public Delta(EObject object, IEObjectSnapshot snapshot, List<IEObjectDescription> descriptions) {
|
||||
super();
|
||||
this.object = object;
|
||||
this.snapshot = snapshot;
|
||||
this.descriptions = descriptions;
|
||||
this.simpleNameAndUserDataChanged = !forAll(this::isSimpleNameAndUserdataEqual);
|
||||
this.qualifiedNameChanged = !forAll(this::isQualifiedNameEqual);
|
||||
}
|
||||
|
||||
protected boolean forAll(BiFunction<IEObjectDescription, IEObjectDescription, Boolean> comparator) {
|
||||
if (snapshot == null) {
|
||||
return false;
|
||||
}
|
||||
List<IEObjectDescription> snap = snapshot.getDescriptions();
|
||||
if (snap == null && descriptions == null) {
|
||||
return true;
|
||||
}
|
||||
if (snap == null || descriptions == null) {
|
||||
return false;
|
||||
}
|
||||
int size = descriptions.size();
|
||||
if (size == 0 && snap.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (size != snap.size()) {
|
||||
return false;
|
||||
}
|
||||
if (size == 1) {
|
||||
return comparator.apply(snap.get(0), descriptions.get(0));
|
||||
}
|
||||
Set<IEObjectDescription> candidates = Sets.newHashSet(snap);
|
||||
FOR: for (IEObjectDescription d : descriptions) {
|
||||
Iterator<IEObjectDescription> it = candidates.iterator();
|
||||
while (it.hasNext()) {
|
||||
IEObjectDescription s = it.next();
|
||||
if (comparator.apply(d, s)) {
|
||||
it.remove();
|
||||
continue FOR;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<IEObjectDescription> getDescriptions() {
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
public EObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public IEObjectSnapshot getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public EObject getObject() {
|
||||
return object;
|
||||
public boolean hasQualifiedNameChanged() {
|
||||
return qualifiedNameChanged;
|
||||
}
|
||||
|
||||
public boolean hasSimpleNameOrUserdataChanged() {
|
||||
return simpleNameAndUserDataChanged;
|
||||
}
|
||||
|
||||
protected boolean isQualifiedNameEqual(IEObjectDescription desc1, IEObjectDescription desc2) {
|
||||
return desc1.getQualifiedName().equals(desc2.getQualifiedName());
|
||||
}
|
||||
|
||||
protected boolean isSimpleNameAndUserdataEqual(IEObjectDescription oldDesc, IEObjectDescription newDesc) {
|
||||
if (!oldDesc.getName().equals(newDesc.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (!isUserDataEqual(oldDesc, newDesc)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isUserDataEqual(IEObjectDescription oldObj, IEObjectDescription newObj) {
|
||||
String[] oldKeys = oldObj.getUserDataKeys();
|
||||
String[] newKeys = newObj.getUserDataKeys();
|
||||
if (oldKeys.length != newKeys.length)
|
||||
return false;
|
||||
for (String key : oldKeys) {
|
||||
if (!Arrays.contains(newKeys, key))
|
||||
return false;
|
||||
String oldValue = oldObj.getUserData(key);
|
||||
String newValue = newObj.getUserData(key);
|
||||
if (!Objects.equal(oldValue, newValue))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Deltas {
|
||||
private Collection<IResourceSnapshot> snapshots;
|
||||
private final Map<EObject, Delta> deltas = Maps.newLinkedHashMap();
|
||||
private Collection<IResourceSnapshot> snapshots;
|
||||
|
||||
public Delta getDelta(EObject obj) {
|
||||
return deltas.get(obj);
|
||||
}
|
||||
|
||||
public Collection<IResourceSnapshot> getSnapshots() {
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
public Delta findContainingDelta(EObject obj) {
|
||||
EObject current = obj;
|
||||
while (current != null) {
|
||||
Delta delta = deltas.get(current);
|
||||
if (delta != null) {
|
||||
return delta;
|
||||
}
|
||||
current = current.eContainer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Group {
|
||||
|
@ -87,37 +161,7 @@ public class EObjectDescriptionDeltaProvider {
|
|||
}
|
||||
|
||||
protected Delta createDelta(EObject object, IEObjectSnapshot snapshot, List<IEObjectDescription> descriptions) {
|
||||
Delta delta = new Delta(object);
|
||||
delta.descriptions = descriptions;
|
||||
delta.snapshot = snapshot;
|
||||
if (snapshot == null || snapshot.getDescriptions() == null || delta.descriptions == null) {
|
||||
return delta;
|
||||
}
|
||||
if (delta.descriptions.size() != snapshot.getDescriptions().size()) {
|
||||
return delta;
|
||||
}
|
||||
if (delta.snapshot.getDescriptions().size() == 1 && delta.descriptions.size() == 1) {
|
||||
if (!simpleNameAndUserDataEquals(delta.snapshot.getDescriptions().get(0), delta.descriptions.get(0))) {
|
||||
return delta;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Set<IEObjectDescription> remaining = Sets.newHashSet(snapshot.getDescriptions());
|
||||
FOR: for (IEObjectDescription desc : descriptions) {
|
||||
Iterator<IEObjectDescription> it = remaining.iterator();
|
||||
while (it.hasNext()) {
|
||||
IEObjectDescription next = it.next();
|
||||
if (simpleNameAndUserDataEquals(desc, next)) {
|
||||
it.remove();
|
||||
continue FOR;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (remaining.isEmpty()) {
|
||||
return delta;
|
||||
}
|
||||
return null;
|
||||
return new Delta(object, snapshot, descriptions);
|
||||
}
|
||||
|
||||
public Deltas getDelta(ChangeSerializer serializer, Collection<IResourceSnapshot> snapshots) {
|
||||
|
@ -144,37 +188,11 @@ public class EObjectDescriptionDeltaProvider {
|
|||
result.snapshots = snapshots;
|
||||
for (Group g : groups.values()) {
|
||||
Delta delta = createDelta(g.object, g.snapshot, g.descriptions);
|
||||
if (delta != null) {
|
||||
if (delta.hasQualifiedNameChanged() || delta.hasSimpleNameOrUserdataChanged()) {
|
||||
result.deltas.put(delta.object, delta);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean simpleNameAndUserDataEquals(IEObjectDescription oldDesc, IEObjectDescription newDesc) {
|
||||
if (!oldDesc.getName().equals(newDesc.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (!userDataEuqals(oldDesc, newDesc)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean userDataEuqals(IEObjectDescription oldObj, IEObjectDescription newObj) {
|
||||
String[] oldKeys = oldObj.getUserDataKeys();
|
||||
String[] newKeys = newObj.getUserDataKeys();
|
||||
if (oldKeys.length != newKeys.length)
|
||||
return false;
|
||||
for (String key : oldKeys) {
|
||||
if (!Arrays.contains(newKeys, key))
|
||||
return false;
|
||||
String oldValue = oldObj.getUserData(key);
|
||||
String newValue = newObj.getUserData(key);
|
||||
if (!Objects.equal(oldValue, newValue))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,12 +9,14 @@ package org.eclipse.xtext.ide.serializer.impl;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.emf.common.util.TreeIterator;
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.EReference;
|
||||
import org.eclipse.xtext.EcoreUtil2;
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.xtext.CrossReference;
|
||||
import org.eclipse.xtext.GrammarUtil;
|
||||
import org.eclipse.xtext.conversion.IValueConverterService;
|
||||
import org.eclipse.xtext.conversion.ValueConverterException;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionDiffBuilder;
|
||||
import org.eclipse.xtext.ide.serializer.hooks.IReferenceSnapshot;
|
||||
|
@ -39,6 +41,9 @@ import com.google.inject.Inject;
|
|||
*/
|
||||
public class ReferenceUpdater implements IReferenceUpdater {
|
||||
|
||||
@Inject
|
||||
private IQualifiedNameConverter converter;
|
||||
|
||||
@Inject
|
||||
private LinkingHelper linkingHelper;
|
||||
|
||||
|
@ -51,6 +56,52 @@ public class ReferenceUpdater implements IReferenceUpdater {
|
|||
@Inject
|
||||
private IValueConverterService valueConverter;
|
||||
|
||||
protected boolean containsReferenceText(Delta delta, QualifiedName exp) {
|
||||
DESC: for (IEObjectDescription desc : delta.getDescriptions()) {
|
||||
QualifiedName cand = desc.getQualifiedName();
|
||||
if (cand.getSegmentCount() >= exp.getSegmentCount()) {
|
||||
for (int i = 1; i <= exp.getSegmentCount(); i++) {
|
||||
String expSeg = exp.getSegment(exp.getSegmentCount() - i);
|
||||
String candSeg = cand.getSegment(cand.getSegmentCount() - i);
|
||||
if (!expSeg.equals(candSeg)) {
|
||||
continue DESC;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected IUpdatableReference createUpdatableReference(ISemanticRegion current) {
|
||||
EReference ref = (EReference) current.getContainingFeature();
|
||||
CrossReference crossRef = GrammarUtil.containingCrossReference(current.getGrammarElement());
|
||||
EObject owner = current.getContainingRegion().getSemanticElement();
|
||||
Object value = owner.eGet(ref);
|
||||
if (value instanceof List<?>) {
|
||||
List<?> targets = (List<?>) value;
|
||||
int i = current.getIndexInContainingFeature();
|
||||
EObject t = (EObject) targets.get(i);
|
||||
return new UpdatableReference(owner, ref, i, t, crossRef, current);
|
||||
} else if (value instanceof EObject) {
|
||||
return new UpdatableReference(owner, ref, -1, (EObject) value, crossRef, current);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public Delta findContainingDelta(Deltas deltas, EObject obj) {
|
||||
EObject current = obj;
|
||||
while (current != null) {
|
||||
Delta delta = deltas.getDelta(current);
|
||||
if (delta != null && delta.hasSimpleNameOrUserdataChanged()) {
|
||||
return delta;
|
||||
}
|
||||
current = current.eContainer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String findValidName(IUpdatableReference updatable, IScope scope) {
|
||||
Iterable<IEObjectDescription> elements = scope.getElements(updatable.getTargetEObject());
|
||||
String ruleName = linkingHelper.getRuleNameFrom(updatable.getCrossReference());
|
||||
|
@ -66,10 +117,24 @@ public class ReferenceUpdater implements IReferenceUpdater {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected QualifiedName getQualifiedName(IUpdatableReference updatable) {
|
||||
String text = updatable.getReferenceRegion().getText();
|
||||
String ruleName = linkingHelper.getRuleNameFrom(updatable.getCrossReference());
|
||||
try {
|
||||
Object converted = valueConverter.toValue(text, ruleName, null);
|
||||
if (converted != null) {
|
||||
return converter.toQualifiedName(converted.toString());
|
||||
}
|
||||
} catch (ValueConverterException e) {
|
||||
// do nothing
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAffected(Deltas deltas, RelatedResource resource) {
|
||||
for (IReferenceSnapshot ref : resource.outgoingReferences) {
|
||||
Delta delta = deltas.findContainingDelta(ref.getTarget().getObject());
|
||||
Delta delta = deltas.getDelta(ref.getTarget().getObject());
|
||||
if (delta != null) {
|
||||
return true;
|
||||
}
|
||||
|
@ -77,41 +142,38 @@ public class ReferenceUpdater implements IReferenceUpdater {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected boolean needsUpdating(Deltas deltas, EObject source, EObject target) {
|
||||
Delta targetDelta = deltas.findContainingDelta(target);
|
||||
if (targetDelta != null && targetDelta.getObject() == target)
|
||||
protected boolean needsUpdating(IReferenceUpdaterContext context, IUpdatableReference ref) {
|
||||
QualifiedName fqn = getQualifiedName(ref);
|
||||
if (fqn == null) {
|
||||
return false;
|
||||
}
|
||||
EObject target = ref.getTargetEObject();
|
||||
Deltas deltas = context.getEObjectDescriptionDeltas();
|
||||
Delta delta = deltas.getDelta(target);
|
||||
if (delta != null && !containsReferenceText(delta, fqn)) {
|
||||
return true;
|
||||
Delta sourceDelta = deltas.findContainingDelta(source);
|
||||
}
|
||||
Delta targetDelta = findContainingDelta(deltas, target);
|
||||
if (targetDelta != null && targetDelta.getObject() == target) {
|
||||
return true;
|
||||
}
|
||||
Delta sourceDelta = findContainingDelta(deltas, ref.getSourceEObject());
|
||||
return !Objects.equal(sourceDelta, targetDelta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(IReferenceUpdaterContext context) {
|
||||
EObject root = context.getResource().getContents().get(0);
|
||||
Deltas deltas = context.getEObjectDescriptionDeltas();
|
||||
|
||||
TreeIterator<EObject> iterator = EcoreUtil2.eAll(root);
|
||||
while (iterator.hasNext()) {
|
||||
EObject next = iterator.next();
|
||||
for (EReference ref : next.eClass().getEAllReferences()) {
|
||||
if (ref.isContainment()) {
|
||||
continue;
|
||||
}
|
||||
Object value = next.eGet(ref);
|
||||
if (value instanceof List<?>) {
|
||||
List<?> targets = (List<?>) value;
|
||||
for (int i = 0; i < targets.size(); i++) {
|
||||
EObject t = (EObject) targets.get(i);
|
||||
if (needsUpdating(deltas, next, t)) {
|
||||
context.updateReference(next, ref, i);
|
||||
}
|
||||
}
|
||||
} else if (value instanceof EObject) {
|
||||
if (needsUpdating(deltas, next, (EObject) value)) {
|
||||
context.updateReference(next, ref);
|
||||
}
|
||||
IEObjectRegion root = context.getModifyableDocument().getOriginalTextRegionAccess().regionForRootEObject();
|
||||
ISemanticRegion current = root.getPreviousHiddenRegion().getNextSemanticRegion();
|
||||
while (current != null) {
|
||||
EStructuralFeature feature = current.getContainingFeature();
|
||||
if (feature instanceof EReference && !((EReference) feature).isContainment()) {
|
||||
IUpdatableReference updatable = createUpdatableReference(current);
|
||||
if (needsUpdating(context, updatable)) {
|
||||
context.updateReference(updatable);
|
||||
}
|
||||
}
|
||||
current = current.getNextSemanticRegion();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,19 +9,12 @@ package org.eclipse.xtext.ide.serializer.impl;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.EReference;
|
||||
import org.eclipse.xtext.CrossReference;
|
||||
import org.eclipse.xtext.GrammarUtil;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionDiffBuilder;
|
||||
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdaterContext;
|
||||
import org.eclipse.xtext.ide.serializer.hooks.IUpdatableReference;
|
||||
import org.eclipse.xtext.ide.serializer.impl.EObjectDescriptionDeltaProvider.Deltas;
|
||||
import org.eclipse.xtext.resource.XtextResource;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
|
@ -70,33 +63,8 @@ public class ReferenceUpdaterContext implements IReferenceUpdaterContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateReference(EObject owner, EReference reference) {
|
||||
IEObjectRegion objectRegion = diffBuilder.getOriginalTextRegionAccess().regionForEObject(owner);
|
||||
ISemanticRegion region = objectRegion.getRegionFor().feature(reference);
|
||||
EObject target = (EObject) owner.eGet(reference);
|
||||
CrossReference crossref = GrammarUtil.containingCrossReference(region.getGrammarElement());
|
||||
updateReference(owner, reference, -1, region, target, crossref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateReference(EObject owner, EReference reference, int index) {
|
||||
IEObjectRegion objectRegion = diffBuilder.getOriginalTextRegionAccess().regionForEObject(owner);
|
||||
List<ISemanticRegion> regions = objectRegion.getRegionFor().features(reference);
|
||||
ISemanticRegion region = regions.get(index);
|
||||
EObject target = (EObject) ((List<?>) owner.eGet(reference)).get(index);
|
||||
CrossReference crossref = GrammarUtil.containingCrossReference(region.getGrammarElement());
|
||||
updateReference(owner, reference, index, region, target, crossref);
|
||||
}
|
||||
|
||||
private void updateReference(EObject owner, EReference reference, int index, ISemanticRegion region, EObject target,
|
||||
CrossReference crossref) {
|
||||
Preconditions.checkNotNull(owner);
|
||||
Preconditions.checkNotNull(reference);
|
||||
Preconditions.checkArgument(!reference.isContainment());
|
||||
if (region == null || target == null || target.eIsProxy() || crossref == null) {
|
||||
return;
|
||||
}
|
||||
references.add(new UpdatableReference(owner, reference, index, target, crossref, region));
|
||||
public void updateReference(IUpdatableReference updatableReference) {
|
||||
references.add(updatableReference);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import org.eclipse.xtext.CrossReference;
|
|||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.ide.serializer.hooks.IUpdatableReference;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
|
@ -27,6 +29,12 @@ public class UpdatableReference implements IUpdatableReference {
|
|||
public UpdatableReference(EObject owner, EReference reference, int index, EObject target, CrossReference crossref,
|
||||
ISemanticRegion region) {
|
||||
super();
|
||||
Preconditions.checkNotNull(owner);
|
||||
Preconditions.checkNotNull(reference);
|
||||
Preconditions.checkArgument(!reference.isContainment());
|
||||
Preconditions.checkNotNull(target);
|
||||
Preconditions.checkNotNull(crossref);
|
||||
Preconditions.checkNotNull(region);
|
||||
this.owner = owner;
|
||||
this.reference = reference;
|
||||
this.index = index;
|
||||
|
|
|
@ -20,7 +20,7 @@ class FileAwareTestLanguageReferenceUpdater extends ReferenceUpdater {
|
|||
val pkgName = names.getFullyQualifiedName(pkg)
|
||||
val actual = pkg.imports.toMap[element]
|
||||
val targets = context.updatableReferences.map[targetEObject].filter(Element)
|
||||
val expected = targets.filter[names.getFullyQualifiedName(it).skipLast(1) != pkgName].toSet
|
||||
val expected = targets.filter[!names.getFullyQualifiedName(it).startsWith(pkgName)].toSet
|
||||
val toAdd = expected.filter[!actual.containsKey(it)].toSet
|
||||
val toDelete = actual.filter[!expected.contains($0)]
|
||||
if (!toAdd.isEmpty || !toDelete.isEmpty) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.eclipse.xtext.testlanguages.fileAware.ide.refactoring;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -50,8 +49,8 @@ public class FileAwareTestLanguageReferenceUpdater extends ReferenceUpdater {
|
|||
};
|
||||
final Iterable<Element> targets = Iterables.<Element>filter(ListExtensions.<IUpdatableReference, EObject>map(context.getUpdatableReferences(), _function_1), Element.class);
|
||||
final Function1<Element, Boolean> _function_2 = (Element it) -> {
|
||||
QualifiedName _skipLast = this.names.getFullyQualifiedName(it).skipLast(1);
|
||||
return Boolean.valueOf((!Objects.equal(_skipLast, pkgName)));
|
||||
boolean _startsWith = this.names.getFullyQualifiedName(it).startsWith(pkgName);
|
||||
return Boolean.valueOf((!_startsWith));
|
||||
};
|
||||
final Set<Element> expected = IterableExtensions.<Element>toSet(IterableExtensions.<Element>filter(targets, _function_2));
|
||||
final Function1<Element, Boolean> _function_3 = (Element it) -> {
|
||||
|
|
Loading…
Reference in a new issue