aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java8
-rw-r--r--language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java79
-rw-r--r--language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java19
-rw-r--r--language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java43
4 files changed, 148 insertions, 1 deletions
diff --git a/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java b/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java
index 3502c29f..51cecf06 100644
--- a/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java
+++ b/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java
@@ -4,9 +4,11 @@
4package tools.refinery.language.ide; 4package tools.refinery.language.ide;
5 5
6import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher; 6import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher;
7import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider;
7import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator; 8import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator;
8 9
9import tools.refinery.language.ide.contentassist.FuzzyMatcher; 10import tools.refinery.language.ide.contentassist.FuzzyMatcher;
11import tools.refinery.language.ide.contentassist.ProblemCrossrefProposalProvider;
10import tools.refinery.language.ide.syntaxcoloring.ProblemSemanticHighlightingCalculator; 12import tools.refinery.language.ide.syntaxcoloring.ProblemSemanticHighlightingCalculator;
11 13
12/** 14/**
@@ -16,9 +18,13 @@ public class ProblemIdeModule extends AbstractProblemIdeModule {
16 public Class<? extends ISemanticHighlightingCalculator> bindISemanticHighlightingCalculator() { 18 public Class<? extends ISemanticHighlightingCalculator> bindISemanticHighlightingCalculator() {
17 return ProblemSemanticHighlightingCalculator.class; 19 return ProblemSemanticHighlightingCalculator.class;
18 } 20 }
19 21
20 @Override 22 @Override
21 public Class<? extends IPrefixMatcher> bindIPrefixMatcher() { 23 public Class<? extends IPrefixMatcher> bindIPrefixMatcher() {
22 return FuzzyMatcher.class; 24 return FuzzyMatcher.class;
23 } 25 }
26
27 public Class<? extends IdeCrossrefProposalProvider> bindIdeCrossrefProposalProvider() {
28 return ProblemCrossrefProposalProvider.class;
29 }
24} 30}
diff --git a/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java b/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
new file mode 100644
index 00000000..416535ce
--- /dev/null
+++ b/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
@@ -0,0 +1,79 @@
1package tools.refinery.language.ide.contentassist;
2
3import java.util.Objects;
4
5import org.eclipse.emf.ecore.EObject;
6import org.eclipse.emf.ecore.util.EcoreUtil;
7import org.eclipse.xtext.CrossReference;
8import org.eclipse.xtext.GrammarUtil;
9import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext;
10import org.eclipse.xtext.ide.editor.contentassist.ContentAssistEntry;
11import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider;
12import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
13import org.eclipse.xtext.resource.IEObjectDescription;
14
15import com.google.inject.Inject;
16
17import tools.refinery.language.model.ProblemUtil;
18import tools.refinery.language.model.problem.Problem;
19import tools.refinery.language.resource.ReferenceCounter;
20
21public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider {
22 @Inject
23 private ReferenceCounter referenceCounter;
24
25 @Override
26 protected ContentAssistEntry createProposal(IEObjectDescription candidate, CrossReference crossRef,
27 ContentAssistContext context) {
28 if (!shouldCreateProposal(candidate, crossRef, context)) {
29 return null;
30 }
31 return super.createProposal(candidate, crossRef, context);
32 }
33
34 protected boolean shouldCreateProposal(IEObjectDescription candidate, CrossReference crossRef,
35 ContentAssistContext context) {
36 var rootModel = context.getRootModel();
37 var eObjectOrProxy = candidate.getEObjectOrProxy();
38 if (!Objects.equals(rootModel.eResource(), eObjectOrProxy.eResource())) {
39 return true;
40 }
41 var currentValue = getCurrentValue(crossRef, context);
42 if (currentValue == null) {
43 return true;
44 }
45 var eObject = EcoreUtil.resolve(eObjectOrProxy, rootModel);
46 if (!Objects.equals(currentValue, eObject)) {
47 return true;
48 }
49 if (!ProblemUtil.isImplicit(eObject)) {
50 return true;
51 }
52 if (rootModel instanceof Problem problem) {
53 var referenceCounts = referenceCounter.getReferenceCounts(problem);
54 var count = referenceCounts.get(eObject);
55 return count != null && count >= 2;
56 }
57 return true;
58 }
59
60 protected EObject getCurrentValue(CrossReference crossRef, ContentAssistContext context) {
61 var value = getCurrentValue(crossRef, context.getCurrentModel());
62 if (value != null) {
63 return value;
64 }
65 var currentNodeSemanticObject = NodeModelUtils.findActualSemanticObjectFor(context.getCurrentNode());
66 return getCurrentValue(crossRef, currentNodeSemanticObject);
67 }
68
69 protected EObject getCurrentValue(CrossReference crossRef, EObject context) {
70 if (context == null) {
71 return null;
72 }
73 var eReference = GrammarUtil.getReference(crossRef, context.eClass());
74 if (eReference == null || eReference.isMany()) {
75 return null;
76 }
77 return (EObject) context.eGet(eReference);
78 }
79}
diff --git a/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java b/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java
index b6b199f8..5f8641bf 100644
--- a/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java
+++ b/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java
@@ -12,6 +12,7 @@ import org.eclipse.emf.ecore.EObject;
12import org.eclipse.emf.ecore.resource.Resource; 12import org.eclipse.emf.ecore.resource.Resource;
13 13
14import tools.refinery.language.model.problem.ClassDeclaration; 14import tools.refinery.language.model.problem.ClassDeclaration;
15import tools.refinery.language.model.problem.ImplicitVariable;
15import tools.refinery.language.model.problem.Node; 16import tools.refinery.language.model.problem.Node;
16import tools.refinery.language.model.problem.Problem; 17import tools.refinery.language.model.problem.Problem;
17import tools.refinery.language.model.problem.ProblemPackage; 18import tools.refinery.language.model.problem.ProblemPackage;
@@ -33,6 +34,24 @@ public final class ProblemUtil {
33 public static boolean isSingletonVariable(Variable variable) { 34 public static boolean isSingletonVariable(Variable variable) {
34 return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE; 35 return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE;
35 } 36 }
37
38 public static boolean isImplicitVariable(Variable variable) {
39 return variable instanceof ImplicitVariable;
40 }
41
42 public static boolean isImplicitNode(Node node) {
43 return node.eContainingFeature() == ProblemPackage.Literals.PROBLEM__NODES;
44 }
45
46 public static boolean isImplicit(EObject eObject) {
47 if (eObject instanceof Node node) {
48 return isImplicitNode(node);
49 } else if (eObject instanceof Variable variable) {
50 return isImplicitVariable(variable);
51 } else {
52 return false;
53 }
54 }
36 55
37 public static boolean isUniqueNode(Node node) { 56 public static boolean isUniqueNode(Node node) {
38 var containingFeature = node.eContainingFeature(); 57 var containingFeature = node.eContainingFeature();
diff --git a/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java b/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
new file mode 100644
index 00000000..56186bc9
--- /dev/null
+++ b/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
@@ -0,0 +1,43 @@
1package tools.refinery.language.resource;
2
3import java.util.HashMap;
4import java.util.Map;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.xtext.util.IResourceScopeCache;
8
9import com.google.inject.Inject;
10import com.google.inject.Singleton;
11
12import tools.refinery.language.model.problem.Problem;
13
14@Singleton
15public class ReferenceCounter {
16 @Inject
17 private IResourceScopeCache cache;
18
19 public Map<EObject, Integer> getReferenceCounts(Problem problem) {
20 var resource = problem.eResource();
21 if (resource == null) {
22 return doGetReferenceCounts(problem);
23 }
24 return cache.get(problem, resource, () -> doGetReferenceCounts(problem));
25 }
26
27 protected Map<EObject, Integer> doGetReferenceCounts(Problem problem) {
28 var map = new HashMap<EObject, Integer>();
29 countCrossReferences(problem, map);
30 var iterator = problem.eAllContents();
31 while (iterator.hasNext()) {
32 var eObject = iterator.next();
33 countCrossReferences(eObject, map);
34 }
35 return map;
36 }
37
38 protected void countCrossReferences(EObject eObject, Map<EObject, Integer> map) {
39 for (var referencedObject : eObject.eCrossReferences()) {
40 map.compute(referencedObject, (key, currentValue) -> currentValue == null ? 1 : currentValue + 1);
41 }
42 }
43}