aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-11-06 00:13:48 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-11-17 12:41:34 +0100
commit9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0 (patch)
tree1a40c4d5ef9fa2482a9e4e65805a02f0b341d815
parentfeat(language): validate unique names (diff)
downloadrefinery-9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0.tar.gz
refinery-9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0.tar.zst
refinery-9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0.zip
feat(language-ide): content assist filtering
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java94
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java9
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java13
3 files changed, 109 insertions, 7 deletions
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
index 9bbce54b..8c787dfd 100644
--- a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
@@ -6,7 +6,9 @@
6package tools.refinery.language.ide.contentassist; 6package tools.refinery.language.ide.contentassist;
7 7
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import org.eclipse.emf.ecore.EClass;
9import org.eclipse.emf.ecore.EObject; 10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.emf.ecore.EReference;
10import org.eclipse.emf.ecore.util.EcoreUtil; 12import org.eclipse.emf.ecore.util.EcoreUtil;
11import org.eclipse.xtext.CrossReference; 13import org.eclipse.xtext.CrossReference;
12import org.eclipse.xtext.GrammarUtil; 14import org.eclipse.xtext.GrammarUtil;
@@ -16,10 +18,14 @@ import org.eclipse.xtext.naming.QualifiedName;
16import org.eclipse.xtext.nodemodel.util.NodeModelUtils; 18import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
17import org.eclipse.xtext.resource.IEObjectDescription; 19import org.eclipse.xtext.resource.IEObjectDescription;
18import org.eclipse.xtext.scoping.IScope; 20import org.eclipse.xtext.scoping.IScope;
19import tools.refinery.language.model.problem.Problem; 21import org.eclipse.xtext.xtext.CurrentTypeFinder;
22import org.jetbrains.annotations.Nullable;
23import tools.refinery.language.model.problem.*;
20import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; 24import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
21import tools.refinery.language.validation.ReferenceCounter; 25import tools.refinery.language.utils.BuiltinSymbols;
26import tools.refinery.language.utils.ProblemDesugarer;
22import tools.refinery.language.utils.ProblemUtil; 27import tools.refinery.language.utils.ProblemUtil;
28import tools.refinery.language.validation.ReferenceCounter;
23 29
24import java.util.ArrayList; 30import java.util.ArrayList;
25import java.util.HashMap; 31import java.util.HashMap;
@@ -28,8 +34,14 @@ import java.util.Objects;
28 34
29public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider { 35public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider {
30 @Inject 36 @Inject
37 private CurrentTypeFinder currentTypeFinder;
38
39 @Inject
31 private ReferenceCounter referenceCounter; 40 private ReferenceCounter referenceCounter;
32 41
42 @Inject
43 private ProblemDesugarer desugarer;
44
33 @Override 45 @Override
34 protected Iterable<IEObjectDescription> queryScope(IScope scope, CrossReference crossReference, 46 protected Iterable<IEObjectDescription> queryScope(IScope scope, CrossReference crossReference,
35 ContentAssistContext context) { 47 ContentAssistContext context) {
@@ -49,7 +61,7 @@ public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider
49 for (var candidates : eObjectDescriptionsByName.values()) { 61 for (var candidates : eObjectDescriptionsByName.values()) {
50 if (candidates.size() == 1) { 62 if (candidates.size() == 1) {
51 var candidate = candidates.get(0); 63 var candidate = candidates.get(0);
52 if (shouldBeVisible(candidate)) { 64 if (shouldBeVisible(candidate, crossReference, context)) {
53 eObjectDescriptions.add(candidate); 65 eObjectDescriptions.add(candidate);
54 } 66 }
55 } 67 }
@@ -81,9 +93,81 @@ public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider
81 return true; 93 return true;
82 } 94 }
83 95
84 protected boolean shouldBeVisible(IEObjectDescription candidate) { 96 protected boolean shouldBeVisible(IEObjectDescription candidate, CrossReference crossReference,
97 ContentAssistContext context) {
85 var errorPredicate = candidate.getUserData(ProblemResourceDescriptionStrategy.ERROR_PREDICATE); 98 var errorPredicate = candidate.getUserData(ProblemResourceDescriptionStrategy.ERROR_PREDICATE);
86 return !ProblemResourceDescriptionStrategy.ERROR_PREDICATE_TRUE.equals(errorPredicate); 99 if (ProblemResourceDescriptionStrategy.ERROR_PREDICATE_TRUE.equals(errorPredicate)) {
100 return false;
101 }
102
103 var eReference = getEReference(crossReference);
104 if (eReference == null) {
105 return true;
106 }
107
108 var builtinSymbolsOption = desugarer.getBuiltinSymbols(context.getRootModel());
109 if (builtinSymbolsOption.isEmpty()) {
110 return true;
111 }
112 var builtinSymbols = builtinSymbolsOption.get();
113
114 var candidateEObjectOrProxy = candidate.getEObjectOrProxy();
115
116 if (eReference.equals(ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE) &&
117 context.getCurrentModel() instanceof ReferenceDeclaration referenceDeclaration &&
118 (referenceDeclaration.getKind() == ReferenceKind.CONTAINMENT ||
119 referenceDeclaration.getKind() == ReferenceKind.CONTAINER)) {
120 // Containment or container references must have a class type.
121 // We don't support {@code node} as a container or contained type.
122 return ProblemPackage.Literals.CLASS_DECLARATION.isSuperTypeOf(candidate.getEClass()) &&
123 !builtinSymbols.node().equals(candidateEObjectOrProxy);
124 }
125
126 if (eReference.equals(ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE) ||
127 eReference.equals(ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE) ||
128 eReference.equals(ProblemPackage.Literals.TYPE_SCOPE__TARGET_TYPE)) {
129 if (builtinSymbols.exists().equals(candidateEObjectOrProxy)) {
130 return false;
131 }
132 var arity = candidate.getUserData(ProblemResourceDescriptionStrategy.ARITY);
133 return arity == null || arity.equals("1");
134 }
135
136 if (eReference.equals(ProblemPackage.Literals.CLASS_DECLARATION__SUPER_TYPES)) {
137 return supertypeShouldBeVisible(candidate, context, builtinSymbols, candidateEObjectOrProxy);
138 }
139
140 if (eReference.equals(ProblemPackage.Literals.ASSERTION__RELATION)) {
141 // Currently, we don't support assertions on the {@code contains} relation.
142 return !builtinSymbols.contains().equals(candidateEObjectOrProxy) &&
143 !builtinSymbols.contained().equals(candidateEObjectOrProxy);
144 }
145
146 return true;
147 }
148
149 private boolean supertypeShouldBeVisible(IEObjectDescription candidate, ContentAssistContext context,
150 BuiltinSymbols builtinSymbols, EObject candidateEObjectOrProxy) {
151 if (!ProblemPackage.Literals.CLASS_DECLARATION.isSuperTypeOf(candidate.getEClass()) ||
152 builtinSymbols.node().equals(candidateEObjectOrProxy) ||
153 builtinSymbols.contained().equals(candidateEObjectOrProxy)) {
154 return false;
155 }
156 if (context.getCurrentModel() instanceof ClassDeclaration classDeclaration &&
157 candidateEObjectOrProxy instanceof ClassDeclaration candidateClassDeclaration) {
158 return !classDeclaration.equals(candidateClassDeclaration) &&
159 !classDeclaration.getSuperTypes().contains(candidateClassDeclaration);
160 }
161 return true;
162 }
163
164 @Nullable
165 private EReference getEReference(CrossReference crossReference) {
166 var type = currentTypeFinder.findCurrentTypeAfter(crossReference);
167 if (!(type instanceof EClass eClass)) {
168 return null;
169 }
170 return GrammarUtil.getReference(crossReference, eClass);
87 } 171 }
88 172
89 protected EObject getCurrentValue(CrossReference crossRef, ContentAssistContext context) { 173 protected EObject getCurrentValue(CrossReference crossRef, ContentAssistContext context) {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
index a16f77eb..cac1f265 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
@@ -24,8 +24,9 @@ import java.util.Map;
24 24
25@Singleton 25@Singleton
26public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { 26public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
27 public static final String ERROR_PREDICATE = "tools.refinery.language.resource" + 27 private static final String DATA_PREFIX = "tools.refinery.language.resource.ProblemResourceDescriptionStrategy.";
28 ".ProblemResourceDescriptionStrategy.ERROR_PREDICATE"; 28 public static final String ARITY = DATA_PREFIX + "ARITY";
29 public static final String ERROR_PREDICATE = DATA_PREFIX + "ERROR_PREDICATE";
29 public static final String ERROR_PREDICATE_TRUE = "true"; 30 public static final String ERROR_PREDICATE_TRUE = "true";
30 31
31 @Inject 32 @Inject
@@ -97,6 +98,10 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
97 98
98 protected Map<String, String> getUserData(EObject eObject) { 99 protected Map<String, String> getUserData(EObject eObject) {
99 var builder = ImmutableMap.<String, String>builder(); 100 var builder = ImmutableMap.<String, String>builder();
101 if (eObject instanceof Relation relation) {
102 int arity = ProblemUtil.getArity(relation);
103 builder.put(ARITY, Integer.toString(arity));
104 }
100 if (eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError()) { 105 if (eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError()) {
101 builder.put(ERROR_PREDICATE, ERROR_PREDICATE_TRUE); 106 builder.put(ERROR_PREDICATE, ERROR_PREDICATE_TRUE);
102 } 107 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
index a9efc4bb..b3c93b07 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
@@ -83,6 +83,19 @@ public final class ProblemUtil {
83 return true; 83 return true;
84 } 84 }
85 85
86 public static int getArity(Relation relation) {
87 if (relation instanceof ClassDeclaration || relation instanceof EnumDeclaration) {
88 return 1;
89 }
90 if (relation instanceof ReferenceDeclaration) {
91 return 2;
92 }
93 if (relation instanceof PredicateDefinition predicateDefinition) {
94 return predicateDefinition.getParameters().size();
95 }
96 throw new IllegalArgumentException("Unknown Relation: " + relation);
97 }
98
86 private static URI getLibraryUri(String libraryName) { 99 private static URI getLibraryUri(String libraryName) {
87 return URI.createURI(ProblemUtil.class.getClassLoader() 100 return URI.createURI(ProblemUtil.class.getClassLoader()
88 .getResource("tools/refinery/language/%s.problem".formatted(libraryName)).toString()); 101 .getResource("tools/refinery/language/%s.problem".formatted(libraryName)).toString());