aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language-ide
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-01-03 02:13:15 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-01-03 13:33:55 +0100
commit2fe65e414ff3194cdddde01bea6818bbab5290e9 (patch)
treea597343718059a2ee8727a296e817f997876f248 /subprojects/language-ide
parentrefactor: matching node names in CLI and web (diff)
downloadrefinery-2fe65e414ff3194cdddde01bea6818bbab5290e9.tar.gz
refinery-2fe65e414ff3194cdddde01bea6818bbab5290e9.tar.zst
refinery-2fe65e414ff3194cdddde01bea6818bbab5290e9.zip
feat(web): color identifiers and nodes
We use a palette-based coloring strategy, where each class and enum gets a color from
Diffstat (limited to 'subprojects/language-ide')
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java11
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/TypeHashProvider.java93
2 files changed, 103 insertions, 1 deletions
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
index ae8c70e0..4c775fc6 100644
--- 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
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -38,6 +38,9 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli
38 @Inject 38 @Inject
39 private ProblemDesugarer desugarer; 39 private ProblemDesugarer desugarer;
40 40
41 @Inject
42 private TypeHashProvider typeHashProvider;
43
41 @Override 44 @Override
42 protected boolean highlightElement(EObject object, IHighlightedPositionAcceptor acceptor, 45 protected boolean highlightElement(EObject object, IHighlightedPositionAcceptor acceptor,
43 CancelIndicator cancelIndicator) { 46 CancelIndicator cancelIndicator) {
@@ -127,6 +130,12 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli
127 classesBuilder.add(NEW_NODE_CLASS); 130 classesBuilder.add(NEW_NODE_CLASS);
128 } 131 }
129 } 132 }
133 if (eObject instanceof Relation relation) {
134 var typeHash = typeHashProvider.getTypeHash(relation);
135 if (typeHash != null) {
136 classesBuilder.add("typeHash-" + typeHash);
137 }
138 }
130 List<String> classes = classesBuilder.build(); 139 List<String> classes = classesBuilder.build();
131 return classes.toArray(new String[0]); 140 return classes.toArray(new String[0]);
132 } 141 }
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/TypeHashProvider.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/TypeHashProvider.java
new file mode 100644
index 00000000..f75ecdb2
--- /dev/null
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/TypeHashProvider.java
@@ -0,0 +1,93 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.ide.syntaxcoloring;
7
8import com.google.common.collect.ImmutableMap;
9import com.google.inject.Inject;
10import com.google.inject.Singleton;
11import org.eclipse.xtext.EcoreUtil2;
12import org.eclipse.xtext.naming.IQualifiedNameConverter;
13import org.eclipse.xtext.naming.IQualifiedNameProvider;
14import org.eclipse.xtext.scoping.IScopeProvider;
15import org.eclipse.xtext.util.IResourceScopeCache;
16import tools.refinery.language.model.problem.*;
17import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
18import tools.refinery.language.utils.ProblemUtil;
19
20import java.util.*;
21
22@Singleton
23public class TypeHashProvider {
24 private static final String CACHE_KEY = "tools.refinery.language.ide.syntaxcoloring.TypeHashProvider";
25 private static final int COLOR_COUNT = 10;
26
27 @Inject
28 private IResourceScopeCache resourceScopeCache;
29
30 @Inject
31 private IScopeProvider scopeProvider;
32
33 @Inject
34 private IQualifiedNameProvider qualifiedNameProvider;
35
36 @Inject
37 private IQualifiedNameConverter qualifiedNameConverter;
38
39 public String getTypeHash(Relation relation) {
40 if (!(relation instanceof ClassDeclaration || relation instanceof EnumDeclaration) ||
41 ProblemUtil.isBuiltIn(relation)) {
42 return null;
43 }
44 var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(relation);
45 if (qualifiedName == null) {
46 return null;
47 }
48 var qualifiedNameString = qualifiedNameConverter.toString(qualifiedName);
49 var problem = EcoreUtil2.getContainerOfType(relation, Problem.class);
50 if (problem == null) {
51 return null;
52 }
53 var cache = resourceScopeCache.get(CACHE_KEY, problem.eResource(), () -> computeHashes(problem));
54 return cache.get(qualifiedNameString);
55 }
56
57 private Map<String, String> computeHashes(Problem problem) {
58 var qualifiedNameStrings = new TreeSet<String>();
59 var scope = scopeProvider.getScope(problem, ProblemPackage.Literals.ASSERTION__RELATION);
60 for (var description : scope.getAllElements()) {
61 if (ProblemResourceDescriptionStrategy.COLOR_RELATION_TRUE.equals(
62 description.getUserData(ProblemResourceDescriptionStrategy.COLOR_RELATION))) {
63 var qualifiedNameString = qualifiedNameConverter.toString(description.getQualifiedName());
64 qualifiedNameStrings.add(qualifiedNameString);
65 }
66 }
67 var stringList = new ArrayList<>(qualifiedNameStrings);
68 int size = stringList.size();
69 if (size == 0) {
70 return Map.of();
71 }
72 // The use of a non-cryptographic random generator is safe here, because we only use it to shuffle the color
73 // IDs in a pseudo-random way. The shuffle depends on the size of the list of identifiers before padding to
74 // make sure that adding a new class randomizes all color IDs.
75 @SuppressWarnings("squid:S2245")
76 var random = new Random(size);
77 int padding = COLOR_COUNT - (size % COLOR_COUNT);
78 for (int i = 0; i < padding; i++) {
79 stringList.add(null);
80 }
81 size += padding;
82 Collections.shuffle(stringList, random);
83 var mapBuilder = ImmutableMap.<String, String>builder();
84 for (int i = 0; i < size; i++) {
85 var key = stringList.get(i);
86 if (key != null) {
87 int colorId = i % COLOR_COUNT;
88 mapBuilder.put(key, Integer.toString(colorId));
89 }
90 }
91 return mapBuilder.build();
92 }
93}