diff options
author | Kristóf Marussy <kristof@marussy.com> | 2023-11-06 00:13:48 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2023-11-17 12:41:34 +0100 |
commit | 9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0 (patch) | |
tree | 1a40c4d5ef9fa2482a9e4e65805a02f0b341d815 /subprojects | |
parent | feat(language): validate unique names (diff) | |
download | refinery-9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0.tar.gz refinery-9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0.tar.zst refinery-9b6eeb1e77b05e2c2c0be5dcb285bbef128ab1f0.zip |
feat(language-ide): content assist filtering
Diffstat (limited to 'subprojects')
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 @@ | |||
6 | package tools.refinery.language.ide.contentassist; | 6 | package tools.refinery.language.ide.contentassist; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.emf.ecore.EClass; | ||
9 | import org.eclipse.emf.ecore.EObject; | 10 | import org.eclipse.emf.ecore.EObject; |
11 | import org.eclipse.emf.ecore.EReference; | ||
10 | import org.eclipse.emf.ecore.util.EcoreUtil; | 12 | import org.eclipse.emf.ecore.util.EcoreUtil; |
11 | import org.eclipse.xtext.CrossReference; | 13 | import org.eclipse.xtext.CrossReference; |
12 | import org.eclipse.xtext.GrammarUtil; | 14 | import org.eclipse.xtext.GrammarUtil; |
@@ -16,10 +18,14 @@ import org.eclipse.xtext.naming.QualifiedName; | |||
16 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | 18 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; |
17 | import org.eclipse.xtext.resource.IEObjectDescription; | 19 | import org.eclipse.xtext.resource.IEObjectDescription; |
18 | import org.eclipse.xtext.scoping.IScope; | 20 | import org.eclipse.xtext.scoping.IScope; |
19 | import tools.refinery.language.model.problem.Problem; | 21 | import org.eclipse.xtext.xtext.CurrentTypeFinder; |
22 | import org.jetbrains.annotations.Nullable; | ||
23 | import tools.refinery.language.model.problem.*; | ||
20 | import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; | 24 | import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; |
21 | import tools.refinery.language.validation.ReferenceCounter; | 25 | import tools.refinery.language.utils.BuiltinSymbols; |
26 | import tools.refinery.language.utils.ProblemDesugarer; | ||
22 | import tools.refinery.language.utils.ProblemUtil; | 27 | import tools.refinery.language.utils.ProblemUtil; |
28 | import tools.refinery.language.validation.ReferenceCounter; | ||
23 | 29 | ||
24 | import java.util.ArrayList; | 30 | import java.util.ArrayList; |
25 | import java.util.HashMap; | 31 | import java.util.HashMap; |
@@ -28,8 +34,14 @@ import java.util.Objects; | |||
28 | 34 | ||
29 | public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider { | 35 | public 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 |
26 | public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { | 26 | public 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()); |