aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language-ide
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-12 17:48:47 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-12 17:48:47 +0100
commitfc7e9312d00e60171ed77c477ed91231d3dbfff9 (patch)
treecc185dd088b5fa6e9357aab3c9062a70626d1953 /subprojects/language-ide
parentbuild: refactor java-application conventions (diff)
downloadrefinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.tar.gz
refinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.tar.zst
refinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.zip
build: move modules into subproject directory
Diffstat (limited to 'subprojects/language-ide')
-rw-r--r--subprojects/language-ide/build.gradle18
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java30
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java24
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java44
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java77
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java122
6 files changed, 315 insertions, 0 deletions
diff --git a/subprojects/language-ide/build.gradle b/subprojects/language-ide/build.gradle
new file mode 100644
index 00000000..3786762b
--- /dev/null
+++ b/subprojects/language-ide/build.gradle
@@ -0,0 +1,18 @@
1plugins {
2 id 'refinery-java-library'
3 id 'refinery-xtext-conventions'
4}
5
6dependencies {
7 api project(':refinery-language')
8 api libs.xtext.ide
9 api libs.xtext.xbase.ide
10}
11
12def generateXtextLanguage = project(':refinery-language').tasks.named('generateXtextLanguage')
13
14for (taskName in ['compileJava', 'processResources']) {
15 tasks.named(taskName) {
16 dependsOn generateXtextLanguage
17 }
18}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java
new file mode 100644
index 00000000..51cecf06
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeModule.java
@@ -0,0 +1,30 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.ide;
5
6import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher;
7import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider;
8import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator;
9
10import tools.refinery.language.ide.contentassist.FuzzyMatcher;
11import tools.refinery.language.ide.contentassist.ProblemCrossrefProposalProvider;
12import tools.refinery.language.ide.syntaxcoloring.ProblemSemanticHighlightingCalculator;
13
14/**
15 * Use this class to register ide components.
16 */
17public class ProblemIdeModule extends AbstractProblemIdeModule {
18 public Class<? extends ISemanticHighlightingCalculator> bindISemanticHighlightingCalculator() {
19 return ProblemSemanticHighlightingCalculator.class;
20 }
21
22 @Override
23 public Class<? extends IPrefixMatcher> bindIPrefixMatcher() {
24 return FuzzyMatcher.class;
25 }
26
27 public Class<? extends IdeCrossrefProposalProvider> bindIdeCrossrefProposalProvider() {
28 return ProblemCrossrefProposalProvider.class;
29 }
30}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java
new file mode 100644
index 00000000..5b88d41f
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/ProblemIdeSetup.java
@@ -0,0 +1,24 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.ide;
5
6import org.eclipse.xtext.util.Modules2;
7
8import com.google.inject.Guice;
9import com.google.inject.Injector;
10
11import tools.refinery.language.ProblemRuntimeModule;
12import tools.refinery.language.ProblemStandaloneSetup;
13
14/**
15 * Initialization support for running Xtext languages as language servers.
16 */
17public class ProblemIdeSetup extends ProblemStandaloneSetup {
18
19 @Override
20 public Injector createInjector() {
21 return Guice.createInjector(Modules2.mixin(new ProblemRuntimeModule(), new ProblemIdeModule()));
22 }
23
24}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java
new file mode 100644
index 00000000..fe722ca1
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/FuzzyMatcher.java
@@ -0,0 +1,44 @@
1package tools.refinery.language.ide.contentassist;
2
3import org.eclipse.xtext.ide.editor.contentassist.IPrefixMatcher;
4
5import com.google.inject.Singleton;
6
7/**
8 * Implements the candidate matching algoritm used by CodeMirror 6.
9 *
10 * Using this class ensures that the same candidates will be returned when
11 * filtering content assist proposals on the server as on the client.
12 *
13 * The matching is "fuzzy" (<code>fzf</code>-like), i.e., the prefix characters
14 * may occur anywhere in the name, but must be in the same order as in the
15 * prefix.
16 *
17 * @author Kristóf Marussy
18 */
19@Singleton
20public class FuzzyMatcher implements IPrefixMatcher {
21 @Override
22 public boolean isCandidateMatchingPrefix(String name, String prefix) {
23 var nameIgnoreCase = name.toLowerCase();
24 var prefixIgnoreCase = prefix.toLowerCase();
25 int prefixLength = prefixIgnoreCase.length();
26 if (prefixLength == 0) {
27 return true;
28 }
29 int nameLength = nameIgnoreCase.length();
30 if (prefixLength > nameLength) {
31 return false;
32 }
33 int prefixIndex = 0;
34 for (int nameIndex = 0; nameIndex < nameLength; nameIndex++) {
35 if (nameIgnoreCase.charAt(nameIndex) == prefixIgnoreCase.charAt(prefixIndex)) {
36 prefixIndex++;
37 if (prefixIndex == prefixLength) {
38 return true;
39 }
40 }
41 }
42 return false;
43 }
44}
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
new file mode 100644
index 00000000..f828e836
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
@@ -0,0 +1,77 @@
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 return referenceCounter.countReferences(problem, eObject) >= 2;
54 }
55 return true;
56 }
57
58 protected EObject getCurrentValue(CrossReference crossRef, ContentAssistContext context) {
59 var value = getCurrentValue(crossRef, context.getCurrentModel());
60 if (value != null) {
61 return value;
62 }
63 var currentNodeSemanticObject = NodeModelUtils.findActualSemanticObjectFor(context.getCurrentNode());
64 return getCurrentValue(crossRef, currentNodeSemanticObject);
65 }
66
67 protected EObject getCurrentValue(CrossReference crossRef, EObject context) {
68 if (context == null) {
69 return null;
70 }
71 var eReference = GrammarUtil.getReference(crossRef, context.eClass());
72 if (eReference == null || eReference.isMany()) {
73 return null;
74 }
75 return (EObject) context.eGet(eReference);
76 }
77}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java
new file mode 100644
index 00000000..01ac33f7
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java
@@ -0,0 +1,122 @@
1package tools.refinery.language.ide.syntaxcoloring;
2
3import java.util.List;
4
5import org.eclipse.emf.common.util.EList;
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.emf.ecore.EReference;
8import org.eclipse.xtext.ide.editor.syntaxcoloring.DefaultSemanticHighlightingCalculator;
9import org.eclipse.xtext.ide.editor.syntaxcoloring.IHighlightedPositionAcceptor;
10import org.eclipse.xtext.nodemodel.INode;
11import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
12import org.eclipse.xtext.service.OperationCanceledManager;
13import org.eclipse.xtext.util.CancelIndicator;
14
15import com.google.common.collect.ImmutableList;
16import com.google.inject.Inject;
17
18import tools.refinery.language.model.ProblemUtil;
19import tools.refinery.language.model.problem.ClassDeclaration;
20import tools.refinery.language.model.problem.NamedElement;
21import tools.refinery.language.model.problem.Node;
22import tools.refinery.language.model.problem.PredicateDefinition;
23import tools.refinery.language.model.problem.ProblemPackage;
24import tools.refinery.language.model.problem.ReferenceDeclaration;
25
26public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighlightingCalculator {
27 private static final String BUILTIN_CLASS = "builtin";
28 private static final String ABSTRACT_CLASS = "abstract";
29 private static final String CONTAINMENT_CLASS = "containment";
30 private static final String ERROR_CLASS = "error";
31 private static final String NODE_CLASS = "node";
32 private static final String INDIVIDUAL_NODE_CLASS = "individual";
33 private static final String NEW_NODE_CLASS = "new";
34
35 @Inject
36 private OperationCanceledManager operationCanceledManager;
37
38 @Override
39 protected boolean highlightElement(EObject object, IHighlightedPositionAcceptor acceptor,
40 CancelIndicator cancelIndicator) {
41 highlightName(object, acceptor);
42 highlightCrossReferences(object, acceptor, cancelIndicator);
43 return false;
44 }
45
46 protected void highlightName(EObject object, IHighlightedPositionAcceptor acceptor) {
47 if (!(object instanceof NamedElement)) {
48 return;
49 }
50 String[] highlightClass = getHighlightClass(object, null);
51 if (highlightClass.length > 0) {
52 highlightFeature(acceptor, object, ProblemPackage.Literals.NAMED_ELEMENT__NAME, highlightClass);
53 }
54 }
55
56 protected void highlightCrossReferences(EObject object, IHighlightedPositionAcceptor acceptor,
57 CancelIndicator cancelIndicator) {
58 for (EReference reference : object.eClass().getEAllReferences()) {
59 if (reference.isContainment()) {
60 continue;
61 }
62 operationCanceledManager.checkCanceled(cancelIndicator);
63 if (reference.isMany()) {
64 highlightManyValues(object, reference, acceptor);
65 } else {
66 highlightSingleValue(object, reference, acceptor);
67 }
68 }
69 }
70
71 protected void highlightSingleValue(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) {
72 EObject valueObj = (EObject) object.eGet(reference);
73 String[] highlightClass = getHighlightClass(valueObj, reference);
74 if (highlightClass.length > 0) {
75 highlightFeature(acceptor, object, reference, highlightClass);
76 }
77 }
78
79 protected void highlightManyValues(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) {
80 @SuppressWarnings("unchecked")
81 EList<? extends EObject> values = (EList<? extends EObject>) object.eGet(reference);
82 List<INode> nodes = NodeModelUtils.findNodesForFeature(object, reference);
83 int size = Math.min(values.size(), nodes.size());
84 for (var i = 0; i < size; i++) {
85 EObject valueInList = values.get(i);
86 INode node = nodes.get(i);
87 String[] highlightClass = getHighlightClass(valueInList, reference);
88 if (highlightClass.length > 0) {
89 highlightNode(acceptor, node, highlightClass);
90 }
91 }
92 }
93
94 protected String[] getHighlightClass(EObject eObject, EReference reference) {
95 if (ProblemUtil.isBuiltIn(eObject)) {
96 return new String[] { BUILTIN_CLASS };
97 }
98 ImmutableList.Builder<String> classesBuilder = ImmutableList.builder();
99 if (eObject instanceof ClassDeclaration classDeclaration && classDeclaration.isAbstract()) {
100 classesBuilder.add(ABSTRACT_CLASS);
101 }
102 if (eObject instanceof ReferenceDeclaration referenceDeclaration && referenceDeclaration.isContainment()) {
103 classesBuilder.add(CONTAINMENT_CLASS);
104 }
105 if (eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError()) {
106 classesBuilder.add(ERROR_CLASS);
107 }
108 if (eObject instanceof Node node) {
109 if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE) {
110 classesBuilder.add(NODE_CLASS);
111 }
112 if (ProblemUtil.isIndividualNode(node)) {
113 classesBuilder.add(INDIVIDUAL_NODE_CLASS);
114 }
115 if (ProblemUtil.isNewNode(node)) {
116 classesBuilder.add(NEW_NODE_CLASS);
117 }
118 }
119 List<String> classes = classesBuilder.build();
120 return classes.toArray(new String[0]);
121 }
122}